@defra/forms-engine-plugin 2.1.10 → 3.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (136) hide show
  1. package/.server/server/index.js +2 -1
  2. package/.server/server/index.js.map +1 -1
  3. package/.server/server/plugins/engine/README.md +2 -2
  4. package/.server/server/plugins/engine/configureEnginePlugin.d.ts +2 -1
  5. package/.server/server/plugins/engine/configureEnginePlugin.js +4 -4
  6. package/.server/server/plugins/engine/configureEnginePlugin.js.map +1 -1
  7. package/.server/server/plugins/engine/helpers.d.ts +7 -11
  8. package/.server/server/plugins/engine/helpers.js +2 -2
  9. package/.server/server/plugins/engine/helpers.js.map +1 -1
  10. package/.server/server/plugins/engine/models/FormModel.d.ts +2 -0
  11. package/.server/server/plugins/engine/models/FormModel.js +5 -2
  12. package/.server/server/plugins/engine/models/FormModel.js.map +1 -1
  13. package/.server/server/plugins/engine/models/SummaryViewModel.d.ts +1 -1
  14. package/.server/server/plugins/engine/models/SummaryViewModel.js +1 -1
  15. package/.server/server/plugins/engine/models/SummaryViewModel.js.map +1 -1
  16. package/.server/server/plugins/engine/options.js +3 -6
  17. package/.server/server/plugins/engine/options.js.map +1 -1
  18. package/.server/server/plugins/engine/options.test.js +2 -8
  19. package/.server/server/plugins/engine/options.test.js.map +1 -1
  20. package/.server/server/plugins/engine/outputFormatters/adapter/v1.d.ts +4 -0
  21. package/.server/server/plugins/engine/outputFormatters/adapter/v1.js +25 -0
  22. package/.server/server/plugins/engine/outputFormatters/adapter/v1.js.map +1 -1
  23. package/.server/server/plugins/engine/outputFormatters/machine/v2.js +7 -6
  24. package/.server/server/plugins/engine/outputFormatters/machine/v2.js.map +1 -1
  25. package/.server/server/plugins/engine/pageControllers/FileUploadPageController.d.ts +5 -6
  26. package/.server/server/plugins/engine/pageControllers/FileUploadPageController.js.map +1 -1
  27. package/.server/server/plugins/engine/pageControllers/PageController.d.ts +6 -6
  28. package/.server/server/plugins/engine/pageControllers/PageController.js +4 -4
  29. package/.server/server/plugins/engine/pageControllers/PageController.js.map +1 -1
  30. package/.server/server/plugins/engine/pageControllers/QuestionPageController.d.ts +13 -13
  31. package/.server/server/plugins/engine/pageControllers/QuestionPageController.js +12 -20
  32. package/.server/server/plugins/engine/pageControllers/QuestionPageController.js.map +1 -1
  33. package/.server/server/plugins/engine/pageControllers/RepeatPageController.d.ts +7 -8
  34. package/.server/server/plugins/engine/pageControllers/RepeatPageController.js.map +1 -1
  35. package/.server/server/plugins/engine/pageControllers/StartPageController.d.ts +2 -2
  36. package/.server/server/plugins/engine/pageControllers/StartPageController.js +1 -1
  37. package/.server/server/plugins/engine/pageControllers/StartPageController.js.map +1 -1
  38. package/.server/server/plugins/engine/pageControllers/StatusPageController.d.ts +3 -4
  39. package/.server/server/plugins/engine/pageControllers/StatusPageController.js +1 -1
  40. package/.server/server/plugins/engine/pageControllers/StatusPageController.js.map +1 -1
  41. package/.server/server/plugins/engine/pageControllers/SummaryPageController.d.ts +5 -4
  42. package/.server/server/plugins/engine/pageControllers/SummaryPageController.js +12 -1
  43. package/.server/server/plugins/engine/pageControllers/SummaryPageController.js.map +1 -1
  44. package/.server/server/plugins/engine/pageControllers/TerminalPageController.d.ts +4 -4
  45. package/.server/server/plugins/engine/pageControllers/TerminalPageController.js +1 -1
  46. package/.server/server/plugins/engine/pageControllers/TerminalPageController.js.map +1 -1
  47. package/.server/server/plugins/engine/pageControllers/__stubs__/server.d.ts +1 -1
  48. package/.server/server/plugins/engine/pageControllers/__stubs__/server.js +2 -6
  49. package/.server/server/plugins/engine/pageControllers/__stubs__/server.js.map +1 -1
  50. package/.server/server/plugins/engine/plugin.js +7 -12
  51. package/.server/server/plugins/engine/plugin.js.map +1 -1
  52. package/.server/server/plugins/engine/routes/index.d.ts +5 -5
  53. package/.server/server/plugins/engine/routes/index.js +3 -1
  54. package/.server/server/plugins/engine/routes/index.js.map +1 -1
  55. package/.server/server/plugins/engine/routes/questions.d.ts +4 -4
  56. package/.server/server/plugins/engine/routes/questions.js.map +1 -1
  57. package/.server/server/plugins/engine/routes/repeaters/item-delete.js.map +1 -1
  58. package/.server/server/plugins/engine/routes/repeaters/summary.js.map +1 -1
  59. package/.server/server/plugins/engine/types/index.d.ts +2 -2
  60. package/.server/server/plugins/engine/types/index.js.map +1 -1
  61. package/.server/server/plugins/engine/types/schema.js +3 -2
  62. package/.server/server/plugins/engine/types/schema.js.map +1 -1
  63. package/.server/server/plugins/engine/types.d.ts +13 -12
  64. package/.server/server/plugins/engine/types.js.map +1 -1
  65. package/.server/server/plugins/engine/views/partials/form.html +3 -3
  66. package/.server/server/plugins/engine/views/summary.html +21 -5
  67. package/.server/server/plugins/nunjucks/context.d.ts +5 -6
  68. package/.server/server/plugins/nunjucks/context.js +3 -3
  69. package/.server/server/plugins/nunjucks/context.js.map +1 -1
  70. package/.server/server/routes/types.d.ts +3 -2
  71. package/.server/server/routes/types.js +1 -1
  72. package/.server/server/routes/types.js.map +1 -1
  73. package/.server/server/schemas/index.js +1 -1
  74. package/.server/server/schemas/index.js.map +1 -1
  75. package/.server/server/services/cacheService.d.ts +11 -19
  76. package/.server/server/services/cacheService.js +9 -30
  77. package/.server/server/services/cacheService.js.map +1 -1
  78. package/.server/server/types.d.ts +4 -1
  79. package/.server/server/types.js.map +1 -1
  80. package/.server/typings/hapi/index.d.js.map +1 -1
  81. package/package.json +4 -2
  82. package/src/server/index.test.ts +0 -39
  83. package/src/server/index.ts +4 -1
  84. package/src/server/plugins/engine/README.md +2 -2
  85. package/src/server/plugins/engine/components/helpers/helpers.test.ts +1 -1
  86. package/src/server/plugins/engine/configureEnginePlugin.ts +15 -11
  87. package/src/server/plugins/engine/helpers.test.ts +3 -2
  88. package/src/server/plugins/engine/helpers.ts +6 -6
  89. package/src/server/plugins/engine/models/FormModel.test.ts +66 -2
  90. package/src/server/plugins/engine/models/FormModel.ts +6 -4
  91. package/src/server/plugins/engine/models/SummaryViewModel.test.ts +7 -7
  92. package/src/server/plugins/engine/models/SummaryViewModel.ts +1 -1
  93. package/src/server/plugins/engine/options.js +6 -6
  94. package/src/server/plugins/engine/options.test.js +2 -6
  95. package/src/server/plugins/engine/outputFormatters/adapter/v1.test.ts +446 -13
  96. package/src/server/plugins/engine/outputFormatters/adapter/v1.ts +37 -0
  97. package/src/server/plugins/engine/outputFormatters/machine/v2.ts +8 -6
  98. package/src/server/plugins/engine/pageControllers/FileUploadPageController.test.ts +8 -10
  99. package/src/server/plugins/engine/pageControllers/FileUploadPageController.ts +9 -8
  100. package/src/server/plugins/engine/pageControllers/PageController.test.ts +11 -8
  101. package/src/server/plugins/engine/pageControllers/PageController.ts +9 -15
  102. package/src/server/plugins/engine/pageControllers/QuestionPageController.test.ts +35 -102
  103. package/src/server/plugins/engine/pageControllers/QuestionPageController.ts +24 -36
  104. package/src/server/plugins/engine/pageControllers/RepeatPageController.test.ts +4 -6
  105. package/src/server/plugins/engine/pageControllers/RepeatPageController.ts +8 -11
  106. package/src/server/plugins/engine/pageControllers/StartPageController.test.ts +4 -4
  107. package/src/server/plugins/engine/pageControllers/StartPageController.ts +1 -1
  108. package/src/server/plugins/engine/pageControllers/StatusPageController.test.ts +4 -4
  109. package/src/server/plugins/engine/pageControllers/StatusPageController.ts +6 -4
  110. package/src/server/plugins/engine/pageControllers/SummaryPageController.ts +15 -5
  111. package/src/server/plugins/engine/pageControllers/TerminalController.test.ts +4 -4
  112. package/src/server/plugins/engine/pageControllers/TerminalPageController.ts +7 -4
  113. package/src/server/plugins/engine/pageControllers/__stubs__/server.ts +5 -6
  114. package/src/server/plugins/engine/plugin.ts +7 -13
  115. package/src/server/plugins/engine/routes/index.ts +9 -12
  116. package/src/server/plugins/engine/routes/questions.test.ts +29 -53
  117. package/src/server/plugins/engine/routes/questions.ts +6 -8
  118. package/src/server/plugins/engine/routes/repeaters/item-delete.ts +5 -14
  119. package/src/server/plugins/engine/routes/repeaters/summary.ts +5 -14
  120. package/src/server/plugins/engine/types/index.ts +4 -1
  121. package/src/server/plugins/engine/types/schema.test.ts +40 -0
  122. package/src/server/plugins/engine/types/schema.ts +3 -1
  123. package/src/server/plugins/engine/types.ts +22 -13
  124. package/src/server/plugins/engine/views/partials/form.html +3 -3
  125. package/src/server/plugins/engine/views/summary.html +21 -5
  126. package/src/server/plugins/nunjucks/context.js +3 -3
  127. package/src/server/routes/types.ts +7 -2
  128. package/src/server/schemas/index.ts +1 -1
  129. package/src/server/services/cacheService.test.ts +1 -117
  130. package/src/server/services/cacheService.ts +22 -73
  131. package/src/server/types.ts +4 -1
  132. package/src/typings/hapi/index.d.ts +6 -7
  133. package/.server/server/plugins/engine/routes/exit.d.ts +0 -46
  134. package/.server/server/plugins/engine/routes/exit.js +0 -36
  135. package/.server/server/plugins/engine/routes/exit.js.map +0 -1
  136. package/src/server/plugins/engine/routes/exit.ts +0 -47
