Skip to content

Commit 63eb960

Browse files
committed
Merge branch 'main' into feature/CCM-11163_routing-config-api-get-endpoints
2 parents 1466e0b + 0c64e15 commit 63eb960

File tree

66 files changed

+1979
-338
lines changed

Some content is hidden

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

66 files changed

+1979
-338
lines changed

.github/workflows/stage-1-commit.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -149,7 +149,7 @@ jobs:
149149
trivy:
150150
name: "Trivy Scan"
151151
runs-on: ubuntu-latest
152-
timeout-minutes: 5
152+
timeout-minutes: 10
153153
needs: detect-terraform-changes
154154
if: needs.detect-terraform-changes.outputs.terraform_changed == 'true'
155155
steps:

.github/workflows/stage-2-test.yaml

Lines changed: 23 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -198,26 +198,26 @@ jobs:
198198
name: code-coverage-report
199199
path: .reports/lcov.info
200200

201-
perform-static-analysis:
202-
name: "Perform static analysis"
203-
needs: [test-unit, merge-coverage]
204-
runs-on: ubuntu-latest
205-
permissions:
206-
id-token: write
207-
contents: read
208-
timeout-minutes: 5
209-
steps:
210-
- name: "Checkout code"
211-
uses: actions/[email protected]
212-
with:
213-
fetch-depth: 0 # Full history is needed to improving relevancy of reporting
214-
- name: "Download coverage report for SONAR"
215-
uses: actions/download-artifact@v5
216-
with:
217-
name: code-coverage-report
218-
- name: "Perform static analysis"
219-
uses: ./.github/actions/perform-static-analysis
220-
with:
221-
sonar_organisation_key: "${{ vars.SONAR_ORGANISATION_KEY }}"
222-
sonar_project_key: "${{ vars.SONAR_PROJECT_KEY }}"
223-
sonar_token: "${{ secrets.SONAR_TOKEN }}"
201+
# perform-static-analysis:
202+
# name: "Perform static analysis"
203+
# needs: [test-unit, merge-coverage]
204+
# runs-on: ubuntu-latest
205+
# permissions:
206+
# id-token: write
207+
# contents: read
208+
# timeout-minutes: 5
209+
# steps:
210+
# - name: "Checkout code"
211+
# uses: actions/[email protected]
212+
# with:
213+
# fetch-depth: 0 # Full history is needed to improving relevancy of reporting
214+
# - name: "Download coverage report for SONAR"
215+
# uses: actions/download-artifact@v5
216+
# with:
217+
# name: code-coverage-report
218+
# - name: "Perform static analysis"
219+
# uses: ./.github/actions/perform-static-analysis
220+
# with:
221+
# sonar_organisation_key: "${{ vars.SONAR_ORGANISATION_KEY }}"
222+
# sonar_project_key: "${{ vars.SONAR_PROJECT_KEY }}"
223+
# sonar_token: "${{ secrets.SONAR_TOKEN }}"

.gitleaksignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,3 +7,4 @@ b19d88d1d92b0530f065feefcf25d8cdd82a876a:tests/test-team/auth/user.json:jwt:15
77
b19d88d1d92b0530f065feefcf25d8cdd82a876a:tests/test-team/auth/user.json:jwt:25
88
bc79df4f82052918ae6bf69d36279e5dd391d61e:tests/test-team/auth/user.json:jwt:15
99
bc79df4f82052918ae6bf69d36279e5dd391d61e:tests/test-team/auth/user.json:jwt:25
10+
306d9ec55d3498b86d5506da9a90ac486fc66563:frontend/src/components/molecules/MessagePlanFallbackConditions/MessagePlanFallbackConditions.tsx:ipv4:92

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

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,35 @@
11
// Jest Snapshot v1, https://goo.gl/fbAQLP
22

