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`] =
+

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" /> -
({ }, })); +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 }); - }); -});