From a608721fee70b7aa6ba55b5630219dbe087ace3f Mon Sep 17 00:00:00 2001 From: adamwhitingnhs Date: Thu, 12 Feb 2026 11:31:24 +0000 Subject: [PATCH] [PRMP-1425] Update upload server error handling --- Makefile | 12 + app/.eslintrc | 3 +- .../download_lloyd_george_workflow.cy.js | 6 +- .../DocumentUploadCompleteStage.scss | 6 + .../DocumentUploadCompleteStage.tsx | 117 ++++--- app/src/helpers/utils/documentUpload.test.ts | 322 ++++++++++++++++++ app/src/helpers/utils/documentUpload.ts | 59 ++++ app/src/helpers/utils/errorCodes.ts | 2 + .../documentUploadPage/DocumentUploadPage.tsx | 54 +-- .../serverErrorPage/ServerErrorPage.test.tsx | 18 +- .../pages/serverErrorPage/ServerErrorPage.tsx | 19 +- 11 files changed, 499 insertions(+), 119 deletions(-) diff --git a/Makefile b/Makefile index f9739a9c22..a283afa55f 100644 --- a/Makefile +++ b/Makefile @@ -305,13 +305,25 @@ docker-down: docker-compose -f ./app/docker-compose.yml down cypress-open: +ifeq ($(CONTAINER), true) xvfb-run -- npm --prefix ./app run cypress +else + npm --prefix ./app run cypress +endif cypress-run: +ifeq ($(CONTAINER), true) xvfb-run -- npm --prefix ./app run cypress-run +else + npm --prefix ./app run cypress-run +endif cypress-report: +ifeq ($(CONTAINER), true) xvfb-run -- npm --prefix ./app run cypress-report +else + npm --prefix ./app run cypress-report +endif install-cypress: npm install --save-dev cypress diff --git a/app/.eslintrc b/app/.eslintrc index ce1616713e..0853cd6b61 100644 --- a/app/.eslintrc +++ b/app/.eslintrc @@ -84,7 +84,8 @@ "import/no-extraneous-dependencies": [ "error", { "devDependencies": true } - ] + ], + "@typescript-eslint/explicit-function-return-type": "off" } }, { diff --git a/app/cypress/e2e/0-ndr-core-tests/gp_user_workflows/download_lloyd_george_workflow.cy.js b/app/cypress/e2e/0-ndr-core-tests/gp_user_workflows/download_lloyd_george_workflow.cy.js index f36f814113..87ad495249 100644 --- a/app/cypress/e2e/0-ndr-core-tests/gp_user_workflows/download_lloyd_george_workflow.cy.js +++ b/app/cypress/e2e/0-ndr-core-tests/gp_user_workflows/download_lloyd_george_workflow.cy.js @@ -267,7 +267,7 @@ describe('GP Workflow: View Lloyd George record', () => { statusCode: 200, body: { references: singleTestFile, - nextPageToken: 'abc' + nextPageToken: 'abc', }, }).as('searchDocumentReferences'); @@ -360,7 +360,7 @@ describe('GP Workflow: View Lloyd George record', () => { statusCode: 200, body: { jobStatus: 'Pending' }, }); - if (pendingCounts >= 3) { + if (pendingCounts >= 10) { req.alias = 'documentManifestThirdTimePending'; } }); @@ -372,7 +372,7 @@ describe('GP Workflow: View Lloyd George record', () => { cy.getByTestId('toggle-selection-btn').click(); cy.getByTestId('download-selected-files-btn').click(); - cy.wait('@documentManifestThirdTimePending'); + cy.wait('@documentManifestThirdTimePending', { timeout: 20000 }); cy.title().should('have.string', 'Service error'); cy.url().should('have.string', '/server-error?encodedError='); diff --git a/app/src/components/blocks/_documentUpload/documentUploadCompleteStage/DocumentUploadCompleteStage.scss b/app/src/components/blocks/_documentUpload/documentUploadCompleteStage/DocumentUploadCompleteStage.scss index e414d2f769..f425f340ec 100644 --- a/app/src/components/blocks/_documentUpload/documentUploadCompleteStage/DocumentUploadCompleteStage.scss +++ b/app/src/components/blocks/_documentUpload/documentUploadCompleteStage/DocumentUploadCompleteStage.scss @@ -30,4 +30,10 @@ transform: rotate(90deg); } } + + #failed-files-list { + p:not(:last-child) { + margin: 0; + } + } } diff --git a/app/src/components/blocks/_documentUpload/documentUploadCompleteStage/DocumentUploadCompleteStage.tsx b/app/src/components/blocks/_documentUpload/documentUploadCompleteStage/DocumentUploadCompleteStage.tsx index 994d6a7afe..bbf1275988 100644 --- a/app/src/components/blocks/_documentUpload/documentUploadCompleteStage/DocumentUploadCompleteStage.tsx +++ b/app/src/components/blocks/_documentUpload/documentUploadCompleteStage/DocumentUploadCompleteStage.tsx @@ -62,57 +62,76 @@ const DocumentUploadCompleteStage = ({ documents, documentConfig }: Props): Reac {failedDocuments.length > 0 ? ( -
-

