@defra/forms-engine-plugin 1.4.0 → 2.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.
- package/.server/server/plugins/engine/README.md +2 -46
- package/.server/server/plugins/engine/configureEnginePlugin.d.ts +1 -1
- package/.server/server/plugins/engine/configureEnginePlugin.js +5 -2
- package/.server/server/plugins/engine/configureEnginePlugin.js.map +1 -1
- package/.server/server/plugins/engine/helpers.d.ts +11 -0
- package/.server/server/plugins/engine/helpers.js +7 -1
- package/.server/server/plugins/engine/helpers.js.map +1 -1
- package/.server/server/plugins/engine/models/FormModel.js +2 -2
- package/.server/server/plugins/engine/models/FormModel.js.map +1 -1
- package/.server/server/plugins/engine/models/SummaryViewModel.d.ts +1 -1
- package/.server/server/plugins/engine/models/SummaryViewModel.js +1 -1
- package/.server/server/plugins/engine/models/SummaryViewModel.js.map +1 -1
- package/.server/server/plugins/engine/options.js +5 -3
- package/.server/server/plugins/engine/options.js.map +1 -1
- package/.server/server/plugins/engine/options.test.js +18 -9
- package/.server/server/plugins/engine/options.test.js.map +1 -1
- package/.server/server/plugins/engine/pageControllers/PageController.d.ts +3 -1
- package/.server/server/plugins/engine/pageControllers/PageController.js +5 -1
- package/.server/server/plugins/engine/pageControllers/PageController.js.map +1 -1
- package/.server/server/plugins/engine/pageControllers/QuestionPageController.d.ts +1 -1
- package/.server/server/plugins/engine/pageControllers/QuestionPageController.js +2 -4
- package/.server/server/plugins/engine/pageControllers/QuestionPageController.js.map +1 -1
- package/.server/server/plugins/engine/pageControllers/StartPageController.d.ts +2 -2
- package/.server/server/plugins/engine/pageControllers/StartPageController.js +1 -3
- package/.server/server/plugins/engine/pageControllers/StartPageController.js.map +1 -1
- package/.server/server/plugins/engine/pageControllers/StatusPageController.d.ts +1 -0
- package/.server/server/plugins/engine/pageControllers/StatusPageController.js +1 -0
- package/.server/server/plugins/engine/pageControllers/StatusPageController.js.map +1 -1
- package/.server/server/plugins/engine/pageControllers/SummaryPageController.d.ts +0 -1
- package/.server/server/plugins/engine/pageControllers/SummaryPageController.js +1 -4
- package/.server/server/plugins/engine/pageControllers/SummaryPageController.js.map +1 -1
- package/.server/server/plugins/engine/pageControllers/TerminalPageController.d.ts +1 -0
- package/.server/server/plugins/engine/pageControllers/TerminalPageController.js +1 -0
- package/.server/server/plugins/engine/pageControllers/TerminalPageController.js.map +1 -1
- package/.server/server/plugins/engine/pageControllers/__stubs__/request.d.ts +4 -0
- package/.server/server/plugins/engine/pageControllers/__stubs__/request.js +14 -0
- package/.server/server/plugins/engine/pageControllers/__stubs__/request.js.map +1 -0
- package/.server/server/plugins/engine/pageControllers/__stubs__/server.d.ts +3 -0
- package/.server/server/plugins/engine/pageControllers/__stubs__/server.js +23 -0
- package/.server/server/plugins/engine/pageControllers/__stubs__/server.js.map +1 -0
- package/.server/server/plugins/engine/plugin.js +5 -6
- package/.server/server/plugins/engine/plugin.js.map +1 -1
- package/.server/server/plugins/engine/types.d.ts +7 -5
- package/.server/server/plugins/engine/types.js.map +1 -1
- package/.server/server/types.d.ts +2 -1
- package/.server/server/types.js.map +1 -1
- package/.server/typings/hapi/index.d.js.map +1 -1
- package/package.json +1 -1
- package/src/server/plugins/engine/README.md +2 -46
- package/src/server/plugins/engine/configureEnginePlugin.ts +4 -2
- package/src/server/plugins/engine/helpers.test.ts +3 -2
- package/src/server/plugins/engine/helpers.ts +9 -1
- package/src/server/plugins/engine/models/FormModel.test.ts +96 -21
- package/src/server/plugins/engine/models/FormModel.ts +5 -2
- package/src/server/plugins/engine/models/SummaryViewModel.test.ts +7 -5
- package/src/server/plugins/engine/models/SummaryViewModel.ts +1 -1
- package/src/server/plugins/engine/options.js +5 -3
- package/src/server/plugins/engine/options.test.js +22 -11
- package/src/server/plugins/engine/outputFormatters/human/v1.test.ts +3 -3
- package/src/server/plugins/engine/pageControllers/FileUploadPageController.test.ts +12 -1
- package/src/server/plugins/engine/pageControllers/PageController.test.ts +9 -0
- package/src/server/plugins/engine/pageControllers/PageController.ts +10 -1
- package/src/server/plugins/engine/pageControllers/QuestionPageController.test.ts +34 -28
- package/src/server/plugins/engine/pageControllers/QuestionPageController.ts +2 -5
- package/src/server/plugins/engine/pageControllers/RepeatPageController.test.ts +19 -4
- package/src/server/plugins/engine/pageControllers/StartPageController.test.ts +32 -0
- package/src/server/plugins/engine/pageControllers/StartPageController.ts +2 -4
- package/src/server/plugins/engine/pageControllers/StatusPageController.test.ts +32 -0
- package/src/server/plugins/engine/pageControllers/StatusPageController.ts +1 -0
- package/src/server/plugins/engine/pageControllers/SummaryPageController.ts +1 -5
- package/src/server/plugins/engine/pageControllers/TerminalController.test.ts +9 -0
- package/src/server/plugins/engine/pageControllers/TerminalPageController.ts +1 -0
- package/src/server/plugins/engine/pageControllers/__stubs__/request.ts +21 -0
- package/src/server/plugins/engine/pageControllers/__stubs__/server.ts +27 -0
- package/src/server/plugins/engine/plugin.ts +6 -6
- package/src/server/plugins/engine/types.ts +14 -9
- package/src/server/types.ts +2 -0
- package/src/typings/hapi/index.d.ts +2 -0
|
@@ -6,13 +6,14 @@ import {
|
|
|
6
6
|
|
|
7
7
|
import { todayAsDateOnly } from '~/src/server/plugins/engine/date-helper.js'
|
|
8
8
|
import { FormModel } from '~/src/server/plugins/engine/models/FormModel.js'
|
|
9
|
+
import { buildFormContextRequest } from '~/src/server/plugins/engine/pageControllers/__stubs__/request.js'
|
|
9
10
|
import { type FormContextRequest } from '~/src/server/plugins/engine/types.js'
|
|
10
11
|
import { V2 as definitionV2 } from '~/test/form/definitions/conditions-basic.js'
|
|
11
12
|
import definition from '~/test/form/definitions/conditions-escaping.js'
|
|
12
13
|
import conditionsListDefinition from '~/test/form/definitions/conditions-list.js'
|
|
13
14
|
import relativeDatesDefinition from '~/test/form/definitions/conditions-relative-dates-v2.js'
|
|
14
15
|
import fieldsRequiredDefinition from '~/test/form/definitions/fields-required.js'
|
|
15
|
-
import joinedConditionsDefinition from '~/test/form/definitions/joined-conditions-
|
|
16
|
+
import joinedConditionsDefinition from '~/test/form/definitions/joined-conditions-simple-v2.js'
|
|
16
17
|
|
|
17
18
|
jest.mock('~/src/server/plugins/engine/date-helper.ts')
|
|
18
19
|
|
|
@@ -151,7 +152,7 @@ describe('FormModel', () => {
|
|
|
151
152
|
}
|
|
152
153
|
const pageUrl = new URL('http://example.com/components/fields-required')
|
|
153
154
|
|
|
154
|
-
const request: FormContextRequest = {
|
|
155
|
+
const request: FormContextRequest = buildFormContextRequest({
|
|
155
156
|
method: 'post',
|
|
156
157
|
payload: { crumb: 'dummyCrumb', action: 'validate' },
|
|
157
158
|
query: {},
|
|
@@ -159,7 +160,7 @@ describe('FormModel', () => {
|
|
|
159
160
|
params: { path: 'components', slug: 'fields-required' },
|
|
160
161
|
url: pageUrl,
|
|
161
162
|
app: { model: formModel }
|
|
162
|
-
}
|
|
163
|
+
})
|
|
163
164
|
|
|
164
165
|
const context = formModel.getFormContext(request, state)
|
|
165
166
|
|
|
@@ -180,7 +181,7 @@ describe('FormModel', () => {
|
|
|
180
181
|
}
|
|
181
182
|
const pageUrl = new URL('http://example.com/components/fields-required')
|
|
182
183
|
|
|
183
|
-
const request: FormContextRequest = {
|
|
184
|
+
const request: FormContextRequest = buildFormContextRequest({
|
|
184
185
|
method: 'post',
|
|
185
186
|
payload: { crumb: 'dummyCrumb', action: 'validate' },
|
|
186
187
|
query: {},
|
|
@@ -188,7 +189,7 @@ describe('FormModel', () => {
|
|
|
188
189
|
params: { path: 'components', slug: 'fields-required' },
|
|
189
190
|
url: pageUrl,
|
|
190
191
|
app: { model: formModel }
|
|
191
|
-
}
|
|
192
|
+
})
|
|
192
193
|
|
|
193
194
|
expect(() => formModel.getFormContext(request, state)).toThrow(
|
|
194
195
|
'Reference number not found in form state'
|
|
@@ -206,7 +207,7 @@ describe('FormModel', () => {
|
|
|
206
207
|
}
|
|
207
208
|
const pageUrl = new URL('http://example.com/components/fields-required')
|
|
208
209
|
|
|
209
|
-
const request: FormContextRequest = {
|
|
210
|
+
const request: FormContextRequest = buildFormContextRequest({
|
|
210
211
|
method: 'post',
|
|
211
212
|
payload: { crumb: 'dummyCrumb', action: 'validate' },
|
|
212
213
|
query: {},
|
|
@@ -214,7 +215,7 @@ describe('FormModel', () => {
|
|
|
214
215
|
params: { path: 'components', slug: 'fields-required' },
|
|
215
216
|
url: pageUrl,
|
|
216
217
|
app: { model: formModel }
|
|
217
|
-
}
|
|
218
|
+
})
|
|
218
219
|
|
|
219
220
|
expect(() => formModel.getFormContext(request, state)).toThrow(
|
|
220
221
|
'Reference number not found in form state'
|
|
@@ -236,14 +237,14 @@ describe('FormModel', () => {
|
|
|
236
237
|
'http://example.com/conditional-list-items/summary'
|
|
237
238
|
)
|
|
238
239
|
|
|
239
|
-
const request: FormContextRequest = {
|
|
240
|
+
const request: FormContextRequest = buildFormContextRequest({
|
|
240
241
|
method: 'get',
|
|
241
242
|
query: {},
|
|
242
243
|
path: pageUrl.pathname,
|
|
243
244
|
params: { path: 'summary', slug: 'conditional-list-items' },
|
|
244
245
|
url: pageUrl,
|
|
245
246
|
app: { model: formModel }
|
|
246
|
-
}
|
|
247
|
+
})
|
|
247
248
|
|
|
248
249
|
const context = formModel.getFormContext(request, state)
|
|
249
250
|
|
|
@@ -271,14 +272,14 @@ describe('FormModel', () => {
|
|
|
271
272
|
'http://example.com/conditional-list-items/summary'
|
|
272
273
|
)
|
|
273
274
|
|
|
274
|
-
const request: FormContextRequest = {
|
|
275
|
+
const request: FormContextRequest = buildFormContextRequest({
|
|
275
276
|
method: 'get',
|
|
276
277
|
query: {},
|
|
277
278
|
path: pageUrl.pathname,
|
|
278
279
|
params: { path: 'summary', slug: 'conditional-list-items' },
|
|
279
280
|
url: pageUrl,
|
|
280
281
|
app: { model: formModel }
|
|
281
|
-
}
|
|
282
|
+
})
|
|
282
283
|
|
|
283
284
|
const context = formModel.getFormContext(request, state)
|
|
284
285
|
|
|
@@ -357,15 +358,15 @@ describe('FormModel - Joined Conditions', () => {
|
|
|
357
358
|
const joinedCondition =
|
|
358
359
|
model.conditions['db43c6bc-9ce6-478b-8345-4fff5eff2ba3']
|
|
359
360
|
expect(joinedCondition).toBeDefined()
|
|
360
|
-
expect(joinedCondition?.displayName).toBe('
|
|
361
|
+
expect(joinedCondition?.displayName).toBe('is Bob AND over 18')
|
|
361
362
|
|
|
362
|
-
const stateAllTrue = {
|
|
363
|
+
const stateAllTrue = { userName: 'Bob', isOverEighteen: true }
|
|
363
364
|
expect(joinedCondition?.fn(stateAllTrue)).toBe(true)
|
|
364
365
|
|
|
365
|
-
const statePartialTrue = {
|
|
366
|
+
const statePartialTrue = { userName: 'Alice', isOverEighteen: true }
|
|
366
367
|
expect(joinedCondition?.fn(statePartialTrue)).toBe(false)
|
|
367
368
|
|
|
368
|
-
const stateFalse = {
|
|
369
|
+
const stateFalse = { userName: 'Alice', isOverEighteen: false }
|
|
369
370
|
expect(joinedCondition?.fn(stateFalse)).toBe(false)
|
|
370
371
|
})
|
|
371
372
|
|
|
@@ -379,18 +380,92 @@ describe('FormModel - Joined Conditions', () => {
|
|
|
379
380
|
})
|
|
380
381
|
|
|
381
382
|
const joinedConditionPage = model.pages.find(
|
|
382
|
-
(page) => page.path === '/
|
|
383
|
+
(page) => page.path === '/simple-and-page'
|
|
383
384
|
)
|
|
384
385
|
|
|
385
386
|
expect(joinedConditionPage?.condition).toBeDefined()
|
|
386
387
|
|
|
387
|
-
const trueState = {
|
|
388
|
+
const trueState = { userName: 'Bob', isOverEighteen: true }
|
|
388
389
|
expect(joinedConditionPage?.condition?.fn(trueState)).toBe(true)
|
|
389
390
|
|
|
390
|
-
const falseState = {
|
|
391
|
+
const falseState = { userName: 'Bob', isOverEighteen: false }
|
|
391
392
|
expect(joinedConditionPage?.condition?.fn(falseState)).toBe(false)
|
|
392
393
|
})
|
|
393
394
|
|
|
395
|
+
it('should handle V1 joined conditions without aliases', () => {
|
|
396
|
+
formDefinitionV2Schema.validate = jest
|
|
397
|
+
.fn()
|
|
398
|
+
.mockReturnValue({ value: definition })
|
|
399
|
+
|
|
400
|
+
const model = new FormModel(definition, {
|
|
401
|
+
basePath: 'test'
|
|
402
|
+
})
|
|
403
|
+
|
|
404
|
+
expect(model.conditions).toBeDefined()
|
|
405
|
+
expect(Object.keys(model.conditions)).toHaveLength(1)
|
|
406
|
+
|
|
407
|
+
const joinedCondition = model.conditions.ZCXeMz
|
|
408
|
+
expect(joinedCondition).toBeDefined()
|
|
409
|
+
expect(joinedCondition?.displayName).toBe('test')
|
|
410
|
+
|
|
411
|
+
const testState = { NIJphU: "ap'ostrophe's", iraEpG: "shouldn't've" }
|
|
412
|
+
expect(joinedCondition?.fn(testState)).toBe(true)
|
|
413
|
+
|
|
414
|
+
const testStateFalse = { NIJphU: 'other', iraEpG: "shouldn't've" }
|
|
415
|
+
expect(joinedCondition?.fn(testStateFalse)).toBe(false)
|
|
416
|
+
|
|
417
|
+
const context = model.toConditionContext(testState, model.conditions)
|
|
418
|
+
|
|
419
|
+
expect(context).not.toHaveProperty('cond_ZCXeMz')
|
|
420
|
+
|
|
421
|
+
expect(context).toHaveProperty('ZCXeMz')
|
|
422
|
+
|
|
423
|
+
expect(context).toHaveProperty('NIJphU', "ap'ostrophe's")
|
|
424
|
+
expect(context).toHaveProperty('iraEpG', "shouldn't've")
|
|
425
|
+
})
|
|
426
|
+
|
|
427
|
+
it('should use schema version to determine condition aliases', () => {
|
|
428
|
+
const v1Definition = { ...definition, schema: SchemaVersion.V1 }
|
|
429
|
+
formDefinitionV2Schema.validate = jest
|
|
430
|
+
.fn()
|
|
431
|
+
.mockReturnValue({ value: v1Definition })
|
|
432
|
+
|
|
433
|
+
const v1Model = new FormModel(v1Definition, { basePath: 'test' })
|
|
434
|
+
expect(v1Model.schemaVersion).toBe(SchemaVersion.V1)
|
|
435
|
+
|
|
436
|
+
const v1TestState = { NIJphU: "ap'ostrophe's", iraEpG: "shouldn't've" }
|
|
437
|
+
const v1Context = v1Model.toConditionContext(
|
|
438
|
+
v1TestState,
|
|
439
|
+
v1Model.conditions
|
|
440
|
+
)
|
|
441
|
+
|
|
442
|
+
expect(v1Context).toHaveProperty('ZCXeMz')
|
|
443
|
+
expect(v1Context).not.toHaveProperty('cond_ZCXeMz')
|
|
444
|
+
|
|
445
|
+
formDefinitionV2Schema.validate = jest
|
|
446
|
+
.fn()
|
|
447
|
+
.mockReturnValue({ value: joinedConditionsDefinition })
|
|
448
|
+
|
|
449
|
+
const v2Model = new FormModel(joinedConditionsDefinition, {
|
|
450
|
+
basePath: 'test'
|
|
451
|
+
})
|
|
452
|
+
expect(v2Model.schemaVersion).toBe(SchemaVersion.V2)
|
|
453
|
+
|
|
454
|
+
const v2TestState = { userName: 'Bob', isOverEighteen: true }
|
|
455
|
+
const v2Context = v2Model.toConditionContext(
|
|
456
|
+
v2TestState,
|
|
457
|
+
v2Model.conditions
|
|
458
|
+
)
|
|
459
|
+
|
|
460
|
+
expect(v2Context).toHaveProperty('cond_d15aff7a622440a28e5f51a5af2f7910')
|
|
461
|
+
expect(v2Context).toHaveProperty('cond_d1f9fcc7f09847e79d314f5ee57ba985')
|
|
462
|
+
expect(v2Context).toHaveProperty('cond_db43c6bc9ce6478b83454fff5eff2ba3')
|
|
463
|
+
|
|
464
|
+
expect(v2Context).not.toHaveProperty('d15aff7a-6224-40a2-8e5f-51a5af2f7910')
|
|
465
|
+
expect(v2Context).not.toHaveProperty('d1f9fcc7-f098-47e7-9d31-4f5ee57ba985')
|
|
466
|
+
expect(v2Context).not.toHaveProperty('db43c6bc-9ce6-478b-8345-4fff5eff2ba3')
|
|
467
|
+
})
|
|
468
|
+
|
|
394
469
|
describe('generateConditionAlias', () => {
|
|
395
470
|
it('should generate valid JavaScript identifiers from condition IDs', () => {
|
|
396
471
|
formDefinitionV2Schema.validate = jest
|
|
@@ -401,7 +476,7 @@ describe('FormModel - Joined Conditions', () => {
|
|
|
401
476
|
basePath: 'test'
|
|
402
477
|
})
|
|
403
478
|
|
|
404
|
-
const evaluationState = {
|
|
479
|
+
const evaluationState = { userName: 'Bob', isOverEighteen: true }
|
|
405
480
|
|
|
406
481
|
const context = model.toConditionContext(
|
|
407
482
|
evaluationState,
|
|
@@ -428,8 +503,8 @@ describe('FormModel - Joined Conditions', () => {
|
|
|
428
503
|
model.conditions['db43c6bc-9ce6-478b-8345-4fff5eff2ba3']
|
|
429
504
|
expect(joinedCondition).toBeDefined()
|
|
430
505
|
|
|
431
|
-
const stateTrue = {
|
|
432
|
-
const stateFalse = {
|
|
506
|
+
const stateTrue = { userName: 'Bob', isOverEighteen: true }
|
|
507
|
+
const stateFalse = { userName: 'Alice', isOverEighteen: false }
|
|
433
508
|
|
|
434
509
|
expect(joinedCondition?.fn(stateTrue)).toBe(true)
|
|
435
510
|
expect(joinedCondition?.fn(stateFalse)).toBe(false)
|
|
@@ -278,9 +278,12 @@ export class FormModel {
|
|
|
278
278
|
const context = { ...evaluationState }
|
|
279
279
|
|
|
280
280
|
for (const conditionId in conditions) {
|
|
281
|
-
const
|
|
281
|
+
const propertyName =
|
|
282
|
+
this.schemaVersion === SchemaVersion.V2
|
|
283
|
+
? generateConditionAlias(conditionId)
|
|
284
|
+
: conditionId
|
|
282
285
|
|
|
283
|
-
Object.defineProperty(context,
|
|
286
|
+
Object.defineProperty(context, propertyName, {
|
|
284
287
|
get() {
|
|
285
288
|
return conditions[conditionId]?.fn(evaluationState)
|
|
286
289
|
}
|
|
@@ -4,6 +4,8 @@ import {
|
|
|
4
4
|
SummaryViewModel
|
|
5
5
|
} from '~/src/server/plugins/engine/models/index.js'
|
|
6
6
|
import { SummaryPageController } from '~/src/server/plugins/engine/pageControllers/SummaryPageController.js'
|
|
7
|
+
import { buildFormContextRequest } from '~/src/server/plugins/engine/pageControllers/__stubs__/request.js'
|
|
8
|
+
import { serverWithSaveAndReturn } from '~/src/server/plugins/engine/pageControllers/__stubs__/server.js'
|
|
7
9
|
import {
|
|
8
10
|
createPage,
|
|
9
11
|
type PageControllerClass
|
|
@@ -14,7 +16,6 @@ import {
|
|
|
14
16
|
type FormState
|
|
15
17
|
} from '~/src/server/plugins/engine/types.js'
|
|
16
18
|
import definition from '~/test/form/definitions/repeat-mixed.js'
|
|
17
|
-
|
|
18
19
|
const basePath = `${FORM_PREFIX}/test`
|
|
19
20
|
|
|
20
21
|
describe('SummaryViewModel', () => {
|
|
@@ -36,7 +37,7 @@ describe('SummaryViewModel', () => {
|
|
|
36
37
|
page = createPage(model, definition.pages[2])
|
|
37
38
|
pageUrl = new URL('http://example.com/repeat/pizza-order/summary')
|
|
38
39
|
|
|
39
|
-
request = {
|
|
40
|
+
request = buildFormContextRequest({
|
|
40
41
|
method: 'get',
|
|
41
42
|
url: pageUrl,
|
|
42
43
|
path: pageUrl.pathname,
|
|
@@ -46,7 +47,7 @@ describe('SummaryViewModel', () => {
|
|
|
46
47
|
},
|
|
47
48
|
query: {},
|
|
48
49
|
app: { model }
|
|
49
|
-
}
|
|
50
|
+
})
|
|
50
51
|
})
|
|
51
52
|
|
|
52
53
|
describe.each([
|
|
@@ -272,13 +273,14 @@ describe('SummaryPageController', () => {
|
|
|
272
273
|
slug: 'repeat'
|
|
273
274
|
},
|
|
274
275
|
query: {},
|
|
275
|
-
app: { model }
|
|
276
|
+
app: { model },
|
|
277
|
+
server: serverWithSaveAndReturn
|
|
276
278
|
}
|
|
277
279
|
})
|
|
278
280
|
|
|
279
281
|
describe('Save and Return functionality', () => {
|
|
280
282
|
it('should show save and return button on summary page', () => {
|
|
281
|
-
expect(controller.shouldShowSaveAndReturn()).toBe(true)
|
|
283
|
+
expect(controller.shouldShowSaveAndReturn(request.server)).toBe(true)
|
|
282
284
|
})
|
|
283
285
|
|
|
284
286
|
it('should handle save and return from summary page', () => {
|
|
@@ -20,9 +20,11 @@ const pluginRegistrationOptionsSchema = Joi.object({
|
|
|
20
20
|
preparePageEventRequestOptions: Joi.function().optional(),
|
|
21
21
|
onRequest: Joi.function().optional(),
|
|
22
22
|
baseUrl: Joi.string().uri().required(),
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
23
|
+
saveAndReturn: Joi.object({
|
|
24
|
+
keyGenerator: Joi.function(),
|
|
25
|
+
sessionHydrator: Joi.function(),
|
|
26
|
+
sessionPersister: Joi.function()
|
|
27
|
+
}).optional()
|
|
26
28
|
})
|
|
27
29
|
|
|
28
30
|
/**
|
|
@@ -2,6 +2,9 @@ import { validatePluginOptions } from '~/src/server/plugins/engine/options.js'
|
|
|
2
2
|
|
|
3
3
|
describe('validatePluginOptions', () => {
|
|
4
4
|
it('returns the validated value for valid options', () => {
|
|
5
|
+
/**
|
|
6
|
+
* @type {PluginOptions}
|
|
7
|
+
*/
|
|
5
8
|
const validOptions = {
|
|
6
9
|
nunjucks: {
|
|
7
10
|
baseLayoutPath: 'dxt-devtool-baselayout.html',
|
|
@@ -17,6 +20,9 @@ describe('validatePluginOptions', () => {
|
|
|
17
20
|
})
|
|
18
21
|
|
|
19
22
|
it('accepts optional properties keyGenerator, sessionHydrator, and sessionPersister', () => {
|
|
23
|
+
/**
|
|
24
|
+
* @type {PluginOptions}
|
|
25
|
+
*/
|
|
20
26
|
const validOptionsWithOptionals = {
|
|
21
27
|
nunjucks: {
|
|
22
28
|
baseLayoutPath: 'dxt-devtool-baselayout.html',
|
|
@@ -26,9 +32,11 @@ describe('validatePluginOptions', () => {
|
|
|
26
32
|
return { hello: 'world' }
|
|
27
33
|
},
|
|
28
34
|
baseUrl: 'http://localhost:3009',
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
35
|
+
saveAndReturn: {
|
|
36
|
+
keyGenerator: () => 'test-key',
|
|
37
|
+
sessionHydrator: () => Promise.resolve({ someState: 'value' }),
|
|
38
|
+
sessionPersister: () => Promise.resolve(undefined)
|
|
39
|
+
}
|
|
32
40
|
}
|
|
33
41
|
|
|
34
42
|
expect(validatePluginOptions(validOptionsWithOptionals)).toEqual(
|
|
@@ -40,17 +48,20 @@ describe('validatePluginOptions', () => {
|
|
|
40
48
|
* tsc would usually check compliance with the type, but given a user might be using plain JS we still want a test
|
|
41
49
|
*/
|
|
42
50
|
it('fails if a required attribute is missing', () => {
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
51
|
+
const invalidOptions =
|
|
52
|
+
/** @type {PluginOptions} testing without viewContext */ ({
|
|
53
|
+
nunjucks: {
|
|
54
|
+
baseLayoutPath: 'dxt-devtool-baselayout.html',
|
|
55
|
+
paths: ['src/server/devserver'] // custom layout to make it really clear this is not the same as the runner
|
|
56
|
+
}
|
|
57
|
+
})
|
|
50
58
|
|
|
51
|
-
// @ts-expect-error -- add a test for JS users
|
|
52
59
|
expect(() => validatePluginOptions(invalidOptions)).toThrow(
|
|
53
60
|
'Invalid plugin options'
|
|
54
61
|
)
|
|
55
62
|
})
|
|
56
63
|
})
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* @import { PluginOptions } from '~/src/server/plugins/engine/types.js'
|
|
67
|
+
*/
|
|
@@ -7,7 +7,7 @@ import {
|
|
|
7
7
|
SummaryPageController,
|
|
8
8
|
getFormSubmissionData
|
|
9
9
|
} from '~/src/server/plugins/engine/pageControllers/SummaryPageController.js'
|
|
10
|
-
import {
|
|
10
|
+
import { buildFormContextRequest } from '~/src/server/plugins/engine/pageControllers/__stubs__/request.js'
|
|
11
11
|
import { FormStatus } from '~/src/server/routes/types.js'
|
|
12
12
|
import definition from '~/test/form/definitions/repeat-mixed.js'
|
|
13
13
|
|
|
@@ -52,7 +52,7 @@ const pageUrl = new URL('http://example.com/repeat/pizza-order/summary')
|
|
|
52
52
|
|
|
53
53
|
const controller = new SummaryPageController(model, pageDef)
|
|
54
54
|
|
|
55
|
-
const request = {
|
|
55
|
+
const request = buildFormContextRequest({
|
|
56
56
|
method: 'get',
|
|
57
57
|
url: pageUrl,
|
|
58
58
|
path: pageUrl.pathname,
|
|
@@ -62,7 +62,7 @@ const request = {
|
|
|
62
62
|
},
|
|
63
63
|
query: {},
|
|
64
64
|
app: { model }
|
|
65
|
-
}
|
|
65
|
+
})
|
|
66
66
|
|
|
67
67
|
const context = model.getFormContext(request, state)
|
|
68
68
|
const summaryViewModel = controller.getSummaryViewModel(request, context)
|
|
@@ -14,6 +14,7 @@ import {
|
|
|
14
14
|
prepareStatus
|
|
15
15
|
} from '~/src/server/plugins/engine/pageControllers/FileUploadPageController.js'
|
|
16
16
|
import { QuestionPageController } from '~/src/server/plugins/engine/pageControllers/QuestionPageController.js'
|
|
17
|
+
import { serverWithSaveAndReturn } from '~/src/server/plugins/engine/pageControllers/__stubs__/server.js'
|
|
17
18
|
import * as pageHelpers from '~/src/server/plugins/engine/pageControllers/helpers.js'
|
|
18
19
|
import * as uploadService from '~/src/server/plugins/engine/services/uploadService.js'
|
|
19
20
|
import {
|
|
@@ -31,6 +32,7 @@ import {
|
|
|
31
32
|
type FormRequest,
|
|
32
33
|
type FormRequestPayload
|
|
33
34
|
} from '~/src/server/routes/types.js'
|
|
35
|
+
import { type CacheService } from '~/src/server/services/index.js'
|
|
34
36
|
import definition from '~/test/form/definitions/file-upload-basic.js'
|
|
35
37
|
|
|
36
38
|
type TestableFileUploadPageController = FileUploadPageController & {
|
|
@@ -77,12 +79,13 @@ describe('FileUploadPageController', () => {
|
|
|
77
79
|
server: {
|
|
78
80
|
plugins: {
|
|
79
81
|
'forms-engine-plugin': {
|
|
82
|
+
baseLayoutPath: '',
|
|
80
83
|
cacheService: {
|
|
81
84
|
setFlash: jest.fn(),
|
|
82
85
|
setState: jest
|
|
83
86
|
.fn()
|
|
84
87
|
.mockImplementation((req, updated) => Promise.resolve(updated))
|
|
85
|
-
}
|
|
88
|
+
} as unknown as CacheService
|
|
86
89
|
}
|
|
87
90
|
}
|
|
88
91
|
},
|
|
@@ -1115,4 +1118,12 @@ describe('FileUploadPageController', () => {
|
|
|
1115
1118
|
)
|
|
1116
1119
|
})
|
|
1117
1120
|
})
|
|
1121
|
+
|
|
1122
|
+
describe('shouldShowSaveAndReturn', () => {
|
|
1123
|
+
it('should return true when save and return is enabled', () => {
|
|
1124
|
+
expect(controller.shouldShowSaveAndReturn(serverWithSaveAndReturn)).toBe(
|
|
1125
|
+
true
|
|
1126
|
+
)
|
|
1127
|
+
})
|
|
1128
|
+
})
|
|
1118
1129
|
})
|
|
@@ -3,6 +3,7 @@ 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'
|
|
6
7
|
import { type FormRequest } from '~/src/server/routes/types.js'
|
|
7
8
|
import definition from '~/test/form/definitions/basic.js'
|
|
8
9
|
|
|
@@ -230,4 +231,12 @@ describe('PageController', () => {
|
|
|
230
231
|
)
|
|
231
232
|
})
|
|
232
233
|
})
|
|
234
|
+
|
|
235
|
+
describe('shouldShowSaveAndReturn', () => {
|
|
236
|
+
it('should return false (PageController does not allow save and return)', () => {
|
|
237
|
+
expect(controller1.shouldShowSaveAndReturn(serverWithSaveAndReturn)).toBe(
|
|
238
|
+
false
|
|
239
|
+
)
|
|
240
|
+
})
|
|
241
|
+
})
|
|
233
242
|
})
|
|
@@ -9,12 +9,14 @@ import Boom from '@hapi/boom'
|
|
|
9
9
|
import {
|
|
10
10
|
type Lifecycle,
|
|
11
11
|
type ResponseToolkit,
|
|
12
|
-
type RouteOptions
|
|
12
|
+
type RouteOptions,
|
|
13
|
+
type Server
|
|
13
14
|
} from '@hapi/hapi'
|
|
14
15
|
|
|
15
16
|
import { type ComponentCollection } from '~/src/server/plugins/engine/components/ComponentCollection.js'
|
|
16
17
|
import {
|
|
17
18
|
encodeUrl,
|
|
19
|
+
getSaveAndReturnHelpers,
|
|
18
20
|
getStartPath,
|
|
19
21
|
normalisePath
|
|
20
22
|
} from '~/src/server/plugins/engine/helpers.js'
|
|
@@ -45,6 +47,7 @@ export class PageController {
|
|
|
45
47
|
events?: Events
|
|
46
48
|
collection?: ComponentCollection
|
|
47
49
|
viewName = 'index'
|
|
50
|
+
allowSaveAndReturn = false
|
|
48
51
|
|
|
49
52
|
constructor(model: FormModel, pageDef: Page) {
|
|
50
53
|
const { def } = model
|
|
@@ -183,4 +186,10 @@ export class PageController {
|
|
|
183
186
|
) => ReturnType<Lifecycle.Method<FormRequestPayloadRefs>> {
|
|
184
187
|
throw Boom.badRequest('Unsupported POST route handler for this page')
|
|
185
188
|
}
|
|
189
|
+
|
|
190
|
+
shouldShowSaveAndReturn(server: Server): boolean {
|
|
191
|
+
return (
|
|
192
|
+
getSaveAndReturnHelpers(server) !== undefined && this.allowSaveAndReturn
|
|
193
|
+
)
|
|
194
|
+
}
|
|
186
195
|
}
|