Skip to content

Commit de17575

Browse files
committed
CCM-10893: server render header
1 parent 9fc9516 commit de17575

26 files changed

+1029
-786
lines changed

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

Lines changed: 18 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,13 @@
11
import { render, screen } from '@testing-library/react';
2-
import { mockDeep } from 'jest-mock-extended';
3-
import { type UseAuthenticator, useAuthenticator } from '@aws-amplify/ui-react';
2+
import { useAuthStatus } from '@hooks/use-auth-status';
43
import { AuthLink } from '@molecules/AuthLink/AuthLink';
54

6-
jest.mock('@aws-amplify/ui-react');
5+
jest.mock('@hooks/use-auth-status');
6+
const mockUseAuthStatus = jest.mocked(useAuthStatus);
77

88
beforeEach(() => {
99
jest.resetAllMocks();
10-
jest.mocked(useAuthenticator).mockImplementation((cb) => {
11-
const context = mockDeep<UseAuthenticator>({
12-
authStatus: 'configuring',
13-
});
14-
15-
if (cb) {
16-
cb(context);
17-
}
18-
19-
return context;
20-
});
10+
mockUseAuthStatus.mockReturnValue('configuring');
2111
});
2212

2313
describe('AuthLink', () => {
@@ -29,28 +19,29 @@ describe('AuthLink', () => {
2919
expect(container.asFragment()).toMatchSnapshot();
3020
});
3121

32-
it('renders Sign in link when authStatus is unauthenticated', async () => {
33-
jest.mocked(useAuthenticator).mockReturnValueOnce(
34-
mockDeep<UseAuthenticator>({
35-
authStatus: 'unauthenticated',
36-
})
37-
);
38-
22+
it('renders Sign in link when authStatus changes to unauthenticated', async () => {
3923
const container = render(<AuthLink />);
4024

4125
await screen.findByText('Sign in');
4226

27+
mockUseAuthStatus.mockReturnValue('unauthenticated');
28+
29+
container.rerender(<AuthLink />);
30+
31+
await screen.findByText('Sign in');
32+
4333
expect(container.asFragment()).toMatchSnapshot();
4434
});
45-
it('renders Sign out link when authStatus is authenticated', async () => {
46-
jest.mocked(useAuthenticator).mockReturnValueOnce(
47-
mockDeep<UseAuthenticator>({
48-
authStatus: 'authenticated',
49-
})
50-
);
5135

36+
it('renders Sign out link when authStatus changes to authenticated', async () => {
5237
const container = render(<AuthLink />);
5338

39+
await screen.findByText('Sign in');
40+
41+
mockUseAuthStatus.mockReturnValue('authenticated');
42+
43+
container.rerender(<AuthLink />);
44+
5445
await screen.findByText('Sign out');
5546

5647
expect(container.asFragment()).toMatchSnapshot();
Lines changed: 208 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,32 +1,219 @@
1-
import { render, screen } from '@testing-library/react';
2-
import { mockDeep } from 'jest-mock-extended';
3-
import { type UseAuthenticator, useAuthenticator } from '@aws-amplify/ui-react';
4-
import { NHSNotifyHeader } from '@molecules/Header/Header';
1+
import { render, screen, within } from '@testing-library/react';
2+
import { useAuthStatus } from '@hooks/use-auth-status';
3+
import { getSessionServer } from '@utils/amplify-utils';
4+
import { getIdTokenClaims } from '@utils/token-utils';
5+
import { NhsNotifyHeader } from '@molecules/Header/Header';
56

6-
jest.mock('@aws-amplify/ui-react');
7+
jest.mock('@hooks/use-auth-status');
8+
const mockUseAuthStatus = jest.mocked(useAuthStatus);
79

8-
jest.mocked(useAuthenticator).mockReturnValue(
9-
mockDeep<UseAuthenticator>({
10-
authStatus: 'configuring',
11-
})
12-
);
10+
jest.mock('@utils/token-utils');
11+
const mockGetIdTokenClaims = jest.mocked(getIdTokenClaims);
1312

14-
describe('Header component', () => {
15-
it('should contain the logo', () => {
16-
render(<NHSNotifyHeader />);
13+
jest.mock('@utils/amplify-utils');
14+
const mockGetSessionServer = jest.mocked(getSessionServer);
1715

18-
expect(screen.getByTestId('page-header-logo')).toBeInTheDocument();
19-
});
16+
jest.mock('nhs-notify-web-template-management-utils/logger');
17+
18+
beforeEach(() => {
19+
jest.resetAllMocks();
20+
mockUseAuthStatus.mockImplementation((status) => status ?? 'configuring');
21+
});
22+
23+
describe('NhsNotifyHeader', () => {
24+
describe('when unauthenticated', () => {
25+
beforeEach(() => {
26+
mockGetSessionServer.mockResolvedValue({});
27+
mockGetIdTokenClaims.mockReturnValue({});
28+
});
29+
30+
it('initializes the authStatus as unauthenticated', async () => {
31+
const header = await NhsNotifyHeader({});
32+
33+
render(header);
34+
35+
expect(mockUseAuthStatus).toHaveBeenCalledTimes(3);
36+
37+
for (const call of mockUseAuthStatus.mock.calls) {
38+
expect(call[0]).toBe('unauthenticated');
39+
}
40+
});
41+
42+
it('renders the logo and service name with the correct url', async () => {
43+
const header = await NhsNotifyHeader({});
44+
45+
render(header);
46+
47+
const logoServiceLink = screen.getByTestId('header-logo-service-link');
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+
});
2055

21-
it('should contain an authentication link', () => {
22-
render(<NHSNotifyHeader />);
56+
it(`renders the authentication link as 'sign in'`, async () => {
57+
const header = await NhsNotifyHeader({});
2358

24-
expect(screen.getByTestId('sign-in-link')).toBeInTheDocument();
59+
render(header);
60+
61+
expect(screen.getByTestId('sign-in-link')).toHaveTextContent('Sign in');
62+
});
63+
64+
it('does not show the navigation links', async () => {
65+
const header = await NhsNotifyHeader({});
66+
67+
render(header);
68+
69+
expect(screen.getByTestId('page-header')).toBeVisible();
70+
71+
expect(screen.queryByTestId('navigation-links')).not.toBeInTheDocument();
72+
});
73+
74+
it('matches snapshot (unauthenticated)', async () => {
75+
const header = await NhsNotifyHeader({});
76+
77+
const container = render(header);
78+
79+
expect(container.asFragment()).toMatchSnapshot();
80+
});
2581
});
2682

27-
it('should render correctly', () => {
28-
const container = render(<NHSNotifyHeader />);
83+
describe('when authenticated', () => {
84+
beforeEach(() => {
85+
mockGetSessionServer.mockResolvedValue({
86+
accessToken: 'fake.access.token',
87+
idToken: 'fake.id.token',
88+
});
89+
90+
mockGetIdTokenClaims.mockReturnValue({
91+
displayName: 'Dr Test Example',
92+
clientName: 'NHS England',
93+
});
94+
});
95+
96+
it('initializes the authStatus as authenticated', async () => {
97+
const header = await NhsNotifyHeader({});
98+
99+
render(header);
100+
101+
// hook used in AuthLink, HeaderNavigation, HeaderAccountDetails
102+
expect(mockUseAuthStatus).toHaveBeenCalledTimes(3);
103+
104+
for (const call of mockUseAuthStatus.mock.calls) {
105+
expect(call[0]).toBe('authenticated');
106+
}
107+
});
108+
109+
it('renders the users display name', async () => {
110+
const header = await NhsNotifyHeader({});
111+
112+
render(header);
113+
114+
expect(screen.getByTestId('account-display-name')).toHaveTextContent(
115+
'Dr Test Example'
116+
);
117+
});
118+
119+
it('renders the client name', async () => {
120+
const header = await NhsNotifyHeader({});
121+
122+
render(header);
123+
124+
expect(screen.getByTestId('account-client-name')).toHaveTextContent(
125+
'NHS England'
126+
);
127+
});
128+
129+
it(`renders auth link as 'Sign out'`, async () => {
130+
const header = await NhsNotifyHeader({});
131+
132+
render(header);
133+
expect(screen.getByTestId('sign-out-link')).toHaveTextContent('Sign out');
134+
});
135+
136+
it('does not render names but renders sign out link and navigation when id token is missing', async () => {
137+
mockGetSessionServer.mockResolvedValueOnce({
138+
accessToken: 'fake.access.token',
139+
});
140+
mockGetIdTokenClaims.mockReturnValueOnce({});
141+
142+
const header = await NhsNotifyHeader({});
143+
144+
render(header);
145+
146+
expect(
147+
screen.queryByTestId('account-display-name')
148+
).not.toBeInTheDocument();
149+
expect(
150+
screen.queryByTestId('account-client-name')
151+
).not.toBeInTheDocument();
152+
expect(screen.getByTestId('sign-out-link')).toHaveTextContent('Sign out');
153+
expect(screen.getByTestId('navigation-links')).toBeInTheDocument();
154+
});
155+
156+
it('matches snapshot (authenticated)', async () => {
157+
const header = await NhsNotifyHeader({});
158+
159+
const container = render(header);
160+
expect(container.asFragment()).toMatchSnapshot();
161+
});
162+
163+
describe(`with 'routing' flag enabled`, () => {
164+
it('renders both the navigation links with correct hrefs', async () => {
165+
const header = await NhsNotifyHeader({
166+
features: { routing: true },
167+
});
168+
169+
render(header);
170+
171+
screen.getByTestId('page-header');
172+
const nav = screen.getByTestId('navigation-links');
173+
const templatesLink = within(nav).getByRole('link', {
174+
name: 'Templates',
175+
});
176+
expect(templatesLink).toHaveAttribute('href', '/message-templates');
177+
const plansLink = within(nav).getByRole('link', {
178+
name: 'Message plans',
179+
});
180+
expect(plansLink).toHaveAttribute(
181+
'href',
182+
'/templates-and-message-plans/message-plans'
183+
);
184+
});
185+
});
186+
187+
describe(`with 'routing' flag disabled`, () => {
188+
it('renders the templates link with correct href', async () => {
189+
const header = await NhsNotifyHeader({
190+
features: { routing: false },
191+
});
192+
193+
render(header);
194+
195+
screen.getByTestId('page-header');
196+
const nav = screen.getByTestId('navigation-links');
197+
const templatesLink = within(nav).getByRole('link', {
198+
name: 'Templates',
199+
});
200+
expect(templatesLink).toHaveAttribute('href', '/message-templates');
201+
});
202+
203+
it('should not render the message plans link', async () => {
204+
const header = await NhsNotifyHeader({
205+
features: { routing: false },
206+
});
207+
208+
render(header);
29209

30-
expect(container.asFragment()).toMatchSnapshot();
210+
screen.getByTestId('page-header');
211+
const nav = screen.getByTestId('navigation-links');
212+
const plansLink = within(nav).queryByRole('link', {
213+
name: 'Message plans',
214+
});
215+
expect(plansLink).not.toBeInTheDocument();
216+
});
217+
});
31218
});
32219
});

0 commit comments

Comments
 (0)