Skip to content

Commit 4f0b069

Browse files
[PRMP-1481] Implement search staff for new restriction
1 parent 582c929 commit 4f0b069

File tree

19 files changed

+767
-6
lines changed

19 files changed

+767
-6
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
import { render, screen } from '@testing-library/react';
2+
import UserPatientRestrictionsAddCancelStage from './UserPatientRestrictionsAddCancelStage';
3+
import userEvent from '@testing-library/user-event';
4+
import { Mock } from 'vitest';
5+
import { routes } from '../../../../types/generic/routes';
6+
7+
vi.mock('react-router-dom', async () => {
8+
const actual = await vi.importActual('react-router-dom');
9+
return {
10+
...actual,
11+
useNavigate: (): Mock => mockNavigate,
12+
Link: ({
13+
children,
14+
onClick,
15+
'data-testid': dataTestId,
16+
}: {
17+
children: React.ReactNode;
18+
onClick: (e: React.MouseEvent<HTMLButtonElement>) => void;
19+
'data-testid': string;
20+
}): React.JSX.Element => (
21+
<button onClick={onClick} data-testid={dataTestId}>
22+
{children}
23+
</button>
24+
),
25+
};
26+
});
27+
vi.mock('../../../../helpers/hooks/usePatient');
28+
29+
const mockNavigate = vi.fn();
30+
31+
describe('UserPatientRestrictionsAddCancelStage', () => {
32+
it('navigates to restrictions root page when continue is clicked', async () => {
33+
render(<UserPatientRestrictionsAddCancelStage />);
34+
35+
const continueButton = screen.getByTestId('confirm-cancel-button');
36+
await userEvent.click(continueButton);
37+
38+
expect(mockNavigate).toHaveBeenCalledWith(routes.USER_PATIENT_RESTRICTIONS);
39+
});
40+
41+
it('navigates back when go back is clicked', async () => {
42+
render(<UserPatientRestrictionsAddCancelStage />);
43+
44+
const goBackLink = screen.getByTestId('go-back-link');
45+
await userEvent.click(goBackLink);
46+
47+
expect(mockNavigate).toHaveBeenCalledWith(-1);
48+
});
49+
});
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
import { Button } from 'nhsuk-react-components';
2+
import useTitle from '../../../../helpers/hooks/useTitle';
3+
import BackButton from '../../../generic/backButton/BackButton';
4+
import PatientSummary from '../../../generic/patientSummary/PatientSummary';
5+
import { useNavigate, Link } from 'react-router-dom';
6+
import { routes } from '../../../../types/generic/routes';
7+
8+
const UserPatientRestrictionsCancelAddStage = (): React.JSX.Element => {
9+
const navigate = useNavigate();
10+
const pageTitle = 'Are you sure you want to cancel adding this restriction?';
11+
useTitle({ pageTitle });
12+
13+
return (
14+
<>
15+
<BackButton />
16+
17+
<h1>{pageTitle}</h1>
18+
19+
<PatientSummary oneLine />
20+
21+
<p>If you cancel, a restriction will not be added to this patient's record.</p>
22+
23+
<div className="action-button-group">
24+
<Button
25+
data-testid="confirm-cancel-button"
26+
onClick={(): void => {
27+
navigate(routes.USER_PATIENT_RESTRICTIONS);
28+
}}
29+
>
30+
Continue to cancel
31+
</Button>
32+
<Link
33+
data-testid="go-back-link"
34+
className="ml-4"
35+
to=""
36+
onClick={(e): void => {
37+
e.preventDefault();
38+
navigate(-1);
39+
}}
40+
>
41+
Go back to add the restriction
42+
</Link>
43+
</div>
44+
</>
45+
);
46+
};
47+
48+
export default UserPatientRestrictionsCancelAddStage;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
import { render, screen } from '@testing-library/react';
2+
import UserPatientRestrictionsAddConfirmStage from './UserPatientRestrictionsAddConfirmStage';
3+
4+
vi.mock('react-router-dom');
5+
vi.mock('../../../../helpers/hooks/usePatient');
6+
7+
describe('UserPatientRestrictionsAddConfirmStage', () => {
8+
it('renders correctly', () => {
9+
const userInformation = {
10+
name: 'John Doe',
11+
smartcardId: '123456789012',
12+
firstName: 'John',
13+
lastName: 'Doe',
14+
};
15+
16+
render(<UserPatientRestrictionsAddConfirmStage userInformation={userInformation} />);
17+
18+
expect(screen.getByTestId('smartcard-id')).toHaveTextContent(userInformation.smartcardId);
19+
expect(screen.getByTestId('staff-member')).toHaveTextContent(
20+
`${userInformation.firstName} ${userInformation.lastName}`,
21+
);
22+
});
23+
});
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
import { Link } from 'react-router-dom';
2+
import useTitle from '../../../../helpers/hooks/useTitle';
3+
import { UserInformation } from '../../../../types/generic/userPatientRestriction';
4+
import BackButton from '../../../generic/backButton/BackButton';
5+
import PatientSummary from '../../../generic/patientSummary/PatientSummary';
6+
import StaffMemberDetails from '../../../generic/staffMemberDetails/StaffMemberDetails';
7+
import { routeChildren } from '../../../../types/generic/routes';
8+
import { Button } from 'nhsuk-react-components';
9+
10+
type Props = {
11+
userInformation: UserInformation;
12+
};
13+
14+
const UserPatientRestrictionsAddConfirmStage = ({ userInformation }: Props): React.JSX.Element => {
15+
const pageTitle = 'Check the details of the restriction';
16+
useTitle({ pageTitle });
17+
18+
const addRestriction = (): void => {};
19+
20+
return (
21+
<>
22+
<BackButton />
23+
24+
<h1>{pageTitle}</h1>
25+
26+
<h3>You are adding a restriction to this patient record:</h3>
27+
<PatientSummary oneLine />
28+
29+
<p>
30+
When you add a restriction, it will stay with this patient's record until it is
31+
removed. If the patient moves practice, the new practice will see the name and NHS
32+
smartcard number of the staff member you've restricted.
33+
</p>
34+
35+
<StaffMemberDetails userInformation={userInformation} />
36+
37+
<div className="action-button-group">
38+
<Button data-testid="continue-button" onClick={addRestriction}>
39+
Continue to add this restriction
40+
</Button>
41+
<Link className="ml-4" to={routeChildren.USER_PATIENT_RESTRICTIONS_ADD_CANCEL}>
42+
Cancel without adding a restriction
43+
</Link>
44+
</div>
45+
</>
46+
);
47+
};
48+
49+
export default UserPatientRestrictionsAddConfirmStage;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,162 @@
1+
import { Mock } from 'vitest';
2+
import getUserInformation from '../../../../helpers/requests/userPatientRestrictions/getUserInformation';
3+
import useSmartcardNumber from '../../../../helpers/hooks/useSmartcardNumber';
4+
import { buildUserInformation, buildUserRestrictions } from '../../../../helpers/test/testBuilders';
5+
import { render, screen, waitFor } from '@testing-library/react';
6+
import UserPatientRestrictionsSearchStaffStage from './UserPatientRestrictionsSearchStaffStage';
7+
import userEvent from '@testing-library/user-event';
8+
import { routeChildren, routes } from '../../../../types/generic/routes';
9+
import {
10+
userRestrictionsStaffSearchNotFoundError,
11+
userRestrictionStaffSearchEmptyValueError,
12+
userRestrictionStaffSearchInvalidFormatError,
13+
userRestrictionStaffSearchRestrictionExistsError,
14+
} from '../../../../helpers/constants/errors';
15+
import { UIErrorCode } from '../../../../types/generic/errors';
16+
17+
vi.mock('react-router-dom', () => ({
18+
...vi.importActual('react-router-dom'),
19+
useNavigate: (): Mock => mockNavigate,
20+
}));
21+
vi.mock('../../../../helpers/requests/userPatientRestrictions/getUserInformation');
22+
vi.mock('../../../../helpers/hooks/useBaseAPIUrl');
23+
vi.mock('../../../../helpers/hooks/useBaseAPIHeaders');
24+
vi.mock('../../../../helpers/hooks/useSmartcardNumber');
25+
26+
const mockNavigate = vi.fn();
27+
const mockGetUserInformation = getUserInformation as Mock;
28+
const mockUseSmartcardNumber = useSmartcardNumber as Mock;
29+
const mockSetUserInformation = vi.fn();
30+
const mockRestrictions = buildUserRestrictions();
31+
const mockUserInformation = buildUserInformation();
32+
33+
describe('UserPatientRestrictionsSearchStaffStage', () => {
34+
beforeEach(() => {
35+
vi.resetAllMocks();
36+
mockUseSmartcardNumber.mockReturnValue('777777777777');
37+
mockGetUserInformation.mockResolvedValue(mockUserInformation);
38+
});
39+
40+
it('should fetch user information and navigate on valid form submission', async () => {
41+
renderComponent();
42+
43+
const smartcardNumber = '111111111111';
44+
const input = screen.getByTestId('smartcard-number-input');
45+
await userEvent.type(input, smartcardNumber);
46+
const submitButton = screen.getByTestId('continue-button');
47+
await userEvent.click(submitButton);
48+
49+
expect(mockGetUserInformation).toHaveBeenCalledWith(
50+
expect.objectContaining({
51+
smartcardId: smartcardNumber,
52+
}),
53+
);
54+
expect(mockSetUserInformation).toHaveBeenCalledWith(mockUserInformation);
55+
expect(mockNavigate).toHaveBeenCalledWith(
56+
routeChildren.USER_PATIENT_RESTRICTIONS_VERIFY_STAFF,
57+
);
58+
});
59+
60+
it('should show validation error for empty input', async () => {
61+
renderComponent();
62+
63+
const submitButton = screen.getByTestId('continue-button');
64+
await userEvent.click(submitButton);
65+
66+
expect(screen.getByText(userRestrictionStaffSearchEmptyValueError)).toBeInTheDocument();
67+
});
68+
69+
it('should show validation error for invalid input', async () => {
70+
renderComponent();
71+
72+
const input = screen.getByTestId('smartcard-number-input');
73+
await userEvent.type(input, 'invalid');
74+
const submitButton = screen.getByTestId('continue-button');
75+
await userEvent.click(submitButton);
76+
77+
expect(screen.getByText(userRestrictionStaffSearchInvalidFormatError)).toBeInTheDocument();
78+
});
79+
80+
it('should navigate to error page when search for yourself', async () => {
81+
renderComponent();
82+
83+
const input = screen.getByTestId('smartcard-number-input');
84+
await userEvent.type(input, '777777777777');
85+
const submitButton = screen.getByTestId('continue-button');
86+
await userEvent.click(submitButton);
87+
88+
expect(mockNavigate).toHaveBeenCalledWith(
89+
routes.GENERIC_ERROR + `?errorCode=${UIErrorCode.USER_PATIENT_RESTRICTIONS_SELF_ADD}`,
90+
);
91+
});
92+
93+
it('should show validation error when trying to add a restriction that already exists', async () => {
94+
renderComponent();
95+
96+
const existingSmartcardNumber = mockRestrictions[0].restrictedUser;
97+
const input = screen.getByTestId('smartcard-number-input');
98+
await userEvent.type(input, existingSmartcardNumber);
99+
const submitButton = screen.getByTestId('continue-button');
100+
await userEvent.click(submitButton);
101+
102+
expect(
103+
screen.getByText(userRestrictionStaffSearchRestrictionExistsError),
104+
).toBeInTheDocument();
105+
});
106+
107+
it('should navigate to session expired page on 403 error', async () => {
108+
mockGetUserInformation.mockRejectedValue({
109+
response: { status: 403 },
110+
});
111+
112+
renderComponent();
113+
114+
const input = screen.getByTestId('smartcard-number-input');
115+
await userEvent.type(input, '111111111111');
116+
const submitButton = screen.getByTestId('continue-button');
117+
await userEvent.click(submitButton);
118+
119+
expect(mockNavigate).toHaveBeenCalledWith(routes.SESSION_EXPIRED);
120+
});
121+
122+
it('should navigate to server error page on 500 error', async () => {
123+
mockGetUserInformation.mockRejectedValue({
124+
response: { status: 500 },
125+
});
126+
127+
renderComponent();
128+
129+
const input = screen.getByTestId('smartcard-number-input');
130+
await userEvent.type(input, '111111111111');
131+
const submitButton = screen.getByTestId('continue-button');
132+
await userEvent.click(submitButton);
133+
134+
expect(mockNavigate).toHaveBeenCalledWith(expect.stringContaining(routes.SERVER_ERROR));
135+
});
136+
137+
it('should show error message when staff member not found', async () => {
138+
mockGetUserInformation.mockRejectedValue({
139+
response: { status: 404 },
140+
});
141+
142+
renderComponent();
143+
144+
const input = screen.getByTestId('smartcard-number-input');
145+
await userEvent.type(input, '111111111111');
146+
const submitButton = screen.getByTestId('continue-button');
147+
await userEvent.click(submitButton);
148+
149+
await waitFor(() => {
150+
expect(screen.getByText(userRestrictionsStaffSearchNotFoundError)).toBeInTheDocument();
151+
});
152+
});
153+
});
154+
155+
const renderComponent = (): void => {
156+
render(
157+
<UserPatientRestrictionsSearchStaffStage
158+
existingRestrictions={mockRestrictions}
159+
setUserInformation={mockSetUserInformation}
160+
/>,
161+
);
162+
};

0 commit comments

Comments
 (0)