3-
exports[`UploadLetterTemplatePage should render UploadLetterTemplatePage 1`] = `
3+
exports[`UploadLetterTemplatePage should render UploadLetterTemplatePage with campaignId field when campaignIds is not available 1`] = `
44
<LetterTemplateForm
5+
campaignIds={
6+
[
7+
"campaign-id",
8+
]
9+
}
10+
initialState={
11+
{
12+
"campaignId": "campaign-id",
13+
"language": "en",
14+
"letterType": "x0",
15+
"name": "",
16+
"templateType": "LETTER",
17+
}
18+
}
19+
/>
20+
`;
21+
22+
exports[`UploadLetterTemplatePage should render UploadLetterTemplatePage with campaignIds field when available 1`] = `
23+
<LetterTemplateForm
24+
campaignIds={
25+
[
26+
"campaign-id",
27+
"other-campaign-id",
28+
]
29+
}
530
initialState={
631
{
32+
"campaignId": "",
733
"language": "en",
834
"letterType": "x0",
935
"name": "",

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

Lines changed: 69 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ describe('UploadLetterTemplatePage', () => {
2323
jest.resetAllMocks();
2424
});
2525

26-
it('should render UploadLetterTemplatePage', async () => {
26+
it('should check client ID and campaign ID', async () => {
2727
mockGetSessionServer.mockResolvedValueOnce({
2828
accessToken: 'mocktoken',
2929
clientId: 'client1',
@@ -35,28 +35,67 @@ describe('UploadLetterTemplatePage', () => {
3535
},
3636
});
3737

38+
await UploadLetterTemplatePage();
39+
40+
expect(mockGetSessionServer).toHaveBeenCalled();
41+
expect(mockFetchClient).toHaveBeenCalled();
42+
});
43+
44+
it('should render UploadLetterTemplatePage with campaignIds field when available', async () => {
45+
mockGetSessionServer.mockResolvedValueOnce({
46+
accessToken: 'mocktoken',
47+
clientId: 'client1',
48+
});
49+
mockFetchClient.mockResolvedValueOnce({
50+
data: {
51+
campaignIds: ['campaign-id', 'other-campaign-id'],
52+
campaignId: 'campaign-id',
53+
features: {},
54+
},
55+
});
56+
3857
const page = await UploadLetterTemplatePage();
3958

4059
expect(await generateMetadata()).toEqual({ title: pageTitle });
4160
expect(page).toMatchSnapshot();
4261
});
4362

44-
it('should check client ID and campaign ID', async () => {
63+
it('should render UploadLetterTemplatePage with campaignId field when campaignIds is not available', async () => {
4564
mockGetSessionServer.mockResolvedValueOnce({
4665
accessToken: 'mocktoken',
4766
clientId: 'client1',
4867
});
4968
mockFetchClient.mockResolvedValueOnce({
5069
data: {
51-
campaignId: 'campaign2',
70+
campaignIds: undefined,
71+
campaignId: 'campaign-id',
5272
features: {},
5373
},
5474
});
5575

76+
const page = await UploadLetterTemplatePage();
77+
78+
expect(await generateMetadata()).toEqual({ title: pageTitle });
79+
expect(page).toMatchSnapshot();
80+
});
81+
82+
it('should redirect to error page when client configuration is not present', async () => {
83+
const mockRedirect = jest.mocked(redirect);
84+
85+
mockGetSessionServer.mockResolvedValueOnce({
86+
accessToken: 'mocktoken',
87+
clientId: 'client-id',
88+
});
89+
mockFetchClient.mockResolvedValueOnce({
90+
data: null,
91+
});
92+
5693
await UploadLetterTemplatePage();
5794

58-
expect(mockGetSessionServer).toHaveBeenCalled();
59-
expect(mockFetchClient).toHaveBeenCalled();
95+
expect(mockRedirect).toHaveBeenCalledWith(
96+
'/upload-letter-template/client-id-and-campaign-id-required',
97+
RedirectType.replace
98+
);
6099
});
61100

62101
it('should redirect to error page when client ID is not present', async () => {
@@ -81,7 +120,30 @@ describe('UploadLetterTemplatePage', () => {
81120
);
82121
});
83122

84-
it('should redirect to error page when campaign ID is not present', async () => {
123+
it('should redirect to error page when campaignIds is present and empty', async () => {
124+
const mockRedirect = jest.mocked(redirect);
125+
126+
mockGetSessionServer.mockResolvedValueOnce({
127+
accessToken: 'mocktoken',
128+
clientId: 'client2',
129+
});
130+
mockFetchClient.mockResolvedValueOnce({
131+
data: {
132+
campaignIds: [],
133+
campaignId: 'campaign-id',
134+
features: {},
135+
},
136+
});
137+
138+
await UploadLetterTemplatePage();
139+
140+
expect(mockRedirect).toHaveBeenCalledWith(
141+
'/upload-letter-template/client-id-and-campaign-id-required',
142+
RedirectType.replace
143+
);
144+
});
145+
146+
it('should redirect to error page when neither campaignIds nor campaignId is present', async () => {
85147
const mockRedirect = jest.mocked(redirect);
86148

87149
mockGetSessionServer.mockResolvedValueOnce({
@@ -90,6 +152,7 @@ describe('UploadLetterTemplatePage', () => {
90152
});
91153
mockFetchClient.mockResolvedValueOnce({
92154
data: {
155+
campaignIds: undefined,
93156
campaignId: undefined,
94157
features: {},
95158
},

frontend/src/__tests__/components/forms/EmailTemplateForm/server-action.test.ts

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,28 @@ describe('CreateEmailTemplate server actions', () => {
6868
});
6969
});
7070

71+
it('create-email-template - should return response when when template message contains insecure url', async () => {
72+
const response = await processFormActions(
73+
initialState,
74+
getMockFormData({
75+
'form-id': 'create-email-template',
76+
emailTemplateName: 'template-name',
77+
emailTemplateSubjectLine: 'template-subject-line',
78+
emailTemplateMessage: '**a message linking to http://www.example.com**',
79+
})
80+
);
81+
82+
expect(response).toEqual({
83+
...initialState,
84+
errorState: {
85+
formErrors: [],
86+
fieldErrors: {
87+
emailTemplateMessage: ['URLs must start with https://'],
88+
},
89+
},
90+
});
91+
});
92+
7193
test('should save the template and redirect', async () => {
7294
saveTemplateMock.mockResolvedValue({
7395
...initialState,

frontend/src/__tests__/components/forms/LetterTemplateForm.test.tsx/LetterTemplateForm.test.tsx renamed to frontend/src/__tests__/components/forms/LetterTemplateForm/LetterTemplateForm.test.tsx

Lines changed: 35 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import { fireEvent, render, screen } from '@testing-library/react';
22
import { mockDeep } from 'jest-mock-extended';
33
import {
44
TemplateFormState,
5-
LetterTemplate,
5+
UploadLetterTemplate,
66
} from 'nhs-notify-web-template-management-utils';
77
import { LetterTemplateForm } from '@forms/LetterTemplateForm/LetterTemplateForm';
88

@@ -26,12 +26,30 @@ jest.mock('react', () => {
2626
test('renders page with preloaded field values', () => {
2727
const container = render(
2828
<LetterTemplateForm
29-
initialState={mockDeep<TemplateFormState<LetterTemplate>>({
29+
initialState={mockDeep<TemplateFormState<UploadLetterTemplate>>({
30+
campaignId: 'campaign-id',
3031
errorState: undefined,
3132
name: 'template-name',
3233
letterType: 'x1',
3334
language: 'ar',
3435
})}
36+
campaignIds={['campaign-id']}
37+
/>
38+
);
39+
expect(container.asFragment()).toMatchSnapshot();
40+
});
41+
42+
test('renders page with multiple campaign ids', () => {
43+
const container = render(
44+
<LetterTemplateForm
45+
initialState={mockDeep<TemplateFormState<UploadLetterTemplate>>({
46+
campaignId: 'campaign-id',
47+
errorState: undefined,
48+
name: 'template-name',
49+
letterType: 'x1',
50+
language: 'ar',
51+
})}
52+
campaignIds={['campaign-id', 'other-campaign-id']}
3553
/>
3654
);
3755
expect(container.asFragment()).toMatchSnapshot();
@@ -43,12 +61,14 @@ test('shows right-to-left language warning when language changes', () => {
4361

4462
const container = render(
4563
<LetterTemplateForm
46-
initialState={mockDeep<TemplateFormState<LetterTemplate>>({
64+
initialState={mockDeep<TemplateFormState<UploadLetterTemplate>>({
65+
campaignId: 'campaign-id',
4766
errorState: undefined,
4867
name: 'template-name',
4968
letterType: 'x1',
5069
language: initialLanguage,
5170
})}
71+
campaignIds={['campaign-id']}
5272
/>
5373
);
5474

@@ -68,12 +88,14 @@ test('hides right-to-left language warning when language changes', () => {
6888

6989
const container = render(
7090
<LetterTemplateForm
71-
initialState={mockDeep<TemplateFormState<LetterTemplate>>({
91+
initialState={mockDeep<TemplateFormState<UploadLetterTemplate>>({
92+
campaignId: 'campaign-id',
7293
errorState: undefined,
7394
name: 'template-name',
7495
letterType: 'x1',
7596
language: initialLanguage,
7697
})}
98+
campaignIds={['campaign-id']}
7799
/>
78100
);
79101

@@ -90,7 +112,8 @@ test('hides right-to-left language warning when language changes', () => {
90112
test('renders page one error', () => {
91113
const container = render(
92114
<LetterTemplateForm
93-
initialState={mockDeep<TemplateFormState<LetterTemplate>>({
115+
initialState={mockDeep<TemplateFormState<UploadLetterTemplate>>({
116+
campaignId: 'campaign-id',
94117
errorState: {
95118
formErrors: [],
96119
fieldErrors: {
@@ -101,6 +124,7 @@ test('renders page one error', () => {
101124
letterType: 'x0',
102125
language: 'en',
103126
})}
127+
campaignIds={['campaign-id']}
104128
/>
105129
);
106130
expect(container.asFragment()).toMatchSnapshot();
@@ -109,7 +133,8 @@ test('renders page one error', () => {
109133
test('renders page with multiple errors', () => {
110134
const container = render(
111135
<LetterTemplateForm
112-
initialState={mockDeep<TemplateFormState<LetterTemplate>>({
136+
initialState={mockDeep<TemplateFormState<UploadLetterTemplate>>({
137+
campaignId: 'campaign-id',
113138
errorState: {
114139
formErrors: [],
115140
fieldErrors: {
@@ -124,6 +149,7 @@ test('renders page with multiple errors', () => {
124149
letterType: undefined,
125150
language: undefined,
126151
})}
152+
campaignIds={['campaign-id']}
127153
/>
128154
);
129155
expect(container.asFragment()).toMatchSnapshot();
@@ -132,12 +158,14 @@ test('renders page with multiple errors', () => {
132158
test('Client-side validation triggers', () => {
133159
const container = render(
134160
<LetterTemplateForm
135-
initialState={mockDeep<TemplateFormState<LetterTemplate>>({
161+
initialState={mockDeep<TemplateFormState<UploadLetterTemplate>>({
162+
campaignId: 'campaign-id',
136163
errorState: undefined,
137164
name: undefined,
138165
letterType: undefined,
139166
language: undefined,
140167
})}
168+
campaignIds={['campaign-id']}
141169
/>
142170
);
143171
const submitButton = screen.getByTestId('submit-button');

0 commit comments

Comments
 (0)