@@ -1,6 +1,5 @@
1
1
  import { ComponentType, type PageFileUpload } from '@defra/forms-model'
2
2
  import Boom from '@hapi/boom'
3
- import { type ResponseToolkit } from '@hapi/hapi'
4
3
  import { wait } from '@hapi/hoek'
5
4
  import { type ValidationErrorItem } from 'joi'
6
5
 
@@ -23,6 +22,7 @@ import {
23
22
  import {
24
23
  FileStatus,
25
24
  UploadStatus,
25
+ type AnyFormRequest,
26
26
  type FeaturedFormPageViewModel,
27
27
  type FileState,
28
28
  type FormContext,
@@ -35,7 +35,8 @@ import {
35
35
  } from '~/src/server/plugins/engine/types.js'
36
36
  import {
37
37
  type FormRequest,
38
- type FormRequestPayload
38
+ type FormRequestPayload,
39
+ type FormResponseToolkit
39
40
  } from '~/src/server/routes/types.js'
40
41
 
41
42
  const MAX_UPLOADS = 25
@@ -111,7 +112,7 @@ export class FileUploadPageController extends QuestionPageController {
111
112
  return payload
112
113
  }
113
114
 
114
- async getState(request: FormRequest | FormRequestPayload) {
115
+ async getState(request: AnyFormRequest) {
115
116
  const { fileUpload } = this
116
117
 
117
118
  // Get the actual state
@@ -148,7 +149,7 @@ export class FileUploadPageController extends QuestionPageController {
148
149
  return (
149
150
  request: FormRequest,
150
151
  context: FormContext,
151
- h: Pick<ResponseToolkit, 'redirect' | 'view'>
152
+ h: FormResponseToolkit
152
153
  ) => {
153
154
  const { viewModel } = this
154
155
  const { params } = request
@@ -183,7 +184,7 @@ export class FileUploadPageController extends QuestionPageController {
183
184
  return async (
184
185
  request: FormRequestPayload,
185
186
  context: FormContext,
186
- h: Pick<ResponseToolkit, 'redirect' | 'view'>
187
+ h: FormResponseToolkit
187
188
  ) => {
188
189
  const { path } = this
189
190
  const { state } = context
@@ -279,7 +280,7 @@ export class FileUploadPageController extends QuestionPageController {
279
280
  * @param state - the form state
280
281
  */
281
282
  private async refreshUpload(
282
- request: FormRequest | FormRequestPayload,
283
+ request: AnyFormRequest,
283
284
  state: FormSubmissionState
284
285
  ) {
285
286
  state = await this.checkUploadStatus(request, state)
@@ -295,7 +296,7 @@ export class FileUploadPageController extends QuestionPageController {
295
296
  * @param depth - the number of retries so far
296
297
  */
297
298
  private async checkUploadStatus(
298
- request: FormRequest | FormRequestPayload,
299
+ request: AnyFormRequest,
299
300
  state: FormSubmissionState,
300
301
  depth = 1
301
302
  ): Promise<FormSubmissionState> {
@@ -417,7 +418,7 @@ export class FileUploadPageController extends QuestionPageController {
417
418
  * @param state - the form state
418
419
  */
419
420
  private async initiateAndStoreNewUpload(
420
- request: FormRequest | FormRequestPayload,
421
+ request: AnyFormRequest,
421
422
  state: FormSubmissionState
422
423
  ) {
423
424
  const { fileUpload, href, path } = this
@@ -3,8 +3,11 @@ import { type ResponseToolkit } from '@hapi/hapi'
3
3
  import { FORM_PREFIX } from '~/src/server/constants.js'
4
4
  import { FormModel } from '~/src/server/plugins/engine/models/FormModel.js'
5
5
  import { PageController } from '~/src/server/plugins/engine/pageControllers/PageController.js'
6
- import { serverWithSaveAndReturn } from '~/src/server/plugins/engine/pageControllers/__stubs__/server.js'
7
- import { type FormRequest } from '~/src/server/routes/types.js'
6
+ import { serverWithSaveAndExit } from '~/src/server/plugins/engine/pageControllers/__stubs__/server.js'
7
+ import {
8
+ type FormRequest,
9
+ type FormResponseToolkit
10
+ } from '~/src/server/routes/types.js'
8
11
  import definition from '~/test/form/definitions/basic.js'
9
12
 
10
13
  describe('PageController', () => {
@@ -155,7 +158,7 @@ describe('PageController', () => {
155
158
  app: { model }
156
159
  } as FormRequest
157
160
 
158
- const h: Pick<ResponseToolkit, 'redirect' | 'view'> = {
161
+ const h: FormResponseToolkit = {
159
162
  redirect: jest.fn(),
160
163
  view: jest.fn()
161
164
  }
@@ -206,10 +209,10 @@ describe('PageController', () => {
206
209
  )
207
210
  })
208
211
 
209
- it('supports save and return functionality', async () => {
212
+ it('supports save and exit functionality', async () => {
210
213
  const mockRequest = {
211
214
  ...request,
212
- payload: { saveAndReturn: true }
215
+ payload: { saveAndExit: true }
213
216
  } as FormRequest
214
217
 
215
218
  const mockResponse = {
@@ -232,9 +235,9 @@ describe('PageController', () => {
232
235
  })
233
236
  })
234
237
 
235
- describe('shouldShowSaveAndReturn', () => {
236
- it('should return false (PageController does not allow save and return)', () => {
237
- expect(controller1.shouldShowSaveAndReturn(serverWithSaveAndReturn)).toBe(
238
+ describe('shouldShowSaveAndExit', () => {
239
+ it('should return false (PageController does not allow save and exit)', () => {
240
+ expect(controller1.shouldShowSaveAndExit(serverWithSaveAndExit)).toBe(
238
241
  false
239
242
  )
240
243
  })
@@ -6,17 +6,12 @@ import {
6
6
  type Section
7
7
  } from '@defra/forms-model'
8
8
  import Boom from '@hapi/boom'
9
- import {
10
- type Lifecycle,
11
- type ResponseToolkit,
12
- type RouteOptions,
13
- type Server
14
- } from '@hapi/hapi'
9
+ import { type Lifecycle, type RouteOptions, type Server } from '@hapi/hapi'
15
10
 
16
11
  import { type ComponentCollection } from '~/src/server/plugins/engine/components/ComponentCollection.js'
17
12
  import {
18
13
  encodeUrl,
19
- getSaveAndReturnHelpers,
14
+ getSaveAndExitHelpers,
20
15
  getStartPath,
21
16
  normalisePath
22
17
  } from '~/src/server/plugins/engine/helpers.js'
@@ -30,7 +25,8 @@ import {
30
25
  type FormRequest,
31
26
  type FormRequestPayload,
32
27
  type FormRequestPayloadRefs,
33
- type FormRequestRefs
28
+ type FormRequestRefs,
29
+ type FormResponseToolkit
34
30
  } from '~/src/server/routes/types.js'
35
31
 
36
32
  export class PageController {
@@ -47,7 +43,7 @@ export class PageController {
47
43
  events?: Events
48
44
  collection?: ComponentCollection
49
45
  viewName = 'index'
50
- allowSaveAndReturn = false
46
+ allowSaveAndExit = false
51
47
 
52
48
  constructor(model: FormModel, pageDef: Page) {
53
49
  const { def } = model
@@ -171,7 +167,7 @@ export class PageController {
171
167
  makeGetRouteHandler(): (
172
168
  request: FormRequest,
173
169
  context: FormContext,
174
- h: Pick<ResponseToolkit, 'redirect' | 'view'>
170
+ h: FormResponseToolkit
175
171
  ) => ReturnType<Lifecycle.Method<FormRequestRefs>> {
176
172
  return (request, context, h) => {
177
173
  const { viewModel, viewName } = this
@@ -182,14 +178,12 @@ export class PageController {
182
178
  makePostRouteHandler(): (
183
179
  request: FormRequestPayload,
184
180
  context: FormContext,
185
- h: Pick<ResponseToolkit, 'redirect' | 'view'>
181
+ h: FormResponseToolkit
186
182
  ) => ReturnType<Lifecycle.Method<FormRequestPayloadRefs>> {
187
183
  throw Boom.badRequest('Unsupported POST route handler for this page')
188
184
  }
189
185
 
190
- shouldShowSaveAndReturn(server: Server): boolean {
191
- return (
192
- getSaveAndReturnHelpers(server) !== undefined && this.allowSaveAndReturn
193
- )
186
+ shouldShowSaveAndExit(server: Server): boolean {
187
+ return getSaveAndExitHelpers(server) !== undefined && this.allowSaveAndExit
194
188
  }
195
189
  }
@@ -1,14 +1,12 @@
1
1
  import { type PageQuestion } from '@defra/forms-model'
2
- import { type ResponseToolkit } from '@hapi/hapi'
3
2
 
4
- import { getCacheService } from '~/src/server/plugins/engine/helpers.js'
5
3
  import { FormModel } from '~/src/server/plugins/engine/models/FormModel.js'
6
4
  import { QuestionPageController } from '~/src/server/plugins/engine/pageControllers/QuestionPageController.js'
7
5
  import {
8
6
  buildFormContextRequest,
9
7
  buildFormRequest
10
8
  } from '~/src/server/plugins/engine/pageControllers/__stubs__/request.js'
11
- import { serverWithSaveAndReturn } from '~/src/server/plugins/engine/pageControllers/__stubs__/server.js'
9
+ import { serverWithSaveAndExit } from '~/src/server/plugins/engine/pageControllers/__stubs__/server.js'
12
10
  import {
13
11
  type FormContext,
14
12
  type FormPageViewModel,
@@ -17,7 +15,8 @@ import {
17
15
  } from '~/src/server/plugins/engine/types.js'
18
16
  import {
19
17
  type FormRequest,
20
- type FormRequestPayload
18
+ type FormRequestPayload,
19
+ type FormResponseToolkit
21
20
  } from '~/src/server/routes/types.js'
22
21
  import { CacheService } from '~/src/server/services/cacheService.js'
23
22
  import conditionalReveal from '~/test/form/definitions/conditional-reveal.js'
@@ -594,7 +593,7 @@ describe('QuestionPageController', () => {
594
593
  code: jest.fn().mockImplementation(() => response)
595
594
  }
596
595
 
597
- const h: Pick<ResponseToolkit, 'redirect' | 'view'> = {
596
+ const h: FormResponseToolkit = {
598
597
  redirect: jest.fn().mockReturnValue(response),
599
598
  view: jest.fn()
600
599
  }
@@ -1155,7 +1154,7 @@ describe('QuestionPageController V2', () => {
1155
1154
  code: jest.fn().mockImplementation(() => response)
1156
1155
  }
1157
1156
 
1158
- const h: Pick<ResponseToolkit, 'redirect' | 'view'> = {
1157
+ const h: FormResponseToolkit = {
1159
1158
  redirect: jest.fn().mockReturnValue(response),
1160
1159
  view: jest.fn()
1161
1160
  }
@@ -1283,7 +1282,7 @@ describe('QuestionPageController V2', () => {
1283
1282
  })
1284
1283
  })
1285
1284
 
1286
- describe('Save and Return functionality', () => {
1285
+ describe('Save and Exit functionality', () => {
1287
1286
  let model: FormModel
1288
1287
  let controller1: QuestionPageController
1289
1288
  let requestPage1: FormRequest
@@ -1314,7 +1313,7 @@ describe('Save and Return functionality', () => {
1314
1313
  code: jest.fn().mockImplementation(() => response)
1315
1314
  }
1316
1315
 
1317
- const h: Pick<ResponseToolkit, 'redirect' | 'view'> = {
1316
+ const h: FormResponseToolkit = {
1318
1317
  redirect: jest.fn().mockReturnValue(response),
1319
1318
  view: jest.fn()
1320
1319
  }
@@ -1324,17 +1323,17 @@ describe('Save and Return functionality', () => {
1324
1323
  jest.spyOn(CacheService.prototype, 'setState')
1325
1324
  })
1326
1325
 
1327
- describe('shouldShowSaveAndReturn', () => {
1326
+ describe('shouldShowSaveAndExit', () => {
1328
1327
  it('should return true by default', () => {
1329
- expect(controller1.shouldShowSaveAndReturn(serverWithSaveAndReturn)).toBe(
1328
+ expect(controller1.shouldShowSaveAndExit(serverWithSaveAndExit)).toBe(
1330
1329
  true
1331
1330
  )
1332
1331
  })
1333
1332
  })
1334
1333
 
1335
- describe('handleSaveAndReturn', () => {
1336
- it('should save state and redirect to exit page', async () => {
1337
- const sessionPersisterMock = jest.fn()
1334
+ describe('handleSaveAndExit', () => {
1335
+ it('should invoke saveAndExit plugin option', () => {
1336
+ const saveAndExitMock = jest.fn(() => ({}))
1338
1337
  const state: FormSubmissionState = {
1339
1338
  $$__referenceNumber: 'foobar',
1340
1339
  yesNoField: true
@@ -1344,9 +1343,7 @@ describe('Save and Return functionality', () => {
1344
1343
  server: {
1345
1344
  plugins: {
1346
1345
  'forms-engine-plugin': {
1347
- saveAndReturn: {
1348
- sessionPersister: sessionPersisterMock
1349
- },
1346
+ saveAndExit: saveAndExitMock,
1350
1347
  cacheService: {
1351
1348
  clearState: jest.fn()
1352
1349
  } as unknown as CacheService
@@ -1354,52 +1351,18 @@ describe('Save and Return functionality', () => {
1354
1351
  }
1355
1352
  },
1356
1353
  method: 'post',
1357
- payload: { yesNoField: true, action: 'save-and-return' }
1358
- } as unknown as FormRequestPayload
1359
-
1360
- const cacheService = getCacheService(request.server)
1361
-
1362
- const context = model.getFormContext(request, state)
1363
-
1364
- await controller1.handleSaveAndReturn(request, context, h)
1365
-
1366
- expect(sessionPersisterMock).toHaveBeenCalledWith(context.state, request)
1367
- expect(cacheService.clearState).toHaveBeenCalledWith(request)
1368
- expect(h.redirect).toHaveBeenCalledWith('/test/exit')
1369
- })
1370
-
1371
- it('should throw if sessionPersister inside saveAndReturn options provided', async () => {
1372
- const sessionPersisterMock = jest.fn()
1373
- const state: FormSubmissionState = {
1374
- $$__referenceNumber: 'foobar',
1375
- yesNoField: true
1376
- }
1377
- const request = {
1378
- ...requestPage1,
1379
- server: {
1380
- plugins: {
1381
- 'forms-engine-plugin': {
1382
- // No sessionPersister object
1383
- saveAndReturn: {}
1384
- }
1385
- }
1386
- },
1387
- method: 'post',
1388
- payload: { yesNoField: true, action: 'save-and-return' }
1354
+ payload: { yesNoField: true, action: 'save-and-exit' }
1389
1355
  } as unknown as FormRequestPayload
1390
1356
 
1391
1357
  const context = model.getFormContext(request, state)
1392
1358
 
1393
- await expect(
1394
- controller1.handleSaveAndReturn(request, context, h)
1395
- ).rejects.toThrow('Server misconfigured for save and return')
1359
+ controller1.handleSaveAndExit(request, context, h)
1396
1360
 
1397
- expect(sessionPersisterMock).not.toHaveBeenCalled()
1398
- expect(h.redirect).not.toHaveBeenCalled()
1361
+ expect(saveAndExitMock).toHaveBeenCalledWith(request, h, context)
1399
1362
  })
1400
1363
 
1401
- it('should throw if no saveAndReturn options provided', async () => {
1402
- const sessionPersisterMock = jest.fn()
1364
+ it('should throw if saveAndExit option not provided', () => {
1365
+ const saveAndExitMock = jest.fn()
1403
1366
  const state: FormSubmissionState = {
1404
1367
  $$__referenceNumber: 'foobar',
1405
1368
  yesNoField: true
@@ -1409,57 +1372,27 @@ describe('Save and Return functionality', () => {
1409
1372
  server: {
1410
1373
  plugins: {
1411
1374
  'forms-engine-plugin': {
1412
- // No saveAndReturn object
1375
+ // No function
1376
+ saveAndExit: undefined
1413
1377
  }
1414
1378
  }
1415
1379
  },
1416
1380
  method: 'post',
1417
- payload: { yesNoField: true, action: 'save-and-return' }
1418
- } as unknown as FormRequestPayload
1419
-
1420
- const context = model.getFormContext(request, state)
1421
-
1422
- await expect(
1423
- controller1.handleSaveAndReturn(request, context, h)
1424
- ).rejects.toThrow('Server misconfigured for save and return')
1425
-
1426
- expect(sessionPersisterMock).not.toHaveBeenCalled()
1427
- expect(h.redirect).not.toHaveBeenCalled()
1428
- })
1429
-
1430
- it('should throw if sessionPersister throws as well with validation errors', async () => {
1431
- const sessionPersisterMock = jest.fn().mockImplementation(() => {
1432
- throw new Error('Session persister error')
1433
- })
1434
- const state: FormSubmissionState = { $$__referenceNumber: 'foobar' }
1435
- const request = {
1436
- ...requestPage1,
1437
- method: 'post',
1438
- server: {
1439
- plugins: {
1440
- 'forms-engine-plugin': {
1441
- saveAndReturn: {
1442
- sessionPersister: sessionPersisterMock
1443
- }
1444
- }
1445
- }
1446
- },
1447
- payload: { action: 'save-and-return' }
1381
+ payload: { yesNoField: true, action: 'save-and-exit' }
1448
1382
  } as unknown as FormRequestPayload
1449
1383
 
1450
1384
  const context = model.getFormContext(request, state)
1451
1385
 
1452
- await expect(
1453
- controller1.handleSaveAndReturn(request, context, h)
1454
- ).rejects.toThrow('Session persister error')
1386
+ expect(() => controller1.handleSaveAndExit(request, context, h)).toThrow(
1387
+ 'Server misconfigured for save and exit'
1388
+ )
1455
1389
 
1456
- expect(sessionPersisterMock).toHaveBeenCalledWith(context.state, request)
1457
- expect(h.redirect).not.toHaveBeenCalledWith('/test/exit')
1390
+ expect(saveAndExitMock).not.toHaveBeenCalled()
1458
1391
  })
1459
1392
  })
1460
1393
 
1461
- describe('POST handler with save-and-return action', () => {
1462
- it('should handle FormAction.SaveAndReturn', async () => {
1394
+ describe('POST handler with save-and-exit action', () => {
1395
+ it('should handle FormAction.SaveAndExit', async () => {
1463
1396
  const state: FormSubmissionState = {
1464
1397
  $$__referenceNumber: 'foobar',
1465
1398
  yesNoField: true
@@ -1467,27 +1400,27 @@ describe('Save and Return functionality', () => {
1467
1400
  const request = {
1468
1401
  ...requestPage1,
1469
1402
  method: 'post',
1470
- payload: { yesNoField: true, action: 'save-and-return' }
1403
+ payload: { yesNoField: true, action: 'save-and-exit' }
1471
1404
  } as unknown as FormRequestPayload
1472
1405
 
1473
1406
  const context = model.getFormContext(request, state)
1474
1407
 
1475
1408
  jest.spyOn(controller1, 'getState').mockResolvedValue({})
1476
1409
  jest
1477
- .spyOn(controller1, 'handleSaveAndReturn')
1478
- .mockResolvedValue(h.redirect('/test/exit'))
1410
+ .spyOn(controller1, 'handleSaveAndExit')
1411
+ .mockReturnValue(h.redirect('/custom-save-and-exit'))
1479
1412
 
1480
1413
  const postHandler = controller1.makePostRouteHandler()
1481
1414
  await postHandler(request, context, h)
1482
1415
 
1483
- expect(controller1.handleSaveAndReturn).toHaveBeenCalledWith(
1416
+ expect(controller1.handleSaveAndExit).toHaveBeenCalledWith(
1484
1417
  request,
1485
1418
  context,
1486
1419
  h
1487
1420
  )
1488
1421
  })
1489
1422
 
1490
- it('should not call handleSaveAndReturn for continue action', async () => {
1423
+ it('should not call handleSaveAndExit for continue action', async () => {
1491
1424
  const state: FormSubmissionState = {
1492
1425
  $$__referenceNumber: 'foobar',
1493
1426
  yesNoField: true
@@ -1502,8 +1435,8 @@ describe('Save and Return functionality', () => {
1502
1435
 
1503
1436
  jest.spyOn(controller1, 'getState').mockResolvedValue({})
1504
1437
  jest
1505
- .spyOn(controller1, 'handleSaveAndReturn')
1506
- .mockResolvedValue(h.redirect('/test/exit'))
1438
+ .spyOn(controller1, 'handleSaveAndExit')
1439
+ .mockReturnValue(h.redirect('/custom-save-and-exit'))
1507
1440
  jest.spyOn(controller1, 'setState').mockResolvedValue(state)
1508
1441
 
1509
1442
  const mockResponse = {
@@ -1518,7 +1451,7 @@ describe('Save and Return functionality', () => {
1518
1451
  const postHandler = controller1.makePostRouteHandler()
1519
1452
  await postHandler(request, context, mockH)
1520
1453
 
1521
- expect(controller1.handleSaveAndReturn).not.toHaveBeenCalled()
1454
+ expect(controller1.handleSaveAndExit).not.toHaveBeenCalled()
1522
1455
  })
1523
1456
  })
1524
1457
  })
@@ -9,7 +9,7 @@ import {
9
9
  type Page
10
10
  } from '@defra/forms-model'
11
11
  import Boom from '@hapi/boom'
12
- import { type ResponseToolkit, type RouteOptions } from '@hapi/hapi'
12
+ import { type RouteOptions } from '@hapi/hapi'
13
13
  import { type ValidationErrorItem } from 'joi'
14
14
 
15
15
  import { ComponentCollection } from '~/src/server/plugins/engine/components/ComponentCollection.js'
@@ -18,13 +18,14 @@ import { type BackLink } from '~/src/server/plugins/engine/components/types.js'
18
18
  import {
19
19
  getCacheService,
20
20
  getErrors,
21
- getSaveAndReturnHelpers,
21
+ getSaveAndExitHelpers,
22
22
  normalisePath,
23
23
  proceed
24
24
  } from '~/src/server/plugins/engine/helpers.js'
25
25
  import { type FormModel } from '~/src/server/plugins/engine/models/index.js'
26
26
  import { PageController } from '~/src/server/plugins/engine/pageControllers/PageController.js'
27
27
  import {
28
+ type AnyFormRequest,
28
29
  type FormContext,
29
30
  type FormContextRequest,
30
31
  type FormPageViewModel,
@@ -39,7 +40,8 @@ import {
39
40
  type FormRequest,
40
41
  type FormRequestPayload,
41
42
  type FormRequestPayloadRefs,
42
- type FormRequestRefs
43
+ type FormRequestRefs,
44
+ type FormResponseToolkit
43
45
  } from '~/src/server/routes/types.js'
44
46
  import {
45
47
  actionSchema,
@@ -51,7 +53,7 @@ import { merge } from '~/src/server/services/cacheService.js'
51
53
  export class QuestionPageController extends PageController {
52
54
  collection: ComponentCollection
53
55
  errorSummaryTitle = 'There is a problem'
54
- allowSaveAndReturn = true
56
+ allowSaveAndExit = true
55
57
 
56
58
  constructor(model: FormModel, pageDef: Page) {
57
59
  super(model, pageDef)
@@ -177,14 +179,11 @@ export class QuestionPageController extends PageController {
177
179
  showTitle,
178
180
  components,
179
181
  errors,
180
- allowSaveAndReturn: this.shouldShowSaveAndReturn(request.server)
182
+ allowSaveAndExit: this.shouldShowSaveAndExit(request.server)
181
183
  }
182
184
  }
183
185
 
184
- getRelevantPath(
185
- request: FormRequest | FormRequestPayload,
186
- context: FormContext
187
- ) {
186
+ getRelevantPath(request: AnyFormRequest, context: FormContext) {
188
187
  const { paths } = context
189
188
 
190
189
  const startPath = this.getStartPath()
@@ -296,7 +295,7 @@ export class QuestionPageController extends PageController {
296
295
  return getErrors(details)
297
296
  }
298
297
 
299
- async getState(request: FormRequest | FormRequestPayload) {
298
+ async getState(request: AnyFormRequest) {
300
299
  const { query } = request
301
300
 
302
301
  // Skip get for preview URL direct access
@@ -309,10 +308,7 @@ export class QuestionPageController extends PageController {
309
308
  return cacheService.getState(request)
310
309
  }
311
310
 
312
- async setState(
313
- request: FormRequest | FormRequestPayload,
314
- state: FormSubmissionState
315
- ) {
311
+ async setState(request: AnyFormRequest, state: FormSubmissionState) {
316
312
  const { query } = request
317
313
 
318
314
  // Skip set for preview URL direct access
@@ -326,7 +322,7 @@ export class QuestionPageController extends PageController {
326
322
  }
327
323
 
328
324
  async mergeState(
329
- request: FormRequest | FormRequestPayload,
325
+ request: AnyFormRequest,
330
326
  state: FormSubmissionState,
331
327
  update: object
332
328
  ) {
@@ -397,7 +393,7 @@ export class QuestionPageController extends PageController {
397
393
  return async (
398
394
  request: FormRequest,
399
395
  context: FormContext,
400
- h: Pick<ResponseToolkit, 'redirect' | 'view'>
396
+ h: FormResponseToolkit
401
397
  ) => {
402
398
  const { collection, model, viewName } = this
403
399
  const { evaluationState } = context
@@ -492,7 +488,7 @@ export class QuestionPageController extends PageController {
492
488
  return async (
493
489
  request: FormRequestPayload,
494
490
  context: FormContext,
495
- h: Pick<ResponseToolkit, 'redirect' | 'view'>
491
+ h: FormResponseToolkit
496
492
  ) => {
497
493
  const { collection, viewName, model } = this
498
494
  const { isForceAccess, state, evaluationState } = context
@@ -515,10 +511,10 @@ export class QuestionPageController extends PageController {
515
511
  return h.view(viewName, viewModel)
516
512
  }
517
513
 
518
- // Check if this is a save-and-return action
514
+ // Check if this is a save-and-exit action
519
515
  const { action } = request.payload
520
- if (action === FormAction.SaveAndReturn) {
521
- return this.handleSaveAndReturn(request, context, h)
516
+ if (action === FormAction.SaveAndExit) {
517
+ return this.handleSaveAndExit(request, context, h)
522
518
  }
523
519
 
524
520
  // Save and proceed
@@ -529,7 +525,7 @@ export class QuestionPageController extends PageController {
529
525
 
530
526
  proceed(
531
527
  request: FormContextRequest,
532
- h: Pick<ResponseToolkit, 'redirect' | 'view'>,
528
+ h: FormResponseToolkit,
533
529
  nextPath?: string
534
530
  ) {
535
531
  const nextUrl = nextPath
@@ -540,28 +536,20 @@ export class QuestionPageController extends PageController {
540
536
  }
541
537
 
542
538
  /**
543
- * Handle save-and-return action by processing form data and redirecting to exit page
539
+ * Handle save-and-exit action
544
540
  */
545
- async handleSaveAndReturn(
541
+ handleSaveAndExit(
546
542
  request: FormRequestPayload,
547
543
  context: FormContext,
548
- h: Pick<ResponseToolkit, 'redirect' | 'view'>
544
+ h: FormResponseToolkit
549
545
  ) {
550
- const { state } = context
546
+ const saveAndExit = getSaveAndExitHelpers(request.server)
551
547
 
552
- // Save the current state and redirect to exit page
553
- const saveAndReturn = getSaveAndReturnHelpers(request.server)
554
-
555
- if (!saveAndReturn?.sessionPersister) {
556
- throw Boom.internal('Server misconfigured for save and return')
548
+ if (!saveAndExit) {
549
+ throw Boom.internal('Server misconfigured for save and exit')
557
550
  }
558
551
 
559
- await saveAndReturn.sessionPersister(state, request)
560
-
561
- const cacheService = getCacheService(request.server)
562
- await cacheService.clearState(request)
563
-
564
- return h.redirect(this.getHref('/exit'))
552
+ return saveAndExit(request, h, context)
565
553
  }
566
554
 
567
555
  /**
@@ -3,7 +3,7 @@ import { RepeatPageController } from '~/src/server/plugins/engine/pageController
3
3
  import { buildFormContextRequest } from '~/src/server/plugins/engine/pageControllers/__stubs__/request.js'
4
4
  import {
5
5
  server,
6
- serverWithSaveAndReturn
6
+ serverWithSaveAndExit
7
7
  } from '~/src/server/plugins/engine/pageControllers/__stubs__/server.js'
8
8
  import {
9
9
  type FormContextRequest,
@@ -271,11 +271,9 @@ describe('RepeatPageController', () => {
271
271
  })
272
272
  })
273
273
 
274
- describe('shouldShowSaveAndReturn', () => {
275
- it('should return true when save and return is enabled', () => {
276
- expect(controller.shouldShowSaveAndReturn(serverWithSaveAndReturn)).toBe(
277
- true
278
- )
274
+ describe('shouldShowSaveAndExit', () => {
275
+ it('should return true when save and exit is enabled', () => {
276
+ expect(controller.shouldShowSaveAndExit(serverWithSaveAndExit)).toBe(true)
279
277
  })
280
278
  })
281
279
  })