@defra/forms-engine-plugin 2.1.10 → 3.0.0

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 (122) 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.js +1 -1
  11. package/.server/server/plugins/engine/models/FormModel.js.map +1 -1
  12. package/.server/server/plugins/engine/models/SummaryViewModel.d.ts +1 -1
  13. package/.server/server/plugins/engine/models/SummaryViewModel.js +1 -1
  14. package/.server/server/plugins/engine/models/SummaryViewModel.js.map +1 -1
  15. package/.server/server/plugins/engine/options.js +3 -6
  16. package/.server/server/plugins/engine/options.js.map +1 -1
  17. package/.server/server/plugins/engine/options.test.js +2 -8
  18. package/.server/server/plugins/engine/options.test.js.map +1 -1
  19. package/.server/server/plugins/engine/pageControllers/FileUploadPageController.d.ts +5 -6
  20. package/.server/server/plugins/engine/pageControllers/FileUploadPageController.js.map +1 -1
  21. package/.server/server/plugins/engine/pageControllers/PageController.d.ts +6 -6
  22. package/.server/server/plugins/engine/pageControllers/PageController.js +4 -4
  23. package/.server/server/plugins/engine/pageControllers/PageController.js.map +1 -1
  24. package/.server/server/plugins/engine/pageControllers/QuestionPageController.d.ts +13 -13
  25. package/.server/server/plugins/engine/pageControllers/QuestionPageController.js +12 -20
  26. package/.server/server/plugins/engine/pageControllers/QuestionPageController.js.map +1 -1
  27. package/.server/server/plugins/engine/pageControllers/RepeatPageController.d.ts +7 -8
  28. package/.server/server/plugins/engine/pageControllers/RepeatPageController.js.map +1 -1
  29. package/.server/server/plugins/engine/pageControllers/StartPageController.d.ts +2 -2
  30. package/.server/server/plugins/engine/pageControllers/StartPageController.js +1 -1
  31. package/.server/server/plugins/engine/pageControllers/StartPageController.js.map +1 -1
  32. package/.server/server/plugins/engine/pageControllers/StatusPageController.d.ts +3 -4
  33. package/.server/server/plugins/engine/pageControllers/StatusPageController.js +1 -1
  34. package/.server/server/plugins/engine/pageControllers/StatusPageController.js.map +1 -1
  35. package/.server/server/plugins/engine/pageControllers/SummaryPageController.d.ts +5 -4
  36. package/.server/server/plugins/engine/pageControllers/SummaryPageController.js +12 -1
  37. package/.server/server/plugins/engine/pageControllers/SummaryPageController.js.map +1 -1
  38. package/.server/server/plugins/engine/pageControllers/TerminalPageController.d.ts +4 -4
  39. package/.server/server/plugins/engine/pageControllers/TerminalPageController.js +1 -1
  40. package/.server/server/plugins/engine/pageControllers/TerminalPageController.js.map +1 -1
  41. package/.server/server/plugins/engine/pageControllers/__stubs__/server.d.ts +1 -1
  42. package/.server/server/plugins/engine/pageControllers/__stubs__/server.js +2 -6
  43. package/.server/server/plugins/engine/pageControllers/__stubs__/server.js.map +1 -1
  44. package/.server/server/plugins/engine/plugin.js +7 -12
  45. package/.server/server/plugins/engine/plugin.js.map +1 -1
  46. package/.server/server/plugins/engine/routes/index.d.ts +5 -5
  47. package/.server/server/plugins/engine/routes/index.js.map +1 -1
  48. package/.server/server/plugins/engine/routes/questions.d.ts +4 -4
  49. package/.server/server/plugins/engine/routes/questions.js.map +1 -1
  50. package/.server/server/plugins/engine/routes/repeaters/item-delete.js.map +1 -1
  51. package/.server/server/plugins/engine/routes/repeaters/summary.js.map +1 -1
  52. package/.server/server/plugins/engine/types/index.d.ts +2 -2
  53. package/.server/server/plugins/engine/types/index.js.map +1 -1
  54. package/.server/server/plugins/engine/types.d.ts +10 -11
  55. package/.server/server/plugins/engine/types.js.map +1 -1
  56. package/.server/server/plugins/engine/views/partials/form.html +3 -3
  57. package/.server/server/plugins/engine/views/summary.html +21 -5
  58. package/.server/server/plugins/nunjucks/context.d.ts +5 -6
  59. package/.server/server/plugins/nunjucks/context.js +3 -3
  60. package/.server/server/plugins/nunjucks/context.js.map +1 -1
  61. package/.server/server/routes/types.d.ts +3 -2
  62. package/.server/server/routes/types.js +1 -1
  63. package/.server/server/routes/types.js.map +1 -1
  64. package/.server/server/schemas/index.js +1 -1
  65. package/.server/server/schemas/index.js.map +1 -1
  66. package/.server/server/services/cacheService.d.ts +11 -19
  67. package/.server/server/services/cacheService.js +9 -30
  68. package/.server/server/services/cacheService.js.map +1 -1
  69. package/.server/server/types.d.ts +4 -1
  70. package/.server/server/types.js.map +1 -1
  71. package/.server/typings/hapi/index.d.js.map +1 -1
  72. package/package.json +3 -1
  73. package/src/server/index.test.ts +0 -39
  74. package/src/server/index.ts +4 -1
  75. package/src/server/plugins/engine/README.md +2 -2
  76. package/src/server/plugins/engine/components/helpers/helpers.test.ts +1 -1
  77. package/src/server/plugins/engine/configureEnginePlugin.ts +15 -11
  78. package/src/server/plugins/engine/helpers.test.ts +3 -2
  79. package/src/server/plugins/engine/helpers.ts +6 -6
  80. package/src/server/plugins/engine/models/FormModel.test.ts +2 -2
  81. package/src/server/plugins/engine/models/FormModel.ts +1 -2
  82. package/src/server/plugins/engine/models/SummaryViewModel.test.ts +7 -7
  83. package/src/server/plugins/engine/models/SummaryViewModel.ts +1 -1
  84. package/src/server/plugins/engine/options.js +6 -6
  85. package/src/server/plugins/engine/options.test.js +2 -6
  86. package/src/server/plugins/engine/pageControllers/FileUploadPageController.test.ts +8 -10
  87. package/src/server/plugins/engine/pageControllers/FileUploadPageController.ts +9 -8
  88. package/src/server/plugins/engine/pageControllers/PageController.test.ts +11 -8
  89. package/src/server/plugins/engine/pageControllers/PageController.ts +9 -15
  90. package/src/server/plugins/engine/pageControllers/QuestionPageController.test.ts +35 -102
  91. package/src/server/plugins/engine/pageControllers/QuestionPageController.ts +24 -36
  92. package/src/server/plugins/engine/pageControllers/RepeatPageController.test.ts +4 -6
  93. package/src/server/plugins/engine/pageControllers/RepeatPageController.ts +8 -11
  94. package/src/server/plugins/engine/pageControllers/StartPageController.test.ts +4 -4
  95. package/src/server/plugins/engine/pageControllers/StartPageController.ts +1 -1
  96. package/src/server/plugins/engine/pageControllers/StatusPageController.test.ts +4 -4
  97. package/src/server/plugins/engine/pageControllers/StatusPageController.ts +6 -4
  98. package/src/server/plugins/engine/pageControllers/SummaryPageController.ts +15 -5
  99. package/src/server/plugins/engine/pageControllers/TerminalController.test.ts +4 -4
  100. package/src/server/plugins/engine/pageControllers/TerminalPageController.ts +7 -4
  101. package/src/server/plugins/engine/pageControllers/__stubs__/server.ts +5 -6
  102. package/src/server/plugins/engine/plugin.ts +7 -13
  103. package/src/server/plugins/engine/routes/index.ts +6 -11
  104. package/src/server/plugins/engine/routes/questions.test.ts +29 -53
  105. package/src/server/plugins/engine/routes/questions.ts +6 -8
  106. package/src/server/plugins/engine/routes/repeaters/item-delete.ts +5 -14
  107. package/src/server/plugins/engine/routes/repeaters/summary.ts +5 -14
  108. package/src/server/plugins/engine/types/index.ts +4 -1
  109. package/src/server/plugins/engine/types.ts +19 -13
  110. package/src/server/plugins/engine/views/partials/form.html +3 -3
  111. package/src/server/plugins/engine/views/summary.html +21 -5
  112. package/src/server/plugins/nunjucks/context.js +3 -3
  113. package/src/server/routes/types.ts +7 -2
  114. package/src/server/schemas/index.ts +1 -1
  115. package/src/server/services/cacheService.test.ts +1 -117
  116. package/src/server/services/cacheService.ts +22 -73
  117. package/src/server/types.ts +4 -1
  118. package/src/typings/hapi/index.d.ts +6 -7
  119. package/.server/server/plugins/engine/routes/exit.d.ts +0 -46
  120. package/.server/server/plugins/engine/routes/exit.js +0 -36
  121. package/.server/server/plugins/engine/routes/exit.js.map +0 -1
  122. package/src/server/plugins/engine/routes/exit.ts +0 -47
