@@ -33,7 +33,7 @@ function DeletionConfirmationStage({ numberOfFiles, patientDetails, setStage, us
Deletion complete
{numberOfFiles} file{numberOfFiles !== 1 && 's'} from the{' '}
- {userType === USER_ROLE.GP && 'Lloyd George '}
+ {isGP && 'Lloyd George '}
record of:{' '}
@@ -46,7 +46,7 @@ function DeletionConfirmationStage({ numberOfFiles, patientDetails, setStage, us
- {userType === USER_ROLE.GP ? (
+ {isGP ? (
Return to patient's Lloyd George record page
diff --git a/app/src/components/blocks/lloydGeorgeDownloadAllStage/LloydGeorgeDownloadAllStage.test.tsx b/app/src/components/blocks/lloydGeorgeDownloadAllStage/LloydGeorgeDownloadAllStage.test.tsx
index cbca4a826..2c7953c4d 100644
--- a/app/src/components/blocks/lloydGeorgeDownloadAllStage/LloydGeorgeDownloadAllStage.test.tsx
+++ b/app/src/components/blocks/lloydGeorgeDownloadAllStage/LloydGeorgeDownloadAllStage.test.tsx
@@ -3,12 +3,12 @@ import LgDownloadAllStage, { Props } from './LloydGeorgeDownloadAllStage';
import { buildLgSearchResult, buildPatientDetails } from '../../../helpers/test/testBuilders';
import { createMemoryHistory } from 'history';
import * as ReactRouter from 'react-router';
-import SessionProvider from '../../../providers/sessionProvider/SessionProvider';
import axios from 'axios';
-import userEvent from '@testing-library/user-event';
import { act } from 'react-dom/test-utils';
-jest.mock('axios');
+import userEvent from '@testing-library/user-event';
+jest.mock('axios');
+jest.mock('../../../helpers/hooks/useBaseAPIHeaders');
const mockedAxios = axios as jest.Mocked;
const mockPdf = buildLgSearchResult();
const mockPatient = buildPatientDetails();
@@ -49,6 +49,7 @@ describe('LloydGeorgeDownloadAllStage', () => {
});
it('renders download complete on zip success', async () => {
+ window.HTMLAnchorElement.prototype.click = jest.fn();
mockedAxios.get.mockImplementation(() => Promise.resolve({ data: mockPdf.presign_url }));
jest.useFakeTimers();
@@ -68,10 +69,15 @@ describe('LloydGeorgeDownloadAllStage', () => {
expect(screen.queryByText('0% downloaded...')).not.toBeInTheDocument();
expect(screen.getByTestId(mockPdf.presign_url)).toBeInTheDocument();
+ const urlLink = screen.getByTestId(mockPdf.presign_url);
+ urlLink.addEventListener('click', (e) => {
+ e.preventDefault();
+ });
act(() => {
- userEvent.click(screen.getByTestId(mockPdf.presign_url));
+ userEvent.click(urlLink);
});
+
await waitFor(async () => {
expect(screen.queryByText('Downloading documents')).not.toBeInTheDocument();
});
@@ -85,11 +91,9 @@ const TestApp = (props: Omit) => {
initialIndex: 0,
});
return (
-
-
-
-
-
+
+
+
);
};
diff --git a/app/src/helpers/hooks/useRole.test.tsx b/app/src/helpers/hooks/useRole.test.tsx
new file mode 100644
index 000000000..6ced71853
--- /dev/null
+++ b/app/src/helpers/hooks/useRole.test.tsx
@@ -0,0 +1,45 @@
+import { render, screen } from '@testing-library/react';
+import { REPOSITORY_ROLE, authorisedRoles } from '../../types/generic/authRole';
+import useRole from './useRole';
+import SessionProvider, { Session } from '../../providers/sessionProvider/SessionProvider';
+import { buildUserAuth } from '../test/testBuilders';
+
+describe('useRole', () => {
+ beforeEach(() => {
+ sessionStorage.setItem('UserSession', '');
+ process.env.REACT_APP_ENVIRONMENT = 'jest';
+ });
+ afterEach(() => {
+ jest.clearAllMocks();
+ });
+
+ it.each(authorisedRoles)(
+ "returns a role when there is an authorised session for user role '%s'",
+ async (role) => {
+ renderHook(role);
+ expect(screen.getByText(`ROLE: ${role}`)).toBeInTheDocument();
+ },
+ );
+
+ it('returns null when there is no session', () => {
+ renderHook();
+ expect(screen.getByText(`ROLE: null`)).toBeInTheDocument();
+ });
+});
+
+const TestApp = () => {
+ const role = useRole();
+ return {`ROLE: ${role}`.normalize()}
;
+};
+
+const renderHook = (role?: REPOSITORY_ROLE) => {
+ const session: Session = {
+ auth: buildUserAuth({ role }),
+ isLoggedIn: true,
+ };
+ return render(
+
+
+ ,
+ );
+};
diff --git a/app/src/helpers/hooks/useRole.tsx b/app/src/helpers/hooks/useRole.tsx
new file mode 100644
index 000000000..574d6aa5f
--- /dev/null
+++ b/app/src/helpers/hooks/useRole.tsx
@@ -0,0 +1,10 @@
+import { useSessionContext } from '../../providers/sessionProvider/SessionProvider';
+
+function useRole() {
+ const [session] = useSessionContext();
+
+ const role = session.auth ? session.auth.role : null;
+ return role;
+}
+
+export default useRole;
diff --git a/app/src/helpers/requests/deleteAllDocuments.test.ts b/app/src/helpers/requests/deleteAllDocuments.test.ts
new file mode 100644
index 000000000..13d6e043e
--- /dev/null
+++ b/app/src/helpers/requests/deleteAllDocuments.test.ts
@@ -0,0 +1,68 @@
+import axios, { AxiosError } from 'axios';
+import deleteAllDocuments, { DeleteResponse } from './deleteAllDocuments';
+import { DOCUMENT_TYPE } from '../../types/pages/UploadDocumentsPage/types';
+
+// Mock out all top level functions, such as get, put, delete and post:
+jest.mock('axios');
+const mockedAxios = axios as jest.Mocked;
+
+// ...
+
+describe('[DELETE] deleteAllDocuments', () => {
+ test('Delete all documents handles a 2XX response', async () => {
+ mockedAxios.delete.mockImplementation(() => Promise.resolve({ status: 200, data: '' }));
+ const args = {
+ docType: DOCUMENT_TYPE.ARF,
+ nhsNumber: '90000000009',
+ baseUrl: '/test',
+ baseHeaders: { 'Content-Type': 'application/json', test: 'test' },
+ };
+ let response: DeleteResponse | AxiosError;
+ try {
+ response = await deleteAllDocuments(args);
+ } catch (e) {
+ const error = e as AxiosError;
+ response = error;
+ }
+ expect(response).toHaveProperty('status');
+ expect(response?.status).toBe(200);
+ });
+
+ test('Delete all documents catches a 4XX response', async () => {
+ mockedAxios.delete.mockImplementation(() => Promise.reject({ status: 403, data: '' }));
+ const args = {
+ docType: DOCUMENT_TYPE.ARF,
+ nhsNumber: '',
+ baseUrl: '/test',
+ baseHeaders: { 'Content-Type': 'application/json', test: 'test' },
+ };
+ let response: DeleteResponse | AxiosError;
+ try {
+ response = await deleteAllDocuments(args);
+ } catch (e) {
+ const error = e as AxiosError;
+ response = error;
+ }
+ expect(response).toHaveProperty('status');
+ expect(response?.status).toBe(403);
+ });
+
+ test('Delete all documents catches a 5XX response', async () => {
+ mockedAxios.delete.mockImplementation(() => Promise.reject({ status: 500, data: '' }));
+ const args = {
+ docType: DOCUMENT_TYPE.ARF,
+ nhsNumber: '',
+ baseUrl: '/test',
+ baseHeaders: { 'Content-Type': 'application/json', test: 'test' },
+ };
+ let response: DeleteResponse | AxiosError;
+ try {
+ response = await deleteAllDocuments(args);
+ } catch (e) {
+ const error = e as AxiosError;
+ response = error;
+ }
+ expect(response).toHaveProperty('status');
+ expect(response?.status).toBe(500);
+ });
+});
diff --git a/app/src/helpers/requests/getAuthToken.test.ts b/app/src/helpers/requests/getAuthToken.test.ts
new file mode 100644
index 000000000..d2814b3d2
--- /dev/null
+++ b/app/src/helpers/requests/getAuthToken.test.ts
@@ -0,0 +1,84 @@
+import axios, { AxiosError } from 'axios';
+import getDocumentSearchResults from './getDocumentSearchResults';
+import { SearchResult } from '../../types/generic/searchResult';
+import { buildUserAuth } from '../test/testBuilders';
+import { UserAuth } from '../../types/blocks/userAuth';
+import getAuthToken from './getAuthToken';
+
+// Mock out all top level functions, such as get, put, delete and post:
+jest.mock('axios');
+const mockedAxios = axios as jest.Mocked;
+
+// ...
+
+describe('[GET] getDocumentSearchResults', () => {
+ test('Document search results handles a 2XX response', async () => {
+ const mockAuth = buildUserAuth();
+ mockedAxios.get.mockImplementation(() => Promise.resolve({ status: 200, data: mockAuth }));
+ const args = {
+ baseUrl: '/test',
+ code: 'xx',
+ state: 'xx',
+ };
+ let response: UserAuth | AxiosError;
+ try {
+ response = await getAuthToken(args);
+ } catch (e) {
+ const error = e as AxiosError;
+ response = error;
+ }
+ expect(response).not.toHaveProperty('status');
+ expect(response).toHaveProperty('authorisation_token');
+ expect(response).toHaveProperty('role');
+
+ const data = response as UserAuth;
+ expect(data.authorisation_token).toBe(mockAuth.authorisation_token);
+ expect(data.role).toBe(mockAuth.role);
+ });
+
+ test('Document search results catches a 4XX response', async () => {
+ mockedAxios.get.mockImplementation(() => Promise.reject({ status: 403 }));
+ const args = {
+ nhsNumber: '',
+ baseUrl: '/test',
+ baseHeaders: { 'Content-Type': 'application/json', test: 'test' },
+ };
+ let response: SearchResult[] | AxiosError;
+ try {
+ response = await getDocumentSearchResults(args);
+ } catch (e) {
+ const error = e as AxiosError;
+ response = error;
+ }
+
+ expect(response).not.toHaveProperty('authorisation_token');
+ expect(response).not.toHaveProperty('role');
+ expect(response).toHaveProperty('status');
+
+ const { status } = response as AxiosError;
+ expect(status).toBe(403);
+ });
+
+ test('Document search results catches a 5XX response', async () => {
+ mockedAxios.get.mockImplementation(() => Promise.reject({ status: 500 }));
+ const args = {
+ nhsNumber: '',
+ baseUrl: '/test',
+ baseHeaders: { 'Content-Type': 'application/json', test: 'test' },
+ };
+ let response: SearchResult[] | AxiosError;
+ try {
+ response = await getDocumentSearchResults(args);
+ } catch (e) {
+ const error = e as AxiosError;
+ response = error;
+ }
+
+ expect(response).not.toHaveProperty('authorisation_token');
+ expect(response).not.toHaveProperty('role');
+ expect(response).toHaveProperty('status');
+
+ const { status } = response as AxiosError;
+ expect(status).toBe(500);
+ });
+});
diff --git a/app/src/helpers/requests/getDocumentSearchResults.test.ts b/app/src/helpers/requests/getDocumentSearchResults.test.ts
new file mode 100644
index 000000000..be8ce7167
--- /dev/null
+++ b/app/src/helpers/requests/getDocumentSearchResults.test.ts
@@ -0,0 +1,86 @@
+import axios, { AxiosError } from 'axios';
+import getDocumentSearchResults from './getDocumentSearchResults';
+import { SearchResult } from '../../types/generic/searchResult';
+import { buildSearchResult } from '../test/testBuilders';
+
+// Mock out all top level functions, such as get, put, delete and post:
+jest.mock('axios');
+const mockedAxios = axios as jest.Mocked;
+
+// ...
+
+describe('[GET] getDocumentSearchResults', () => {
+ test('Document search results handles a 2XX response', async () => {
+ const searchResult = buildSearchResult();
+ const mockResults = [searchResult];
+ mockedAxios.get.mockImplementation(() =>
+ Promise.resolve({ status: 200, data: mockResults }),
+ );
+ const args = {
+ nhsNumber: '',
+ baseUrl: '/test',
+ baseHeaders: { 'Content-Type': 'application/json', test: 'test' },
+ };
+ let response: SearchResult[] | AxiosError;
+ try {
+ response = await getDocumentSearchResults(args);
+ } catch (e) {
+ const error = e as AxiosError;
+ response = error;
+ }
+ expect(response).toBeInstanceOf(Array);
+ expect(response).toHaveLength(1);
+
+ const data = response as SearchResult[];
+ expect(data[0]).toHaveProperty('fileName');
+ expect(data[0].fileName).toBe(searchResult.fileName);
+ expect(data[0]).toHaveProperty('created');
+ expect(data[0].created).toBe(searchResult.created);
+ expect(data[0]).toHaveProperty('virusScannerResult');
+ expect(data[0].virusScannerResult).toBe(searchResult.virusScannerResult);
+ });
+
+ test('Document search results catches a 4XX response', async () => {
+ mockedAxios.get.mockImplementation(() => Promise.reject({ status: 403 }));
+ const args = {
+ nhsNumber: '',
+ baseUrl: '/test',
+ baseHeaders: { 'Content-Type': 'application/json', test: 'test' },
+ };
+ let response: SearchResult[] | AxiosError;
+ try {
+ response = await getDocumentSearchResults(args);
+ } catch (e) {
+ const error = e as AxiosError;
+ response = error;
+ }
+
+ expect(response).not.toBeInstanceOf(Array);
+ expect(response).toHaveProperty('status');
+
+ const { status } = response as AxiosError;
+ expect(status).toBe(403);
+ });
+
+ test('Document search results catches a 5XX response', async () => {
+ mockedAxios.get.mockImplementation(() => Promise.reject({ status: 500 }));
+ const args = {
+ nhsNumber: '',
+ baseUrl: '/test',
+ baseHeaders: { 'Content-Type': 'application/json', test: 'test' },
+ };
+ let response: SearchResult[] | AxiosError;
+ try {
+ response = await getDocumentSearchResults(args);
+ } catch (e) {
+ const error = e as AxiosError;
+ response = error;
+ }
+
+ expect(response).not.toBeInstanceOf(Array);
+ expect(response).toHaveProperty('status');
+
+ const { status } = response as AxiosError;
+ expect(status).toBe(500);
+ });
+});
diff --git a/app/src/helpers/requests/documentSearchResults.ts b/app/src/helpers/requests/getDocumentSearchResults.ts
similarity index 88%
rename from app/src/helpers/requests/documentSearchResults.ts
rename to app/src/helpers/requests/getDocumentSearchResults.ts
index 2058f4bf8..8672e9daf 100644
--- a/app/src/helpers/requests/documentSearchResults.ts
+++ b/app/src/helpers/requests/getDocumentSearchResults.ts
@@ -10,11 +10,9 @@ type Args = {
baseHeaders: AuthHeaders;
};
-type GetDocumentSearchResultsResponse =
- | {
- data: Array;
- }
- | undefined;
+export type GetDocumentSearchResultsResponse = {
+ data: Array;
+};
const getDocumentSearchResults = async ({ nhsNumber, baseUrl, baseHeaders }: Args) => {
const gatewayUrl = baseUrl + endpoints.DOCUMENT_SEARCH;
diff --git a/app/src/pages/documentSearchResultsPage/DocumentSearchResultsPage.tsx b/app/src/pages/documentSearchResultsPage/DocumentSearchResultsPage.tsx
index 8dd396296..812e74a65 100644
--- a/app/src/pages/documentSearchResultsPage/DocumentSearchResultsPage.tsx
+++ b/app/src/pages/documentSearchResultsPage/DocumentSearchResultsPage.tsx
@@ -12,10 +12,9 @@ import ServiceError from '../../components/layout/serviceErrorBox/ServiceErrorBo
import { useBaseAPIUrl } from '../../providers/configProvider/ConfigProvider';
import DocumentSearchResultsOptions from '../../components/blocks/documentSearchResultsOptions/DocumentSearchResultsOptions';
import { AxiosError } from 'axios';
-import getDocumentSearchResults from '../../helpers/requests/documentSearchResults';
+import getDocumentSearchResults from '../../helpers/requests/getDocumentSearchResults';
import useBaseAPIHeaders from '../../helpers/hooks/useBaseAPIHeaders';
import DeleteDocumentsStage from '../../components/blocks/deleteDocumentsStage/DeleteDocumentsStage';
-import { USER_ROLE } from '../../types/generic/roles';
import { DOCUMENT_TYPE } from '../../types/pages/UploadDocumentsPage/types';
function DocumentSearchResultsPage() {
@@ -130,7 +129,6 @@ function DocumentSearchResultsPage() {
diff --git a/app/src/pages/lloydGeorgeRecordPage/LloydGeorgeRecordPage.tsx b/app/src/pages/lloydGeorgeRecordPage/LloydGeorgeRecordPage.tsx
index 14ea051c1..f64db89e1 100644
--- a/app/src/pages/lloydGeorgeRecordPage/LloydGeorgeRecordPage.tsx
+++ b/app/src/pages/lloydGeorgeRecordPage/LloydGeorgeRecordPage.tsx
@@ -10,7 +10,6 @@ import getLloydGeorgeRecord from '../../helpers/requests/getLloydGeorgeRecord';
import LloydGeorgeRecordStage from '../../components/blocks/lloydGeorgeRecordStage/LloydGeorgeRecordStage';
import LloydGeorgeDownloadAllStage from '../../components/blocks/lloydGeorgeDownloadAllStage/LloydGeorgeDownloadAllStage';
import { DOCUMENT_TYPE } from '../../types/pages/UploadDocumentsPage/types';
-import { USER_ROLE } from '../../types/generic/roles';
export enum LG_RECORD_STAGE {
RECORD = 0,
@@ -104,7 +103,6 @@ function LloydGeorgeRecordPage() {
numberOfFiles={numberOfFiles}
patientDetails={patientDetails}
setStage={setStage}
- userType={USER_ROLE.GP}
setDownloadStage={setDownloadStage}
/>
)
diff --git a/app/src/pages/patientResultPage/PatientResultPage.test.tsx b/app/src/pages/patientResultPage/PatientResultPage.test.tsx
index 966c79e89..5713c9bd0 100644
--- a/app/src/pages/patientResultPage/PatientResultPage.test.tsx
+++ b/app/src/pages/patientResultPage/PatientResultPage.test.tsx
@@ -1,6 +1,5 @@
import { render, screen, waitFor } from '@testing-library/react';
import PatientResultPage from './PatientResultPage';
-import { USER_ROLE } from '../../types/generic/roles';
import PatientDetailsProvider from '../../providers/patientProvider/PatientProvider';
import { buildPatientDetails } from '../../helpers/test/testBuilders';
import { PatientDetails } from '../../types/generic/patientDetails';
@@ -9,9 +8,17 @@ import { createMemoryHistory } from 'history';
import userEvent from '@testing-library/user-event';
import { routes } from '../../types/generic/routes';
import { act } from 'react-dom/test-utils';
-import { REPOSITORY_ROLE } from '../../types/generic/authRole';
+import { REPOSITORY_ROLE, authorisedRoles } from '../../types/generic/authRole';
+import useRole from '../../helpers/hooks/useRole';
+
+jest.mock('../../helpers/hooks/useRole');
+const mockedUseRole = useRole as jest.Mock;
describe('PatientResultPage', () => {
+ beforeEach(() => {
+ process.env.REACT_APP_ENVIRONMENT = 'jest';
+ mockedUseRole.mockReturnValue(REPOSITORY_ROLE.PCSE);
+ });
afterEach(() => {
jest.clearAllMocks();
});
@@ -23,56 +30,66 @@ describe('PatientResultPage', () => {
expect(screen.getByText('Verify patient details')).toBeInTheDocument();
});
- it('displays the patient details page when patient data is found', async () => {
- const nhsNumber = '9000000000';
- const familyName = 'Smith';
- const patientDetails = buildPatientDetails({ familyName, nhsNumber });
+ it.each(authorisedRoles)(
+ "displays the patient details page when patient data is found when user role is '%s'",
+ async (role) => {
+ const nhsNumber = '9000000000';
+ const familyName = 'Smith';
+ const patientDetails = buildPatientDetails({ familyName, nhsNumber });
+ mockedUseRole.mockReturnValue(role);
- renderPatientResultPage(patientDetails);
+ renderPatientResultPage(patientDetails);
- expect(
- screen.getByRole('heading', { name: 'Verify patient details' }),
- ).toBeInTheDocument();
- expect(screen.getByText(familyName)).toBeInTheDocument();
- expect(
- screen.getByRole('button', { name: 'Accept details are correct' }),
- ).toBeInTheDocument();
- expect(screen.getByText(/If patient details are incorrect/)).toBeInTheDocument();
+ expect(
+ screen.getByRole('heading', { name: 'Verify patient details' }),
+ ).toBeInTheDocument();
+ expect(screen.getByText(familyName)).toBeInTheDocument();
+ expect(
+ screen.getByRole('button', { name: 'Accept details are correct' }),
+ ).toBeInTheDocument();
+ expect(screen.getByText(/If patient details are incorrect/)).toBeInTheDocument();
- const nationalServiceDeskLink = screen.getByRole('link', {
- name: /National Service Desk/,
- });
+ const nationalServiceDeskLink = screen.getByRole('link', {
+ name: /National Service Desk/,
+ });
- expect(nationalServiceDeskLink).toHaveAttribute(
- 'href',
- 'https://digital.nhs.uk/about-nhs-digital/contact-us#nhs-digital-service-desks',
- );
- expect(nationalServiceDeskLink).toHaveAttribute('target', '_blank');
- });
+ expect(nationalServiceDeskLink).toHaveAttribute(
+ 'href',
+ 'https://digital.nhs.uk/about-nhs-digital/contact-us#nhs-digital-service-desks',
+ );
+ expect(nationalServiceDeskLink).toHaveAttribute('target', '_blank');
+ },
+ );
- it('displays text specific to upload path if user has selected upload', async () => {
- const nhsNumber = '9000000000';
- const patientDetails = buildPatientDetails({ nhsNumber });
- const uploadRole = REPOSITORY_ROLE.GP_ADMIN;
+ it.each([REPOSITORY_ROLE.GP_ADMIN, REPOSITORY_ROLE.GP_CLINICAL])(
+ "displays text specific to upload path when user role is '%s'",
+ async (role) => {
+ const nhsNumber = '9000000000';
+ const patientDetails = buildPatientDetails({ nhsNumber });
- renderPatientResultPage(patientDetails, uploadRole);
+ mockedUseRole.mockReturnValue(role);
- expect(
- screen.getByRole('heading', { name: 'Verify patient details' }),
- ).toBeInTheDocument();
+ renderPatientResultPage(patientDetails);
- expect(
- screen.getByText(
- 'Ensure these patient details match the records and attachments that you upload',
- ),
- ).toBeInTheDocument();
- });
+ expect(
+ screen.getByRole('heading', { name: 'Verify patient details' }),
+ ).toBeInTheDocument();
+ expect(
+ screen.getByText(
+ 'Ensure these patient details match the records and attachments that you upload',
+ ),
+ ).toBeInTheDocument();
+ },
+ );
- it("doesn't display text specific to upload path if user has selected download", async () => {
+ it("doesn't display text specific to upload path when user role is PCSE", async () => {
const nhsNumber = '9000000000';
const patientDetails = buildPatientDetails({ nhsNumber });
- const downloadRole = REPOSITORY_ROLE.PCSE;
- renderPatientResultPage(patientDetails, downloadRole);
+
+ const role = REPOSITORY_ROLE.PCSE;
+ mockedUseRole.mockReturnValue(role);
+
+ renderPatientResultPage(patientDetails);
expect(
screen.getByRole('heading', { name: 'Verify patient details' }),
@@ -83,7 +100,6 @@ describe('PatientResultPage', () => {
expect(
screen.queryByRole('radio', { name: 'Inactive patient' }),
).not.toBeInTheDocument();
-
expect(
screen.queryByText(
'Ensure these patient details match the electronic health records and attachments you are about to upload.',
@@ -91,6 +107,32 @@ describe('PatientResultPage', () => {
).not.toBeInTheDocument();
});
+ it.each([REPOSITORY_ROLE.GP_ADMIN, REPOSITORY_ROLE.GP_CLINICAL])(
+ "displays an error message if 'active' boolean is missing on the patient, when role is '%s'",
+ async (role) => {
+ const history = createMemoryHistory({ initialEntries: ['/example'] });
+
+ mockedUseRole.mockReturnValue(role);
+
+ renderPatientResultPage({ active: undefined }, history);
+ expect(history.location.pathname).toBe('/example');
+
+ act(() => {
+ userEvent.click(
+ screen.getByRole('button', {
+ name: 'Accept details are correct',
+ }),
+ );
+ });
+
+ await waitFor(() => {
+ expect(
+ screen.getByText('We cannot determine the active state of this patient'),
+ ).toBeInTheDocument();
+ });
+ },
+ );
+
it('displays a message when NHS number is superseded', async () => {
const nhsNumber = '9000000012';
const patientDetails = buildPatientDetails({ superseded: true, nhsNumber });
@@ -127,76 +169,58 @@ describe('PatientResultPage', () => {
});
describe('Navigation', () => {
- it('navigates to LG record page when GP user selects Active patient and submits', async () => {
- const history = createMemoryHistory({
- initialEntries: ['/example'],
- initialIndex: 1,
- });
-
- const uploadRole = REPOSITORY_ROLE.GP_ADMIN;
-
- renderPatientResultPage({ active: true }, uploadRole, history);
- expect(history.location.pathname).toBe('/example');
-
- act(() => {
- userEvent.click(screen.getByRole('button', { name: 'Accept details are correct' }));
- });
-
- await waitFor(() => {
- expect(history.location.pathname).toBe(routes.LLOYD_GEORGE);
- });
- });
-
- it('navigates to Upload docs page when GP user selects Inactive patient and submits', async () => {
- const history = createMemoryHistory({
- initialEntries: ['/example'],
- initialIndex: 1,
- });
-
- const uploadRole = REPOSITORY_ROLE.GP_ADMIN;
-
- renderPatientResultPage({ active: false }, uploadRole, history);
- expect(history.location.pathname).toBe('/example');
- act(() => {
- userEvent.click(screen.getByRole('button', { name: 'Accept details are correct' }));
- });
-
- await waitFor(() => {
- expect(history.location.pathname).toBe(routes.UPLOAD_DOCUMENTS);
- });
- });
-
- it('Shows an error message if the active field is missing on the patient', async () => {
- const history = createMemoryHistory({
- initialEntries: ['/example'],
- initialIndex: 1,
- });
-
- const uploadRole = REPOSITORY_ROLE.GP_ADMIN;
-
- renderPatientResultPage({ active: undefined }, uploadRole, history);
- expect(history.location.pathname).toBe('/example');
-
- act(() => {
- userEvent.click(screen.getByRole('button', { name: 'Accept details are correct' }));
- });
-
- await waitFor(() => {
- expect(
- screen.getByText('We cannot determine the active state of this patient'),
- ).toBeInTheDocument();
- });
- });
-
- it('navigates to download page when user has verified download patient', async () => {
- const history = createMemoryHistory({
- initialEntries: ['/example'],
- initialIndex: 1,
- });
-
- const downloadRole = REPOSITORY_ROLE.PCSE;
-
- renderPatientResultPage({}, downloadRole, history);
+ it.each([REPOSITORY_ROLE.GP_ADMIN, REPOSITORY_ROLE.GP_CLINICAL])(
+ "navigates to Upload docs page after user selects Inactive patient when role is '%s'",
+ async (role) => {
+ const history = createMemoryHistory({
+ initialEntries: ['/example'],
+ });
+
+ mockedUseRole.mockReturnValue(role);
+ renderPatientResultPage({ active: false }, history);
+ expect(history.location.pathname).toBe('/example');
+ act(() => {
+ userEvent.click(
+ screen.getByRole('button', {
+ name: 'Accept details are correct',
+ }),
+ );
+ });
+
+ await waitFor(() => {
+ expect(history.location.pathname).toBe(routes.UPLOAD_DOCUMENTS);
+ });
+ },
+ );
+
+ it.each([REPOSITORY_ROLE.GP_ADMIN, REPOSITORY_ROLE.GP_CLINICAL])(
+ "navigates to Lloyd George Record page after user selects Active patient, when role is '%s'",
+ async (role) => {
+ const history = createMemoryHistory({ initialEntries: ['/example'] });
+ mockedUseRole.mockReturnValue(role);
+
+ renderPatientResultPage({ active: true }, history);
+ expect(history.location.pathname).toBe('/example');
+
+ act(() => {
+ userEvent.click(
+ screen.getByRole('button', { name: 'Accept details are correct' }),
+ );
+ });
+
+ await waitFor(() => {
+ expect(history.location.pathname).toBe(routes.LLOYD_GEORGE);
+ });
+ },
+ );
+
+ it('navigates to ARF Download page when user selects Verify patient, when role is PCSE', async () => {
+ const history = createMemoryHistory({ initialEntries: ['/example'] });
+
+ const role = REPOSITORY_ROLE.PCSE;
+ mockedUseRole.mockReturnValue(role);
+
+ renderPatientResultPage({}, history);
expect(history.location.pathname).toBe('/example');
userEvent.click(screen.getByRole('button', { name: 'Accept details are correct' }));
@@ -211,11 +235,7 @@ describe('PatientResultPage', () => {
const homeRoute = '/example';
const renderPatientResultPage = (
patientOverride: Partial = {},
- role: REPOSITORY_ROLE = REPOSITORY_ROLE.PCSE,
- history = createMemoryHistory({
- initialEntries: [homeRoute],
- initialIndex: 1,
- }),
+ history = createMemoryHistory({ initialEntries: ['/example'] }),
) => {
const patient: PatientDetails = {
...buildPatientDetails(),
@@ -225,7 +245,7 @@ const renderPatientResultPage = (
render(
-
+
,
);
diff --git a/app/src/pages/patientResultPage/PatientResultPage.tsx b/app/src/pages/patientResultPage/PatientResultPage.tsx
index 698bf73b2..8f53162cb 100644
--- a/app/src/pages/patientResultPage/PatientResultPage.tsx
+++ b/app/src/pages/patientResultPage/PatientResultPage.tsx
@@ -8,12 +8,10 @@ import BackButton from '../../components/generic/backButton/BackButton';
import { useForm } from 'react-hook-form';
import ErrorBox from '../../components/layout/errorBox/ErrorBox';
import { REPOSITORY_ROLE } from '../../types/generic/authRole';
+import useRole from '../../helpers/hooks/useRole';
-type Props = {
- role: REPOSITORY_ROLE;
-};
-
-function PatientResultPage({ role }: Props) {
+function PatientResultPage() {
+ const role = useRole();
const userIsPCSE = role === REPOSITORY_ROLE.PCSE;
const userIsGPAdmin = role === REPOSITORY_ROLE.GP_ADMIN;
const userIsGPClinical = role === REPOSITORY_ROLE.GP_CLINICAL;
diff --git a/app/src/pages/patientSearchPage/PatientSearchPage.test.tsx b/app/src/pages/patientSearchPage/PatientSearchPage.test.tsx
index ac7aed051..8fb777daf 100644
--- a/app/src/pages/patientSearchPage/PatientSearchPage.test.tsx
+++ b/app/src/pages/patientSearchPage/PatientSearchPage.test.tsx
@@ -1,5 +1,4 @@
import { render, screen, waitFor } from '@testing-library/react';
-import { USER_ROLE } from '../../types/generic/roles';
import PatientDetailsProvider from '../../providers/patientProvider/PatientProvider';
import { PatientDetails } from '../../types/generic/patientDetails';
import * as ReactRouter from 'react-router';
@@ -9,50 +8,68 @@ import userEvent from '@testing-library/user-event';
import { buildPatientDetails } from '../../helpers/test/testBuilders';
import axios from 'axios';
import { routes } from '../../types/generic/routes';
-import { REPOSITORY_ROLE } from '../../types/generic/authRole';
+import { REPOSITORY_ROLE, authorisedRoles } from '../../types/generic/authRole';
+import useRole from '../../helpers/hooks/useRole';
jest.mock('../../helpers/hooks/useBaseAPIHeaders');
+jest.mock('../../helpers/hooks/useRole');
jest.mock('axios');
const mockedAxios = axios as jest.Mocked;
+const mockedUseRole = useRole as jest.Mock;
describe('PatientSearchPage', () => {
beforeEach(() => {
process.env.REACT_APP_ENVIRONMENT = 'jest';
+ mockedUseRole.mockReturnValue(REPOSITORY_ROLE.PCSE);
});
afterEach(() => {
jest.clearAllMocks();
});
describe('Rendering', () => {
- it('displays component with patient details', () => {
- renderPatientSearchPage();
- expect(screen.getByText('Search for patient')).toBeInTheDocument();
- expect(screen.getByRole('textbox', { name: 'Enter NHS number' })).toBeInTheDocument();
- expect(
- screen.getByText('A 10-digit number, for example, 485 777 3456'),
- ).toBeInTheDocument();
- expect(screen.getByRole('button', { name: 'Search' })).toBeInTheDocument();
- });
+ it.each(authorisedRoles)(
+ "renders the page with patient search form when user role is '%s'",
+ async (role) => {
+ mockedUseRole.mockReturnValue(role);
- it('displays a loading spinner when the patients details are being requested', async () => {
- mockedAxios.get.mockImplementation(async () => {
- await new Promise((resolve) => {
- setTimeout(() => {
- // To delay the mock request, and give a chance for the spinner to appear
- resolve(null);
- }, 500);
+ renderPatientSearchPage();
+ expect(screen.getByText('Search for patient')).toBeInTheDocument();
+ expect(
+ screen.getByRole('textbox', { name: 'Enter NHS number' }),
+ ).toBeInTheDocument();
+ expect(
+ screen.getByText('A 10-digit number, for example, 485 777 3456'),
+ ).toBeInTheDocument();
+ expect(screen.getByRole('button', { name: 'Search' })).toBeInTheDocument();
+ },
+ );
+ it.each(authorisedRoles)(
+ "rdisplays a loading spinner when the patients details are being requested when user role is '%s'",
+ async (role) => {
+ mockedUseRole.mockReturnValue(role);
+
+ mockedAxios.get.mockImplementation(async () => {
+ await new Promise((resolve) => {
+ setTimeout(() => {
+ // To delay the mock request, and give a chance for the spinner to appear
+ resolve(null);
+ }, 500);
+ });
+ return Promise.resolve({ data: buildPatientDetails() });
});
- return Promise.resolve({ data: buildPatientDetails() });
- });
- renderPatientSearchPage();
+ renderPatientSearchPage();
- userEvent.type(screen.getByRole('textbox', { name: 'Enter NHS number' }), '9000000009');
- userEvent.click(screen.getByRole('button', { name: 'Search' }));
+ userEvent.type(
+ screen.getByRole('textbox', { name: 'Enter NHS number' }),
+ '9000000009',
+ );
+ userEvent.click(screen.getByRole('button', { name: 'Search' }));
- await waitFor(() => {
- expect(screen.getByRole('SpinnerButton')).toBeInTheDocument();
- });
- });
+ await waitFor(() => {
+ expect(screen.getByRole('SpinnerButton')).toBeInTheDocument();
+ });
+ },
+ );
it('displays an input error when user attempts to submit an invalid NHS number', async () => {
renderPatientSearchPage();
@@ -117,17 +134,16 @@ describe('PatientSearchPage', () => {
});
describe('Navigation', () => {
- it('navigates to verify page when download patient is requested', async () => {
- const history = createMemoryHistory({
- initialEntries: ['/example'],
- initialIndex: 1,
- });
+ it('navigates to download journey when role is PCSE', async () => {
+ const history = createMemoryHistory({ initialEntries: ['/example'] });
mockedAxios.get.mockImplementation(() =>
Promise.resolve({ data: buildPatientDetails() }),
);
+ const role = REPOSITORY_ROLE.PCSE;
+ mockedUseRole.mockReturnValue(role);
- renderPatientSearchPage({}, REPOSITORY_ROLE.PCSE, history);
+ renderPatientSearchPage({}, history);
expect(history.location.pathname).toBe('/example');
userEvent.type(screen.getByRole('textbox', { name: 'Enter NHS number' }), '9000000000');
userEvent.click(screen.getByRole('button', { name: 'Search' }));
@@ -137,30 +153,34 @@ describe('PatientSearchPage', () => {
});
});
- it('navigates to verify page when upload patient is requested', async () => {
- const history = createMemoryHistory({
- initialEntries: ['/example'],
- initialIndex: 1,
- });
+ it.each([REPOSITORY_ROLE.GP_ADMIN, REPOSITORY_ROLE.GP_CLINICAL])(
+ "navigates to upload journey when role is '%s'",
+ async (role) => {
+ const history = createMemoryHistory({
+ initialEntries: ['/example'],
+ });
- mockedAxios.get.mockImplementation(() =>
- Promise.resolve({ data: buildPatientDetails() }),
- );
- renderPatientSearchPage({}, REPOSITORY_ROLE.GP_ADMIN, history);
- expect(history.location.pathname).toBe('/example');
- userEvent.type(screen.getByRole('textbox', { name: 'Enter NHS number' }), '9000000000');
- userEvent.click(screen.getByRole('button', { name: 'Search' }));
+ mockedAxios.get.mockImplementation(() =>
+ Promise.resolve({ data: buildPatientDetails() }),
+ );
+ mockedUseRole.mockReturnValue(role);
+ renderPatientSearchPage({}, history);
+ expect(history.location.pathname).toBe('/example');
+ userEvent.type(
+ screen.getByRole('textbox', { name: 'Enter NHS number' }),
+ '9000000000',
+ );
+ userEvent.click(screen.getByRole('button', { name: 'Search' }));
- await waitFor(() => {
- expect(history.location.pathname).toBe(routes.UPLOAD_VERIFY);
- });
- });
+ await waitFor(() => {
+ expect(history.location.pathname).toBe(routes.UPLOAD_VERIFY);
+ });
+ },
+ );
it('navigates to start page when user is unauthorized to make request', async () => {
- const history = createMemoryHistory({
- initialEntries: ['/example'],
- initialIndex: 1,
- });
+ const history = createMemoryHistory({ initialEntries: ['/example'] });
+
const errorResponse = {
response: {
status: 403,
@@ -170,7 +190,7 @@ describe('PatientSearchPage', () => {
mockedAxios.get.mockImplementation(() => Promise.reject(errorResponse));
- renderPatientSearchPage({}, REPOSITORY_ROLE.GP_ADMIN, history);
+ renderPatientSearchPage({}, history);
expect(history.location.pathname).toBe('/example');
userEvent.type(screen.getByRole('textbox', { name: 'Enter NHS number' }), '9000000000');
userEvent.click(screen.getByRole('button', { name: 'Search' }));
@@ -193,7 +213,7 @@ describe('PatientSearchPage', () => {
Promise.resolve({ data: buildPatientDetails() }),
);
- renderPatientSearchPage({}, REPOSITORY_ROLE.PCSE, history);
+ renderPatientSearchPage({}, history);
expect(history.location.pathname).toBe('/example');
userEvent.type(screen.getByRole('textbox', { name: 'Enter NHS number' }), testNumber);
userEvent.click(screen.getByRole('button', { name: 'Search' }));
@@ -205,16 +225,13 @@ describe('PatientSearchPage', () => {
it('allows NHS number with dashes to be submitted', async () => {
const testNumber = '900-000-0000';
- const history = createMemoryHistory({
- initialEntries: ['/example'],
- initialIndex: 1,
- });
+ const history = createMemoryHistory({ initialEntries: ['/example'] });
mockedAxios.get.mockImplementation(() =>
Promise.resolve({ data: buildPatientDetails() }),
);
- renderPatientSearchPage({}, REPOSITORY_ROLE.PCSE, history);
+ renderPatientSearchPage({}, history);
expect(history.location.pathname).toBe('/example');
userEvent.type(screen.getByRole('textbox', { name: 'Enter NHS number' }), testNumber);
userEvent.click(screen.getByRole('button', { name: 'Search' }));
@@ -252,10 +269,8 @@ describe('PatientSearchPage', () => {
const testRoute = '/example';
const renderPatientSearchPage = (
patientOverride: Partial = {},
- role: REPOSITORY_ROLE = REPOSITORY_ROLE.PCSE,
history = createMemoryHistory({
initialEntries: [testRoute],
- initialIndex: 1,
}),
) => {
const patient: PatientDetails = {
@@ -266,7 +281,7 @@ const renderPatientSearchPage = (
render(
-
+
,
);
diff --git a/app/src/pages/patientSearchPage/PatientSearchPage.tsx b/app/src/pages/patientSearchPage/PatientSearchPage.tsx
index e238b26ea..0461f843f 100644
--- a/app/src/pages/patientSearchPage/PatientSearchPage.tsx
+++ b/app/src/pages/patientSearchPage/PatientSearchPage.tsx
@@ -19,15 +19,13 @@ import { PatientDetails } from '../../types/generic/patientDetails';
import { buildPatientDetails } from '../../helpers/test/testBuilders';
import { isMock } from '../../helpers/utils/isLocal';
import useBaseAPIHeaders from '../../helpers/hooks/useBaseAPIHeaders';
-
-type Props = {
- role: REPOSITORY_ROLE;
-};
+import useRole from '../../helpers/hooks/useRole';
export const incorrectFormatMessage = "Enter patient's 10 digit NHS number";
-function PatientSearchPage({ role }: Props) {
+function PatientSearchPage() {
const [, setPatientDetails] = usePatientDetailsContext();
+ const role = useRole();
const [submissionState, setSubmissionState] = useState(SEARCH_STATES.IDLE);
const [statusCode, setStatusCode] = useState(null);
const [inputError, setInputError] = useState(null);
diff --git a/app/src/pages/roleSelectPage/RoleSelectPage.tsx b/app/src/pages/roleSelectPage/RoleSelectPage.tsx
index 7fe37bcee..54e94db41 100644
--- a/app/src/pages/roleSelectPage/RoleSelectPage.tsx
+++ b/app/src/pages/roleSelectPage/RoleSelectPage.tsx
@@ -7,6 +7,9 @@ import ErrorBox from '../../components/layout/errorBox/ErrorBox';
import { useSessionContext } from '../../providers/sessionProvider/SessionProvider';
function RoleSelectPage() {
+ /**
+ * [DEPRECATED] Removed from routing
+ */
const navigate = useNavigate();
const [inputError, setInputError] = useState('');
const [session, setSession] = useSessionContext();
diff --git a/app/src/types/generic/authRole.ts b/app/src/types/generic/authRole.ts
index 16e3ccaff..67b665860 100644
--- a/app/src/types/generic/authRole.ts
+++ b/app/src/types/generic/authRole.ts
@@ -3,3 +3,5 @@ export enum REPOSITORY_ROLE {
GP_CLINICAL = 'GP_CLINICAL',
PCSE = 'PCSE',
}
+
+export const authorisedRoles: Array = Object.values(REPOSITORY_ROLE);
diff --git a/app/src/types/generic/routes.ts b/app/src/types/generic/routes.ts
index fd73f1492..ab05ac056 100644
--- a/app/src/types/generic/routes.ts
+++ b/app/src/types/generic/routes.ts
@@ -1,6 +1,5 @@
export enum routes {
HOME = '/',
- SELECT_ORG = '/select-organisation',
AUTH_CALLBACK = '/auth-callback',
NOT_FOUND = '/*',
UNAUTHORISED = '/unauthorised',