Skip to content
Draft
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
Original file line number Diff line number Diff line change
@@ -1,23 +1,106 @@
import { render, screen } from '@testing-library/react';
import UserPatientRestrictionsAddConfirmStage from './UserPatientRestrictionsAddConfirmStage';
import { Mock } from 'vitest';
import usePatient from '../../../../helpers/hooks/usePatient';
import postUserPatientRestriction from '../../../../helpers/requests/userPatientRestrictions/createUserPatientRestriction';
import { buildPatientDetails } from '../../../../helpers/test/testBuilders';
import { userEvent } from '@testing-library/user-event';
import { routeChildren, routes } from '../../../../types/generic/routes';

vi.mock('react-router-dom');
vi.mock('react-router-dom', async () => {
const actual = await vi.importActual('react-router-dom');
return {
...actual,
useNavigate: (): Mock => mockNavigate,
Link: ({ children, to }: { children: React.ReactNode; to: string }): React.JSX.Element => (
<a href={to}>{children}</a>
),
};
});
vi.mock('../../../../helpers/hooks/usePatient');
vi.mock('../../../../helpers/hooks/useBaseAPIUrl');
vi.mock('../../../../helpers/hooks/useBaseAPIHeaders');
vi.mock('../../../../helpers/requests/userPatientRestrictions/createUserPatientRestriction');

const mockNavigate = vi.fn();
const mockUsePatient = usePatient as Mock;
const mockPostUserPatientRestriction = postUserPatientRestriction as Mock;
const mockPatient = buildPatientDetails();
const userInformation = {
name: 'John Doe',
smartcardId: '123456789012',
firstName: 'John',
lastName: 'Doe',
};

describe('UserPatientRestrictionsAddConfirmStage', () => {
it('renders correctly', () => {
const userInformation = {
name: 'John Doe',
smartcardId: '123456789012',
firstName: 'John',
lastName: 'Doe',
};
beforeEach(() => {
vi.resetAllMocks();
mockUsePatient.mockReturnValue(mockPatient);
mockPostUserPatientRestriction.mockResolvedValue({});
});

it('renders correctly', () => {
render(<UserPatientRestrictionsAddConfirmStage userInformation={userInformation} />);

expect(screen.getByTestId('smartcard-id')).toHaveTextContent(userInformation.smartcardId);
expect(screen.getByTestId('staff-member')).toHaveTextContent(
`${userInformation.firstName} ${userInformation.lastName}`,
);
});

it('submits new patient restriction and navigates to success page on button click', async () => {
render(<UserPatientRestrictionsAddConfirmStage userInformation={userInformation} />);

const button = screen.getByTestId('continue-button');
await userEvent.click(button);

expect(mockPostUserPatientRestriction).toHaveBeenCalledWith(
expect.objectContaining({
nhsNumber: mockPatient.nhsNumber,
smartcardId: userInformation.smartcardId,
}),
);

expect(mockNavigate).toHaveBeenCalledWith(
routeChildren.USER_PATIENT_RESTRICTIONS_ACTION_COMPLETE,
);
});

it('shows loading state when submitting restriction', async () => {
render(<UserPatientRestrictionsAddConfirmStage userInformation={userInformation} />);

mockPostUserPatientRestriction.mockReturnValue(new Promise(() => {}));

const button = screen.getByTestId('continue-button');
await userEvent.click(button);

expect(screen.getByText('Processing...')).toBeInTheDocument();
});

it('navigates to error page on 500', async () => {
render(<UserPatientRestrictionsAddConfirmStage userInformation={userInformation} />);

mockPostUserPatientRestriction.mockRejectedValue({
response: { status: 500 },
});

const button = screen.getByTestId('continue-button');
await userEvent.click(button);

expect(mockNavigate).toHaveBeenCalledWith(expect.stringContaining(routes.SERVER_ERROR));
});

it('navigates to session expired page on 403', async () => {
render(<UserPatientRestrictionsAddConfirmStage userInformation={userInformation} />);

mockPostUserPatientRestriction.mockRejectedValue({
response: { status: 403 },
});

const button = screen.getByTestId('continue-button');
await userEvent.click(button);

expect(mockNavigate).toHaveBeenCalledWith(routes.SESSION_EXPIRED);
});
});
Original file line number Diff line number Diff line change
@@ -1,21 +1,63 @@
import { Link } from 'react-router-dom';
import { Link, useNavigate } from 'react-router-dom';
import useTitle from '../../../../helpers/hooks/useTitle';
import { UserInformation } from '../../../../types/generic/userPatientRestriction';
import BackButton from '../../../generic/backButton/BackButton';
import PatientSummary from '../../../generic/patientSummary/PatientSummary';
import StaffMemberDetails from '../../../generic/staffMemberDetails/StaffMemberDetails';
import { routeChildren } from '../../../../types/generic/routes';
import { routeChildren, routes } from '../../../../types/generic/routes';
import { Button } from 'nhsuk-react-components';
import postUserPatientRestriction from '../../../../helpers/requests/userPatientRestrictions/createUserPatientRestriction';
import usePatient from '../../../../helpers/hooks/usePatient';
import useBaseAPIUrl from '../../../../helpers/hooks/useBaseAPIUrl';
import useBaseAPIHeaders from '../../../../helpers/hooks/useBaseAPIHeaders';
import { AxiosError } from 'axios';
import { isMock } from '../../../../helpers/utils/isLocal';
import { errorToParams } from '../../../../helpers/utils/errorToParams';
import { useState } from 'react';
import SpinnerButton from '../../../generic/spinnerButton/SpinnerButton';

type Props = {
userInformation: UserInformation;
};

const UserPatientRestrictionsAddConfirmStage = ({ userInformation }: Props): React.JSX.Element => {
const navigate = useNavigate();
const patient = usePatient();
const baseAPIUrl = useBaseAPIUrl();
const baseAPIHeaders = useBaseAPIHeaders();

const pageTitle = 'Check the details of the restriction';
useTitle({ pageTitle });

const addRestriction = (): void => {};
const [creatingRestriction, setCreatingRestriction] = useState(false);

const addRestriction = async (): Promise<void> => {
setCreatingRestriction(true);

try {
await postUserPatientRestriction({
nhsNumber: patient!.nhsNumber,
smartcardId: userInformation.smartcardId,
baseAPIUrl,
baseAPIHeaders,
});

handleSuccess();
} catch (e) {
const error = e as AxiosError;
if (isMock(error)) {
handleSuccess();
} else if (error.response?.status === 403) {
navigate(routes.SESSION_EXPIRED);
} else {
navigate(routes.SERVER_ERROR + errorToParams(error));
}
}
};

const handleSuccess = (): void => {
navigate(routeChildren.USER_PATIENT_RESTRICTIONS_ACTION_COMPLETE);
};

return (
<>
Expand All @@ -34,14 +76,18 @@ const UserPatientRestrictionsAddConfirmStage = ({ userInformation }: Props): Rea

<StaffMemberDetails userInformation={userInformation} />

<div className="action-button-group">
<Button data-testid="continue-button" onClick={addRestriction}>
Continue to add this restriction
</Button>
<Link className="ml-4" to={routeChildren.USER_PATIENT_RESTRICTIONS_ADD_CANCEL}>
Cancel without adding a restriction
</Link>
</div>
{creatingRestriction ? (
<SpinnerButton id="creating-restriction-spinner" status="Processing..." />
) : (
<div className="action-button-group">
<Button data-testid="continue-button" onClick={addRestriction}>
Continue to add this restriction
</Button>
<Link className="ml-4" to={routeChildren.USER_PATIENT_RESTRICTIONS_ADD_CANCEL}>
Cancel without adding a restriction
</Link>
</div>
)}
</>
);
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ describe('UserPatientRestrictionsRemoveConfirmStage', () => {
nhsNumber: mockRestriction.nhsNumber,
});
expect(mockNavigate).toHaveBeenCalledWith(
routeChildren.USER_PATIENT_RESTRICTIONS_REMOVE_COMPLETE,
routeChildren.USER_PATIENT_RESTRICTIONS_ACTION_COMPLETE,
);
});
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ const UserPatientRestrictionsRemoveConfirmStage = ({ restriction }: Props): Reac
}
}

