Skip to content

Commit 08f29f0

Browse files
authored
CCM-10424: Error page for missing client or campaign ID (#562)
1 parent d7b8601 commit 08f29f0

File tree

25 files changed

+531
-31
lines changed

25 files changed

+531
-31
lines changed
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
// Jest Snapshot v1, https://goo.gl/fbAQLP
2+
3+
exports[`ClientIdAndCampaignIdRequiredPage 1`] = `
4+
<DocumentFragment>
5+
<main
6+
class="nhsuk-main-wrapper"
7+
id="maincontent"
8+
role="main"
9+
>
10+
<div
11+
class="nhsuk-grid-row"
12+
data-testid="page-content-wrapper"
13+
>
14+
<div
15+
class="nhsuk-grid-column-two-thirds"
16+
>
17+
<h1
18+
class="nhsuk-heading-xl"
19+
data-testid="page-heading"
20+
>
21+
You cannot create letter templates yet
22+
</h1>
23+
<p>
24+
To get access, contact your onboarding manager and give them this error message:
25+
</p>
26+
<div
27+
class="nhsuk-inset-text"
28+
>
29+
<p>
30+
Account needs a client ID and campaign ID
31+
</p>
32+
</div>
33+
<p>
34+
<a
35+
data-testid="back-to-choose-template-type-link"
36+
href="/choose-a-template-type"
37+
>
38+
Go back
39+
</a>
40+
</p>
41+
</div>
42+
</div>
43+
</main>
44+
</DocumentFragment>
45+
`;
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
import ClientIdAndCampaignIdRequiredPage from '@app/create-letter-template/client-id-and-campaign-id-required/page';
2+
import { render } from '@testing-library/react';
3+
4+
test('ClientIdAndCampaignIdRequiredPage', async () => {
5+
const container = render(<ClientIdAndCampaignIdRequiredPage />);
6+
7+
expect(container.asFragment()).toMatchSnapshot();
8+
});

frontend/src/__tests__/app/create-letter-template/page.test.tsx

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,102 @@
22
* @jest-environment node
33
*/
44
import CreateLetterTemplatePage from '@app/create-letter-template/page';
5+
import { getSessionServer } from '@utils/amplify-utils';
6+
import { fetchClient } from '@utils/server-features';
7+
import { redirect, RedirectType } from 'next/navigation';
8+
9+
jest.mock('next/navigation');
10+
jest.mock('@utils/amplify-utils');
11+
jest.mock('@utils/server-features');
12+
13+
const mockGetSessionServer = jest.mocked(getSessionServer);
14+
const mockFetchClient = jest.mocked(fetchClient);
515

616
describe('CreateLetterTemplatePage', () => {
717
beforeEach(() => {
818
jest.resetAllMocks();
919
});
1020

1121
it('should render CreateLetterTemplatePage', async () => {
22+
mockGetSessionServer.mockResolvedValueOnce({
23+
accessToken: 'mocktoken',
24+
clientId: 'client1',
25+
userSub: 'sub',
26+
});
27+
mockFetchClient.mockResolvedValueOnce({
28+
data: {
29+
campaignId: 'campaign2',
30+
features: {},
31+
},
32+
});
33+
1234
const page = await CreateLetterTemplatePage();
1335

1436
expect(page).toMatchSnapshot();
1537
});
38+
39+
it('should check client ID and campaign ID', async () => {
40+
mockGetSessionServer.mockResolvedValueOnce({
41+
accessToken: 'mocktoken',
42+
clientId: 'client1',
43+
userSub: 'sub',
44+
});
45+
mockFetchClient.mockResolvedValueOnce({
46+
data: {
47+
campaignId: 'campaign2',
48+
features: {},
49+
},
50+
});
51+
52+
await CreateLetterTemplatePage();
53+
54+
expect(mockGetSessionServer).toHaveBeenCalled();
55+
expect(mockFetchClient).toHaveBeenCalled();
56+
});
57+
58+
it('should redirect to error page when client ID is not present', async () => {
59+
const mockRedirect = jest.mocked(redirect);
60+
61+
mockGetSessionServer.mockResolvedValueOnce({
62+
accessToken: 'mocktoken',
63+
clientId: undefined,
64+
userSub: 'sub',
65+
});
66+
mockFetchClient.mockResolvedValueOnce({
67+
data: {
68+
campaignId: 'campaign2',
69+
features: {},
70+
},
71+
});
72+
73+
await CreateLetterTemplatePage();
74+
75+
expect(mockRedirect).toHaveBeenCalledWith(
76+
'/create-letter-template/client-id-and-campaign-id-required',
77+
RedirectType.replace
78+
);
79+
});
80+
81+
it('should redirect to error page when campaign ID is not present', async () => {
82+
const mockRedirect = jest.mocked(redirect);
83+
84+
mockGetSessionServer.mockResolvedValueOnce({
85+
accessToken: 'mocktoken',
86+
clientId: 'client2',
87+
userSub: 'sub',
88+
});
89+
mockFetchClient.mockResolvedValueOnce({
90+
data: {
91+
campaignId: undefined,
92+
features: {},
93+
},
94+
});
95+
96+
await CreateLetterTemplatePage();
97+
98+
expect(mockRedirect).toHaveBeenCalledWith(
99+
'/create-letter-template/client-id-and-campaign-id-required',
100+
RedirectType.replace
101+
);
102+
});
16103
});

frontend/src/__tests__/app/invalid-template/__snapshots__/page.test.tsx.snap

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ exports[`InvalidTemplatePage 1`] = `
88
role="main"
99
>
1010
<div
11-
class="nhsuk-grid-row"
11+
class="nhsuk-grid-row nhsuk-u-padding-left-3 nhsuk-u-padding-right-3"
1212
data-testid="page-content-wrapper"
1313
>
1414
<h1
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
import InvalidConfig from '@molecules/InvalidConfig/InvalidConfig';
2+
import { render } from '@testing-library/react';
3+
4+
test('InvalidConfig', async () => {
5+
const container = render(<InvalidConfig />);
6+
7+
expect(container.asFragment()).toMatchSnapshot();
8+
});

frontend/src/__tests__/components/molecules/__snapshots__/404.test.tsx.snap

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ exports[`ErrorPage404 1`] = `
88
role="main"
99
>
1010
<div
11-
class="nhsuk-grid-row"
11+
class="nhsuk-grid-row nhsuk-u-padding-left-3 nhsuk-u-padding-right-3"
1212
data-testid="page-content-wrapper"
1313
>
1414
<h1
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
// Jest Snapshot v1, https://goo.gl/fbAQLP
2+
3+
exports[`InvalidConfig 1`] = `
4+
<DocumentFragment>
5+
<main
6+
class="nhsuk-main-wrapper"
7+
id="maincontent"
8+
role="main"
9+
>
10+
<div
11+
class="nhsuk-grid-row"
12+
data-testid="page-content-wrapper"
13+
>
14+
<div
15+
class="nhsuk-grid-column-two-thirds"
16+
>
17+
<h1
18+
class="nhsuk-heading-xl"
19+
data-testid="page-heading"
20+
>
21+
You cannot create letter templates yet
22+
</h1>
23+
<p>
24+
To get access, contact your onboarding manager and give them this error message:
25+
</p>
26+
<div
27+
class="nhsuk-inset-text"
28+
>
29+
<p>
30+
Account needs a client ID and campaign ID
31+
</p>
32+
</div>
33+
<p>
34+
<a
35+
data-testid="back-to-choose-template-type-link"
36+
href="/choose-a-template-type"
37+
>
38+
Go back
39+
</a>
40+
</p>
41+
</div>
42+
</div>
43+
</main>
44+
</DocumentFragment>
45+
`;

frontend/src/__tests__/middleware.test.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ describe('middleware function', () => {
3535

3636
getTokenMock.mockResolvedValueOnce({
3737
accessToken: undefined,
38+
clientId: undefined,
3839
userSub: undefined,
3940
});
4041

@@ -53,6 +54,7 @@ describe('middleware function', () => {
5354
it('if request path is protected, and access token is obtained, respond with CSP', async () => {
5455
getTokenMock.mockResolvedValueOnce({
5556
accessToken: 'token',
57+
clientId: 'client1',
5658
userSub: 'sub',
5759
});
5860

frontend/src/__tests__/utils/amplify-utils.test.ts

Lines changed: 66 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,11 @@
33
*/
44
import { sign } from 'jsonwebtoken';
55
import { fetchAuthSession } from 'aws-amplify/auth/server';
6-
import { getSessionServer, getSessionId } from '../../utils/amplify-utils';
6+
import {
7+
getSessionServer,
8+
getSessionId,
9+
getClientId,
10+
} from '../../utils/amplify-utils';
711

812
jest.mock('aws-amplify/auth/server');
913
jest.mock('@aws-amplify/adapter-nextjs/api');
@@ -19,20 +23,52 @@ jest.mock('@/amplify_outputs.json', () => ({
1923
const fetchAuthSessionMock = jest.mocked(fetchAuthSession);
2024

2125
describe('amplify-utils', () => {
22-
test('getSessionServer - should return the auth token and userSub', async () => {
26+
beforeEach(() => {
27+
jest.resetAllMocks();
28+
});
29+
30+
test('getSessionServer - should return the auth token, clientID and userSub', async () => {
31+
const mockAccessToken = {
32+
toString: () =>
33+
sign(
34+
{
35+
['nhs-notify:client-id']: 'client1',
36+
},
37+
'mockToken'
38+
),
39+
payload: {},
40+
};
2341
fetchAuthSessionMock.mockResolvedValueOnce({
2442
tokens: {
25-
accessToken: {
26-
toString: () => 'mockToken',
27-
payload: {},
28-
},
43+
accessToken: mockAccessToken,
2944
},
3045
userSub: 'sub',
3146
});
3247

3348
const result = await getSessionServer();
3449

35-
expect(result).toEqual({ accessToken: 'mockToken', userSub: 'sub' });
50+
expect(result).toEqual({
51+
accessToken: mockAccessToken.toString(),
52+
clientId: 'client1',
53+
userSub: 'sub',
54+
});
55+
});
56+
57+
test('getSessionServer - client ID should be undefined if not present on token', async () => {
58+
const mockAccessToken = {
59+
toString: () => sign({}, 'mockToken'),
60+
payload: {},
61+
};
62+
fetchAuthSessionMock.mockResolvedValueOnce({
63+
tokens: {
64+
accessToken: mockAccessToken,
65+
},
66+
userSub: 'sub',
67+
});
68+
69+
const result = await getSessionServer();
70+
71+
expect(result.clientId).toBeUndefined();
3672
});
3773

3874
test('getSessionServer - should return undefined properties when no auth session', async () => {
@@ -54,13 +90,13 @@ describe('amplify-utils', () => {
5490
});
5591

5692
describe('getSessionId', () => {
57-
test('returns void when access token not found', async () => {
93+
test('returns undefined when access token not found', async () => {
5894
fetchAuthSessionMock.mockResolvedValueOnce({});
5995

6096
await expect(getSessionId()).resolves.toBeUndefined();
6197
});
6298

63-
test('errors when session ID not found', async () => {
99+
test('returns undefined when session ID not found', async () => {
64100
fetchAuthSessionMock.mockResolvedValueOnce({
65101
tokens: {
66102
accessToken: {
@@ -94,4 +130,25 @@ describe('amplify-utils', () => {
94130
expect(sessionId).toEqual('jti');
95131
});
96132
});
133+
134+
describe('getClientId', () => {
135+
test('returns undefined when client ID not found', async () => {
136+
const mockAccessToken = sign({}, 'key');
137+
138+
const clientId = await getClientId(mockAccessToken);
139+
140+
expect(clientId).toBeUndefined();
141+
});
142+
143+
test('retrieves client id from access token param', async () => {
144+
const mockAccessToken = sign(
145+
{ ['nhs-notify:client-id']: 'client2' },
146+
'key'
147+
);
148+
149+
const clientId = await getClientId(mockAccessToken);
150+
151+
expect(clientId).toEqual('client2');
152+
});
153+
});
97154
});

0 commit comments

Comments
 (0)