Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

PRMDR 448 UI RBAC #152

Merged
merged 18 commits into from
Nov 20, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions app/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion app/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -87,4 +87,4 @@
"axios": "axios/dist/node/axios.cjs"
}
}
}
}
80 changes: 2 additions & 78 deletions app/src/App.tsx
Original file line number Diff line number Diff line change
@@ -1,93 +1,17 @@
import React from 'react';
import './styles/App.scss';
import HomePage from './pages/homePage/HomePage';
import ConfigProvider from './providers/configProvider/ConfigProvider';
import config from './config';
import { routes } from './types/generic/routes';
import Layout from './components/layout/Layout';
import PatientDetailsProvider from './providers/patientProvider/PatientProvider';
import { BrowserRouter as Router, Route, Routes, Outlet } from 'react-router-dom';
import SessionProvider from './providers/sessionProvider/SessionProvider';
import AuthCallbackPage from './pages/authCallbackPage/AuthCallbackPage';
import NotFoundPage from './pages/notFoundPage/NotFoundPage';
import UnauthorisedPage from './pages/unauthorisedPage/UnauthorisedPage';
import AuthGuard from './components/blocks/authGuard/AuthGuard';
import PatientSearchPage from './pages/patientSearchPage/PatientSearchPage';
import LogoutPage from './pages/logoutPage/LogoutPage';
import PatientGuard from './components/blocks/patientGuard/PatientGuard';
import PatientResultPage from './pages/patientResultPage/PatientResultPage';
import UploadDocumentsPage from './pages/uploadDocumentsPage/UploadDocumentsPage';
import DocumentSearchResultsPage from './pages/documentSearchResultsPage/DocumentSearchResultsPage';
import AuthErrorPage from './pages/authErrorPage/AuthErrorPage';
import LloydGeorgeRecordPage from './pages/lloydGeorgeRecordPage/LloydGeorgeRecordPage';
import AppRouter from './router/AppRouter';