@@ -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
  })
@@ -2,7 +2,6 @@ import { randomUUID } from 'crypto'
2
2
 
3
3
  import { type PageRepeat, type Repeat } from '@defra/forms-model'
4
4
  import Boom from '@hapi/boom'
5
- import { type ResponseToolkit } from '@hapi/hapi'
6
5
  import Joi from 'joi'
7
6
 
8
7
  import { isRepeatState } from '~/src/server/plugins/engine/components/FormComponent.js'
@@ -25,7 +24,8 @@ import {
25
24
  import {
26
25
  FormAction,
27
26
  type FormRequest,
28
- type FormRequestPayload
27
+ type FormRequestPayload,
28
+ type FormResponseToolkit
29
29
  } from '~/src/server/routes/types.js'
30
30
 
31
31
  export class RepeatPageController extends QuestionPageController {
@@ -128,10 +128,7 @@ export class RepeatPageController extends QuestionPageController {
128
128
  }
129
129
  }
130
130
 
131
- proceed(
132
- request: FormContextRequest,
133
- h: Pick<ResponseToolkit, 'redirect' | 'view'>
134
- ) {
131
+ proceed(request: FormContextRequest, h: FormResponseToolkit) {
135
132
  const nextPath = this.getSummaryPath(request)
136
133
  return super.proceed(request, h, nextPath)
137
134
  }
@@ -151,7 +148,7 @@ export class RepeatPageController extends QuestionPageController {
151
148
  return async (
152
149
  request: FormRequest,
153
150
  context: FormContext,
154
- h: Pick<ResponseToolkit, 'redirect' | 'view'>
151
+ h: FormResponseToolkit
155
152
  ) => {
156
153
  const { path } = this
157
154
  const { query } = request
@@ -179,7 +176,7 @@ export class RepeatPageController extends QuestionPageController {
179
176
  return (
180
177
  request: FormRequest,
181
178
  context: FormContext,
182
- h: Pick<ResponseToolkit, 'redirect' | 'view'>
179
+ h: FormResponseToolkit
183
180
  ) => {
184
181
  const { path } = this
185
182
  const { query } = request
@@ -205,7 +202,7 @@ export class RepeatPageController extends QuestionPageController {
205
202
  return (
206
203
  request: FormRequestPayload,
207
204
  context: FormContext,
208
- h: Pick<ResponseToolkit, 'redirect' | 'view'>
205
+ h: FormResponseToolkit
209
206
  ) => {
210
207
  const { path, repeat } = this
211
208
  const { query } = request
@@ -269,7 +266,7 @@ export class RepeatPageController extends QuestionPageController {
269
266
  return (
270
267
  request: FormRequest,
271
268
  context: FormContext,
272
- h: Pick<ResponseToolkit, 'redirect' | 'view'>
269
+ h: FormResponseToolkit
273
270
  ) => {
274
271
  const { viewModel } = this
275
272
  const { state } = context
@@ -304,7 +301,7 @@ export class RepeatPageController extends QuestionPageController {
304
301
  return async (
305
302
  request: FormRequestPayload,
306
303
  context: FormContext,
307
- h: Pick<ResponseToolkit, 'redirect' | 'view'>
304
+ h: FormResponseToolkit
308
305
  ) => {
309
306
  const { repeat } = this
310
307
  const { state } = context
@@ -1,6 +1,6 @@
1
1
  import { FormModel } from '~/src/server/plugins/engine/models/FormModel.js'
2
2
  import { StartPageController } from '~/src/server/plugins/engine/pageControllers/StartPageController.js'
3
- import { serverWithSaveAndReturn } from '~/src/server/plugins/engine/pageControllers/__stubs__/server.js'
3
+ import { serverWithSaveAndExit } from '~/src/server/plugins/engine/pageControllers/__stubs__/server.js'
4
4
  import definition from '~/test/form/definitions/basic.js'
5
5
 
6
6
  describe('StartPageController', () => {
@@ -22,9 +22,9 @@ describe('StartPageController', () => {
22
22
  controller = new StartPageController(model, mockPage as any)
23
23
  })
24
24
 
25
- describe('shouldShowSaveAndReturn', () => {
26
- it('should return false (StartPageController does not allow save and return)', () => {
27
- expect(controller.shouldShowSaveAndReturn(serverWithSaveAndReturn)).toBe(
25
+ describe('shouldShowSaveAndExit', () => {
26
+ it('should return false (StartPageController does not allow save and exit)', () => {
27
+ expect(controller.shouldShowSaveAndExit(serverWithSaveAndExit)).toBe(
28
28
  false
29
29
  )
30
30
  })
@@ -9,7 +9,7 @@ export class StartPageController extends QuestionPageController {
9
9
  * but start pages should really live on gov.uk (whitehall publisher) so a user can be properly signposted.
10
10
  */
11
11
 
12
- allowSaveAndReturn = false
12
+ allowSaveAndExit = false
13
13
 
14
14
  getViewModel(request: FormRequest, context: FormContext) {
15
15
  return {
@@ -1,6 +1,6 @@
1
1
  import { FormModel } from '~/src/server/plugins/engine/models/FormModel.js'
2
2
  import { StatusPageController } from '~/src/server/plugins/engine/pageControllers/StatusPageController.js'
3
- import { serverWithSaveAndReturn } from '~/src/server/plugins/engine/pageControllers/__stubs__/server.js'
3
+ import { serverWithSaveAndExit } from '~/src/server/plugins/engine/pageControllers/__stubs__/server.js'
4
4
  import definition from '~/test/form/definitions/basic.js'
5
5
 
6
6
  describe('StatusPageController', () => {
@@ -22,9 +22,9 @@ describe('StatusPageController', () => {
22
22
  controller = new StatusPageController(model, mockPage as any)
23
23
  })
24
24
 
25
- describe('shouldShowSaveAndReturn', () => {
26
- it('should return false (StatusPageController does not allow save and return)', () => {
27
- expect(controller.shouldShowSaveAndReturn(serverWithSaveAndReturn)).toBe(
25
+ describe('shouldShowSaveAndExit', () => {
26
+ it('should return false (StatusPageController does not allow save and exit)', () => {
27
+ expect(controller.shouldShowSaveAndExit(serverWithSaveAndExit)).toBe(
28
28
  false
29
29
  )
30
30
  })
@@ -1,15 +1,17 @@
1
1
  import { type PageStatus } from '@defra/forms-model'
2
- import { type ResponseToolkit } from '@hapi/hapi'
3
2
 
4
3
  import { getCacheService } from '~/src/server/plugins/engine/helpers.js'
5
4
  import { type FormModel } from '~/src/server/plugins/engine/models/index.js'
6
5
  import { QuestionPageController } from '~/src/server/plugins/engine/pageControllers/QuestionPageController.js'
7
6
  import { type FormContext } from '~/src/server/plugins/engine/types.js'
8
- import { type FormRequest } from '~/src/server/routes/types.js'
7
+ import {
8
+ type FormRequest,
9
+ type FormResponseToolkit
10
+ } from '~/src/server/routes/types.js'
9
11
 
10
12
  export class StatusPageController extends QuestionPageController {
11
13
  declare pageDef: PageStatus
12
- allowSaveAndReturn = false
14
+ allowSaveAndExit = false
13
15
 
14
16
  constructor(model: FormModel, pageDef: PageStatus) {
15
17
  super(model, pageDef)
@@ -24,7 +26,7 @@ export class StatusPageController extends QuestionPageController {
24
26
  return async (
25
27
  request: FormRequest,
26
28
  context: FormContext,
27
- h: Pick<ResponseToolkit, 'redirect' | 'view'>
29
+ h: FormResponseToolkit
28
30
  ) => {
29
31
  const { viewModel, viewName } = this
30
32