Some of your files failed to upload

- - {showFiles && ( -
- {failedDocuments.map((doc) => ( -
- {doc.file.name} -
-
- ))} -
- )} -
-

What you need to do

+ <>

- You must note which files uploaded successfully, then return to the - patient's record to upload any files that failed. + We uploaded {documents.length - failedDocuments.length} out of{' '} + {documents.length} files. +
+ {failedDocuments.length} files could not be uploaded.

- -
+

There may be a problem with your files.

+ +
+

Files that could not be uploaded

+ + + {showFiles && ( +
+ {failedDocuments.map((doc) => ( +
+

{doc.file.name}

+
+ ))} +
+ )} + +

What you need to do

+

You must note which files did not upload.

+ +

+ Remove any passwords from files and check that all files open correctly. + Then return to the patient's record to upload them again. +

+ +

Get help

+

+ Contact your local IT support desk to resolve the problems with these + files. +

+ + +
+ ) : ( <>

What happens next

diff --git a/app/src/helpers/utils/documentUpload.test.ts b/app/src/helpers/utils/documentUpload.test.ts index fb4e11fdc5..edf9ba662f 100644 --- a/app/src/helpers/utils/documentUpload.test.ts +++ b/app/src/helpers/utils/documentUpload.test.ts @@ -5,6 +5,7 @@ import { goToPreviousDocType, handleDocReviewStatusResult, handleDocStatusResult, + handleDocumentStatusUpdates, reduceDocumentsForUpload, startIntervalTimer, } from './documentUpload'; @@ -22,6 +23,12 @@ import { uploadDocumentForReview } from '../requests/documentReview'; import * as isLocal from './isLocal'; import { DocumentReviewStatus } from '../../types/blocks/documentReview'; import { buildDocumentConfig } from '../test/testBuilders'; +import { routes, routeChildren } from '../../types/generic/routes'; +import { + MAX_POLLING_TIME, + UPDATE_DOCUMENT_STATE_FREQUENCY_MILLISECONDS, +} from '../constants/network'; +import * as urlManipulations from './urlManipulations'; vi.mock('../requests/uploadDocuments'); vi.mock('../requests/documentReview'); @@ -1031,4 +1038,319 @@ describe('documentUpload', () => { expect(mockSetShowSkipLink).not.toHaveBeenCalled(); }); }); + + describe('handleDocumentStatusUpdates', () => { + const mockNavigate = Object.assign(vi.fn(), { + withParams: vi.fn(), + }); + + let mockInterval: { current: number }; + let mockVirusRef: { current: boolean }; + let mockCompleteRef: { current: boolean }; + + beforeEach(() => { + vi.clearAllMocks(); + vi.spyOn(globalThis, 'clearInterval'); + vi.spyOn(window, 'clearInterval'); + vi.spyOn(urlManipulations, 'getJourney').mockReturnValue('new'); + mockInterval = { current: 1 }; + mockVirusRef = { current: false }; + mockCompleteRef = { current: false }; + }); + + it('should navigate to SERVER_ERROR when journey param is update but journey type does not match', () => { + vi.spyOn(urlManipulations, 'getJourney').mockReturnValue('update'); + + const intervalTimer = 123; + + handleDocumentStatusUpdates( + 'new', + mockNavigate, + intervalTimer, + mockInterval, + mockDocuments, + mockVirusRef, + mockCompleteRef, + ); + + expect(globalThis.clearInterval).toHaveBeenCalledWith(intervalTimer); + expect(mockNavigate).toHaveBeenCalledWith(routes.SERVER_ERROR); + }); + + it('should navigate to SERVER_ERROR when polling time exceeds MAX_POLLING_TIME', () => { + mockInterval.current = + Math.ceil(MAX_POLLING_TIME / UPDATE_DOCUMENT_STATE_FREQUENCY_MILLISECONDS) + 1; + + const intervalTimer = 456; + + handleDocumentStatusUpdates( + 'new', + mockNavigate, + intervalTimer, + mockInterval, + mockDocuments, + mockVirusRef, + mockCompleteRef, + ); + + expect(window.clearInterval).toHaveBeenCalledWith(intervalTimer); + expect(mockNavigate).toHaveBeenCalledWith(routes.SERVER_ERROR); + }); + + it('should return early when documents array is empty', () => { + handleDocumentStatusUpdates( + 'new', + mockNavigate, + 123, + mockInterval, + [], + mockVirusRef, + mockCompleteRef, + ); + + expect(mockNavigate).not.toHaveBeenCalled(); + expect(mockNavigate.withParams).not.toHaveBeenCalled(); + }); + + it('should navigate to DOCUMENT_UPLOAD_INFECTED when a document has virus', () => { + const documentsWithVirus: UploadDocument[] = [ + { + ...mockDocuments[0], + state: DOCUMENT_UPLOAD_STATE.INFECTED, + }, + ]; + + const intervalTimer = 789; + + handleDocumentStatusUpdates( + 'new', + mockNavigate, + intervalTimer, + mockInterval, + documentsWithVirus, + mockVirusRef, + mockCompleteRef, + ); + + expect(mockVirusRef.current).toBe(true); + expect(window.clearInterval).toHaveBeenCalledWith(intervalTimer); + expect(mockNavigate).toHaveBeenCalledWith(routeChildren.DOCUMENT_UPLOAD_INFECTED); + }); + + it('should not navigate to infected page again if virusReference is already true', () => { + const documentsWithVirus: UploadDocument[] = [ + { + ...mockDocuments[0], + state: DOCUMENT_UPLOAD_STATE.INFECTED, + }, + ]; + + mockVirusRef.current = true; + + handleDocumentStatusUpdates( + 'new', + mockNavigate, + 123, + mockInterval, + documentsWithVirus, + mockVirusRef, + mockCompleteRef, + ); + + expect(mockNavigate).not.toHaveBeenCalledWith(routeChildren.DOCUMENT_UPLOAD_INFECTED); + }); + + it('should navigate to SERVER_ERROR with error params when all documents have failed', () => { + const allFailedDocs: UploadDocument[] = [ + { + ...mockDocuments[0], + state: DOCUMENT_UPLOAD_STATE.ERROR, + errorCode: 'UC_4006', + }, + { + ...mockDocuments[1], + state: DOCUMENT_UPLOAD_STATE.ERROR, + errorCode: 'UC_4007', + }, + ]; + + handleDocumentStatusUpdates( + 'new', + mockNavigate, + 123, + mockInterval, + allFailedDocs, + mockVirusRef, + mockCompleteRef, + ); + + expect(mockNavigate).toHaveBeenCalledWith(expect.stringContaining(routes.SERVER_ERROR)); + expect(mockNavigate).toHaveBeenCalledWith(expect.stringContaining('?encodedError=')); + }); + + it('should navigate to DOCUMENT_UPLOAD_COMPLETED when all documents finished successfully', () => { + const allSucceededDocs: UploadDocument[] = [ + { + ...mockDocuments[0], + state: DOCUMENT_UPLOAD_STATE.SUCCEEDED, + }, + { + ...mockDocuments[1], + state: DOCUMENT_UPLOAD_STATE.SUCCEEDED, + }, + ]; + + const intervalTimer = 101; + + handleDocumentStatusUpdates( + 'new', + mockNavigate, + intervalTimer, + mockInterval, + allSucceededDocs, + mockVirusRef, + mockCompleteRef, + ); + + expect(mockCompleteRef.current).toBe(true); + expect(window.clearInterval).toHaveBeenCalledWith(intervalTimer); + expect(mockNavigate.withParams).toHaveBeenCalledWith( + routeChildren.DOCUMENT_UPLOAD_COMPLETED, + ); + }); + + it('should not navigate to completed page again if completeRef is already true', () => { + const allSucceededDocs: UploadDocument[] = [ + { + ...mockDocuments[0], + state: DOCUMENT_UPLOAD_STATE.SUCCEEDED, + }, + ]; + + mockCompleteRef.current = true; + + handleDocumentStatusUpdates( + 'new', + mockNavigate, + 123, + mockInterval, + allSucceededDocs, + mockVirusRef, + mockCompleteRef, + ); + + expect(mockNavigate.withParams).not.toHaveBeenCalled(); + }); + + it('should navigate to completed page when mix of succeeded and error documents', () => { + const mixedDocs: UploadDocument[] = [ + { + ...mockDocuments[0], + state: DOCUMENT_UPLOAD_STATE.SUCCEEDED, + }, + { + ...mockDocuments[1], + state: DOCUMENT_UPLOAD_STATE.ERROR, + errorCode: 'UC_4006', + }, + ]; + + const intervalTimer = 202; + + handleDocumentStatusUpdates( + 'new', + mockNavigate, + intervalTimer, + mockInterval, + mixedDocs, + mockVirusRef, + mockCompleteRef, + ); + + expect(mockCompleteRef.current).toBe(true); + expect(window.clearInterval).toHaveBeenCalledWith(intervalTimer); + expect(mockNavigate.withParams).toHaveBeenCalledWith( + routeChildren.DOCUMENT_UPLOAD_COMPLETED, + ); + }); + + it('should not navigate when documents are still uploading', () => { + const uploadingDocs: UploadDocument[] = [ + { + ...mockDocuments[0], + state: DOCUMENT_UPLOAD_STATE.UPLOADING, + }, + { + ...mockDocuments[1], + state: DOCUMENT_UPLOAD_STATE.SUCCEEDED, + }, + ]; + + handleDocumentStatusUpdates( + 'new', + mockNavigate, + 123, + mockInterval, + uploadingDocs, + mockVirusRef, + mockCompleteRef, + ); + + expect(mockNavigate).not.toHaveBeenCalled(); + expect(mockNavigate.withParams).not.toHaveBeenCalled(); + }); + + it('should not navigate when documents are still scanning', () => { + const scanningDocs: UploadDocument[] = [ + { + ...mockDocuments[0], + state: DOCUMENT_UPLOAD_STATE.SCANNING, + }, + ]; + + handleDocumentStatusUpdates( + 'new', + mockNavigate, + 123, + mockInterval, + scanningDocs, + mockVirusRef, + mockCompleteRef, + ); + + expect(mockNavigate).not.toHaveBeenCalled(); + expect(mockNavigate.withParams).not.toHaveBeenCalled(); + }); + + it('should prioritize virus detection over all documents failed', () => { + const virusAndFailedDocs: UploadDocument[] = [ + { + ...mockDocuments[0], + state: DOCUMENT_UPLOAD_STATE.INFECTED, + }, + { + ...mockDocuments[1], + state: DOCUMENT_UPLOAD_STATE.ERROR, + errorCode: 'UC_4006', + }, + ]; + + const intervalTimer = 303; + + handleDocumentStatusUpdates( + 'new', + mockNavigate, + intervalTimer, + mockInterval, + virusAndFailedDocs, + mockVirusRef, + mockCompleteRef, + ); + + expect(mockNavigate).toHaveBeenCalledWith(routeChildren.DOCUMENT_UPLOAD_INFECTED); + expect(mockNavigate).not.toHaveBeenCalledWith( + expect.stringContaining(routes.SERVER_ERROR + '?'), + ); + }); + }); }); diff --git a/app/src/helpers/utils/documentUpload.ts b/app/src/helpers/utils/documentUpload.ts index a61ce070e4..20b0d9ce5e 100644 --- a/app/src/helpers/utils/documentUpload.ts +++ b/app/src/helpers/utils/documentUpload.ts @@ -22,6 +22,13 @@ import { } from '../../types/blocks/documentReview'; import { getDocumentReviewStatus, uploadDocumentForReview } from '../requests/documentReview'; import { Dispatch, RefObject, SetStateAction } from 'react'; +import { EnhancedNavigate, getJourney, JourneyType } from './urlManipulations'; +import { routeChildren, routes } from '../../types/generic/routes'; +import { + MAX_POLLING_TIME, + UPDATE_DOCUMENT_STATE_FREQUENCY_MILLISECONDS, +} from '../constants/network'; +import { errorCodeToParams } from './errorToParams'; export const reduceDocumentsForUpload = async ( documents: UploadDocument[], @@ -243,6 +250,7 @@ export const startIntervalTimer = ( doc.state = DOCUMENT_UPLOAD_STATE.INFECTED; } else if (doc.file.name.toLocaleLowerCase() === 'virus-failed.pdf') { doc.state = DOCUMENT_UPLOAD_STATE.ERROR; + doc.errorCode = 'UC_4006'; } else { doc.state = DOCUMENT_UPLOAD_STATE.SUCCEEDED; } @@ -307,3 +315,54 @@ export const goToPreviousDocType = ( setShowSkipLink(true); setDocumentType(documentTypeList[previousDocTypeIndex]); }; + +export const handleDocumentStatusUpdates = ( + journey: JourneyType, + navigate: EnhancedNavigate, + intervalTimer: number, + interval: RefObject, + documents: UploadDocument[], + virusReference: RefObject, + completeRef: RefObject, +): void => { + const journeyParam = getJourney(); + + if (journeyParam === 'update' && journey !== journeyParam) { + globalThis.clearInterval(intervalTimer); + navigate(routes.SERVER_ERROR); + return; + } + + if (interval.current * UPDATE_DOCUMENT_STATE_FREQUENCY_MILLISECONDS > MAX_POLLING_TIME) { + globalThis.clearInterval(intervalTimer); + navigate(routes.SERVER_ERROR); + return; + } + + if (documents.length === 0) { + return; + } + + const hasVirus = documents.some((d) => d.state === DOCUMENT_UPLOAD_STATE.INFECTED); + const failedDocs = documents.filter((d) => d.state === DOCUMENT_UPLOAD_STATE.ERROR); + const allFinished = + documents.length > 0 && + documents.every( + (d) => + d.state === DOCUMENT_UPLOAD_STATE.SUCCEEDED || + d.state === DOCUMENT_UPLOAD_STATE.ERROR, + ); + + if (hasVirus && !virusReference.current) { + virusReference.current = true; + globalThis.clearInterval(intervalTimer); + navigate(routeChildren.DOCUMENT_UPLOAD_INFECTED); + } else if (failedDocs.length === documents.length) { + const errorParams = errorCodeToParams(failedDocs[0].errorCode!); + navigate(routes.SERVER_ERROR + errorParams); + } else if (allFinished && !completeRef.current) { + completeRef.current = true; + globalThis.clearInterval(intervalTimer); + navigate.withParams(routeChildren.DOCUMENT_UPLOAD_COMPLETED); + } +}; diff --git a/app/src/helpers/utils/errorCodes.ts b/app/src/helpers/utils/errorCodes.ts index 93664201c6..0f05528d40 100644 --- a/app/src/helpers/utils/errorCodes.ts +++ b/app/src/helpers/utils/errorCodes.ts @@ -42,6 +42,8 @@ const errorCodes: { [key: string]: string } = { "You cannot access this patient's record because they are not registered at your practice. The patient's current practice can access this record if it's stored in this service.", UC_4002: 'There was an issue when attempting to virus scan your uploaded files', UC_4004: technicalIssueMsg, + UC_4006: + "1 or more files failed to upload. Remove any passwords from files and check that all files open correctly. Then return to the patient's record to upload them again.", }; export default errorCodes; diff --git a/app/src/pages/documentUploadPage/DocumentUploadPage.tsx b/app/src/pages/documentUploadPage/DocumentUploadPage.tsx index 6bee84c0c1..fd61ada2f6 100644 --- a/app/src/pages/documentUploadPage/DocumentUploadPage.tsx +++ b/app/src/pages/documentUploadPage/DocumentUploadPage.tsx @@ -14,7 +14,7 @@ import useBaseAPIUrl from '../../helpers/hooks/useBaseAPIUrl'; import useConfig from '../../helpers/hooks/useConfig'; import usePatient from '../../helpers/hooks/usePatient'; import { uploadDocumentToS3 } from '../../helpers/requests/uploadDocuments'; -import { errorCodeToParams, errorToParams } from '../../helpers/utils/errorToParams'; +import { errorToParams } from '../../helpers/utils/errorToParams'; import { isLocal, isMock } from '../../helpers/utils/isLocal'; import { markDocumentsAsUploading, @@ -44,10 +44,12 @@ import { getUploadSession, goToNextDocType, goToPreviousDocType, + handleDocumentStatusUpdates, reduceDocumentsForUpload, startIntervalTimer, } from '../../helpers/utils/documentUpload'; import DocumentUploadIndex from '../../components/blocks/_documentUpload/documentUploadIndex/DocumentUploadIndex'; +import { UPDATE_DOCUMENT_STATE_FREQUENCY_MILLISECONDS } from '../../helpers/constants/network'; const DocumentUploadPage = (): React.JSX.Element => { const patientDetails = usePatient(); @@ -74,9 +76,6 @@ const DocumentUploadPage = (): React.JSX.Element => { const [showSkipLink, setShowSkipLink] = useState(undefined); const [documentTypeList, setDocumentTypeList] = useState([]); - const UPDATE_DOCUMENT_STATE_FREQUENCY_MILLISECONDS = 5000; - const MAX_POLLING_TIME = 600000; - useEffect(() => { const journeyParam = getJourney(); if (journeyParam === 'update') { @@ -91,44 +90,15 @@ const DocumentUploadPage = (): React.JSX.Element => { }, []); useEffect(() => { - const journeyParam = getJourney(); - - if (journeyParam === 'update' && journey !== journeyParam) { - globalThis.clearInterval(intervalTimer); - navigate(routes.SERVER_ERROR); - return; - } - - if (interval.current * UPDATE_DOCUMENT_STATE_FREQUENCY_MILLISECONDS > MAX_POLLING_TIME) { - window.clearInterval(intervalTimer); - navigate(routes.SERVER_ERROR); - return; - } - - const hasVirus = documents.some((d) => d.state === DOCUMENT_UPLOAD_STATE.INFECTED); - const docWithError = - documents.length === 1 && - documents.find((d) => d.state === DOCUMENT_UPLOAD_STATE.ERROR); - const allFinished = - documents.length > 0 && - documents.every( - (d) => - d.state === DOCUMENT_UPLOAD_STATE.SUCCEEDED || - d.state === DOCUMENT_UPLOAD_STATE.ERROR, - ); - - if (hasVirus && !virusReference.current) { - virusReference.current = true; - window.clearInterval(intervalTimer); - navigate(routeChildren.DOCUMENT_UPLOAD_INFECTED); - } else if (docWithError) { - const errorParams = docWithError.error ? errorCodeToParams(docWithError.error) : ''; - navigate(routes.SERVER_ERROR + errorParams); - } else if (allFinished && !completeRef.current) { - completeRef.current = true; - window.clearInterval(intervalTimer); - navigate.withParams(routeChildren.DOCUMENT_UPLOAD_COMPLETED); - } + handleDocumentStatusUpdates( + journey, + navigate, + intervalTimer, + interval, + documents, + virusReference, + completeRef, + ); }, [ baseHeaders, baseUrl, diff --git a/app/src/pages/serverErrorPage/ServerErrorPage.test.tsx b/app/src/pages/serverErrorPage/ServerErrorPage.test.tsx index 17445e22ee..ddd7da93af 100644 --- a/app/src/pages/serverErrorPage/ServerErrorPage.test.tsx +++ b/app/src/pages/serverErrorPage/ServerErrorPage.test.tsx @@ -1,10 +1,10 @@ import { render, screen, waitFor } from '@testing-library/react'; -import { act } from 'react'; import ServerErrorPage from './ServerErrorPage'; import userEvent from '@testing-library/user-event'; import { unixTimestamp } from '../../helpers/utils/createTimestamp'; import { runAxeTest } from '../../helpers/test/axeTestHelper'; import { afterEach, beforeEach, describe, expect, it, Mock, vi } from 'vitest'; +import { routes } from '../../types/generic/routes'; const mockedUseNavigate = vi.fn(); const mockSearchParamsGet = vi.fn(); @@ -38,12 +38,12 @@ describe('ServerErrorPage', () => { expect(screen.getByText('There was an unexplained error')).toBeInTheDocument(); expect( screen.getByText( - "Try again by returning to the previous page. You'll need to enter any information you submitted again.", + "Try again by returning to the home page. You'll need to enter any information you submitted again.", ), ).toBeInTheDocument(); expect( screen.getByRole('button', { - name: 'Return to previous page', + name: 'Go to home', }), ).toBeInTheDocument(); expect( @@ -128,16 +128,14 @@ describe('ServerErrorPage', () => { mockSearchParamsGet.mockReturnValue(mockEncoded); render(); - const returnButtonLink = screen.getByRole('button', { - name: 'Return to previous page', - }); - expect(returnButtonLink).toBeInTheDocument(); - act(() => { - userEvent.click(returnButtonLink); + const homeButtonLink = screen.getByRole('button', { + name: 'Go to home', }); + expect(homeButtonLink).toBeInTheDocument(); + await userEvent.click(homeButtonLink); await waitFor(() => { - expect(mockedUseNavigate).toHaveBeenCalledWith(-2); + expect(mockedUseNavigate).toHaveBeenCalledWith(routes.HOME); }); }); }); diff --git a/app/src/pages/serverErrorPage/ServerErrorPage.tsx b/app/src/pages/serverErrorPage/ServerErrorPage.tsx index 1d1d0c7caf..0436db7976 100644 --- a/app/src/pages/serverErrorPage/ServerErrorPage.tsx +++ b/app/src/pages/serverErrorPage/ServerErrorPage.tsx @@ -3,6 +3,7 @@ import { ButtonLink } from 'nhsuk-react-components'; import errorCodes from '../../helpers/utils/errorCodes'; import { unixTimestamp } from '../../helpers/utils/createTimestamp'; import useTitle from '../../helpers/hooks/useTitle'; +import { routes } from '../../types/generic/routes'; type ServerError = [errorCode: string | null, interactionId: string | null]; @@ -26,27 +27,17 @@ const ServerErrorPage = (): React.JSX.Element => {

Sorry, there is a problem with the service

{errorMessage}

- Try again by returning to the previous page. You'll need to enter any information - you submitted again. + Try again by returning to the home page. You'll need to enter any information you + submitted again.

{ e.preventDefault(); - const errorUrl = window.location.href; - // Navigate back two paces incase the previous page has an error in the prefetch - navigate(-2); - - // If this code is reached, we can assume that the component - // has not destroyed and navigate(-2) has no where to go - const urlAfterMinusTwoNavigate = window.location.href; - const urlHasNotChanged = errorUrl === urlAfterMinusTwoNavigate; - if (urlHasNotChanged) { - navigate(-1); - } + navigate(routes.HOME); }} > - Return to previous page + Go to home

If this error keeps appearing