function App() {
return (
<ConfigProvider config={config}>
<SessionProvider>
<PatientDetailsProvider>
<Router>
<Layout>
<Routes>
<Route element={<HomePage />} path={routes.HOME} />

<Route element={<NotFoundPage />} path={routes.NOT_FOUND} />
<Route element={<UnauthorisedPage />} path={routes.UNAUTHORISED} />
<Route element={<AuthErrorPage />} path={routes.AUTH_ERROR} />

<Route element={<AuthCallbackPage />} path={routes.AUTH_CALLBACK} />

<Route
element={
<AuthGuard>
<Outlet />
</AuthGuard>
}
>
{[routes.DOWNLOAD_SEARCH, routes.UPLOAD_SEARCH].map(
(searchRoute) => (
<Route
key={searchRoute}
element={<PatientSearchPage />}
path={searchRoute}
/>
),
)}

<Route element={<LogoutPage />} path={routes.LOGOUT} />
<Route
element={
<PatientGuard>
<Outlet />
</PatientGuard>
}
>
{[routes.DOWNLOAD_VERIFY, routes.UPLOAD_VERIFY].map(
(searchResultRoute) => (
<Route
key={searchResultRoute}
element={<PatientResultPage />}
path={searchResultRoute}
/>
),
)}
<Route
element={<LloydGeorgeRecordPage />}
path={routes.LLOYD_GEORGE}
/>
<Route
element={<UploadDocumentsPage />}
path={routes.UPLOAD_DOCUMENTS}
/>
<Route
element={<DocumentSearchResultsPage />}
path={routes.DOWNLOAD_DOCUMENTS}
/>
</Route>
</Route>
</Routes>
</Layout>
</Router>
<AppRouter />
</PatientDetailsProvider>
</SessionProvider>
</ConfigProvider>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,14 @@ 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';
import * as ReactRouter from 'react-router';
import { createMemoryHistory } from 'history';
import useRole from '../../../helpers/hooks/useRole';
import { REPOSITORY_ROLE, authorisedRoles } from '../../../types/generic/authRole';
import { routes } from '../../../types/generic/routes';
import { LG_RECORD_STAGE } from '../../../types/blocks/lloydGeorgeStages';

jest.mock('../../../helpers/hooks/useBaseAPIHeaders');
jest.mock('../../../helpers/hooks/useRole');
Expand Down Expand Up @@ -60,23 +60,20 @@ describe('DeleteDocumentsStage', () => {
},
);

it.each([REPOSITORY_ROLE.GP_ADMIN, REPOSITORY_ROLE.GP_CLINICAL])(
"renders LgRecordStage when No is selected and Continue clicked, when user role is '%s'",
async (role) => {
mockedUseRole.mockReturnValue(role);
it('renders DocumentSearchResults when No is selected and Continue clicked, when user role is GP Admin', async () => {
mockedUseRole.mockReturnValue(REPOSITORY_ROLE.GP_ADMIN);

renderComponent(DOCUMENT_TYPE.LLOYD_GEORGE);
renderComponent(DOCUMENT_TYPE.LLOYD_GEORGE);

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

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

it('renders DocumentSearchResults when No is selected and Continue clicked, when user role is PCSE', async () => {
mockedUseRole.mockReturnValue(REPOSITORY_ROLE.PCSE);
Expand All @@ -93,7 +90,22 @@ describe('DeleteDocumentsStage', () => {
});
});

it.each([REPOSITORY_ROLE.GP_ADMIN, REPOSITORY_ROLE.GP_CLINICAL, REPOSITORY_ROLE.PCSE])(
it('does not render a view DocumentSearchResults when No is selected and Continue clicked, when user role is GP Clinical', async () => {
mockedUseRole.mockReturnValue(REPOSITORY_ROLE.GP_ADMIN);

renderComponent(DOCUMENT_TYPE.LLOYD_GEORGE);

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

await waitFor(() => {
expect(mockSetStage).toHaveBeenCalledTimes(0);
});
});

it.each([REPOSITORY_ROLE.GP_ADMIN, REPOSITORY_ROLE.PCSE])(
"renders DeletionConfirmationStage when the Yes is selected and Continue clicked, when user role is '%s'",
async (role) => {
mockedAxios.delete.mockReturnValue(
Expand All @@ -117,6 +129,25 @@ describe('DeleteDocumentsStage', () => {
},
);

it('does not render DeletionConfirmationStage when the Yes is selected, Continue clicked, and user role is GP Clinical', async () => {
mockedAxios.delete.mockReturnValue(Promise.resolve({ status: 200, data: 'Success' }));
mockedUseRole.mockReturnValue(REPOSITORY_ROLE.GP_CLINICAL);

renderComponent(DOCUMENT_TYPE.LLOYD_GEORGE);

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.queryByText('Deletion complete')).not.toBeInTheDocument();
});
});

it('renders a service error when service is down', async () => {
const errorResponse = {
response: {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ 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, { DeleteResponse } from '../../../helpers/requests/deleteAllDocuments';
import { useBaseAPIUrl } from '../../../providers/configProvider/ConfigProvider';
Expand All @@ -19,6 +18,7 @@ import { routes } from '../../../types/generic/routes';
import { useNavigate } from 'react-router-dom';
import useRole from '../../../helpers/hooks/useRole';
import { REPOSITORY_ROLE } from '../../../types/generic/authRole';
import { LG_RECORD_STAGE } from '../../../types/blocks/lloydGeorgeStages';

export type Props = {
docType: DOCUMENT_TYPE;
Expand Down Expand Up @@ -96,19 +96,21 @@ function DeleteDocumentsStage({
};

const handleNoOption = () => {
const isGp = role === REPOSITORY_ROLE.GP_ADMIN || role === REPOSITORY_ROLE.GP_CLINICAL;
if (isGp && setStage) {
if (role === REPOSITORY_ROLE.GP_ADMIN && setStage) {
setStage(LG_RECORD_STAGE.RECORD);
} else if (role === REPOSITORY_ROLE.PCSE && setIsDeletingDocuments) {
setIsDeletingDocuments(false);
}
};

const submit = async (fieldValues: FieldValues) => {
if (fieldValues.deleteDocs === DELETE_DOCUMENTS_OPTION.YES) {
await handleYesOption();
} else if (fieldValues.deleteDocs === DELETE_DOCUMENTS_OPTION.NO) {
handleNoOption();
const allowedRoles = [REPOSITORY_ROLE.GP_ADMIN, REPOSITORY_ROLE.PCSE];
if (role && allowedRoles.includes(role)) {
if (fieldValues.deleteDocs === DELETE_DOCUMENTS_OPTION.YES) {
await handleYesOption();
} else if (fieldValues.deleteDocs === DELETE_DOCUMENTS_OPTION.NO) {
handleNoOption();
}
}
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,12 @@ import { buildLgSearchResult, buildPatientDetails } from '../../../helpers/test/
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';
import * as ReactRouter from 'react-router';
import { createMemoryHistory } from 'history';
import { routes } from '../../../types/generic/routes';
import useRole from '../../../helpers/hooks/useRole';
import { REPOSITORY_ROLE } from '../../../types/generic/authRole';

import { LG_RECORD_STAGE } from '../../../types/blocks/lloydGeorgeStages';
jest.mock('../../../helpers/hooks/useRole');
const mockedUseRole = useRole as jest.Mock;

Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
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';
import { routes } from '../../../types/generic/routes';
import { Link } from 'react-router-dom';
import { useNavigate } from 'react-router';
import { formatNhsNumber } from '../../../helpers/utils/formatNhsNumber';
import useRole from '../../../helpers/hooks/useRole';
import { REPOSITORY_ROLE } from '../../../types/generic/authRole';
import { LG_RECORD_STAGE } from '../../../types/blocks/lloydGeorgeStages';

export type Props = {
numberOfFiles: number;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,13 @@ import React, {
} from 'react';
import { Card } from 'nhsuk-react-components';
import { Link } from 'react-router-dom';
import { LG_RECORD_STAGE } from '../../../pages/lloydGeorgeRecordPage/LloydGeorgeRecordPage';
import { PatientDetails } from '../../../types/generic/patientDetails';
import { useBaseAPIUrl } from '../../../providers/configProvider/ConfigProvider';
import useBaseAPIHeaders from '../../../helpers/hooks/useBaseAPIHeaders';
import getPresignedUrlForZip from '../../../helpers/requests/getPresignedUrlForZip';
import { DOCUMENT_TYPE } from '../../../types/pages/UploadDocumentsPage/types';
import LgDownloadComplete from '../lloydGeorgeDownloadComplete/LloydGeorgeDownloadComplete';
import { LG_RECORD_STAGE } from '../../../types/blocks/lloydGeorgeStages';
const FakeProgress = require('fake-progress');

export type Props = {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { buildPatientDetails } from '../../../helpers/test/testBuilders';
import { LG_RECORD_STAGE } from '../../../pages/lloydGeorgeRecordPage/LloydGeorgeRecordPage';
import { LG_RECORD_STAGE } from '../../../types/blocks/lloydGeorgeStages';
import { PatientDetails } from '../../../types/generic/patientDetails';
import LgDownloadComplete, { Props } from './LloydGeorgeDownloadComplete';
import { render, screen, waitFor } from '@testing-library/react';
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import React, { Dispatch, SetStateAction } from 'react';
import { PatientDetails } from '../../../types/generic/patientDetails';
import { Button, Card } from 'nhsuk-react-components';
import { LG_RECORD_STAGE } from '../../../pages/lloydGeorgeRecordPage/LloydGeorgeRecordPage';
import { LG_RECORD_STAGE } from '../../../types/blocks/lloydGeorgeStages';

export type Props = {
patientDetails: PatientDetails;
Expand Down
Loading
Loading