Skip to content

Commit

Permalink
PRMDR-294- Delete document confirmation page
Browse files Browse the repository at this point in the history
  • Loading branch information
abbas-khan10 committed Oct 25, 2023
1 parent 0131702 commit 25fc63e
Show file tree
Hide file tree
Showing 8 changed files with 1,066 additions and 974 deletions.
1,603 changes: 633 additions & 970 deletions app/package-lock.json

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
import { render, screen, waitFor } from '@testing-library/react';
import SessionProvider, { Session } from '../../../providers/sessionProvider/SessionProvider';
import {
buildLgSearchResult,
buildPatientDetails,
buildUserAuth,
} from '../../../helpers/test/testBuilders';
import DeleteDocumentsStage, { Props } from './DeleteDocumentsStage';
import { getFormattedDate } from '../../../helpers/utils/formatDate';
import { act } from 'react-dom/test-utils';
import userEvent from '@testing-library/user-event';
import { LG_RECORD_STAGE } from '../../../pages/lloydGeorgeRecordPage/LloydGeorgeRecordPage';
import { DOCUMENT_TYPE } from '../../../types/pages/UploadDocumentsPage/types';
import axios from 'axios/index';
jest.mock('axios');

const mockPatientDetails = buildPatientDetails();
const mockLgSearchResult = buildLgSearchResult();
const mockSetStage = jest.fn();
const mockSetDownloadStage = jest.fn();
const mockNavigateCallback = jest.fn();
const mockedAxios = axios as jest.Mocked<typeof axios>;

describe('DeleteAllDocumentsStage', () => {
beforeEach(() => {
process.env.REACT_APP_ENVIRONMENT = 'jest';
});
afterEach(() => {
jest.clearAllMocks();
});
it('renders the page with patient details', async () => {
const patientName = `${mockPatientDetails.givenName} ${mockPatientDetails.familyName}`;
const dob = getFormattedDate(new Date(mockPatientDetails.birthDate));

renderComponent();

await waitFor(async () => {
expect(
screen.getByText('Are you sure you want to permanently delete files for:')
).toBeInTheDocument();
});

expect(screen.getByText(patientName)).toBeInTheDocument();
expect(screen.getByText(`Date of birth: ${dob}`)).toBeInTheDocument();
expect(screen.getByText(/NHS number/)).toBeInTheDocument();
expect(screen.getByRole('radio', { name: 'Yes' })).toBeInTheDocument();
expect(screen.getByRole('radio', { name: 'No' })).toBeInTheDocument();
expect(screen.getByRole('button', { name: 'Continue' })).toBeInTheDocument();
});

it('renders LgRecordStage when the No radio button is selected and Continue button clicked', async () => {
renderComponent();

act(() => {
userEvent.click(screen.getByRole('radio', { name: 'No' }));
userEvent.click(screen.getByRole('button', { name: 'Continue' }));
});

await waitFor(() => {
expect(mockSetStage).toHaveBeenCalledWith(LG_RECORD_STAGE.RECORD);
});
});

it('renders DeletionConfirmationStage when the Yes radio button is selected and Continue button clicked', async () => {
mockedAxios.delete.mockReturnValue(Promise.resolve({ status: 200, data: 'Success' }));

renderComponent();

expect(screen.getByRole('radio', { name: 'Yes' })).toBeInTheDocument();
expect(screen.getByRole('button', { name: 'Continue' })).toBeInTheDocument();

act(() => {
userEvent.click(screen.getByRole('radio', { name: 'Yes' }));
userEvent.click(screen.getByRole('button', { name: 'Continue' }));
});

await waitFor(() => {
expect(screen.getByText('Deletion complete')).toBeInTheDocument();
});
});
});

const TestApp = (props: Omit<Props, 'setStage' | 'setDownloadStage'>) => {
return (
<DeleteDocumentsStage
{...props}
setStage={mockSetStage}
setDownloadStage={mockSetDownloadStage}
/>
);
};

