diff --git a/frontend/src/__tests__/utils/form-actions.test.ts b/frontend/src/__tests__/utils/form-actions.test.ts
index c7f87ef49..f64934c60 100644
--- a/frontend/src/__tests__/utils/form-actions.test.ts
+++ b/frontend/src/__tests__/utils/form-actions.test.ts
@@ -17,6 +17,7 @@ import {
setTemplateToDeleted,
setTemplateToSubmitted,
requestTemplateProof,
+ createProofingRequest,
getLetterVariantsForTemplate,
getLetterVariantById,
uploadDocxTemplate,
@@ -25,7 +26,9 @@ import {
} from '@utils/form-actions';
import { getSessionServer } from '@utils/amplify-utils';
import type {
+ CreateProofingRequest,
LetterVariant,
+ ProofRequest,
TemplateDto,
TemplateStatus,
} from 'nhs-notify-web-template-management-types';
@@ -1354,4 +1357,83 @@ describe('form-actions', () => {
).rejects.toThrow('Failed to get access token');
});
});
+
+ describe('createProofingRequest', () => {
+ test('creates proofing request successfully', async () => {
+ const responseData: ProofRequest = {
+ id: 'proof-request-id',
+ contactDetailValue: 'test@example.com',
+ createdAt: '2026-05-20T10:00:00.000Z',
+ createdBy: 'user1',
+ templateId: 'template-id',
+ templateType: 'EMAIL',
+ testPatientNhsNumber: '1234567890',
+ personalisation: {
+ firstName: 'Jo',
+ },
+ };
+
+ mockedTemplateClient.createProofingRequest.mockResolvedValueOnce({
+ data: responseData,
+ });
+
+ const request: CreateProofingRequest = {
+ contactDetailId: 'contact-detail-id',
+ personalisation: {
+ firstName: 'Jo',
+ },
+ testPatientNhsNumber: '1234567890',
+ };
+
+ const response = await createProofingRequest('template-id', request);
+
+ expect(mockedTemplateClient.createProofingRequest).toHaveBeenCalledWith(
+ 'template-id',
+ 'token',
+ request
+ );
+
+ expect(response).toEqual(responseData);
+ });
+
+ test('should throw error when creation unexpectedly fails', async () => {
+ mockedTemplateClient.createProofingRequest.mockResolvedValueOnce({
+ error: {
+ errorMeta: {
+ code: 400,
+ description: 'Bad request',
+ },
+ },
+ });
+
+ const request: CreateProofingRequest = {
+ testPatientNhsNumber: '1234567890',
+ };
+
+ await expect(
+ createProofingRequest('template-id', request)
+ ).rejects.toThrow('Failed to create a proofing request');
+
+ expect(mockedTemplateClient.createProofingRequest).toHaveBeenCalledWith(
+ 'template-id',
+ 'token',
+ request
+ );
+ });
+
+ test('should throw error when no token', async () => {
+ authIdTokenServerMock.mockResolvedValueOnce({
+ accessToken: undefined,
+ clientId: undefined,
+ });
+
+ const request: CreateProofingRequest = {
+ testPatientNhsNumber: '1234567890',
+ };
+
+ await expect(
+ createProofingRequest('template-id', request)
+ ).rejects.toThrow('Failed to get access token');
+ });
+ });
});
diff --git a/frontend/src/components/forms/SendTestMessage/SendTestMessage.tsx b/frontend/src/components/forms/SendTestMessage/SendTestMessage.tsx
index 212c7c1ec..f0b4f9641 100644
--- a/frontend/src/components/forms/SendTestMessage/SendTestMessage.tsx
+++ b/frontend/src/components/forms/SendTestMessage/SendTestMessage.tsx
@@ -66,6 +66,7 @@ export function SendTestMessage({
+
{pageHeading}
-
-
{children}
{
'Enter a test patient NHS number that starts with 9',
'4010232137',
],
+ [
+ 'less than 10 digits NHS number',
+ 'The test patient NHS number must be 10 digits',
+ '943476591',
+ ],
] as const)(
'should display error messages for %s',
(_caseName, errorMessage, testNhsNumber) => {
diff --git a/frontend/src/components/forms/SendTestMessage/__tests__/__snapshots__/SendTestMessage.test.tsx.snap b/frontend/src/components/forms/SendTestMessage/__tests__/__snapshots__/SendTestMessage.test.tsx.snap
index 94a8e6a1f..661b339ce 100644
--- a/frontend/src/components/forms/SendTestMessage/__tests__/__snapshots__/SendTestMessage.test.tsx.snap
+++ b/frontend/src/components/forms/SendTestMessage/__tests__/__snapshots__/SendTestMessage.test.tsx.snap
@@ -211,6 +211,38 @@ exports[`SendTestNhsAppMessage matches snapshot in validation error state 1`] =
+
+
+ There is a problem
+
+
+
Send a test NHS App message
@@ -253,38 +285,6 @@ exports[`SendTestNhsAppMessage matches snapshot in validation error state 1`] =
type="hidden"
value="app-template-id"
/>
-
-
- There is a problem
-
-
-
({
},
}));
+jest.mock('@utils/form-actions', () => ({
+ createProofingRequest: jest.fn(),
+}));
+const createProofingRequestMock = jest.mocked(createProofingRequest);
+
describe('$SendTestNhsAppMessageSchema', () => {
const baseData = {
testNhsNumber: '9434765919',
@@ -131,55 +137,99 @@ describe('sendTestNhsAppMessageAction', () => {
jest.clearAllMocks();
});
- // TODO: CCM-8366 - Add test for request with custom fields when implemented
+ const proofRequestId = 'test-proofing-request-id';
+ const testNhsNumber = '9434765919';
test('redirects to confirmation screen on successful validation without custom fields', async () => {
const formData = getMockFormData({
- testNhsNumber: '9434765919',
+ testNhsNumber: testNhsNumber,
templateId: validTemplateId,
});
+ createProofingRequestMock.mockResolvedValue({
+ id: proofRequestId,
+ contactDetailValue: 'test',
+ createdAt: 'test',
+ createdBy: 'test',
+ templateId: validTemplateId,
+ templateType: 'NHS_APP',
+ testPatientNhsNumber: testNhsNumber,
+ });
+
await sendTestNhsAppMessageAction(mockFormState, formData);
- // TODO: CCM-8366 - Update to use proofing request ID instead of template ID
expect(redirect).toHaveBeenCalledWith(
- `/test-nhs-app-message-sent/${validTemplateId}`,
+ `/test-nhs-app-message-sent/${proofRequestId}`,
'push'
);
+ expect(createProofingRequestMock).toHaveBeenCalledWith(validTemplateId, {
+ testPatientNhsNumber: testNhsNumber,
+ personalisation: {},
+ });
});
test('redirects to confirmation screen when multiple custom fields are populated', async () => {
const formData = getMockFormData({
- testNhsNumber: '9434765919',
+ testNhsNumber: testNhsNumber,
templateId: validTemplateId,
'personalisation|appointmentDate': 'Monday 10th March',
'personalisation|clinicLocation': 'Clinic A',
'personalisation|nameOfSurgery': 'Surgery B',
});
+ createProofingRequestMock.mockResolvedValue({
+ id: proofRequestId,
+ contactDetailValue: 'test',
+ createdAt: 'test',
+ createdBy: 'test',
+ templateId: validTemplateId,
+ templateType: 'NHS_APP',
+ testPatientNhsNumber: testNhsNumber,
+ });
+
await sendTestNhsAppMessageAction(mockFormState, formData);
- // TODO: CCM-8366 - Update to use proofing request ID instead of template ID
expect(redirect).toHaveBeenCalledWith(
- `/test-nhs-app-message-sent/${validTemplateId}`,
+ `/test-nhs-app-message-sent/${proofRequestId}`,
'push'
);
+ expect(createProofingRequestMock).toHaveBeenCalledWith(validTemplateId, {
+ testPatientNhsNumber: testNhsNumber,
+ personalisation: {
+ appointmentDate: 'Monday 10th March',
+ clinicLocation: 'Clinic A',
+ nameOfSurgery: 'Surgery B',
+ },
+ });
});
test('redirects to confirmation screen when valid test NHS number contains spaces', async () => {
+ const nhsNumberWithSpaces = '943 476 5919';
const formData = getMockFormData({
- testNhsNumber: '943 476 5919',
+ testNhsNumber: nhsNumberWithSpaces,
templateId: validTemplateId,
});
+ createProofingRequestMock.mockResolvedValue({
+ id: proofRequestId,
+ contactDetailValue: 'test',
+ createdAt: 'test',
+ createdBy: 'test',
+ templateId: validTemplateId,
+ templateType: 'NHS_APP',
+ testPatientNhsNumber: testNhsNumber,
+ });
+
await sendTestNhsAppMessageAction(mockFormState, formData);
- // TODO: CCM-8366 - Add test for request with normalised NHS number
- // TODO: CCM-8366 - Update to use proofing request ID instead of template ID
expect(redirect).toHaveBeenCalledWith(
- `/test-nhs-app-message-sent/${validTemplateId}`,
+ `/test-nhs-app-message-sent/${proofRequestId}`,
'push'
);
+ expect(createProofingRequestMock).toHaveBeenCalledWith(validTemplateId, {
+ testPatientNhsNumber: '9434765919',
+ personalisation: {},
+ });
});
describe('error scenarios', () => {
diff --git a/frontend/src/components/forms/SendTestMessage/server-action.ts b/frontend/src/components/forms/SendTestMessage/server-action.ts
index 73d98d7f9..614a5aa78 100644
--- a/frontend/src/components/forms/SendTestMessage/server-action.ts
+++ b/frontend/src/components/forms/SendTestMessage/server-action.ts
@@ -1,14 +1,15 @@
-import { redirect, RedirectType } from 'next/navigation';
-import { z } from 'zod';
+import content from '@content/content';
+import { PERSONALISATION_FORMDATA_PREFIX } from '@utils/constants';
+import { createProofingRequest } from '@utils/form-actions';
+import { formDataToFormStateFields } from '@utils/form-data-to-form-state';
+import { interpolate } from '@utils/interpolate';
import {
isTestNHSNumber,
isValidNHSNumber,
} from 'nhs-notify-backend-client/schemas';
-import { formDataToFormStateFields } from '@utils/form-data-to-form-state';
-import { PERSONALISATION_FORMDATA_PREFIX } from '@utils/constants';
-import { interpolate } from '@utils/interpolate';
import type { FormState } from '@utils/types';
-import content from '@content/content';
+import { redirect, RedirectType } from 'next/navigation';
+import { z } from 'zod';
const {
fields: {
@@ -95,7 +96,7 @@ export async function sendTestNhsAppMessageAction(
const fields = formDataToFormStateFields(formData);
const personalisationFieldErrors: Record
= {};
- const customPersonalisation: Record = {};
+ const personalisation: Record = {};
for (const [key, value] of Object.entries(fields)) {
if (!key.startsWith(PERSONALISATION_FORMDATA_PREFIX)) {
@@ -105,7 +106,7 @@ export async function sendTestNhsAppMessageAction(
const fieldName = key.slice(PERSONALISATION_FORMDATA_PREFIX.length);
const fieldValue = String(value);
- customPersonalisation[fieldName] = fieldValue;
+ personalisation[fieldName] = fieldValue;
if (!fieldValue.trim()) {
personalisationFieldErrors[`custom-${fieldName}`] = [
@@ -135,16 +136,13 @@ export async function sendTestNhsAppMessageAction(
const { templateId, testNhsNumber: testPatientNhsNumber } = result.data;
- const _proofRequest = {
- templateId,
+ const proofRequest = await createProofingRequest(templateId, {
testPatientNhsNumber,
- customPersonalisation,
- };
+ personalisation,
+ });
- // TODO: CCM-8366 - Implement actual call to send test message
- // TODO: CCM-8366 - Update URL to contain proofing request ID instead of template ID
return redirect(
- `/test-nhs-app-message-sent/${templateId}`,
+ `/test-nhs-app-message-sent/${proofRequest.id}`,
RedirectType.push
);
}
diff --git a/frontend/src/utils/form-actions.ts b/frontend/src/utils/form-actions.ts
index 987740121..b3d45dbe8 100644
--- a/frontend/src/utils/form-actions.ts
+++ b/frontend/src/utils/form-actions.ts
@@ -13,6 +13,8 @@ import type {
TemplateDto,
LetterVariant,
LetterProofRequest,
+ CreateProofingRequest,
+ ProofRequest,
} from 'nhs-notify-web-template-management-types';
import { logger } from 'nhs-notify-web-template-management-utils/logger';
import {
@@ -281,6 +283,30 @@ export async function generateLetterProof(
return data;
}
+export async function createProofingRequest(
+ templateId: string,
+ request: CreateProofingRequest
+): Promise {
+ const { accessToken } = await getSessionServer();
+
+ if (!accessToken) {
+ throw new Error('Failed to get access token');
+ }
+
+ const { error, data } = await templateApiClient.createProofingRequest(
+ templateId,
+ accessToken,
+ request
+ );
+
+ if (error) {
+ logger.error('Failed to create a proofing request', error);
+ throw new Error('Failed to create a proofing request');
+ }
+
+ return data;
+}
+
export async function getTemplate(
templateId: string
): Promise {
diff --git a/lambdas/backend-client/src/__tests__/template-api-client.test.ts b/lambdas/backend-client/src/__tests__/template-api-client.test.ts
index 9f14d0908..ca2f61142 100644
--- a/lambdas/backend-client/src/__tests__/template-api-client.test.ts
+++ b/lambdas/backend-client/src/__tests__/template-api-client.test.ts
@@ -1,5 +1,8 @@
import MockAdapter from 'axios-mock-adapter';
-import type { LetterVariant } from 'nhs-notify-web-template-management-types';
+import type {
+ CreateProofingRequest,
+ LetterVariant,
+} from 'nhs-notify-web-template-management-types';
import { templateApiClient as client } from '../template-api-client';
import { httpClient } from '../axios-client';
@@ -340,7 +343,7 @@ describe('TemplateAPIClient', () => {
const headers = axiosMock.history.at(0)?.headers;
- expect(headers ? headers['X-Lock-Number'] : null).toEqual('1');
+ expect(headers?.['X-Lock-Number']).toEqual('1');
});
test('patchTemplate - should return error', async () => {
@@ -408,7 +411,7 @@ describe('TemplateAPIClient', () => {
const headers = axiosMock.history.at(0)?.headers;
- expect(headers ? headers['X-Lock-Number'] : null).toEqual('5');
+ expect(headers?.['X-Lock-Number']).toEqual('5');
});
test('getTemplate - should return error', async () => {
@@ -527,7 +530,7 @@ describe('TemplateAPIClient', () => {
const headers = axiosMock.history.at(0)?.headers;
- expect(headers ? headers['X-Lock-Number'] : null).toEqual('2');
+ expect(headers?.['X-Lock-Number']).toEqual('2');
});
test('should return template', async () => {
@@ -580,7 +583,7 @@ describe('TemplateAPIClient', () => {
const headers = axiosMock.history.at(0)?.headers;
- expect(headers ? headers['X-Lock-Number'] : null).toEqual('2');
+ expect(headers?.['X-Lock-Number']).toEqual('2');
});
test('should return template', async () => {
@@ -743,7 +746,7 @@ describe('TemplateAPIClient', () => {
const headers = axiosMock.history.at(0)?.headers;
- expect(headers ? headers['X-Lock-Number'] : null).toEqual('4');
+ expect(headers?.['X-Lock-Number']).toEqual('4');
});
test('should return content', async () => {
@@ -765,6 +768,82 @@ describe('TemplateAPIClient', () => {
});
});
+ describe('createProofingRequest', () => {
+ const proofingRequest: CreateProofingRequest = {
+ testPatientNhsNumber: '9991234567',
+ personalisation: {
+ firstName: 'Jo',
+ lastName: 'Bloggs',
+ },
+ contactDetailId: 'contact-detail-id',
+ };
+ const templateId = 'real-id';
+
+ test('should return error', async () => {
+ axiosMock
+ .onPost(`/v1/template/${templateId}/proofing-request`)
+ .reply(400, {
+ statusCode: 400,
+ technicalMessage: 'Bad request',
+ details: {
+ message: 'Invalid proofing data',
+ },
+ });
+
+ const result = await client.createProofingRequest(
+ templateId,
+ testToken,
+ proofingRequest
+ );
+
+ expect(result.error).toEqual({
+ errorMeta: {
+ code: 400,
+ description: 'Bad request',
+ details: {
+ message: 'Invalid proofing data',
+ },
+ },
+ });
+
+ expect(result.data).toBeUndefined();
+
+ expect(axiosMock.history.post.length).toBe(1);
+
+ const headers = axiosMock.history.at(0)?.headers;
+
+ expect(headers?.['Authorization']).toEqual(testToken);
+ });
+
+ test('should return proofing request success', async () => {
+ const data = {
+ id: 'proof-id',
+ status: 'PENDING',
+ templateId: templateId,
+ createdAt: '2026-05-20T12:00:00Z',
+ };
+
+ axiosMock
+ .onPost(`/v1/template/${templateId}/proofing-request`)
+ .reply(201, { data, statusCode: 201 });
+
+ const result = await client.createProofingRequest(
+ templateId,
+ testToken,
+ proofingRequest
+ );
+
+ expect(result.data).toEqual(data);
+ expect(result.error).toBeUndefined();
+
+ expect(axiosMock.history.post.length).toBe(1);
+
+ const headers = axiosMock.history.at(0)?.headers;
+
+ expect(headers?.['Authorization']).toEqual(testToken);
+ });
+ });
+
describe('getTemplateLetterVariants', () => {
test('should return error', async () => {
axiosMock.onGet('/v1/template/template-123/letter-variants').reply(500, {
diff --git a/lambdas/backend-client/src/template-api-client.ts b/lambdas/backend-client/src/template-api-client.ts
index f2b81e34f..54e71caee 100644
--- a/lambdas/backend-client/src/template-api-client.ts
+++ b/lambdas/backend-client/src/template-api-client.ts
@@ -7,6 +7,9 @@ import type {
LetterProofRequest,
LetterVariant,
LetterVariantListSuccess,
+ CreateProofingRequest,
+ ProofRequestSuccess,
+ ProofRequest,
} from 'nhs-notify-web-template-management-types';
import { Result } from './types/result';
import { catchAxiosError, httpClient } from './axios-client';
@@ -378,4 +381,33 @@ export const templateApiClient = {
data: response.data.data,
};
},
+
+ async createProofingRequest(
+ templateId: string,
+ owner: string,
+ data: CreateProofingRequest
+ ): Promise> {
+ const response = await catchAxiosError(
+ httpClient.post(
+ `/v1/template/${encodeURIComponent(templateId)}/proofing-request`,
+ data,
+ {
+ headers: {
+ 'Content-Type': 'application/json',
+ Authorization: owner,
+ },
+ }
+ )
+ );
+
+ if (response.error) {
+ return {
+ error: response.error,
+ };
+ }
+
+ return {
+ data: response.data.data,
+ };
+ },
};
diff --git a/tests/test-team/helpers/db/proof-requests-storage-helper.ts b/tests/test-team/helpers/db/proof-requests-storage-helper.ts
index 7bd06ab30..eb9924adf 100644
--- a/tests/test-team/helpers/db/proof-requests-storage-helper.ts
+++ b/tests/test-team/helpers/db/proof-requests-storage-helper.ts
@@ -2,9 +2,10 @@ import { DynamoDBClient } from '@aws-sdk/client-dynamodb';
import {
BatchWriteCommand,
DynamoDBDocumentClient,
+ GetCommand,
} from '@aws-sdk/lib-dynamodb';
-import type { DigitalProofRequest } from '../types';
import { chunk } from 'helpers/chunk';
+import type { DigitalProofRequest } from '../types';
type ProofRequestKey = { id: string; owner: string };
@@ -91,4 +92,18 @@ export class ProofRequestsStorageHelper {
await this.delete(this.adHocKeys);
this.adHocKeys = [];
}
+
+ async get(key: ProofRequestKey) {
+ const { Item } = await this.dynamo.send(
+ new GetCommand({
+ TableName: process.env.PROOF_REQUESTS_TABLE_NAME,
+ Key: {
+ owner: `INTERNAL_USER#${key.owner}`,
+ id: key.id,
+ },
+ })
+ );
+
+ return Item as DigitalProofRequest;
+ }
}
diff --git a/tests/test-team/pages/nhs-app/template-mgmt-test-nhs-app-message-sent-page.ts b/tests/test-team/pages/nhs-app/template-mgmt-test-nhs-app-message-sent-page.ts
new file mode 100644
index 000000000..9b421b82b
--- /dev/null
+++ b/tests/test-team/pages/nhs-app/template-mgmt-test-nhs-app-message-sent-page.ts
@@ -0,0 +1,18 @@
+import { Page } from '@playwright/test';
+import { TemplateMgmtBasePage } from 'pages/template-mgmt-base-page';
+
+export class TemplateMgmtTestNhsAppMessageSentPage extends TemplateMgmtBasePage {
+ static readonly pathTemplate = '/test-nhs-app-message-sent/:proofRequestId';
+
+ public static readonly urlRegexp = new RegExp(
+ /\/templates\/test-nhs-app-message-sent\/([\dA-Fa-f-]+)$/
+ );
+
+ constructor(page: Page) {
+ super(page);
+ }
+
+ get proofRequestId() {
+ return this.getPathParametersFromCurrentPageUrl()['proofRequestId'];
+ }
+}
diff --git a/tests/test-team/template-mgmt-component-tests/nhsapp-template-component/template-mgmt-send-test-nhs-app-message-page.nhsapp-template-component.spec.ts b/tests/test-team/template-mgmt-component-tests/nhsapp-template-component/template-mgmt-send-test-nhs-app-message-page.nhsapp-template-component.spec.ts
index 448f2165e..6e4e69a0f 100644
--- a/tests/test-team/template-mgmt-component-tests/nhsapp-template-component/template-mgmt-send-test-nhs-app-message-page.nhsapp-template-component.spec.ts
+++ b/tests/test-team/template-mgmt-component-tests/nhsapp-template-component/template-mgmt-send-test-nhs-app-message-page.nhsapp-template-component.spec.ts
@@ -16,6 +16,8 @@ import { getTestContext } from 'helpers/context/context';
import { loginAsUser } from 'helpers/auth/login-as-user';
import { TemplateMgmtPreviewNhsAppPage } from 'pages/nhs-app/template-mgmt-preview-nhs-app-page';
import { TemplateMgmtSendTestNhsAppMessagePage } from 'pages/nhs-app/template-mgmt-send-test-nhs-app-message-page';
+import { ProofRequestsStorageHelper } from 'helpers/db/proof-requests-storage-helper';
+import { TemplateMgmtTestNhsAppMessageSentPage } from 'pages/nhs-app/template-mgmt-test-nhs-app-message-sent-page';
function createTemplates(
digitalProofingUser: TestUser,
@@ -66,6 +68,7 @@ test.describe('Send test NHS App message page', () => {
let digitalProofingUser: TestUser;
let digitalProofingDisabledUser: TestUser;
+ const proofRequestStorageHelper = new ProofRequestsStorageHelper();
const templateStorageHelper = new TemplateStorageHelper();
let templates: {
validNhsAppTemplate: Template;
@@ -96,6 +99,7 @@ test.describe('Send test NHS App message page', () => {
test.afterAll(async () => {
await templateStorageHelper.deleteSeededTemplates();
+ await proofRequestStorageHelper.deleteAdHoc();
});
test.describe('when digital proofing feature flag is enabled', () => {
@@ -241,10 +245,28 @@ test.describe('Send test NHS App message page', () => {
await sendTestNhsAppMessagePage.clickSendTestMessageButton();
- // TODO: CCM-8366 - Update URL assertion to use proofing request ID instead of template ID.
await expect(page).toHaveURL(
- `${baseURL}/templates/test-nhs-app-message-sent/${templates.validNhsAppTemplate.id}`
+ TemplateMgmtTestNhsAppMessageSentPage.urlRegexp
);
+
+ const testNhsAppMessageSentPage =
+ new TemplateMgmtTestNhsAppMessageSentPage(page);
+
+ await expect(async () => {
+ const proofRequest = await proofRequestStorageHelper.get({
+ owner: digitalProofingUser.internalUserId,
+ id: testNhsAppMessageSentPage.proofRequestId,
+ });
+
+ expect(proofRequest?.templateId).toBe(templates.validNhsAppTemplate.id);
+ await expect(page).toHaveURL(
+ `${baseURL}/templates/test-nhs-app-message-sent/${proofRequest.id}`
+ );
+ proofRequestStorageHelper.addAdHocKey({
+ id: proofRequest.id,
+ owner: proofRequest.owner,
+ });
+ }).toPass();
});
test('user sees validation errors when test patient NHS number is empty or invalid', async ({
@@ -320,10 +342,28 @@ test.describe('Send test NHS App message page', () => {
await sendTestNhsAppMessagePage.clickSendTestMessageButton();
- // TODO: CCM-8366 - Update URL assertion to use proofing request ID instead of template ID.
await expect(page).toHaveURL(
- `${baseURL}/templates/test-nhs-app-message-sent/${templates.validNhsAppTemplate.id}`
+ TemplateMgmtTestNhsAppMessageSentPage.urlRegexp
);
+
+ const testNhsAppMessageSentPage =
+ new TemplateMgmtTestNhsAppMessageSentPage(page);
+
+ await expect(async () => {
+ const proofRequest = await proofRequestStorageHelper.get({
+ owner: digitalProofingUser.internalUserId,
+ id: testNhsAppMessageSentPage.proofRequestId,
+ });
+
+ expect(proofRequest?.templateId).toBe(templates.validNhsAppTemplate.id);
+ await expect(page).toHaveURL(
+ `${baseURL}/templates/test-nhs-app-message-sent/${proofRequest.id}`
+ );
+ proofRequestStorageHelper.addAdHocKey({
+ id: proofRequest.id,
+ owner: proofRequest.owner,
+ });
+ }).toPass();
});
test('user can enter custom personalisation fields and see validation errors for empty custom values', async ({
@@ -441,10 +481,30 @@ test.describe('Send test NHS App message page', () => {
await sendTestNhsAppMessagePage.clickSendTestMessageButton();
- // TODO: CCM-8366 - Update URL assertion to use proofing request ID instead of template ID.
await expect(page).toHaveURL(
- `${baseURL}/templates/test-nhs-app-message-sent/${templates.validNhsAppTemplateWithCustomFields.id}`
+ TemplateMgmtTestNhsAppMessageSentPage.urlRegexp
);
+
+ const testNhsAppMessageSentPage =
+ new TemplateMgmtTestNhsAppMessageSentPage(page);
+
+ await expect(async () => {
+ const proofRequest = await proofRequestStorageHelper.get({
+ owner: digitalProofingUser.internalUserId,
+ id: testNhsAppMessageSentPage.proofRequestId,
+ });
+
+ expect(proofRequest?.templateId).toBe(
+ templates.validNhsAppTemplateWithCustomFields.id
+ );
+ await expect(page).toHaveURL(
+ `${baseURL}/templates/test-nhs-app-message-sent/${proofRequest.id}`
+ );
+ proofRequestStorageHelper.addAdHocKey({
+ id: proofRequest.id,
+ owner: proofRequest.owner,
+ });
+ }).toPass();
});
test('user can request a test message for a submitted NHS app template', async ({
@@ -477,8 +537,29 @@ test.describe('Send test NHS App message page', () => {
await sendTestNhsAppMessagePage.clickSendTestMessageButton();
await expect(page).toHaveURL(
- `${baseURL}/templates/test-nhs-app-message-sent/${templates.submittedNhsAppTemplate.id}`
+ TemplateMgmtTestNhsAppMessageSentPage.urlRegexp
);
+
+ const testNhsAppMessageSentPage =
+ new TemplateMgmtTestNhsAppMessageSentPage(page);
+
+ await expect(async () => {
+ const proofRequest = await proofRequestStorageHelper.get({
+ owner: digitalProofingUser.internalUserId,
+ id: testNhsAppMessageSentPage.proofRequestId,
+ });
+
+ expect(proofRequest?.templateId).toBe(
+ templates.submittedNhsAppTemplate.id
+ );
+ await expect(page).toHaveURL(
+ `${baseURL}/templates/test-nhs-app-message-sent/${proofRequest.id}`
+ );
+ proofRequestStorageHelper.addAdHocKey({
+ id: proofRequest.id,
+ owner: proofRequest.owner,
+ });
+ }).toPass();
});
test('redirects to invalid template page when template is not an NHS App template', async ({
diff --git a/tests/test-team/template-mgmt-e2e-tests/proof-polling.e2e.spec.ts b/tests/test-team/template-mgmt-e2e-tests/proof-polling.e2e.spec.ts
deleted file mode 100644
index 2c1ae4d33..000000000
--- a/tests/test-team/template-mgmt-e2e-tests/proof-polling.e2e.spec.ts
+++ /dev/null
@@ -1,214 +0,0 @@
-import { readFileSync } from 'node:fs';
-import { expect, test } from '@playwright/test';
-import { testUsers } from '../helpers/auth/cognito-auth-helper';
-import { getTestContext } from '../helpers/context/context';
-import { TemplateFactory } from '../helpers/factories/template-factory';
-import { TemplateStorageHelper } from '../helpers/db/template-storage-helper';
-import { pdfUploadFixtures } from '../fixtures/letters';
-import { SftpHelper } from '../helpers/sftp/sftp-helper';
-import { InvokeCommand, LambdaClient } from '@aws-sdk/client-lambda';
-
-const templateStorageHelper = new TemplateStorageHelper();
-const context = getTestContext();
-const sftpHelper = new SftpHelper();
-const lambdaClient = new LambdaClient({ region: 'eu-west-2' });
-
-test.describe('Letter Proof Polling', () => {
- test.beforeAll(async () => {
- await sftpHelper.connect();
- });
-
- test.afterAll(async () => {
- await sftpHelper.end();
- await templateStorageHelper.deleteSeededTemplates();
- });
-
- test('proofs are downloaded and linked to the DB entry', async () => {
- const templateId = '599b9a9d-17e1-4e54-bce7-645339818a1b';
- const user = await context.auth.getTestUser(testUsers.User1.userId);
-
- // add entries to database
- await templateStorageHelper.seedTemplateData([
- TemplateFactory.uploadPdfLetterTemplate(
- templateId,
- user,
- 'test-template-id-proofing-e2e-success',
- 'WAITING_FOR_PROOF'
- ),
- ]);
-
- // add proofs to SFTP mock
- const pdfContent = readFileSync(
- './fixtures/letters/no-custom-personalisation/template.pdf'
- );
-
- const supplierReference = [
- user.clientId,
- 'campaign',
- templateId,
- 'en',
- 'x0',
- ].join('_');
-
- await sftpHelper.put(
- pdfContent,
- `WTMMOCK/Outgoing/${process.env.SFTP_ENVIRONMENT}/proofs/${supplierReference}/proof-1.pdf`
- );
- await sftpHelper.put(
- pdfContent,
- `WTMMOCK/Outgoing/${process.env.SFTP_ENVIRONMENT}/proofs/${supplierReference}/proof-2.pdf`
- );
- await sftpHelper.put(
- pdfContent,
- `WTMMOCK/Outgoing/${process.env.SFTP_ENVIRONMENT}/proofs/${supplierReference}/proof-3.pdf`
- );
-
- // check for expected results
- await expect(async () => {
- // invoke SFTP poll lambda
- await lambdaClient.send(
- new InvokeCommand({
- FunctionName: process.env.SFTP_POLL_LAMBDA_NAME,
- Payload: JSON.stringify({
- supplier: 'WTMMOCK',
- }),
- })
- );
-
- const template = await templateStorageHelper.getTemplate({
- clientId: user.clientId,
- templateId: templateId,
- });
-
- expect(template.files?.proofs).toEqual({
- 'proof-1.pdf': {
- fileName: 'proof-1.pdf',
- supplier: 'WTMMOCK',
- virusScanStatus: 'PASSED',
- },
- 'proof-2.pdf': {
- fileName: 'proof-2.pdf',
- supplier: 'WTMMOCK',
- virusScanStatus: 'PASSED',
- },
- 'proof-3.pdf': {
- fileName: 'proof-3.pdf',
- supplier: 'WTMMOCK',
- virusScanStatus: 'PASSED',
- },
- });
-
- expect(template.templateStatus).toEqual('PROOF_AVAILABLE');
-
- for (const fileName of ['proof-1', 'proof-2', 'proof-3']) {
- const quarantinePdf =
- await templateStorageHelper.getLetterProofMetadata(
- process.env.TEMPLATES_QUARANTINE_BUCKET_NAME,
- `${process.env.TEMPLATES_QUARANTINE_BUCKET_KEY_PREFIX}/proofs`,
- 'WTMMOCK',
- templateId,
- fileName,
- 'pdf'
- );
-
- expect(quarantinePdf?.ChecksumSHA256).toEqual(
- pdfUploadFixtures.noCustomPersonalisation.pdf.checksumSha256()
- );
-
- const internalPdf =
- await templateStorageHelper.getLetterTemplateMetadata(
- process.env.TEMPLATES_INTERNAL_BUCKET_NAME,
- 'proofs',
- { clientId: user.clientId, templateId },
- fileName,
- 'pdf'
- );
-
- expect(internalPdf?.ChecksumSHA256).toEqual(
- pdfUploadFixtures.noCustomPersonalisation.pdf.checksumSha256()
- );
-
- const downloadPdf = await templateStorageHelper.getS3Metadata(
- process.env.TEMPLATES_DOWNLOAD_BUCKET_NAME,
- `${user.clientId}/proofs/${templateId}/${fileName}.pdf`
- );
-
- expect(downloadPdf?.ChecksumSHA256).toEqual(
- pdfUploadFixtures.noCustomPersonalisation.pdf.checksumSha256()
- );
- }
- }).toPass({ timeout: 60_000 });
- });
-
- test('if the only proof fails the virus scan, the status is not updated to PROOF_AVAILABLE', async () => {
- const templateId = 'a3a9c1e2-3870-407a-a8ce-af2fdcd19573';
- const user = await context.auth.getTestUser(testUsers.User1.userId);
-
- // add entries to database
- await templateStorageHelper.seedTemplateData([
- TemplateFactory.uploadPdfLetterTemplate(
- templateId,
- user,
- 'test-template-id-proofing-e2e-failure',
- 'WAITING_FOR_PROOF'
- ),
- ]);
-
- // add proofs to SFTP mock
- const pdfContent = readFileSync(
- './fixtures/letters/no-custom-personalisation/password.pdf'
- );
-
- const supplierReference = [
- user.clientId,
- 'campaign2',
- templateId,
- 'fr',
- 'q1',
- ].join('_');
-
- await sftpHelper.put(
- pdfContent,
- `WTMMOCK/Outgoing/${process.env.SFTP_ENVIRONMENT}/proofs/${supplierReference}/proof.pdf`
- );
-
- // invoke SFTP poll lambda
- await lambdaClient.send(
- new InvokeCommand({
- FunctionName: process.env.SFTP_POLL_LAMBDA_NAME,
- Payload: JSON.stringify({
- supplier: 'WTMMOCK',
- }),
- })
- );
-
- // check for expected results
- await expect(async () => {
- const template = await templateStorageHelper.getTemplate({
- clientId: user.clientId,
- templateId,
- });
-
- expect(template.files?.proofs).toEqual({
- 'proof.pdf': {
- fileName: `proof.pdf`,
- supplier: 'WTMMOCK',
- virusScanStatus: 'FAILED',
- },
- });
-
- expect(template.templateStatus).toEqual('WAITING_FOR_PROOF');
-
- const pdf = await templateStorageHelper.getLetterProofMetadata(
- process.env.TEMPLATES_QUARANTINE_BUCKET_NAME,
- 'proofs',
- 'WTMMOCK',
- templateId,
- 'proof',
- 'pdf'
- );
-
- expect(pdf).toBe(null);
- }).toPass({ timeout: 60_000 });
- });
-});