From e9ba5c15170c60c5d30f35a7c7a37210d4bb04c6 Mon Sep 17 00:00:00 2001 From: lwih Date: Fri, 1 Mar 2024 15:11:30 +0100 Subject: [PATCH] Frontend - improve error case on Mission page --- .../src/pam/mission/mission-page.test.tsx | 156 ++++++++++-------- frontend/src/pam/mission/mission-page.tsx | 107 ++++++++---- frontend/src/router/router.tsx | 53 +++--- 3 files changed, 188 insertions(+), 128 deletions(-) diff --git a/frontend/src/pam/mission/mission-page.test.tsx b/frontend/src/pam/mission/mission-page.test.tsx index bcb6ab91c..a96162ede 100644 --- a/frontend/src/pam/mission/mission-page.test.tsx +++ b/frontend/src/pam/mission/mission-page.test.tsx @@ -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(); - expect(screen.getByText('Chargement...')).toBeInTheDocument(); - }); + describe('testing rendering', () => { + test('should render loading', () => { + ;(useMissionExcerpt as any).mockReturnValue(mockedQueryResult(undefined, true)) + render(); + expect(screen.getByText('Chargement...')).toBeInTheDocument(); + }); - test('should render error', () => { - ;(useMissionExcerpt as any).mockReturnValue(mockedQueryResult(undefined, false, new GraphQLError("Error!"))) - render(); - expect(screen.getByText('error...')).toBeInTheDocument(); - }); + test('should render error', () => { + ;(useMissionExcerpt as any).mockReturnValue(mockedQueryResult(undefined, false, new GraphQLError("Error!"))) + render(); + expect(screen.getByText('Une erreur est survenue')).toBeInTheDocument(); + }); - test('should render content', () => { - ;(useMissionExcerpt as any).mockReturnValue(mockedQueryResult(mock)) - render(); - expect(screen.getByText('Mission #2024-01-01')).toBeInTheDocument(); - }); + test('should render content', () => { + ;(useMissionExcerpt as any).mockReturnValue(mockedQueryResult(mock)) + render(); + 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(); + 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(); - 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(); + 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(); - 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(); + 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(); + const button = screen.getByText('Retourner à l\'accueil') + fireEvent.click(button) + expect(mockNavigate).toHaveBeenCalledWith('/pam/missions'); + }) + }) }); diff --git a/frontend/src/pam/mission/mission-page.tsx b/frontend/src/pam/mission/mission-page.tsx index 436b1574b..ce45e4c1c 100644 --- a/frontend/src/pam/mission/mission-page.tsx +++ b/frontend/src/pam/mission/mission-page.tsx @@ -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
Chargement...
- } - - if (error) { - return
error...
- } + if (loading) { + return
Chargement...
+ } + if (error) { return ( -
- - - - - -
+
+ + + + Une erreur est survenue + + + {error?.message} + + + + + + +
) + } + + return ( +
+ + + + + +
+ ) } export default MissionPage diff --git a/frontend/src/router/router.tsx b/frontend/src/router/router.tsx index 5dc84c8bb..4083f58f4 100644 --- a/frontend/src/router/router.tsx +++ b/frontend/src/router/router.tsx @@ -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: , - errorElement: - }, - { - path: 'login', - element: - }, - { - path: 'signup', - element: - }, - { - path: 'pam/missions', - element: , - errorElement: - }, - { - path: 'pam/missions/:missionId/:actionId?', - element: - } + { + path: '/', + element: , + errorElement: + }, + { + path: LOGIN_PATH, + element: + }, + { + path: SIGNUP_PATH, + element: + }, + { + path: PAM_HOME_PATH, + element: , + errorElement: + }, + { + path: 'pam/missions/:missionId/:actionId?', + element: + } ])