Skip to content

Commit

Permalink
PRMDR 706 - Server error page on error (#297)
Browse files Browse the repository at this point in the history
Error page on upload fail
  • Loading branch information
RioKnightleyNHS authored Feb 20, 2024
1 parent 563e9b4 commit ccf6a5d
Show file tree
Hide file tree
Showing 11 changed files with 396 additions and 113 deletions.
134 changes: 90 additions & 44 deletions app/src/components/blocks/_arf/selectStage/SelectStage.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,21 +6,42 @@ import {
buildLgFile,
} from '../../../../helpers/test/testBuilders';
import userEvent from '@testing-library/user-event';
import { DOCUMENT_UPLOAD_STATE as documentUploadStates } from '../../../../types/pages/UploadDocumentsPage/types';
import {
UPLOAD_STAGE,
DOCUMENT_UPLOAD_STATE as documentUploadStates,
} from '../../../../types/pages/UploadDocumentsPage/types';
import { act } from 'react-dom/test-utils';
import { PatientDetails } from '../../../../types/generic/patientDetails';
import usePatient from '../../../../helpers/hooks/usePatient';
import axios from 'axios';

jest.mock('axios');
const mockSetStage = jest.fn();
jest.mock('../../../../helpers/hooks/useBaseAPIHeaders');
jest.mock('../../../../helpers/hooks/useBaseAPIUrl');
jest.mock('../../../../helpers/utils/toFileList', () => ({
__esModule: true,
default: () => [],
}));
jest.mock('react-router');
jest.mock('../../../../helpers/hooks/usePatient');
const mockedUsePatient = usePatient as jest.Mock;
const mockPatient = buildPatientDetails();
const mockedAxios = axios as jest.Mocked<typeof axios>;
const documentOne = buildTextFile('one', 100);
const documentTwo = buildTextFile('two', 200);
const documentThree = buildTextFile('three', 100);
const lgDocumentOne = buildLgFile(1, 2, 'Joe Blogs');
const lgDocumentTwo = buildLgFile(2, 2, 'Joe Blogs');
const arfDocuments = [documentOne, documentTwo, documentThree];

const setDocumentMock = jest.fn();
setDocumentMock.mockImplementation((document) => {
document.state = documentUploadStates.SELECTED;
document.id = '1';
});

describe('<UploadDocumentsPage />', () => {
const mockPatientDetails: PatientDetails = buildPatientDetails();
describe('<SelectStage />', () => {
beforeEach(() => {
process.env.REACT_APP_ENVIRONMENT = 'jest';
mockedUsePatient.mockReturnValue(mockPatient);
Expand All @@ -29,24 +50,9 @@ describe('<UploadDocumentsPage />', () => {
jest.clearAllMocks();
});

describe('upload documents with an NHS number', () => {
const documentOne = buildTextFile('one', 100);
const documentTwo = buildTextFile('two', 200);
const documentThree = buildTextFile('three', 100);
const lgDocumentOne = buildLgFile(1, 2, 'Joe Blogs');
const lgDocumentTwo = buildLgFile(2, 2, 'Joe Blogs');
const arfDocuments = [documentOne, documentTwo, documentThree];

const setDocumentMock = jest.fn();
setDocumentMock.mockImplementation((document) => {
document.state = documentUploadStates.SELECTED;
document.id = '1';
});

const mockPatientDetails: PatientDetails = buildPatientDetails();

describe('Rendering', () => {
it('renders the page', async () => {
render(<SelectStage setDocuments={setDocumentMock} uploadDocuments={() => {}} />);
renderApp();

expect(screen.getByRole('heading', { name: 'Upload documents' })).toBeInTheDocument();
expect(screen.getByText(mockPatientDetails.nhsNumber)).toBeInTheDocument();
Expand All @@ -57,8 +63,7 @@ describe('<UploadDocumentsPage />', () => {
});

it('can upload documents to both LG and ARF forms', async () => {
render(<SelectStage setDocuments={setDocumentMock} uploadDocuments={() => {}} />);

renderApp();
expect(screen.getByRole('heading', { name: 'Upload documents' })).toBeInTheDocument();
expect(screen.getByText(mockPatientDetails.nhsNumber)).toBeInTheDocument();
expect(await screen.findAllByText('Select file(s)')).toHaveLength(2);
Expand Down Expand Up @@ -90,8 +95,7 @@ describe('<UploadDocumentsPage />', () => {
it.each([['ARF'], ['LG']])(
"does upload and then remove a file for '%s' input",
async (inputType) => {
render(<SelectStage setDocuments={setDocumentMock} uploadDocuments={() => {}} />);

renderApp();
act(() => {
userEvent.upload(screen.getByTestId(`${inputType}-input`), [
documentOne,
Expand Down Expand Up @@ -122,8 +126,7 @@ describe('<UploadDocumentsPage />', () => {
])(
"does not upload either forms if selected file is more than 5GB for '%s' input",
async (inputType) => {
render(<SelectStage setDocuments={setDocumentMock} uploadDocuments={() => {}} />);

renderApp();
const documentBig =
inputType.name === 'ARF'
? buildTextFile('four', 6 * Math.pow(1024, 3))
Expand Down Expand Up @@ -152,8 +155,7 @@ describe('<UploadDocumentsPage />', () => {
);

it('does not upload LG form if selected file is not PDF', async () => {
render(<SelectStage setDocuments={setDocumentMock} uploadDocuments={() => {}} />);

renderApp();
const lgFileWithBadType = new File(
['test'],
`1of2000_Lloyd_George_Record_[Joe Bloggs]_[1234567890]_[25-12-2019].pdf`,
Expand All @@ -180,8 +182,7 @@ describe('<UploadDocumentsPage />', () => {
});

it('does not upload LG form if total number of file does not match file name', async () => {
render(<SelectStage setDocuments={setDocumentMock} uploadDocuments={() => {}} />);

renderApp();
const lgExtraFile = buildLgFile(3, 3, 'Joe Blogs');

act(() => {
Expand All @@ -202,8 +203,7 @@ describe('<UploadDocumentsPage />', () => {
});

it('does not upload LG form if selected file does not match naming conventions', async () => {
render(<SelectStage setDocuments={setDocumentMock} uploadDocuments={() => {}} />);

renderApp();
const pdfFileWithBadName = new File(['test'], `test_not_up_to_naming_conventions.pdf`, {
type: 'application/pdf',
});
Expand All @@ -225,8 +225,7 @@ describe('<UploadDocumentsPage />', () => {
});

it('does not upload LG form if selected file number is bigger than number of total files', async () => {
render(<SelectStage setDocuments={setDocumentMock} uploadDocuments={() => {}} />);

renderApp();
const pdfFileWithBadNumber = buildLgFile(2, 1, 'Joe Blogs');
act(() => {
userEvent.upload(screen.getByTestId(`LG-input`), pdfFileWithBadNumber);
Expand All @@ -246,8 +245,7 @@ describe('<UploadDocumentsPage />', () => {
});

it('does not upload LG form if files do not match each other', async () => {
render(<SelectStage setDocuments={setDocumentMock} uploadDocuments={() => {}} />);

renderApp();
const joeBloggsFile = new File(
['test'],
`1of2_Lloyd_George_Record_[Joe Bloggs]_[1234567890]_[25-12-2019].pdf`,
Expand Down Expand Up @@ -282,8 +280,7 @@ describe('<UploadDocumentsPage />', () => {

it('does not upload LG form if two or more files match name/size', async () => {
const duplicateFileWarning = 'There are two or more documents with the same name.';
render(<SelectStage setDocuments={setDocumentMock} uploadDocuments={() => {}} />);

renderApp();
act(() => {
userEvent.upload(screen.getByTestId(`LG-input`), [lgDocumentTwo, lgDocumentTwo]);
});
Expand All @@ -306,8 +303,7 @@ describe('<UploadDocumentsPage />', () => {
'shows a duplicate file warning if two or more files match name/size for ARF input only',
async (inputType) => {
const duplicateFileWarning = 'There are two or more documents with the same name.';
render(<SelectStage setDocuments={setDocumentMock} uploadDocuments={() => {}} />);

renderApp();
act(() => {
userEvent.upload(screen.getByTestId(`${inputType}-input`), [
documentOne,
Expand All @@ -333,8 +329,7 @@ describe('<UploadDocumentsPage />', () => {
it.each([['ARF'], ['LG']])(
"does allow the user to add the same file again if they remove for '%s' input",
async (inputType) => {
render(<SelectStage setDocuments={setDocumentMock} uploadDocuments={() => {}} />);

renderApp();
const selectFilesLabel = screen.getByTestId(`${inputType}-input`);

act(() => {
Expand All @@ -357,13 +352,64 @@ describe('<UploadDocumentsPage />', () => {
);

it('renders link to PCSE that opens in a new tab', () => {
render(<SelectStage setDocuments={setDocumentMock} uploadDocuments={() => {}} />);

renderApp();
const pcseLink = screen.getByRole('link', {
name: 'Primary Care Support England',
});
expect(pcseLink).toHaveAttribute('href', 'https://secure.pcse.england.nhs.uk/');
expect(pcseLink).toHaveAttribute('target', '_blank');
});
});

describe('Navigation', () => {
it('sets stage to uploading and complete when upload files is triggered', async () => {
const response = {
response: {
status: 200,
},
};
mockedAxios.post.mockImplementation(() => Promise.resolve(response));

renderApp();
expect(screen.getByRole('heading', { name: 'Upload documents' })).toBeInTheDocument();
expect(screen.getByText(mockPatientDetails.nhsNumber)).toBeInTheDocument();
expect(await screen.findAllByText('Select file(s)')).toHaveLength(2);

expect(screen.getByRole('button', { name: 'Upload' })).toBeInTheDocument();
expect(screen.getByRole('button', { name: 'Upload' })).toBeDisabled();

act(() => {
userEvent.upload(screen.getByTestId('ARF-input'), [
documentOne,
documentTwo,
documentThree,
]);
});

act(() => {
userEvent.upload(screen.getByTestId('LG-input'), [lgDocumentOne, lgDocumentTwo]);
});

expect(await screen.findAllByText(documentOne.name)).toHaveLength(1);
expect(await screen.findAllByText(documentTwo.name)).toHaveLength(1);
expect(await screen.findAllByText(documentThree.name)).toHaveLength(1);
expect(await screen.findAllByText(lgDocumentOne.name)).toHaveLength(1);
expect(await screen.findAllByText(lgDocumentTwo.name)).toHaveLength(1);

expect(screen.getByRole('button', { name: 'Upload' })).toBeEnabled();
act(() => {
userEvent.click(screen.getByRole('button', { name: 'Upload' }));
});
await waitFor(() => {
expect(mockSetStage).toHaveBeenCalledWith(UPLOAD_STAGE.Uploading);
});
await waitFor(() => {
expect(mockSetStage).toHaveBeenCalledWith(UPLOAD_STAGE.Complete);
});
});
});

const renderApp = () => {
render(<SelectStage setDocuments={jest.fn()} setStage={mockSetStage} documents={[]} />);
};
});
32 changes: 26 additions & 6 deletions app/src/components/blocks/_arf/selectStage/SelectStage.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import React, { useRef, useState } from 'react';
import React, { Dispatch, SetStateAction, useRef, useState } from 'react';
import {
DOCUMENT_TYPE,
DOCUMENT_UPLOAD_STATE,
FileInputEvent,
SetUploadDocuments,
UPLOAD_STAGE,
UploadDocument,
} from '../../../../types/pages/UploadDocumentsPage/types';
import { Button, Fieldset } from 'nhsuk-react-components';
Expand All @@ -13,27 +14,46 @@ import PatientSummary from '../../../generic/patientSummary/PatientSummary';
import DocumentInputForm from '../documentInputForm/DocumentInputForm';
import { ARFFormConfig, lloydGeorgeFormConfig } from '../../../../helpers/utils/formConfig';
import { v4 as uuidv4 } from 'uuid';
import uploadDocument from '../../../../helpers/requests/uploadDocument';
import usePatient from '../../../../helpers/hooks/usePatient';
import useBaseAPIUrl from '../../../../helpers/hooks/useBaseAPIUrl';
import useBaseAPIHeaders from '../../../../helpers/hooks/useBaseAPIHeaders';

interface Props {
uploadDocuments: () => void;
setDocuments: SetUploadDocuments;
setStage: Dispatch<SetStateAction<UPLOAD_STAGE>>;
documents: Array<UploadDocument>;
}

function SelectStage({ uploadDocuments, setDocuments }: Props) {
function SelectStage({ setDocuments, setStage, documents }: Props) {
const [arfDocuments, setArfDocuments] = useState<Array<UploadDocument>>([]);
const [lgDocuments, setLgDocuments] = useState<Array<UploadDocument>>([]);

const baseUrl = useBaseAPIUrl();
const baseHeaders = useBaseAPIHeaders();
let arfInputRef = useRef<HTMLInputElement | null>(null);
let lgInputRef = useRef<HTMLInputElement | null>(null);

const patientDetails = usePatient();
const nhsNumber: string = patientDetails?.nhsNumber ?? '';
const mergedDocuments = [...arfDocuments, ...lgDocuments];
const hasFileInput = mergedDocuments.length;

const { handleSubmit, control, formState } = useForm();

const lgController = useController(lloydGeorgeFormConfig(control));
const arfController = useController(ARFFormConfig(control));

const uploadDocuments = async () => {
setStage(UPLOAD_STAGE.Uploading);
try {
await uploadDocument({
nhsNumber,
setDocuments,
documents,
baseUrl,
baseHeaders,
});
} catch (e) {}
setStage(UPLOAD_STAGE.Complete);
};
const onInput = (e: FileInputEvent, docType: DOCUMENT_TYPE) => {
const fileArray = Array.from(e.target.files ?? new FileList());
const documentMap: Array<UploadDocument> = fileArray.map((file) => ({
Expand Down
Loading

0 comments on commit ccf6a5d

Please sign in to comment.