Skip to content

Commit

Permalink
Merge pull request #93 from MTES-MCT/control-infraction-fix
Browse files Browse the repository at this point in the history
Frontend - improve error case on Mission page
  • Loading branch information
lwih authored Mar 1, 2024
2 parents c1bfae4 + e9ba5c1 commit 0818b3d
Show file tree
Hide file tree
Showing 3 changed files with 188 additions and 128 deletions.
156 changes: 87 additions & 69 deletions frontend/src/pam/mission/mission-page.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,100 +8,118 @@ import { useNavigate } from "react-router-dom";

// Mock the useApolloClient hook
const mockApolloClient = {
resetStore: vi.fn(),
cache: {
evict: vi.fn(),
},
resetStore: vi.fn(),
cache: {
evict: vi.fn(),
},
};

vi.mock('@apollo/client', async (importOriginal) => {
const actual = await importOriginal();
return {
...actual,
useApolloClient: vi.fn(() => mockApolloClient),
};
const actual = await importOriginal();
return {
...actual,
useApolloClient: vi.fn(() => mockApolloClient),
};
});

vi.mock('react-router-dom', async (importOriginal) => {
const actual = await importOriginal();
return {
...actual,
useNavigate: vi.fn(),
};
const actual = await importOriginal();
return {
...actual,
useNavigate: vi.fn(),
};
});

vi.mock("./general-info/use-mission-excerpt", async (importOriginal) => {
const actual = await importOriginal();
return {
...actual,
default: vi.fn()
};
const actual = await importOriginal();
return {
...actual,
default: vi.fn()
};
});

const mock = {
id: 1,
startDateTimeUtc: '2024-01-01T00:00:00Z',
endDateTimeUtc: '2024-01-12T01:00:00Z',
actions: []
id: 1,
startDateTimeUtc: '2024-01-01T00:00:00Z',
endDateTimeUtc: '2024-01-12T01:00:00Z',
actions: []
};

const mockedQueryResult = (mission?: Mission, loading: boolean = false, error: any = undefined) => ({
data: mission,
loading,
error,
data: mission,
loading,
error,
});

