Skip to content

Commit

Permalink
PRMDR-185 Auth Tests (#85)
Browse files Browse the repository at this point in the history
Add CIS2 unit tests
  • Loading branch information
RioKnightleyNHS authored Oct 11, 2023
1 parent 502f55e commit e76be5f
Show file tree
Hide file tree
Showing 20 changed files with 494 additions and 50 deletions.
1 change: 1 addition & 0 deletions app/.eslintrc
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@
"ignoreComments": true
}
],
"testing-library/no-unnecessary-act": "off",
"no-plusplus": [
"error",
{
Expand Down
3 changes: 3 additions & 0 deletions app/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import PatientResultPage from './pages/patientResultPage/PatientResultPage';
import UploadDocumentsPage from './pages/uploadDocumentsPage/UploadDocumentsPage';
import DocumentSearchResultsPage from './pages/documentSearchResultsPage/DocumentSearchResultsPage';
import LloydGeorgeRecordPage from './pages/lloydGeorgeRecordPage/LloydGeorgeRecordPage';
import AuthErrorPage from './pages/authErrorPage/AuthErrorPage';

function App() {
return (
Expand All @@ -34,6 +35,8 @@ function App() {

<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={<RoleSelectPage />} path={routes.SELECT_ORG} />

Expand Down
65 changes: 65 additions & 0 deletions app/src/components/blocks/authGuard/AuthGuard.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
import { render, waitFor } from '@testing-library/react';
import SessionProvider, { Session } from '../../../providers/sessionProvider/SessionProvider';
import * as ReactRouter from 'react-router';
import { History, createMemoryHistory } from 'history';
import { buildUserAuth } from '../../../helpers/test/testBuilders';
import AuthGuard from './AuthGuard';
import { routes } from '../../../types/generic/routes';

const guardPage = '/profile';
describe('AuthGuard', () => {
beforeEach(() => {
sessionStorage.setItem('UserSession', '');

process.env.REACT_APP_ENVIRONMENT = 'jest';
});
afterEach(() => {
jest.clearAllMocks();
});
it('navigates user to unauthorised when user is not logged in', async () => {
const auth: Session = {
auth: null,
isLoggedIn: false,
};
const history = createMemoryHistory({
initialEntries: [guardPage],
initialIndex: 0,
});
expect(history.location.pathname).toBe(guardPage);
renderAuthGuard(auth, history);

await waitFor(async () => {
expect(history.location.pathname).toBe(routes.UNAUTHORISED);
});
});

it('navigates user to correct page when user is logged in', async () => {
const auth: Session = {
auth: buildUserAuth(),
isLoggedIn: true,
};
const history = createMemoryHistory({
initialEntries: [guardPage],
initialIndex: 0,
});
expect(history.location.pathname).toBe(guardPage);
renderAuthGuard(auth, history);

await waitFor(async () => {
expect(history.location.pathname).toBe(guardPage);
});
});
});

const renderAuthGuard = (session: Session, history: History) => {
render(
<SessionProvider sessionOverride={session}>
<ReactRouter.Router navigator={history} location={history.location}>
<AuthGuard>
<div>User is logged in</div>
</AuthGuard>
</ReactRouter.Router>
,
</SessionProvider>,
);
};
59 changes: 59 additions & 0 deletions app/src/components/blocks/patientGuard/PatientGuard.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import { render, waitFor } from '@testing-library/react';
import * as ReactRouter from 'react-router';
import { History, createMemoryHistory } from 'history';
import { routes } from '../../../types/generic/routes';
import PatientGuard from './PatientGuard';
import { PatientDetails } from '../../../types/generic/patientDetails';
import PatientDetailsProvider from '../../../providers/patientProvider/PatientProvider';
import { buildPatientDetails } from '../../../helpers/test/testBuilders';

const guardPage = '/profile';
describe('AuthGuard', () => {
beforeEach(() => {
process.env.REACT_APP_ENVIRONMENT = 'jest';
});
afterEach(() => {
jest.clearAllMocks();
});
it('navigates user to unauthorised when no patient is searched', async () => {
const patientDetails = null;
const history = createMemoryHistory({
initialEntries: [guardPage],
initialIndex: 0,
});
expect(history.location.pathname).toBe(guardPage);
renderAuthGuard(patientDetails, history);

await waitFor(async () => {
expect(history.location.pathname).toBe(routes.UNAUTHORISED);
});
});

it('navigates user to correct page when patient is searched', async () => {
const patientDetails = buildPatientDetails();
const history = createMemoryHistory({
initialEntries: [guardPage],
initialIndex: 0,
});
expect(history.location.pathname).toBe(guardPage);
renderAuthGuard(patientDetails, history);

await waitFor(async () => {
expect(history.location.pathname).toBe(guardPage);
});
});
});

const renderAuthGuard = (patient: PatientDetails | null, history: History) => {
return render(
<PatientDetailsProvider patientDetails={patient}>
<ReactRouter.Router navigator={history} location={history.location}>
<PatientGuard>
<div>patient number: {patient?.nhsNumber}</div>
<div>patient postcode: {patient?.postalCode}</div>
</PatientGuard>
</ReactRouter.Router>
,
</PatientDetailsProvider>,
);
};
1 change: 0 additions & 1 deletion app/src/components/blocks/selectStage/SelectStage.test.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
/* eslint-disable testing-library/no-unnecessary-act */
import { render, screen, waitFor } from '@testing-library/react';
import SelectStage from './SelectStage';
import {
Expand Down
33 changes: 20 additions & 13 deletions app/src/helpers/requests/documentSearchResults.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,30 +2,37 @@ import { AuthHeaders } from '../../types/blocks/authHeaders';
import { endpoints } from '../../types/generic/endpoints';
import { SearchResult } from '../../types/generic/searchResult';

import axios from 'axios';
import axios, { AxiosError } from 'axios';

type Args = {
nhsNumber: string;
baseUrl: string;
baseHeaders: AuthHeaders;
};

type GetDocumentSearchResultsResponse = {
data: Array<SearchResult>;
};
type GetDocumentSearchResultsResponse =
| {
data: Array<SearchResult>;
}
| undefined;

const getDocumentSearchResults = async ({ nhsNumber, baseUrl, baseHeaders }: Args) => {
const gatewayUrl = baseUrl + endpoints.DOCUMENT_SEARCH;

const { data }: GetDocumentSearchResultsResponse = await axios.get(gatewayUrl, {
headers: {
...baseHeaders,
},
params: {
patientId: nhsNumber,
},
});
return data;
try {
const response: GetDocumentSearchResultsResponse = await axios.get(gatewayUrl, {
headers: {
...baseHeaders,
},
params: {
patientId: nhsNumber,
},
});
return response?.data;
} catch (e) {
const error = e as AxiosError;
throw error;
}
};

export default getDocumentSearchResults;
116 changes: 116 additions & 0 deletions app/src/pages/authCallbackPage/AuthCallbackPage.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
import { render, screen, waitFor } from '@testing-library/react';
import AuthCallbackPage from './AuthCallbackPage';
import SessionProvider from '../../providers/sessionProvider/SessionProvider';
import * as ReactRouter from 'react-router';
import { History, createMemoryHistory } from 'history';
import axios from 'axios';
import { buildUserAuth } from '../../helpers/test/testBuilders';
import { routes } from '../../types/generic/routes';
jest.mock('axios');

const mockedAxios = axios as jest.Mocked<typeof axios>;
const params = {
code: 'cis2-code',
state: 'cis2-state',
id: 'cis2-id',
};

const codeAndStateQueryParams = `code=${params.code}&state=${params.state}`;
const allQueryParams = `?${codeAndStateQueryParams}&client_id=${params.id}`;
const baseUiUrl = 'http://localhost:3000' + allQueryParams;
const originalWindowLocation = window.location;

const currentPage = '/example';

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

Object.defineProperty(window, 'location', {
configurable: true,
enumerable: true,
value: new URL(baseUiUrl),
});
});
afterEach(() => {
jest.clearAllMocks();
Object.defineProperty(window, 'location', {
configurable: true,
enumerable: true,
value: originalWindowLocation,
});
});

it('returns a loading state until redirection to token request handler', async () => {
const history = createMemoryHistory({
initialEntries: [currentPage],
initialIndex: 0,
});

mockedAxios.get.mockImplementation(() => Promise.resolve({ data: buildUserAuth() }));
renderCallbackPage(history);
expect(screen.getByRole('status')).toBeInTheDocument();
expect(screen.getByText('Logging in...')).toBeInTheDocument();
await waitFor(() => {
expect(history.location.pathname).toBe(routes.SELECT_ORG);
});
});

it('navigates to the select role page when callback token request is successful', async () => {
const history = createMemoryHistory({
initialEntries: [currentPage],
initialIndex: 0,
});

mockedAxios.get.mockImplementation(() => Promise.resolve({ data: buildUserAuth() }));
renderCallbackPage(history);

expect(screen.getByRole('status')).toBeInTheDocument();
expect(screen.getByText('Logging in...')).toBeInTheDocument();
expect(history.location.pathname).toBe(currentPage);

await waitFor(() => {
expect(history.location.pathname).toBe(routes.SELECT_ORG);
});
});

it('navigates to auth error page when callback token request is unsuccessful', async () => {
const errorResponse = {
response: {
status: 400,
message: '400 Bad Request',
},
};
const history = createMemoryHistory({
initialEntries: [currentPage],
initialIndex: 0,
});

mockedAxios.get.mockImplementation(() => Promise.reject(errorResponse));
renderCallbackPage(history);

expect(screen.getByRole('status')).toBeInTheDocument();
expect(screen.getByText('Logging in...')).toBeInTheDocument();
expect(history.location.pathname).toBe(currentPage);

await waitFor(() => {
expect(history.location.pathname).toBe(routes.AUTH_ERROR);
});
});
});

const renderCallbackPage = (
history: History = createMemoryHistory({
initialEntries: [currentPage],
initialIndex: 1,
}),
) => {
render(
<SessionProvider>
<ReactRouter.Router navigator={history} location={history.location}>
<AuthCallbackPage />,
</ReactRouter.Router>
,
</SessionProvider>,
);
};
32 changes: 20 additions & 12 deletions app/src/pages/authCallbackPage/AuthCallbackPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import Spinner from '../../components/generic/spinner/Spinner';
import { isMock } from '../../helpers/utils/isLocal';
import { AxiosError } from 'axios';
import { buildUserAuth } from '../../helpers/test/testBuilders';
import { UserAuth } from '../../types/blocks/userAuth';

type Props = {};

Expand All @@ -17,24 +18,31 @@ const AuthCallbackPage = (props: Props) => {
const navigate = useNavigate();

useEffect(() => {
const handleError = () => {
setSession({
auth: null,
isLoggedIn: false,
});
navigate(routes.AUTH_ERROR);
};
const handleSuccess = (auth: UserAuth) => {
setSession({
auth: auth,
isLoggedIn: false,
});
navigate(routes.SELECT_ORG);
};

const handleCallback = async (args: AuthTokenArgs) => {
try {
const authResponse = await getAuthToken(args);
setSession({
auth: authResponse,
isLoggedIn: false,
});
navigate(routes.SELECT_ORG);
const authData = await getAuthToken(args);
handleSuccess(authData);
} catch (e) {
const error = e as AxiosError;
if (isMock(error)) {
setSession({
auth: buildUserAuth(),
isLoggedIn: false,
});
navigate(routes.SELECT_ORG);
handleSuccess(buildUserAuth());
} else {
navigate(routes.HOME);
handleError();
}
}
};
Expand Down
19 changes: 19 additions & 0 deletions app/src/pages/authErrorPage/AuthErrorPage.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { render, screen } from '@testing-library/react';
import * as ReactRouter from 'react-router';
import { createMemoryHistory } from 'history';
import AuthErrorPage from './AuthErrorPage';

describe('AuthErrorPage', () => {
it('renders unauthorised message', () => {
const history = createMemoryHistory({
initialEntries: ['/'],
initialIndex: 0,
});
render(
<ReactRouter.Router navigator={history} location={history.location}>
<AuthErrorPage />
</ReactRouter.Router>,
);
expect(screen.getByText('You have been logged out')).toBeInTheDocument();
});
});
Loading

0 comments on commit e76be5f

Please sign in to comment.