@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.
- package/.server/server/forms/page-events.yaml +87 -0
- package/.server/server/index.js +2 -1
- package/.server/server/index.js.map +1 -1
- package/.server/server/plugins/engine/helpers.d.ts +1 -1
- package/.server/server/plugins/engine/pageControllers/QuestionPageController.js +9 -2
- package/.server/server/plugins/engine/pageControllers/QuestionPageController.js.map +1 -1
- package/.server/server/plugins/engine/plugin.js +1 -2
- package/.server/server/plugins/engine/plugin.js.map +1 -1
- package/.server/server/plugins/engine/routes/questions.d.ts +4 -2
- package/.server/server/plugins/engine/routes/questions.js +67 -43
- package/.server/server/plugins/engine/routes/questions.js.map +1 -1
- package/.server/server/plugins/engine/services/localFormsService.js +7 -9
- package/.server/server/plugins/engine/services/localFormsService.js.map +1 -1
- package/.server/server/plugins/engine/types.d.ts +1 -1
- package/.server/server/plugins/engine/types.js.map +1 -1
- package/.server/server/routes/dummy-api.d.ts +38 -0
- package/.server/server/routes/dummy-api.js +33 -0
- package/.server/server/routes/dummy-api.js.map +1 -0
- package/.server/server/routes/index.d.ts +1 -0
- package/.server/server/routes/index.js +1 -0
- package/.server/server/routes/index.js.map +1 -1
- package/.server/server/services/cacheService.d.ts +0 -2
- package/.server/server/services/cacheService.js +1 -4
- package/.server/server/services/cacheService.js.map +1 -1
- package/package.json +4 -2
- package/src/server/forms/page-events.yaml +87 -0
- package/src/server/index.ts +4 -2
- package/src/server/plugins/engine/outputFormatters/machine/v1.test.ts +5 -4
- package/src/server/plugins/engine/outputFormatters/machine/v2.test.ts +5 -4
- package/src/server/plugins/engine/pageControllers/QuestionPageController.test.ts +81 -16
- package/src/server/plugins/engine/pageControllers/QuestionPageController.ts +13 -1
- package/src/server/plugins/engine/plugin.ts +1 -2
- package/src/server/plugins/engine/routes/questions.test.ts +416 -0
- package/src/server/plugins/engine/routes/questions.ts +96 -40
- package/src/server/plugins/engine/services/localFormsService.js +7 -8
- package/src/server/plugins/engine/types.ts +0 -1
- package/src/server/routes/dummy-api.test.ts +96 -0
- package/src/server/routes/dummy-api.ts +62 -0
- package/src/server/routes/index.ts +1 -0
- package/src/server/services/cacheService.test.ts +8 -2
- package/src/server/services/cacheService.ts +1 -13
- package/.server/server/forms/register-as-a-unicorn-breeder.json +0 -393
- 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
|
|
1360
|
+
const cacheService = getCacheService(request.server)
|
|
1347
1361
|
|
|
1348
|
-
|
|
1362
|
+
const context = model.getFormContext(request, state)
|
|
1349
1363
|
|
|
1350
1364
|
await controller1.handleSaveAndReturn(request, context, h)
|
|
1351
1365
|
|
|
1352
|
-
expect(
|
|
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
|
|
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:
|
|
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:
|
|
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
|
-
|
|
1393
|
+
await expect(
|
|
1394
|
+
controller1.handleSaveAndReturn(request, context, h)
|
|
1395
|
+
).rejects.toThrow('Server misconfigured for save and return')
|
|
1370
1396
|
|
|
1371
|
-
|
|
1397
|
+
expect(sessionPersisterMock).not.toHaveBeenCalled()
|
|
1398
|
+
expect(h.redirect).not.toHaveBeenCalled()
|
|
1399
|
+
})
|
|
1372
1400
|
|
|
1373
|
-
|
|
1374
|
-
|
|
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
|
|
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
|
-
|
|
1388
|
-
|
|
1389
|
-
|
|
1452
|
+
await expect(
|
|
1453
|
+
controller1.handleSaveAndReturn(request, context, h)
|
|
1454
|
+
).rejects.toThrow('Session persister error')
|
|
1390
1455
|
|
|
1391
|
-
expect(
|
|
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
|
-
|
|
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
|
+
}
|