describe('MissionPage', () => {
describe('testing rendering', () => {
test('should render loading', () => {
;(useMissionExcerpt as any).mockReturnValue(mockedQueryResult(undefined, true))
render(<MissionPage/>);
expect(screen.getByText('Chargement...')).toBeInTheDocument();
});
describe('testing rendering', () => {
test('should render loading', () => {
;(useMissionExcerpt as any).mockReturnValue(mockedQueryResult(undefined, true))
render(<MissionPage/>);
expect(screen.getByText('Chargement...')).toBeInTheDocument();
});

test('should render error', () => {
;(useMissionExcerpt as any).mockReturnValue(mockedQueryResult(undefined, false, new GraphQLError("Error!")))
render(<MissionPage/>);
expect(screen.getByText('error...')).toBeInTheDocument();
});
test('should render error', () => {
;(useMissionExcerpt as any).mockReturnValue(mockedQueryResult(undefined, false, new GraphQLError("Error!")))
render(<MissionPage/>);
expect(screen.getByText('Une erreur est survenue')).toBeInTheDocument();
});

test('should render content', () => {
;(useMissionExcerpt as any).mockReturnValue(mockedQueryResult(mock))
render(<MissionPage/>);
expect(screen.getByText('Mission #2024-01-01')).toBeInTheDocument();
});
test('should render content', () => {
;(useMissionExcerpt as any).mockReturnValue(mockedQueryResult(mock))
render(<MissionPage/>);
expect(screen.getByText('Mission #2024-01-01')).toBeInTheDocument();
});
});

describe('testing the actions', () => {
test('should reset store and navigate when clicking on the X', async () => {
const mockNavigate = vi.fn();
(useNavigate as jest.Mock).mockReturnValue(mockNavigate);
;(useMissionExcerpt as any).mockReturnValue(mockedQueryResult(mock))
render(<MissionPage/>);
describe('testing the actions', () => {
test('should reset store and navigate when clicking on the X', async () => {
const mockNavigate = vi.fn();
(useNavigate as jest.Mock).mockReturnValue(mockNavigate);
;(useMissionExcerpt as any).mockReturnValue(mockedQueryResult(mock))
render(<MissionPage/>);

const button = screen.getByRole('quit-mission-cross');
fireEvent.click(button);
const button = screen.getByRole('quit-mission-cross');
fireEvent.click(button);

expect(mockApolloClient.resetStore).toHaveBeenCalled();
await waitFor(() => {
expect(mockApolloClient.cache.evict).toHaveBeenCalled();
expect(mockNavigate).toHaveBeenCalledWith('..');
});
});
test('should reset store and navigate when clicking on the quit button', async () => {
const mockNavigate = vi.fn();
(useNavigate as jest.Mock).mockReturnValue(mockNavigate);
;(useMissionExcerpt as any).mockReturnValue(mockedQueryResult(mock))
render(<MissionPage/>);
expect(mockApolloClient.resetStore).toHaveBeenCalled();
await waitFor(() => {
expect(mockApolloClient.cache.evict).toHaveBeenCalled();
expect(mockNavigate).toHaveBeenCalledWith('..');
});
});
test('should reset store and navigate when clicking on the quit button', async () => {
const mockNavigate = vi.fn();
(useNavigate as jest.Mock).mockReturnValue(mockNavigate);
;(useMissionExcerpt as any).mockReturnValue(mockedQueryResult(mock))
render(<MissionPage/>);

const button = screen.getByText('Quitter le rapport');
fireEvent.click(button);
const button = screen.getByText('Quitter le rapport');
fireEvent.click(button);

expect(mockApolloClient.resetStore).toHaveBeenCalled();
await waitFor(() => {
expect(mockApolloClient.cache.evict).toHaveBeenCalled();
expect(mockNavigate).toHaveBeenCalledWith('..');
});
});
expect(mockApolloClient.resetStore).toHaveBeenCalled();
await waitFor(() => {
expect(mockApolloClient.cache.evict).toHaveBeenCalled();
expect(mockNavigate).toHaveBeenCalledWith('..');
});
});
});

describe('The error path', () => {
test('should show the error message', () => {
const errorMessage = 'errorMessage'
;(useMissionExcerpt as any).mockReturnValue(mockedQueryResult(undefined, false, new GraphQLError(errorMessage)))
render(<MissionPage/>);
expect(screen.getByText(errorMessage)).toBeInTheDocument();
})
test('should redirect to the missions page when clicking the button', () => {
const mockNavigate = vi.fn();
(useNavigate as jest.Mock).mockReturnValue(mockNavigate);
;(useMissionExcerpt as any).mockReturnValue(mockedQueryResult(undefined, false, new GraphQLError('errorMessage')))
render(<MissionPage/>);
const button = screen.getByText('Retourner à l\'accueil')
fireEvent.click(button)
expect(mockNavigate).toHaveBeenCalledWith('/pam/missions');
})
})
});
107 changes: 71 additions & 36 deletions frontend/src/pam/mission/mission-page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,53 +6,88 @@ import MissionPageFooter from './page-footer'
import { useApolloClient } from '@apollo/client'
import useMissionExcerpt from "./general-info/use-mission-excerpt";
import { formatMissionName } from "./utils";
import { Stack } from "rsuite";
import Text from "../../ui/text.tsx";
import { Accent, Button, Size } from "@mtes-mct/monitor-ui";
import { getPath, PAM_HOME_PATH } from "../../router/router.tsx";

const MissionPage: React.FC = () => {

const navigate = useNavigate()
let {missionId} = useParams()
const apolloClient = useApolloClient()
const navigate = useNavigate()
let {missionId} = useParams()
const apolloClient = useApolloClient()

const {loading, error, data: mission} = useMissionExcerpt(missionId)
const {loading, error, data: mission} = useMissionExcerpt(missionId)


const exitMission = async () => {
// TODO centralise the following into a class - also used in use-auth()
// reset apollo store
await apolloClient.resetStore()
// flush apollo persist cache
apolloClient.cache.evict({})
const exitMission = async () => {
// TODO centralise the following into a class - also used in use-auth()
// reset apollo store
await apolloClient.resetStore()
// flush apollo persist cache
apolloClient.cache.evict({})

navigate('..')
}
navigate('..')
}

if (loading) {
return <div>Chargement...</div>
}

if (error) {
return <div>error...</div>
}
if (loading) {
return <div>Chargement...</div>
}

if (error) {
return (
<div
style={{
margin: 0,
display: 'flex',
flexDirection: 'column',
minHeight: '100vh',
maxHeight: '100vh'
}}
>
<MissionPageHeader missionName={formatMissionName(mission?.startDateTimeUtc)}
missionSource={mission?.missionSource}
onClickClose={exitMission}/>

<MissionContent mission={mission}/>

<MissionPageFooter missionName={`Mission #${missionId}`} exitMission={exitMission}/>
</div>
<div
style={{
margin: 0,
marginTop: '10rem',
display: 'flex',
flexDirection: 'column',
minHeight: '100vh',
maxHeight: '100vh'
}}
>
<Stack justifyContent={"center"} direction={"column"} spacing={"2rem"}>
<Stack.Item style={{maxWidth: '33%'}}>
<Text as={"h2"}>
Une erreur est survenue
</Text>
<Text as={"h3"}>
{error?.message}
</Text>
</Stack.Item>
<Stack.Item>
<Button
accent={Accent.PRIMARY}
size={Size.LARGE}
onClick={() => navigate(getPath(PAM_HOME_PATH))}
>
Retourner à l'accueil
</Button>
</Stack.Item>
</Stack>
</div>
)
}

return (
<div
style={{
margin: 0,
display: 'flex',
flexDirection: 'column',
minHeight: '100vh',
maxHeight: '100vh'
}}
>
<MissionPageHeader missionName={formatMissionName(mission?.startDateTimeUtc)}
missionSource={mission?.missionSource}
onClickClose={exitMission}/>

<MissionContent mission={mission}/>

<MissionPageFooter missionName={`Mission #${missionId}`} exitMission={exitMission}/>
</div>
)
}

export default MissionPage
53 changes: 30 additions & 23 deletions frontend/src/router/router.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,30 +7,37 @@ import MissionsPage from '../pam/missions/missions-page'
import MissionPage from '../pam/mission/mission-page'
import * as Sentry from "@sentry/react"

export const getPath = (path: string) => `/${path}`

export const LOGIN_PATH = 'login'
export const SIGNUP_PATH = 'signup'
export const PAM_HOME_PATH = 'pam/missions'


const sentryCreateBrowserRouter =
Sentry.wrapCreateBrowserRouter(createBrowserRouter);
Sentry.wrapCreateBrowserRouter(createBrowserRouter);

export const router = sentryCreateBrowserRouter([
{
path: '/',
element: <Home/>,
errorElement: <ErrorPage/>
},
{
path: 'login',
element: <Login/>
},
{
path: 'signup',
element: <SignUp/>
},
{
path: 'pam/missions',
element: <MissionsPage/>,
errorElement: <ErrorPage/>
},
{
path: 'pam/missions/:missionId/:actionId?',
element: <MissionPage/>
}
{
path: '/',
element: <Home/>,
errorElement: <ErrorPage/>
},
{
path: LOGIN_PATH,
element: <Login/>
},
{
path: SIGNUP_PATH,
element: <SignUp/>
},
{
path: PAM_HOME_PATH,
element: <MissionsPage/>,
errorElement: <ErrorPage/>
},
{
path: 'pam/missions/:missionId/:actionId?',
element: <MissionPage/>
}
])

0 comments on commit 0818b3d

Please sign in to comment.