const renderComponent = () => {
const auth: Session = {
auth: buildUserAuth(),
isLoggedIn: true,
};

const props: Omit<Props, 'setStage' | 'setDownloadStage'> = {
patientDetails: mockPatientDetails,
numberOfFiles: mockLgSearchResult.number_of_files,
docType: DOCUMENT_TYPE.LLOYD_GEORGE,
passNavigate: mockNavigateCallback,
};

render(
<SessionProvider sessionOverride={auth}>
<TestApp {...props} />
</SessionProvider>
);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
import React, { Dispatch, SetStateAction, useState } from 'react';
import { FieldValues, useForm } from 'react-hook-form';
import { Button, Fieldset, Radios } from 'nhsuk-react-components';
import { getFormattedDate } from '../../../helpers/utils/formatDate';
import { PatientDetails } from '../../../types/generic/patientDetails';
import { LG_RECORD_STAGE } from '../../../pages/lloydGeorgeRecordPage/LloydGeorgeRecordPage';
import DeletionConfirmationStage from '../deletionConfirmationStage/DeletionConfirmationStage';
import deleteAllDocuments from '../../../helpers/requests/deleteAllDocuments';
import { useBaseAPIUrl } from '../../../providers/configProvider/ConfigProvider';
import useBaseAPIHeaders from '../../../helpers/hooks/useBaseAPIHeaders';
import { DOCUMENT_TYPE } from '../../../types/pages/UploadDocumentsPage/types';
import { AxiosError } from 'axios';
import { routes } from '../../../types/generic/routes';
import { DOWNLOAD_STAGE } from '../../../types/generic/downloadStage';
import SpinnerButton from '../../generic/spinnerButton/SpinnerButton';
import ServiceError from '../../layout/serviceErrorBox/ServiceErrorBox';
import { SUBMISSION_STATE } from '../../../types/pages/documentSearchResultsPage/types';

export type Props = {
docType: DOCUMENT_TYPE;
numberOfFiles: number;
patientDetails: PatientDetails;
setStage: Dispatch<SetStateAction<LG_RECORD_STAGE>>;
setDownloadStage: Dispatch<SetStateAction<DOWNLOAD_STAGE>>;
passNavigate: (navigateTo: string) => void;
};

function DeleteDocumentsStage({
docType,
numberOfFiles,
patientDetails,
setStage,
passNavigate,
setDownloadStage,
}: Props) {
const { register, handleSubmit } = useForm();
const { ref: deleteDocsRef, ...radioProps } = register('deleteDocs');
const [deletionStage, setDeletionStage] = useState(SUBMISSION_STATE.INITIAL);
const baseUrl = useBaseAPIUrl();
const baseHeaders = useBaseAPIHeaders();
const dob: String = patientDetails?.birthDate
? getFormattedDate(new Date(patientDetails.birthDate))
: '';

const nhsNumber: string =
patientDetails?.nhsNumber.slice(0, 3) +
' ' +
patientDetails?.nhsNumber.slice(3, 6) +
' ' +
patientDetails?.nhsNumber.slice(6, 10);

const patientInfo = (
<>
<p style={{ marginBottom: 5, fontWeight: '700' }}>
{patientDetails?.givenName?.map((name: String) => `${name} `)}
{patientDetails?.familyName}
</p>
<p style={{ fontSize: '16px', marginBottom: 5 }}>NHS number: {nhsNumber}</p>
<p style={{ fontSize: '16px' }}>Date of birth: {dob}</p>
</>
);

const submit = async (fieldValues: FieldValues) => {
const patientNhsNumber: string = patientDetails?.nhsNumber || '';

if (fieldValues.deleteDocs === 'yes') {
setDeletionStage(SUBMISSION_STATE.PENDING);
try {
const response = await deleteAllDocuments({
docType,
nhsNumber: patientNhsNumber,
baseUrl,
baseHeaders,
});
if (response.status === 200) {
setDeletionStage(SUBMISSION_STATE.SUCCEEDED);
setDownloadStage(DOWNLOAD_STAGE.FAILED);
}
} catch (e) {
setDeletionStage(SUBMISSION_STATE.FAILED);
const error = e as AxiosError;
if (error.response?.status === 403) {
passNavigate(routes.HOME);
}
}
} else {
setStage(LG_RECORD_STAGE.RECORD);
}
};

return deletionStage !== SUBMISSION_STATE.SUCCEEDED ? (
<>
{deletionStage === SUBMISSION_STATE.FAILED && <ServiceError />}
<form onSubmit={handleSubmit(submit)}>
<Fieldset>
<Fieldset.Legend isPageHeading>
Are you sure you want to permanently delete files for:
</Fieldset.Legend>
{patientInfo}
<Radios id="delete-docs">
<Radios.Radio
value="yes"
inputRef={deleteDocsRef}
{...radioProps}
id="yes-radio-button"
defaultChecked
>
Yes
</Radios.Radio>
<Radios.Radio
value="no"
inputRef={deleteDocsRef}
{...radioProps}
id="no-radio-button"
>
No
</Radios.Radio>
</Radios>
</Fieldset>
{deletionStage === SUBMISSION_STATE.PENDING ? (
<SpinnerButton id="delete-docs-spinner" status="Deleting..." disabled={true} />
) : (
<Button type="submit" id="lg-delete-submit">
Continue
</Button>
)}
</form>
</>
) : (
<DeletionConfirmationStage
numberOfFiles={numberOfFiles}
patientDetails={patientDetails}
setStage={setStage}
/>
);
}
export default DeleteDocumentsStage;
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
import { render, screen, waitFor } from '@testing-library/react';
import SessionProvider, { Session } from '../../../providers/sessionProvider/SessionProvider';
import {
buildLgSearchResult,
buildPatientDetails,
buildUserAuth,
} from '../../../helpers/test/testBuilders';
import DeletionConfirmationStage from './DeletionConfirmationStage';
import { act } from 'react-dom/test-utils';
import userEvent from '@testing-library/user-event';
import { LG_RECORD_STAGE } from '../../../pages/lloydGeorgeRecordPage/LloydGeorgeRecordPage';

const mockPatientDetails = buildPatientDetails();
const mockLgSearchResult = buildLgSearchResult();
const mockSetStage = jest.fn();

describe('DeletionConfirmationStage', () => {
beforeEach(() => {
process.env.REACT_APP_ENVIRONMENT = 'jest';
});

it('renders the page with patient details', async () => {
const patientName = `${mockPatientDetails.givenName} ${mockPatientDetails.familyName}`;
const numberOfFiles = mockLgSearchResult.number_of_files;

renderPage();

await waitFor(async () => {
expect(screen.getByText('Deletion complete')).toBeInTheDocument();
});

expect(
screen.getByText(`${numberOfFiles} files from the Lloyd George record of:`)
).toBeInTheDocument();
expect(screen.getByText(patientName)).toBeInTheDocument();
expect(screen.getByText(/NHS number/)).toBeInTheDocument();
expect(
screen.getByRole('button', {
name: "Return to patient's Lloyd George record page",
})
).toBeInTheDocument();
});

it('renders LgRecordStage when button is clicked', async () => {
renderPage();

act(() => {
userEvent.click(
screen.getByRole('button', {
name: "Return to patient's Lloyd George record page",
})
);
});

await waitFor(() => {
expect(mockSetStage).toHaveBeenCalledWith(LG_RECORD_STAGE.RECORD);
});
});
});

const renderPage = () => {
const auth: Session = {
auth: buildUserAuth(),
isLoggedIn: true,
};
render(
<SessionProvider sessionOverride={auth}>
<DeletionConfirmationStage
numberOfFiles={mockLgSearchResult.number_of_files}
patientDetails={mockPatientDetails}
setStage={mockSetStage}
/>
</SessionProvider>
);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import React, { Dispatch, SetStateAction } from 'react';
import { ButtonLink, Card } from 'nhsuk-react-components';
import { PatientDetails } from '../../../types/generic/patientDetails';
import { LG_RECORD_STAGE } from '../../../pages/lloydGeorgeRecordPage/LloydGeorgeRecordPage';

export type Props = {
numberOfFiles: number;
patientDetails: PatientDetails;
setStage: Dispatch<SetStateAction<LG_RECORD_STAGE>>;
};

function DeletionConfirmationStage({ numberOfFiles, patientDetails, setStage }: Props) {
const nhsNumber: String =
patientDetails?.nhsNumber.slice(0, 3) +
' ' +
patientDetails?.nhsNumber.slice(3, 6) +
' ' +
patientDetails?.nhsNumber.slice(6, 10);

return (
<div className="deletion-complete">
<Card style={{ maxWidth: '620px' }} className="deletion-complete-card">
<Card.Content>
<Card.Heading style={{ margin: 'auto' }}>Deletion complete</Card.Heading>
<Card.Description style={{ fontSize: '16px' }}>
{numberOfFiles} files from the Lloyd George record of:{' '}
</Card.Description>
<Card.Description style={{ fontWeight: '700', fontSize: '24px' }}>
{patientDetails?.givenName?.map((name) => `${name} `)}
{patientDetails?.familyName}
</Card.Description>
<Card.Description style={{ fontSize: '16px' }}>
(NHS number: {nhsNumber})
</Card.Description>
</Card.Content>
</Card>
<p style={{ marginTop: 40 }}>
<ButtonLink onClick={() => setStage(LG_RECORD_STAGE.RECORD)}>
Return to patient's Lloyd George record page
</ButtonLink>
</p>
</div>
);
}

export default DeletionConfirmationStage;
Loading

0 comments on commit 25fc63e

Please sign in to comment.