navigate(routeChildren.USER_PATIENT_RESTRICTIONS_REMOVE_COMPLETE);
navigate(routeChildren.USER_PATIENT_RESTRICTIONS_ACTION_COMPLETE);
};

return (
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import axios from 'axios';
import { Mocked } from 'vitest';
import postUserPatientRestriction from './createUserPatientRestriction';
import { endpoints } from '../../../types/generic/endpoints';

vi.mock('axios');
const mockedAxios = axios as Mocked<typeof axios>;

describe('createUserPatientRestriction', () => {
it('should make expected API call', async () => {
const mockPost = vi.fn();
mockedAxios.post.mockImplementation(mockPost);

const args = {
smartcardId: '123',
nhsNumber: '9876543210',
baseAPIUrl: 'http://example.com/api',
baseAPIHeaders: { Authorization: 'Bearer token', 'Content-Type': 'application/json' },
};

await postUserPatientRestriction(args);

expect(mockPost).toHaveBeenCalledWith(
`${args.baseAPIUrl}${endpoints.USER_PATIENT_RESTRICTIONS}`,
{
smartcardId: args.smartcardId,
nhsNumber: args.nhsNumber,
},
{
headers: {
...args.baseAPIHeaders,
},
params: {
patientId: args.nhsNumber,
},
},
);
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import axios, { AxiosError } from 'axios';
import { AuthHeaders } from '../../../types/blocks/authHeaders';
import { endpoints } from '../../../types/generic/endpoints';

type CreateUserPatientRestrictionArgs = {
smartcardId: string;
nhsNumber: string;
baseAPIUrl: string;
baseAPIHeaders: AuthHeaders;
};

const postUserPatientRestriction = async ({
smartcardId,
nhsNumber,
baseAPIUrl,
baseAPIHeaders,
}: CreateUserPatientRestrictionArgs): Promise<void> => {
try {
const url = baseAPIUrl + endpoints.USER_PATIENT_RESTRICTIONS;
await axios.post(
url,
{
smartcardId,
nhsNumber,
},
{
headers: {
...baseAPIHeaders,
},
params: {
patientId: nhsNumber,
},
},
);
} catch (e) {
const error = e as AxiosError;
throw error;
}
};

export default postUserPatientRestriction;
Loading
Loading