@defra/forms-engine-plugin 2.0.2 → 2.1.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 (43) hide show
  1. package/.server/server/forms/page-events.yaml +87 -0
  2. package/.server/server/index.js +2 -1
  3. package/.server/server/index.js.map +1 -1
  4. package/.server/server/plugins/engine/helpers.d.ts +1 -1
  5. package/.server/server/plugins/engine/pageControllers/QuestionPageController.js +9 -2
  6. package/.server/server/plugins/engine/pageControllers/QuestionPageController.js.map +1 -1
  7. package/.server/server/plugins/engine/plugin.js +1 -2
  8. package/.server/server/plugins/engine/plugin.js.map +1 -1
  9. package/.server/server/plugins/engine/routes/questions.d.ts +4 -2
  10. package/.server/server/plugins/engine/routes/questions.js +67 -43
  11. package/.server/server/plugins/engine/routes/questions.js.map +1 -1
  12. package/.server/server/plugins/engine/services/localFormsService.js +7 -9
  13. package/.server/server/plugins/engine/services/localFormsService.js.map +1 -1
  14. package/.server/server/plugins/engine/types.d.ts +1 -1
  15. package/.server/server/plugins/engine/types.js.map +1 -1
  16. package/.server/server/routes/dummy-api.d.ts +38 -0
  17. package/.server/server/routes/dummy-api.js +33 -0
  18. package/.server/server/routes/dummy-api.js.map +1 -0
  19. package/.server/server/routes/index.d.ts +1 -0
  20. package/.server/server/routes/index.js +1 -0
  21. package/.server/server/routes/index.js.map +1 -1
  22. package/.server/server/services/cacheService.d.ts +0 -2
  23. package/.server/server/services/cacheService.js +1 -4
  24. package/.server/server/services/cacheService.js.map +1 -1
  25. package/package.json +4 -2
  26. package/src/server/forms/page-events.yaml +87 -0
  27. package/src/server/index.ts +4 -2
  28. package/src/server/plugins/engine/outputFormatters/machine/v1.test.ts +5 -4
  29. package/src/server/plugins/engine/outputFormatters/machine/v2.test.ts +5 -4
  30. package/src/server/plugins/engine/pageControllers/QuestionPageController.test.ts +81 -16
  31. package/src/server/plugins/engine/pageControllers/QuestionPageController.ts +13 -1
  32. package/src/server/plugins/engine/plugin.ts +1 -2
  33. package/src/server/plugins/engine/routes/questions.test.ts +416 -0
  34. package/src/server/plugins/engine/routes/questions.ts +96 -40
  35. package/src/server/plugins/engine/services/localFormsService.js +7 -8
  36. package/src/server/plugins/engine/types.ts +0 -1
  37. package/src/server/routes/dummy-api.test.ts +96 -0
  38. package/src/server/routes/dummy-api.ts +62 -0
  39. package/src/server/routes/index.ts +1 -0
  40. package/src/server/services/cacheService.test.ts +8 -2
  41. package/src/server/services/cacheService.ts +1 -13
  42. package/.server/server/forms/register-as-a-unicorn-breeder.json +0 -393
  43. package/src/server/forms/register-as-a-unicorn-breeder.json +0 -393
@@ -1,6 +1,7 @@
1
1
  import { type PageQuestion } from '@defra/forms-model'
2
2
  import { type ResponseToolkit } from '@hapi/hapi'
3
3
 
4
+ import { getCacheService } from '~/src/server/plugins/engine/helpers.js'
4
5
  import { FormModel } from '~/src/server/plugins/engine/models/FormModel.js'
5
6
  import { QuestionPageController } from '~/src/server/plugins/engine/pageControllers/QuestionPageController.js'
