Skip to content

Commit 1df9c6a

Browse files
committed
CCM-11496: message plan move to production page
1 parent dc03250 commit 1df9c6a

File tree

20 files changed

+681
-148
lines changed

20 files changed

+681
-148
lines changed

.eslintrc.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@
6464
"jest.config.ts",
6565
"jest.setup.ts",
6666
"**/__tests__/**",
67+
"**/*.test.[jt]s?(x)",
6768
"**/*.dev.[jt]s?(x)"
6869
]
6970
}

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

Lines changed: 40 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import {
44
ContentBlock,
55
ContentItem,
66
} from '@molecules/ContentRenderer/ContentRenderer';
7+
import { markdownList } from '@utils/markdown-list';
78

89
describe('ContentRenderer', () => {
910
it('renders nothing for empty content array', () => {
@@ -130,8 +131,8 @@ describe('ContentRenderer', () => {
130131
it('renders list blocks', () => {
131132
const content: ContentBlock[] = [
132133
{
133-
type: 'list',
134-
items: ['Item 1', 'Item 2', 'Item 3'],
134+
type: 'text',
135+
text: markdownList('ul', ['Item 1', 'Item 2', 'Item 3']),
135136
},
136137
];
137138

@@ -142,4 +143,41 @@ describe('ContentRenderer', () => {
142143
expect(listItems[1]).toHaveTextContent('Item 2');
143144
expect(listItems[2]).toHaveTextContent('Item 3');
144145
});
146+
147+
it('renders with overrides on text blocks', () => {
148+
const content: ContentBlock[] = [
149+
{
150+
type: 'text',
151+
text: 'This is a paragraph.',
152+
overrides: { p: { props: { className: 'foo' } } },
153+
},
154+
{
155+
type: 'text',
156+
text: 'Another paragraph.',
157+
overrides: { p: { props: { className: 'bar' } } },
158+
},
159+
];
160+
161+
render(<ContentRenderer content={content} />);
162+
163+
const listItems = screen.getAllByRole('paragraph');
164+
expect(listItems).toHaveLength(2);
165+
expect(listItems[0]).toHaveClass('foo');
166+
expect(listItems[1]).toHaveClass('bar');
167+
});
168+
169+
it('renders with overrides on inline-text blocks', () => {
170+
const content: ContentBlock[] = [
171+
{
172+
type: 'inline-text',
173+
text: 'This is a [link](https://example.com).',
174+
overrides: { a: { props: { className: 'foo' } } },
175+
},
176+
];
177+
178+
render(<ContentRenderer content={content} />);
179+
180+
const link = screen.getByRole('link');
181+
expect(link).toHaveClass('foo');
182+
});
145183
});

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

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,27 @@ describe('MarkdownContent', () => {
100100
expect(container.querySelector('p')).toHaveTextContent('Block content');
101101
});
102102

103+
it('renders block content with overrides', () => {
104+
const { container } = render(
105+
<MarkdownContent
106+
content='Block content'
107+
overrides={{ p: { props: { className: 'foobar' } } }}
108+
/>
109+
);
110+
expect(container.querySelector('p')).toHaveClass('foobar');
111+
});
112+
113+
it('renders inline mode with overrides', () => {
114+
const { container } = render(
115+
<MarkdownContent
116+
content='Here is a [link](https://example.com)'
117+
mode='inline'
118+
overrides={{ a: { props: { className: 'foobar' } } }}
119+
/>
120+
);
121+
expect(container.querySelector('a')).toHaveClass('foobar');
122+
});
123+
103124
it('renders interpolated variables correctly', () => {
104125
render(
105126
<MarkdownContent

frontend/src/__tests__/utils/form-actions.test.ts

Lines changed: 48 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -16,12 +16,14 @@ import {
1616
setTemplateToSubmitted,
1717
requestTemplateProof,
1818
createRoutingConfig,
19+
submitRoutingConfig,
1920
} from '@utils/form-actions';
2021
import { getSessionServer } from '@utils/amplify-utils';
21-
import { TemplateDto } from 'nhs-notify-backend-client';
22+
import { RoutingConfig, TemplateDto } from 'nhs-notify-backend-client';
2223
import { templateApiClient } from 'nhs-notify-backend-client/src/template-api-client';
2324
import { routingConfigurationApiClient } from 'nhs-notify-backend-client/src/routing-config-api-client';
2425
import { randomUUID } from 'node:crypto';
26+
import { mock } from 'jest-mock-extended';
2527

2628
const mockedTemplateClient = jest.mocked(templateApiClient);
2729
const mockedRoutingConfigClient = jest.mocked(routingConfigurationApiClient);
@@ -34,7 +36,7 @@ jest.mock('nhs-notify-backend-client/src/routing-config-api-client');
3436
describe('form-actions', () => {
3537
beforeEach(() => {
3638
jest.resetAllMocks();
37-
authIdTokenServerMock.mockResolvedValueOnce({
39+
authIdTokenServerMock.mockResolvedValue({
3840
accessToken: 'token',
3941
clientId: 'client1',
4042
});
@@ -98,7 +100,6 @@ describe('form-actions', () => {
98100
});
99101

100102
test('createTemplate - should throw error when no token', async () => {
101-
authIdTokenServerMock.mockReset();
102103
authIdTokenServerMock.mockResolvedValueOnce({
103104
accessToken: undefined,
104105
clientId: undefined,
@@ -266,7 +267,6 @@ describe('form-actions', () => {
266267
});
267268

268269
test('uploadLetterTemplate - should throw error when no token', async () => {
269-
authIdTokenServerMock.mockReset();
270270
authIdTokenServerMock.mockResolvedValueOnce({
271271
accessToken: undefined,
272272
clientId: undefined,
@@ -363,7 +363,6 @@ describe('form-actions', () => {
363363
});
364364

365365
test('saveTemplate - should throw error when no token', async () => {
366-
authIdTokenServerMock.mockReset();
367366
authIdTokenServerMock.mockResolvedValueOnce({
368367
accessToken: undefined,
369368
clientId: undefined,
@@ -431,7 +430,6 @@ describe('form-actions', () => {
431430
});
432431

433432
test('getTemplate - should throw error when no token', async () => {
434-
authIdTokenServerMock.mockReset();
435433
authIdTokenServerMock.mockResolvedValueOnce({
436434
accessToken: undefined,
437435
clientId: undefined,
@@ -481,7 +479,6 @@ describe('form-actions', () => {
481479
});
482480

483481
test('getTemplates - should throw error when no token', async () => {
484-
authIdTokenServerMock.mockReset();
485482
authIdTokenServerMock.mockResolvedValueOnce({
486483
accessToken: undefined,
487484
clientId: undefined,
@@ -574,11 +571,7 @@ describe('form-actions', () => {
574571
});
575572

576573
test('submitTemplate - should throw error when no token', async () => {
577-
authIdTokenServerMock.mockReset();
578-
authIdTokenServerMock.mockResolvedValueOnce({
579-
accessToken: undefined,
580-
clientId: undefined,
581-
});
574+
authIdTokenServerMock.mockResolvedValueOnce({});
582575

583576
await expect(setTemplateToSubmitted('id')).rejects.toThrow(
584577
'Failed to get access token'
@@ -623,11 +616,7 @@ describe('form-actions', () => {
623616
});
624617

625618
test('deleteTemplate - should throw error when no token', async () => {
626-
authIdTokenServerMock.mockReset();
627-
authIdTokenServerMock.mockResolvedValueOnce({
628-
accessToken: undefined,
629-
clientId: undefined,
630-
});
619+
authIdTokenServerMock.mockResolvedValueOnce({});
631620

632621
await expect(setTemplateToDeleted('id')).rejects.toThrow(
633622
'Failed to get access token'
@@ -690,11 +679,7 @@ describe('form-actions', () => {
690679
});
691680

692681
test('requestTemplateProof - should throw error when no token', async () => {
693-
authIdTokenServerMock.mockReset();
694-
authIdTokenServerMock.mockResolvedValueOnce({
695-
accessToken: undefined,
696-
clientId: undefined,
697-
});
682+
authIdTokenServerMock.mockResolvedValueOnce({});
698683

699684
await expect(requestTemplateProof('id')).rejects.toThrow(
700685
'Failed to get access token'
@@ -772,11 +757,7 @@ describe('form-actions', () => {
772757
});
773758

774759
test('errors when no token', async () => {
775-
authIdTokenServerMock.mockReset();
776-
authIdTokenServerMock.mockResolvedValueOnce({
777-
accessToken: undefined,
778-
clientId: undefined,
779-
});
760+
authIdTokenServerMock.mockResolvedValueOnce({});
780761

781762
await expect(
782763
createRoutingConfig({
@@ -824,4 +805,44 @@ describe('form-actions', () => {
824805
).rejects.toThrow('Failed to create message plan');
825806
});
826807
});
808+
809+
describe('submitRoutingConfig', () => {
810+
it('submits the routing config', async () => {
811+
mockedRoutingConfigClient.submit.mockResolvedValueOnce({
812+
data: mock<RoutingConfig>(),
813+
});
814+
815+
await submitRoutingConfig('id');
816+
817+
expect(mockedRoutingConfigClient.submit).toHaveBeenCalledWith(
818+
'id',
819+
'token'
820+
);
821+
});
822+
823+
it('throws if there is no token', async () => {
824+
authIdTokenServerMock.mockResolvedValueOnce({});
825+
826+
await expect(() => submitRoutingConfig('id')).rejects.toThrow(
827+
'Failed to get access token'
828+
);
829+
830+
expect(mockedRoutingConfigClient.submit).not.toHaveBeenCalled();
831+
});
832+
833+
it('throws if the api request returns an error', async () => {
834+
mockedRoutingConfigClient.submit.mockResolvedValueOnce({
835+
error: {
836+
errorMeta: {
837+
code: 400,
838+
description: 'Bad request',
839+
},
840+
},
841+
});
842+
843+
await expect(() => submitRoutingConfig('id')).rejects.toThrow(
844+
'Failed to submit message plan'
845+
);
846+
});
847+
});
827848
});
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import { markdownList } from '@utils/markdown-list';
2+
3+
test('returns a markdown string for an ordered list', () => {
4+
expect(markdownList('ol', ['foo', 'bar', 'baz'])).toEqual(
5+
'1. foo\n2. bar\n3. baz'
6+
);
7+
});
8+
9+
test('returns a markdown string for an unordered list', () => {
10+
expect(markdownList('ul', ['foo', 'bar', 'baz'])).toEqual(
11+
'- foo\n- bar\n- baz'
12+
);
13+
});

frontend/src/app/message-plans/move-message-plan-to-production/[routingConfigId]/page.tsx

Lines changed: 0 additions & 83 deletions
This file was deleted.

0 commit comments

Comments
 (0)