diff --git a/package-lock.json b/package-lock.json index 3b7b9ad6e..1daec55bc 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,7 +10,7 @@ "hasInstallScript": true, "license": "SEE LICENSE IN LICENSE", "dependencies": { - "@defra/forms-model": "^3.0.555", + "@defra/forms-model": "^3.0.559", "@defra/hapi-tracing": "^1.26.0", "@elastic/ecs-pino-format": "^1.5.0", "@hapi/boom": "^10.0.1", @@ -2272,9 +2272,9 @@ } }, "node_modules/@defra/forms-model": { - "version": "3.0.555", - "resolved": "https://registry.npmjs.org/@defra/forms-model/-/forms-model-3.0.555.tgz", - "integrity": "sha512-RgPYOiwdzxI3WSNp+WD/GKZam72GUlZEpUDbciejQBdkpWMbMcKQkPbyOLuHjHp+/ZyR3T7xySlGEkUtPc9Gzw==", + "version": "3.0.559", + "resolved": "https://registry.npmjs.org/@defra/forms-model/-/forms-model-3.0.559.tgz", + "integrity": "sha512-dSMrTnhUXnapflHKdeQLMGDwK2QlFhp/08XwzLNHzLHmgx7pqHAgelzVeRsyHtzYDu7B7tF4r5cyR+SxI4UmXw==", "license": "OGL-UK-3.0", "dependencies": { "@joi/date": "^2.1.1", diff --git a/package.json b/package.json index 57150f31e..18a94e715 100644 --- a/package.json +++ b/package.json @@ -70,7 +70,7 @@ }, "license": "SEE LICENSE IN LICENSE", "dependencies": { - "@defra/forms-model": "^3.0.555", + "@defra/forms-model": "^3.0.559", "@defra/hapi-tracing": "^1.26.0", "@elastic/ecs-pino-format": "^1.5.0", "@hapi/boom": "^10.0.1", diff --git a/src/server/forms/register-as-a-unicorn-breeder.yaml b/src/server/forms/register-as-a-unicorn-breeder.yaml index 345dd4930..3c20764c8 100644 --- a/src/server/forms/register-as-a-unicorn-breeder.yaml +++ b/src/server/forms/register-as-a-unicorn-breeder.yaml @@ -1,5 +1,6 @@ --- name: Register as a unicorn breeder +declaration: "
All the answers you have provided are true to the best of your knowledge.
" pages: - path: '/whats-your-name' title: What's your name? diff --git a/src/server/plugins/engine/pageControllers/SummaryPageController.test.ts b/src/server/plugins/engine/pageControllers/SummaryPageController.test.ts new file mode 100644 index 000000000..3e3a367ef --- /dev/null +++ b/src/server/plugins/engine/pageControllers/SummaryPageController.test.ts @@ -0,0 +1,85 @@ +import { FormModel } from '~/src/server/plugins/engine/models/FormModel.js' +import { SummaryPageController } from '~/src/server/plugins/engine/pageControllers/SummaryPageController.js' +import { buildFormRequest } from '~/src/server/plugins/engine/pageControllers/__stubs__/request.js' +import { type FormSubmissionState } from '~/src/server/plugins/engine/types.js' +import { + type FormRequest, + type FormRequestPayload, + type FormResponseToolkit +} from '~/src/server/routes/types.js' +import { type CacheService } from '~/src/server/services/cacheService.js' +import definition from '~/test/form/definitions/basic.js' + +describe('SummaryPageController', () => { + let model: FormModel + let controller: SummaryPageController + let requestPage: FormRequest + + const response = { + code: jest.fn().mockImplementation(() => response) + } + const h: FormResponseToolkit = { + redirect: jest.fn().mockReturnValue(response), + view: jest.fn() + } + + beforeEach(() => { + model = new FormModel(definition, { + basePath: 'test' + }) + + // Create a mock page for SummaryPageController + const mockPage = { + ...definition.pages[0], + controller: 'summary' + } + + // eslint-disable-next-line @typescript-eslint/no-explicit-any + controller = new SummaryPageController(model, mockPage as any) + + requestPage = buildFormRequest({ + method: 'get', + url: new URL('http://example.com/test/summary'), + path: '/test/summary', + params: { + path: 'summary', + slug: 'test' + }, + query: {}, + app: { model } + } as FormRequest) + }) + + describe('handleSaveAndExit', () => { + it('should invoke saveAndExit plugin option', async () => { + const saveAndExitMock = jest.fn(() => ({})) + const state: FormSubmissionState = { + $$__referenceNumber: 'foobar', + licenceLength: 365, + fullName: 'John Smith' + } + const request = { + ...requestPage, + server: { + plugins: { + 'forms-engine-plugin': { + saveAndExit: saveAndExitMock, + cacheService: { + clearState: jest.fn() + } as unknown as CacheService + } + } + }, + method: 'post', + payload: { fullName: 'John Smith', action: 'save-and-exit' } + } as unknown as FormRequestPayload + + const context = model.getFormContext(request, state) + + const postHandler = controller.makePostRouteHandler() + await postHandler(request, context, h) + + expect(saveAndExitMock).toHaveBeenCalledWith(request, h, context) + }) + }) +}) diff --git a/src/server/plugins/engine/pageControllers/SummaryPageController.ts b/src/server/plugins/engine/pageControllers/SummaryPageController.ts index 14d4ecae4..aac9d3b55 100644 --- a/src/server/plugins/engine/pageControllers/SummaryPageController.ts +++ b/src/server/plugins/engine/pageControllers/SummaryPageController.ts @@ -73,6 +73,7 @@ export class SummaryPageController extends QuestionPageController { viewModel.phaseTag = this.phaseTag viewModel.components = components viewModel.allowSaveAndExit = this.shouldShowSaveAndExit(request.server) + viewModel.errors = errors return viewModel } @@ -107,48 +108,56 @@ export class SummaryPageController extends QuestionPageController { context: FormContext, h: FormResponseToolkit ) => { - const { model } = this - const { params } = request - // Check if this is a save-and-exit action const { action } = request.payload if (action === FormAction.SaveAndExit) { return this.handleSaveAndExit(request, context, h) } - const cacheService = getCacheService(request.server) - - const { formsService } = this.model.services - const { getFormMetadata } = formsService - - // Get the form metadata using the `slug` param - const formMetadata = await getFormMetadata(params.slug) - const { notificationEmail } = formMetadata - const { isPreview } = checkFormStatus(request.params) - const emailAddress = notificationEmail ?? this.model.def.outputEmail - - checkEmailAddressForLiveFormSubmission(emailAddress, isPreview) - - // Send submission email - if (emailAddress) { - const viewModel = this.getSummaryViewModel(request, context) - await submitForm( - context, - request, - viewModel, - model, - emailAddress, - formMetadata - ) - } + return this.handleFormSubmit(request, context, h) + } + } + + async handleFormSubmit( + request: FormRequestPayload, + context: FormContext, + h: FormResponseToolkit + ) { + const { model } = this + const { params } = request + + const cacheService = getCacheService(request.server) - await cacheService.setConfirmationState(request, { confirmed: true }) + const { formsService } = this.model.services + const { getFormMetadata } = formsService - // Clear all form data - await cacheService.clearState(request) + // Get the form metadata using the `slug` param + const formMetadata = await getFormMetadata(params.slug) + const { notificationEmail } = formMetadata + const { isPreview } = checkFormStatus(request.params) + const emailAddress = notificationEmail ?? this.model.def.outputEmail - return this.proceed(request, h, this.getStatusPath()) + checkEmailAddressForLiveFormSubmission(emailAddress, isPreview) + + // Send submission email + if (emailAddress) { + const viewModel = this.getSummaryViewModel(request, context) + await submitForm( + context, + request, + viewModel, + model, + emailAddress, + formMetadata + ) } + + await cacheService.setConfirmationState(request, { confirmed: true }) + + // Clear all form data + await cacheService.clearState(request) + + return this.proceed(request, h, this.getStatusPath()) } get postRouteOptions(): RouteOptions