6
7
  import {
@@ -1333,63 +1334,127 @@ describe('Save and Return functionality', () => {
1333
1334
 
1334
1335
  describe('handleSaveAndReturn', () => {
1335
1336
  it('should save state and redirect to exit page', async () => {
1337
+ const sessionPersisterMock = jest.fn()
1336
1338
  const state: FormSubmissionState = {
1337
1339
  $$__referenceNumber: 'foobar',
1338
1340
  yesNoField: true
1339
1341
  }
1340
1342
  const request = {
1341
1343
  ...requestPage1,
1344
+ server: {
1345
+ plugins: {
1346
+ 'forms-engine-plugin': {
1347
+ saveAndReturn: {
1348
+ sessionPersister: sessionPersisterMock
1349
+ },
1350
+ cacheService: {
1351
+ clearState: jest.fn()
1352
+ } as unknown as CacheService
1353
+ }
1354
+ }
1355
+ },
1342
1356
  method: 'post',
1343
1357
  payload: { yesNoField: true, action: 'save-and-return' }
1344
1358
  } as unknown as FormRequestPayload
1345
1359
 
1346
- const context = model.getFormContext(request, state)
1360
+ const cacheService = getCacheService(request.server)
1347
1361
 
1348
- jest.spyOn(controller1, 'setState').mockResolvedValue(state)
1362
+ const context = model.getFormContext(request, state)
1349
1363
 
1350
1364
  await controller1.handleSaveAndReturn(request, context, h)
1351
1365
 
1352
- expect(controller1.setState).toHaveBeenCalledWith(request, state)
1366
+ expect(sessionPersisterMock).toHaveBeenCalledWith(context.state, request)
1367
+ expect(cacheService.clearState).toHaveBeenCalledWith(request)
1353
1368
  expect(h.redirect).toHaveBeenCalledWith('/test/exit')
1354
1369
  })
1355
1370
 
1356
- it('should handle save-and-return with incomplete data', async () => {
1371
+ it('should throw if sessionPersister inside saveAndReturn options provided', async () => {
1372
+ const sessionPersisterMock = jest.fn()
1357
1373
  const state: FormSubmissionState = {
1358
1374
  $$__referenceNumber: 'foobar',
1359
- yesNoField: null
1375
+ yesNoField: true
1360
1376
  }
1361
1377
  const request = {
1362
1378
  ...requestPage1,
1379
+ server: {
1380
+ plugins: {
1381
+ 'forms-engine-plugin': {
1382
+ // No sessionPersister object
1383
+ saveAndReturn: {}
1384
+ }
1385
+ }
1386
+ },
1363
1387
  method: 'post',
1364
- payload: { yesNoField: '', action: 'save-and-return' }
1388
+ payload: { yesNoField: true, action: 'save-and-return' }
1365
1389
  } as unknown as FormRequestPayload
1366
1390
 
1367
1391
  const context = model.getFormContext(request, state)
1368
1392
 
1369
- jest.spyOn(controller1, 'setState').mockResolvedValue(state)
1393
+ await expect(
1394
+ controller1.handleSaveAndReturn(request, context, h)
1395
+ ).rejects.toThrow('Server misconfigured for save and return')
1370
1396
 
1371
- await controller1.handleSaveAndReturn(request, context, h)
1397
+ expect(sessionPersisterMock).not.toHaveBeenCalled()
1398
+ expect(h.redirect).not.toHaveBeenCalled()
1399
+ })
1372
1400
 
1373
- expect(controller1.setState).toHaveBeenCalledWith(request, state)
1374
- expect(h.redirect).toHaveBeenCalledWith('/test/exit')
1401
+ it('should throw if no saveAndReturn options provided', async () => {
1402
+ const sessionPersisterMock = jest.fn()
1403
+ const state: FormSubmissionState = {
1404
+ $$__referenceNumber: 'foobar',
1405
+ yesNoField: true
1406
+ }
1407
+ const request = {
1408
+ ...requestPage1,
1409
+ server: {
1410
+ plugins: {
1411
+ 'forms-engine-plugin': {
1412
+ // No saveAndReturn object
1413
+ }
1414
+ }
1415
+ },
1416
+ 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()
1375
1428
  })
1376
1429
 
1377
- it('should handle save-and-return with validation errors', async () => {
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
+ })
1378
1434
  const state: FormSubmissionState = { $$__referenceNumber: 'foobar' }
1379
1435
  const request = {
1380
1436
  ...requestPage1,
1381
1437
  method: 'post',
1438
+ server: {
1439
+ plugins: {
1440
+ 'forms-engine-plugin': {
1441
+ saveAndReturn: {
1442
+ sessionPersister: sessionPersisterMock
1443
+ }
1444
+ }
1445
+ }
1446
+ },
1382
1447
  payload: { action: 'save-and-return' }
1383
1448
  } as unknown as FormRequestPayload
1384
1449
 
1385
1450
  const context = model.getFormContext(request, state)
1386
1451
 
1387
- jest.spyOn(controller1, 'setState').mockResolvedValue(state)
1388
-
1389
- await controller1.handleSaveAndReturn(request, context, h)
1452
+ await expect(
1453
+ controller1.handleSaveAndReturn(request, context, h)
1454
+ ).rejects.toThrow('Session persister error')
1390
1455
 
1391
- expect(controller1.setState).toHaveBeenCalledWith(request, state)
1392
- expect(h.redirect).toHaveBeenCalledWith('/test/exit')
1456
+ expect(sessionPersisterMock).toHaveBeenCalledWith(context.state, request)
1457
+ expect(h.redirect).not.toHaveBeenCalledWith('/test/exit')
1393
1458
  })
1394
1459
  })
1395
1460
 
@@ -8,6 +8,7 @@ import {
8
8
  type Link,
9
9
  type Page
10
10
  } from '@defra/forms-model'
11
+ import Boom from '@hapi/boom'
11
12
  import { type ResponseToolkit, type RouteOptions } from '@hapi/hapi'
12
13
  import { type ValidationErrorItem } from 'joi'
13
14
 
@@ -17,6 +18,7 @@ import { type BackLink } from '~/src/server/plugins/engine/components/types.js'
17
18
  import {
18
19
  getCacheService,
19
20
  getErrors,
21
+ getSaveAndReturnHelpers,
20
22
  normalisePath,
21
23
  proceed
22
24
  } from '~/src/server/plugins/engine/helpers.js'
@@ -548,7 +550,17 @@ export class QuestionPageController extends PageController {
548
550
  const { state } = context
549
551
 
550
552
  // Save the current state and redirect to exit page
551
- await this.setState(request, state)
553
+ const saveAndReturn = getSaveAndReturnHelpers(request.server)
554
+
555
+ if (!saveAndReturn?.sessionPersister) {
556
+ throw Boom.internal('Server misconfigured for save and return')
557
+ }
558
+
559
+ await saveAndReturn.sessionPersister(state, request)
560
+
561
+ const cacheService = getCacheService(request.server)
562
+ await cacheService.clearState(request)
563
+
552
564
  return h.redirect(this.getHref('/exit'))
553
565
  }
554
566
 
@@ -43,8 +43,7 @@ export const plugin = {
43
43
  cacheName,
44
44
  options: {
45
45
  keyGenerator: saveAndReturn?.keyGenerator,
46
- sessionHydrator: saveAndReturn?.sessionHydrator,
47
- sessionPersister: saveAndReturn?.sessionPersister
46
+ sessionHydrator: saveAndReturn?.sessionHydrator
48
47
  }
49
48
  })
50
49
 
@@ -0,0 +1,416 @@
1
+ import Boom from '@hapi/boom'
2
+ import { type ResponseObject, type ResponseToolkit } from '@hapi/hapi'
3
+ // eslint-disable-next-line n/no-unpublished-import
4
+ import nock from 'nock'
5
+
6
+ import { type FormModel } from '~/src/server/plugins/engine/models/FormModel.js'
7
+ import { type PageControllerClass } from '~/src/server/plugins/engine/pageControllers/helpers.js'
8
+ import { redirectOrMakeHandler } from '~/src/server/plugins/engine/routes/index.js'
9
+ import {
10
+ makeGetHandler,
11
+ makePostHandler
12
+ } from '~/src/server/plugins/engine/routes/questions.js'
13
+ import { type FormContext } from '~/src/server/plugins/engine/types.js'
14
+ import {
15
+ type FormRequest,
16
+ type FormRequestPayload
17
+ } from '~/src/server/routes/types.js'
18
+ jest.mock('~/src/server/plugins/engine/models/SummaryViewModel', () => ({
19
+ SummaryViewModel: class {
20
+ summary = 'mocked summary'
21
+ }
22
+ }))
23
+
24
+ jest.mock(
25
+ '~/src/server/plugins/engine/pageControllers/SummaryPageController',
26
+ () => ({
27
+ getFormSubmissionData: jest.fn().mockReturnValue([])
28
+ })
29
+ )
30
+
31
+ jest.mock('~/src/server/plugins/engine/outputFormatters/machine/v1', () => ({
32
+ format: jest.fn().mockReturnValue('mocked format')
33
+ }))
34
+
35
+ jest.mock('~/src/server/plugins/engine/routes/index')
36
+
37
+ describe('makeGetHandler', () => {
38
+ const hMock: Pick<ResponseToolkit, 'redirect' | 'view'> = {
39
+ redirect: jest.fn(),
40
+ view: jest.fn()
41
+ }
42
+
43
+ beforeEach(() => {
44
+ nock('http://test').persist().post('/load').reply(200, {
45
+ wasGetCalled: true
46
+ })
47
+ })
48
+
49
+ afterEach(() => {
50
+ jest.mocked(redirectOrMakeHandler).mockRestore()
51
+ nock.cleanAll()
52
+ })
53
+
54
+ it('calls the callback when events.onLoad.type is http', async () => {
55
+ let data = {}
56
+
57
+ const modelMock = {
58
+ basePath: 'some-base-path',
59
+ def: { name: 'Hello world' }
60
+ } as FormModel
61
+
62
+ const pageMock = createMockPageController(
63
+ modelMock,
64
+ (
65
+ _request: FormRequest,
66
+ context: FormContext,
67
+ _h: Pick<ResponseToolkit, 'redirect' | 'view'>
68
+ ) => {
69
+ data = context.data
70
+ return Promise.resolve({} as unknown as ResponseObject)
71
+ }
72
+ )
73
+
74
+ const contextMock = { data: {}, model: {} } as unknown as FormContext
75
+
76
+ const requestMock = {
77
+ params: { path: 'some-path' },
78
+ app: { model: modelMock }
79
+ } as FormRequest
80
+
81
+ jest
82
+ .mocked(redirectOrMakeHandler)
83
+ .mockImplementation(
84
+ (
85
+ _req: FormRequest | FormRequestPayload,
86
+ _h: Pick<ResponseToolkit, 'redirect' | 'view'>,
87
+ fn
88
+ ) => Promise.resolve(fn(pageMock, contextMock))
89
+ )
90
+
91
+ await makeGetHandler()(requestMock, hMock)
92
+
93
+ expect(data).toMatchObject({
94
+ wasGetCalled: true
95
+ })
96
+ })
97
+
98
+ it('does not call the callback when the events.onLoad.type is not http', async () => {
99
+ let data = {}
100
+
101
+ const modelMock = {
102
+ basePath: 'some-base-path',
103
+ def: { name: 'Hello world' }
104
+ } as FormModel
105
+
106
+ const pageMock = createMockPageController(
107
+ modelMock,
108
+ (
109
+ _request: FormRequest,
110
+ context: FormContext,
111
+ _h: Pick<ResponseToolkit, 'redirect' | 'view'>
112
+ ) => {
113
+ data = context.data
114
+ return Promise.resolve({} as unknown as ResponseObject)
115
+ }
116
+ )
117
+
118
+ pageMock.events = {}
119
+
120
+ const contextMock = { data: {}, model: {} } as unknown as FormContext
121
+
122
+ const requestMock = {
123
+ params: { path: 'some-path' },
124
+ app: { model: modelMock }
125
+ } as FormRequest
126
+
127
+ jest
128
+ .mocked(redirectOrMakeHandler)
129
+ .mockImplementation(
130
+ (
131
+ _req: FormRequest | FormRequestPayload,
132
+ _h: Pick<ResponseToolkit, 'redirect' | 'view'>,
133
+ fn
134
+ ) => Promise.resolve(fn(pageMock, contextMock))
135
+ )
136
+
137
+ await makeGetHandler()(requestMock, hMock)
138
+
139
+ expect(data).toMatchObject({})
140
+ })
141
+
142
+ it('throws when model is missing', async () => {
143
+ let error
144
+
145
+ const modelMock = {
146
+ basePath: 'some-base-path',
147
+ def: { name: 'Hello world' }
148
+ } as FormModel
149
+
150
+ const pageMock = createMockPageController(
151
+ modelMock,
152
+ (
153
+ _request: FormRequest,
154
+ _context: FormContext,
155
+ _h: Pick<ResponseToolkit, 'redirect' | 'view'>
156
+ ) => {
157
+ return Promise.resolve({} as unknown as ResponseObject)
158
+ }
159
+ )
160
+
161
+ const contextMock = { data: {}, model: {} } as unknown as FormContext
162
+
163
+ const requestMock = {
164
+ params: { path: 'some-path' },
165
+ app: {}
166
+ } as FormRequest
167
+
168
+ jest
169
+ .mocked(redirectOrMakeHandler)
170
+ .mockImplementation(
171
+ async (
172
+ _req: FormRequest | FormRequestPayload,
173
+ _h: Pick<ResponseToolkit, 'redirect' | 'view'>,
174
+ fn
175
+ ) => {
176
+ try {
177
+ await fn(pageMock, contextMock)
178
+ } catch (err) {
179
+ error = err
180
+ }
181
+
182
+ return Promise.resolve({} as unknown as ResponseObject)
183
+ }
184
+ )
185
+
186
+ await makeGetHandler()(requestMock, hMock)
187
+
188
+ expect(error).toEqual(Boom.notFound('No model found for /some-path'))
189
+ })
190
+ })
191
+
192
+ describe('makePostHandler', () => {
193
+ const hMock: Pick<ResponseToolkit, 'redirect' | 'view'> = {
194
+ redirect: jest.fn(),
195
+ view: jest.fn()
196
+ }
197
+
198
+ beforeEach(() => {
199
+ nock('http://test').post('/save').reply(200, {
200
+ wasPostCalled: true
201
+ })
202
+ })
203
+
204
+ afterEach(() => {
205
+ jest.mocked(redirectOrMakeHandler).mockRestore()
206
+ nock.cleanAll()
207
+ })
208
+
209
+ it('calls the callback when events.onSave.type is http and the page controller was successful', async () => {
210
+ const mockPostResponse: ResponseObject = {
211
+ statusCode: 200
212
+ } as ResponseObject
213
+
214
+ const modelMock = {
215
+ basePath: 'some-base-path',
216
+ def: { name: 'Hello world' }
217
+ } as FormModel
218
+
219
+ const pageMock = createMockPageController(
220
+ modelMock,
221
+ (
222
+ _request: FormRequest,
223
+ _context: FormContext,
224
+ _h: Pick<ResponseToolkit, 'redirect' | 'view'>
225
+ ) => {
226
+ // do return a valid ResponseObject wrapped in Promise.resolve
227
+ return mockPostResponse
228
+ }
229
+ )
230
+
231
+ const contextMock = { data: {}, model: {} } as unknown as FormContext
232
+
233
+ const requestMock = {
234
+ params: { path: 'some-path' },
235
+ app: { model: modelMock },
236
+ payload: { some: 'payload' }
237
+ } as unknown as FormRequestPayload
238
+
239
+ jest
240
+ .mocked(redirectOrMakeHandler)
241
+ .mockImplementation(
242
+ (
243
+ _req: FormRequest | FormRequestPayload,
244
+ _h: Pick<ResponseToolkit, 'redirect' | 'view'>,
245
+ fn
246
+ ) => Promise.resolve(fn(pageMock, contextMock))
247
+ )
248
+
249
+ const response = await makePostHandler()(requestMock, hMock)
250
+
251
+ expect(nock.pendingMocks()).toBeEmpty()
252
+ expect(response).toBe(mockPostResponse)
253
+ })
254
+
255
+ it('does not call the callback when the events.onSave.type is not http', async () => {
256
+ const modelMock = {
257
+ basePath: 'some-base-path',
258
+ def: { name: 'Hello world' }
259
+ } as FormModel
260
+
261
+ const pageMock = createMockPageController(
262
+ modelMock,
263
+ (
264
+ _request: FormRequest,
265
+ _context: FormContext,
266
+ _h: Pick<ResponseToolkit, 'redirect' | 'view'>
267
+ ) => {
268
+ return Promise.resolve({} as unknown as ResponseObject)
269
+ }
270
+ )
271
+
272
+ pageMock.events = {}
273
+
274
+ const contextMock = { data: {}, model: {} } as unknown as FormContext
275
+
276
+ const requestMock = {
277
+ params: { path: 'some-path' },
278
+ app: { model: modelMock },
279
+ payload: { some: 'payload' }
280
+ } as unknown as FormRequestPayload
281
+
282
+ jest
283
+ .mocked(redirectOrMakeHandler)
284
+ .mockImplementation(
285
+ (
286
+ _req: FormRequest | FormRequestPayload,
287
+ _h: Pick<ResponseToolkit, 'redirect' | 'view'>,
288
+ fn
289
+ ) => Promise.resolve(fn(pageMock, contextMock))
290
+ )
291
+
292
+ await makePostHandler()(requestMock, hMock)
293
+
294
+ expect(nock.pendingMocks()).not.toBeEmpty()
295
+ })
296
+
297
+ it('does not call the callback when events.onSave.type is http and the page controller was unsuccessful', async () => {
298
+ const mockPostResponse: ResponseObject = {
299
+ statusCode: 500
300
+ } as ResponseObject
301
+
302
+ const modelMock = {
303
+ basePath: 'some-base-path',
304
+ def: { name: 'Hello world' }
305
+ } as FormModel
306
+
307
+ const pageMock = createMockPageController(
308
+ modelMock,
309
+ (
310
+ _request: FormRequest,
311
+ _context: FormContext,
312
+ _h: Pick<ResponseToolkit, 'redirect' | 'view'>
313
+ ) => {
314
+ // do return a valid ResponseObject wrapped in Promise.resolve
315
+ return mockPostResponse
316
+ }
317
+ )
318
+
319
+ const contextMock = { data: {}, model: {} } as unknown as FormContext
320
+
321
+ const requestMock = {
322
+ params: { path: 'some-path' },
323
+ app: { model: modelMock },
324
+ payload: { some: 'payload' }
325
+ } as unknown as FormRequestPayload
326
+
327
+ jest
328
+ .mocked(redirectOrMakeHandler)
329
+ .mockImplementation(
330
+ (
331
+ _req: FormRequest | FormRequestPayload,
332
+ _h: Pick<ResponseToolkit, 'redirect' | 'view'>,
333
+ fn
334
+ ) => Promise.resolve(fn(pageMock, contextMock))
335
+ )
336
+
337
+ await makePostHandler()(requestMock, hMock)
338
+
339
+ expect(nock.pendingMocks()).not.toBeEmpty()
340
+ })
341
+
342
+ it('throws when model is missing', async () => {
343
+ let error
344
+
345
+ const modelMock = {
346
+ basePath: 'some-base-path',
347
+ def: { name: 'Hello world' }
348
+ } as FormModel
349
+
350
+ const pageMock = createMockPageController(
351
+ modelMock,
352
+ (
353
+ _request: FormRequest,
354
+ _context: FormContext,
355
+ _h: Pick<ResponseToolkit, 'redirect' | 'view'>
356
+ ) => {
357
+ return Promise.resolve({} as unknown as ResponseObject)
358
+ }
359
+ )
360
+
361
+ const contextMock = { data: {}, model: {} } as unknown as FormContext
362
+
363
+ const requestMock = {
364
+ params: { path: 'some-path' },
365
+ app: {},
366
+ payload: { some: 'payload' }
367
+ } as unknown as FormRequestPayload
368
+
369
+ jest
370
+ .mocked(redirectOrMakeHandler)
371
+ .mockImplementation(
372
+ async (
373
+ _req: FormRequest | FormRequestPayload,
374
+ _h: Pick<ResponseToolkit, 'redirect' | 'view'>,
375
+ fn
376
+ ) => {
377
+ try {
378
+ await fn(pageMock, contextMock)
379
+ } catch (err) {
380
+ error = err
381
+ }
382
+
383
+ return Promise.resolve({} as unknown as ResponseObject)
384
+ }
385
+ )
386
+
387
+ await makePostHandler()(requestMock, hMock)
388
+
389
+ expect(error).toEqual(Boom.notFound('No model found for /some-path'))
390
+ })
391
+ })
392
+
393
+ function createMockPageController(
394
+ model: FormModel,
395
+ routeHandler: (
396
+ request: FormRequest,
397
+ context: FormContext,
398
+ h: Pick<ResponseToolkit, 'redirect' | 'view'>
399
+ ) => ResponseObject | Promise<ResponseObject>
400
+ ): PageControllerClass {
401
+ return {
402
+ model,
403
+ events: {
404
+ onLoad: {
405
+ type: 'http',
406
+ options: { method: 'POST', url: 'http://test/load' }
407
+ },
408
+ onSave: {
409
+ type: 'http',
410
+ options: { method: 'POST', url: 'http://test/save' }
411
+ }
412
+ },
413
+ makeGetRouteHandler: () => routeHandler,
414
+ makePostRouteHandler: () => routeHandler
415
+ } as unknown as PageControllerClass
416
+ }