Skip to content

Commit 83b526b

Browse files
committed
Merge branch 'feature/CCM-10893-header-with-account-info' into feature/CCM-10893-account-header
2 parents e8524fc + 66d42ce commit 83b526b

File tree

75 files changed

+1794
-370
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

75 files changed

+1794
-370
lines changed

frontend/public/lib/nhsuk-9.1.0.min.css

Lines changed: 0 additions & 1 deletion
This file was deleted.

frontend/public/lib/nhsuk-9.1.0.min.js

Lines changed: 0 additions & 1 deletion
This file was deleted.

frontend/public/lib/nhsuk-frontend-10.0.0.min.css

Lines changed: 2 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

frontend/public/lib/nhsuk-frontend-10.0.0.min.js

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

frontend/src/__tests__/components/forms/NhsAppTemplateForm/__snapshots__/NhsAppTemplateForm.test.tsx.snap

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -160,7 +160,7 @@ exports[`Client-side validation triggers 1`] = `
160160
class=""
161161
>
162162
<p
163-
id="character-count-0"
163+
data-testid="character-count-0"
164164
>
165165
16 of 5000 characters
166166
</p>
@@ -860,7 +860,7 @@ exports[`renders page 1`] = `
860860
class=""
861861
>
862862
<p
863-
id="character-count-0"
863+
data-testid="character-count-0"
864864
>
865865
16 of 5000 characters
866866
</p>
@@ -1600,7 +1600,7 @@ exports[`renders page one error 1`] = `
16001600
class=""
16011601
>
16021602
<p
1603-
id="character-count-0"
1603+
data-testid="character-count-0"
16041604
>
16051605
0 of 5000 characters
16061606
</p>
@@ -2354,7 +2354,7 @@ exports[`renders page with multiple errors 1`] = `
23542354
class=""
23552355
>
23562356
<p
2357-
id="character-count-0"
2357+
data-testid="character-count-0"
23582358
>
23592359
0 of 5000 characters
23602360
</p>
@@ -3054,7 +3054,7 @@ exports[`renders page with preloaded field values 1`] = `
30543054
class=""
30553055
>
30563056
<p
3057-
id="character-count-0"
3057+
data-testid="character-count-0"
30583058
>
30593059
16 of 5000 characters
30603060
</p>
@@ -3732,7 +3732,7 @@ exports[`renders page without back link for initial state with id - edit mode 1`
37323732
class=""
37333733
>
37343734
<p
3735-
id="character-count-0"
3735+
data-testid="character-count-0"
37363736
>
37373737
16 of 5000 characters
37383738
</p>

frontend/src/__tests__/components/molecules/Footer.test.tsx

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,4 +81,10 @@ describe('Footer component', () => {
8181
screen.getByTestId('nhs-england-copyright-text')
8282
).toBeInTheDocument();
8383
});
84+
85+
it('matches snapshot', () => {
86+
const container = render(<NHSNotifyFooter />);
87+
88+
expect(container.asFragment()).toMatchSnapshot();
89+
});
8490
});

frontend/src/__tests__/components/molecules/Header.test.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ describe('Header component', () => {
2121
it('should contain an authentication link', () => {
2222
render(<NHSNotifyHeader />);
2323

24-
expect(screen.getByTestId('auth-link')).toBeInTheDocument();
24+
expect(screen.getByTestId('sign-in-link')).toBeInTheDocument();
2525
});
2626

2727
it('should render correctly', () => {
Lines changed: 218 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,218 @@
1+
import React from 'react';
2+
import { render, screen, within } from '@testing-library/react';
3+
import { mockDeep } from 'jest-mock-extended';
4+
import NhsNotifyHeaderWithAccount from '@molecules/HeaderWithAccount/HeaderWithAccount';
5+
import { type UseAuthenticator, useAuthenticator } from '@aws-amplify/ui-react';
6+
7+
jest.mock('@aws-amplify/ui-react');
8+
9+
const mockFetchAuthSession = jest.fn();
10+
jest.mock('aws-amplify/auth', () => ({
11+
fetchAuthSession: () => mockFetchAuthSession(),
12+
}));
13+
14+
const mockGetIdTokenClaims = jest.fn();
15+
jest.mock('@utils/token-utils', () => ({
16+
getIdTokenClaims: (token: string) => mockGetIdTokenClaims(token),
17+
}));
18+
19+
const setAuthStatus = (
20+
status: 'authenticated' | 'unauthenticated' | 'configuring'
21+
) => {
22+
jest.mocked(useAuthenticator).mockImplementation((selector) => {
23+
const context = mockDeep<UseAuthenticator>({
24+
authStatus: status,
25+
});
26+
27+
if (selector) {
28+
selector(context);
29+
}
30+
31+
return context;
32+
});
33+
};
34+
35+
describe('NhsNotifyHeaderWithAccount', () => {
36+
describe('when unauthenticated', () => {
37+
beforeEach(() => {
38+
jest.resetAllMocks();
39+
setAuthStatus('unauthenticated');
40+
});
41+
42+
it('renders the logo and service name with the correct url', async () => {
43+
render(<NhsNotifyHeaderWithAccount />);
44+
45+
const logoServiceLink = await screen.findByTestId(
46+
'header-logo-service-link'
47+
);
48+
49+
expect(logoServiceLink).toContainElement(
50+
screen.getByRole('img', { name: 'NHS logo' })
51+
);
52+
expect(logoServiceLink).toHaveAttribute('href', '/message-templates');
53+
expect(logoServiceLink).toHaveTextContent('Notify');
54+
});
55+
56+
it(`renders the authentication link as 'sign in'`, async () => {
57+
render(<NhsNotifyHeaderWithAccount />);
58+
59+
expect(await screen.findByTestId('sign-in-link')).toHaveTextContent(
60+
'Sign in'
61+
);
62+
});
63+
64+
it('does not fetch session or claims', async () => {
65+
render(<NhsNotifyHeaderWithAccount />);
66+
67+
await screen.findByTestId('page-header');
68+
69+
expect(mockFetchAuthSession).not.toHaveBeenCalled();
70+
expect(mockGetIdTokenClaims).not.toHaveBeenCalled();
71+
});
72+
73+
it('does not show the navigation links', async () => {
74+
render(<NhsNotifyHeaderWithAccount />);
75+
76+
await screen.findByTestId('page-header');
77+
78+
expect(screen.queryByTestId('navigation-links')).not.toBeInTheDocument();
79+
});
80+
81+
it('matches snapshot (unauthenticated)', async () => {
82+
const container = render(<NhsNotifyHeaderWithAccount />);
83+
84+
await screen.findByTestId('page-header');
85+
86+
expect(container.asFragment()).toMatchSnapshot();
87+
});
88+
});
89+
90+
describe('when authenticated', () => {
91+
beforeEach(() => {
92+
jest.resetAllMocks();
93+
setAuthStatus('authenticated');
94+
95+
mockFetchAuthSession.mockResolvedValue({
96+
tokens: {
97+
idToken: { toString: () => 'fake.id.token' },
98+
accessToken: { toString: () => 'fake.access.token' },
99+
},
100+
});
101+
102+
mockGetIdTokenClaims.mockReturnValue({
103+
displayName: 'Dr Test Example',
104+
clientName: 'NHS England',
105+
});
106+
});
107+
108+
it('renders the users display name', async () => {
109+
render(<NhsNotifyHeaderWithAccount />);
110+
111+
expect(
112+
await screen.findByTestId('account-display-name')
113+
).toHaveTextContent('Dr Test Example');
114+
});
115+
116+
it('renders the client name', async () => {
117+
render(<NhsNotifyHeaderWithAccount />);
118+
119+
expect(
120+
await screen.findByTestId('account-client-name')
121+
).toHaveTextContent('NHS England');
122+
});
123+
124+
it(`renders auth link as 'Sign out'`, async () => {
125+
render(<NhsNotifyHeaderWithAccount />);
126+
127+
expect(await screen.findByTestId('sign-out-link')).toHaveTextContent(
128+
'Sign out'
129+
);
130+
});
131+
132+
it('handles missing id token by clearing names', async () => {
133+
mockFetchAuthSession.mockResolvedValueOnce({
134+
tokens: {
135+
idToken: undefined,
136+
accessToken: { toString: () => 'fake.access.token' },
137+
},
138+
});
139+
140+
render(<NhsNotifyHeaderWithAccount />);
141+
142+
await screen.findByTestId('page-header');
143+
144+
expect(screen.queryByTestId('account-display-name')).toBeNull();
145+
expect(screen.queryByTestId('account-client-name')).toBeNull();
146+
});
147+
148+
it('handles fetchAuthSession errors by clearing names', async () => {
149+
mockFetchAuthSession.mockRejectedValueOnce(new Error('boom'));
150+
151+
render(<NhsNotifyHeaderWithAccount />);
152+
153+
await screen.findByTestId('page-header');
154+
155+
expect(screen.queryByTestId('account-display-name')).toBeNull();
156+
expect(screen.queryByTestId('account-client-name')).toBeNull();
157+
});
158+
159+
it('matches snapshot (authenticated)', async () => {
160+
const container = render(<NhsNotifyHeaderWithAccount />);
161+
162+
await screen.findByTestId('page-header');
163+
164+
expect(container.asFragment()).toMatchSnapshot();
165+
});
166+
167+
describe(`with 'routing' flag enabled`, () => {
168+
it('renders both the navigation links with correct hrefs', async () => {
169+
render(<NhsNotifyHeaderWithAccount features={{ routing: true }} />);
170+
171+
await screen.findByTestId('page-header');
172+
173+
const nav = screen.getByTestId('navigation-links');
174+
175+
const templatesLink = within(nav).getByRole('link', {
176+
name: 'Templates',
177+
});
178+
expect(templatesLink).toHaveAttribute('href', '/message-templates');
179+
180+
const plansLink = within(nav).getByRole('link', {
181+
name: 'Message plans',
182+
});
183+
expect(plansLink).toHaveAttribute(
184+
'href',
185+
'/templates-and-message-plans/message-plans'
186+
);
187+
});
188+
});
189+
190+
describe(`with 'routing' flag disabled`, () => {
191+
it('renders the templates link with correct href', async () => {
192+
render(<NhsNotifyHeaderWithAccount features={{ routing: false }} />);
193+
194+
await screen.findByTestId('page-header');
195+
196+
const nav = screen.getByTestId('navigation-links');
197+
198+
const templatesLink = within(nav).getByRole('link', {
199+
name: 'Templates',
200+
});
201+
expect(templatesLink).toHaveAttribute('href', '/message-templates');
202+
});
203+
204+
it('should not render the message plans link', async () => {
205+
render(<NhsNotifyHeaderWithAccount features={{ routing: false }} />);
206+
207+
await screen.findByTestId('page-header');
208+
209+
const nav = screen.getByTestId('navigation-links');
210+
211+
const plansLink = within(nav).queryByRole('link', {
212+
name: 'Message plans',
213+
});
214+
expect(plansLink).not.toBeInTheDocument();
215+
});
216+
});
217+
});
218+
});

frontend/src/__tests__/components/molecules/MarkdownContent.test.tsx

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -24,10 +24,10 @@ describe('MarkdownContent', () => {
2424
expect(screen.getByRole('link')).toHaveTextContent('link');
2525
});
2626

27-
it('passes ID through if content is a string', () => {
28-
render(<MarkdownContent content='This is content' id='content-id' />);
27+
it('passes test ID through if content is a string', () => {
28+
render(<MarkdownContent content='This is content' testId='content-id' />);
2929
expect(screen.getByText('This is content')).toHaveAttribute(
30-
'id',
30+
'data-testid',
3131
'content-id-0'
3232
);
3333
});
@@ -45,19 +45,19 @@ describe('MarkdownContent', () => {
4545
expect(screen.getByRole('link')).toHaveTextContent('link');
4646
});
4747

48-
it('passes indexed IDs to each item if content is an array', () => {
48+
it('passes indexed test IDs to each item if content is an array', () => {
4949
render(
5050
<MarkdownContent
5151
content={['First paragraph', 'Second paragraph']}
52-
id='content-id'
52+
testId='content-id'
5353
/>
5454
);
5555

5656
const first = screen.getByText('First paragraph');
57-
expect(first).toHaveAttribute('id', 'content-id-0');
57+
expect(first).toHaveAttribute('data-testid', 'content-id-0');
5858

5959
const second = screen.getByText('Second paragraph');
60-
expect(second).toHaveAttribute('id', 'content-id-1');
60+
expect(second).toHaveAttribute('data-testid', 'content-id-1');
6161
});
6262

6363
it('adds correct attributes to links', () => {

0 commit comments

Comments
 (0)