@defra/forms-engine-plugin 2.1.10 → 3.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.server/server/index.js +2 -1
- package/.server/server/index.js.map +1 -1
- package/.server/server/plugins/engine/README.md +2 -2
- package/.server/server/plugins/engine/configureEnginePlugin.d.ts +2 -1
- package/.server/server/plugins/engine/configureEnginePlugin.js +4 -4
- package/.server/server/plugins/engine/configureEnginePlugin.js.map +1 -1
- package/.server/server/plugins/engine/helpers.d.ts +7 -11
- package/.server/server/plugins/engine/helpers.js +2 -2
- package/.server/server/plugins/engine/helpers.js.map +1 -1
- package/.server/server/plugins/engine/models/FormModel.d.ts +2 -0
- package/.server/server/plugins/engine/models/FormModel.js +5 -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 +3 -6
- package/.server/server/plugins/engine/options.js.map +1 -1
- package/.server/server/plugins/engine/options.test.js +2 -8
- package/.server/server/plugins/engine/options.test.js.map +1 -1
- package/.server/server/plugins/engine/outputFormatters/adapter/v1.d.ts +4 -0
- package/.server/server/plugins/engine/outputFormatters/adapter/v1.js +25 -0
- package/.server/server/plugins/engine/outputFormatters/adapter/v1.js.map +1 -1
- package/.server/server/plugins/engine/outputFormatters/machine/v2.js +7 -6
- package/.server/server/plugins/engine/outputFormatters/machine/v2.js.map +1 -1
- package/.server/server/plugins/engine/pageControllers/FileUploadPageController.d.ts +5 -6
- package/.server/server/plugins/engine/pageControllers/FileUploadPageController.js.map +1 -1
- package/.server/server/plugins/engine/pageControllers/PageController.d.ts +6 -6
- package/.server/server/plugins/engine/pageControllers/PageController.js +4 -4
- package/.server/server/plugins/engine/pageControllers/PageController.js.map +1 -1
- package/.server/server/plugins/engine/pageControllers/QuestionPageController.d.ts +13 -13
- package/.server/server/plugins/engine/pageControllers/QuestionPageController.js +12 -20
- package/.server/server/plugins/engine/pageControllers/QuestionPageController.js.map +1 -1
- package/.server/server/plugins/engine/pageControllers/RepeatPageController.d.ts +7 -8
- package/.server/server/plugins/engine/pageControllers/RepeatPageController.js.map +1 -1
- package/.server/server/plugins/engine/pageControllers/StartPageController.d.ts +2 -2
- package/.server/server/plugins/engine/pageControllers/StartPageController.js +1 -1
- package/.server/server/plugins/engine/pageControllers/StartPageController.js.map +1 -1
- package/.server/server/plugins/engine/pageControllers/StatusPageController.d.ts +3 -4
- package/.server/server/plugins/engine/pageControllers/StatusPageController.js +1 -1
- package/.server/server/plugins/engine/pageControllers/StatusPageController.js.map +1 -1
- package/.server/server/plugins/engine/pageControllers/SummaryPageController.d.ts +5 -4
- package/.server/server/plugins/engine/pageControllers/SummaryPageController.js +12 -1
- package/.server/server/plugins/engine/pageControllers/SummaryPageController.js.map +1 -1
- package/.server/server/plugins/engine/pageControllers/TerminalPageController.d.ts +4 -4
- package/.server/server/plugins/engine/pageControllers/TerminalPageController.js +1 -1
- package/.server/server/plugins/engine/pageControllers/TerminalPageController.js.map +1 -1
- package/.server/server/plugins/engine/pageControllers/__stubs__/server.d.ts +1 -1
- package/.server/server/plugins/engine/pageControllers/__stubs__/server.js +2 -6
- package/.server/server/plugins/engine/pageControllers/__stubs__/server.js.map +1 -1
- package/.server/server/plugins/engine/plugin.js +7 -12
- package/.server/server/plugins/engine/plugin.js.map +1 -1
- package/.server/server/plugins/engine/routes/index.d.ts +5 -5
- package/.server/server/plugins/engine/routes/index.js +3 -1
- package/.server/server/plugins/engine/routes/index.js.map +1 -1
- package/.server/server/plugins/engine/routes/questions.d.ts +4 -4
- package/.server/server/plugins/engine/routes/questions.js.map +1 -1
- package/.server/server/plugins/engine/routes/repeaters/item-delete.js.map +1 -1
- package/.server/server/plugins/engine/routes/repeaters/summary.js.map +1 -1
- package/.server/server/plugins/engine/types/index.d.ts +2 -2
- package/.server/server/plugins/engine/types/index.js.map +1 -1
- package/.server/server/plugins/engine/types/schema.js +3 -2
- package/.server/server/plugins/engine/types/schema.js.map +1 -1
- package/.server/server/plugins/engine/types.d.ts +13 -12
- package/.server/server/plugins/engine/types.js.map +1 -1
- package/.server/server/plugins/engine/views/partials/form.html +3 -3
- package/.server/server/plugins/engine/views/summary.html +21 -5
- package/.server/server/plugins/nunjucks/context.d.ts +5 -6
- package/.server/server/plugins/nunjucks/context.js +3 -3
- package/.server/server/plugins/nunjucks/context.js.map +1 -1
- package/.server/server/routes/types.d.ts +3 -2
- package/.server/server/routes/types.js +1 -1
- package/.server/server/routes/types.js.map +1 -1
- package/.server/server/schemas/index.js +1 -1
- package/.server/server/schemas/index.js.map +1 -1
- package/.server/server/services/cacheService.d.ts +11 -19
- package/.server/server/services/cacheService.js +9 -30
- package/.server/server/services/cacheService.js.map +1 -1
- package/.server/server/types.d.ts +4 -1
- package/.server/server/types.js.map +1 -1
- package/.server/typings/hapi/index.d.js.map +1 -1
- package/package.json +4 -2
- package/src/server/index.test.ts +0 -39
- package/src/server/index.ts +4 -1
- package/src/server/plugins/engine/README.md +2 -2
- package/src/server/plugins/engine/components/helpers/helpers.test.ts +1 -1
- package/src/server/plugins/engine/configureEnginePlugin.ts +15 -11
- package/src/server/plugins/engine/helpers.test.ts +3 -2
- package/src/server/plugins/engine/helpers.ts +6 -6
- package/src/server/plugins/engine/models/FormModel.test.ts +66 -2
- package/src/server/plugins/engine/models/FormModel.ts +6 -4
- package/src/server/plugins/engine/models/SummaryViewModel.test.ts +7 -7
- package/src/server/plugins/engine/models/SummaryViewModel.ts +1 -1
- package/src/server/plugins/engine/options.js +6 -6
- package/src/server/plugins/engine/options.test.js +2 -6
- package/src/server/plugins/engine/outputFormatters/adapter/v1.test.ts +446 -13
- package/src/server/plugins/engine/outputFormatters/adapter/v1.ts +37 -0
- package/src/server/plugins/engine/outputFormatters/machine/v2.ts +8 -6
- package/src/server/plugins/engine/pageControllers/FileUploadPageController.test.ts +8 -10
- package/src/server/plugins/engine/pageControllers/FileUploadPageController.ts +9 -8
- package/src/server/plugins/engine/pageControllers/PageController.test.ts +11 -8
- package/src/server/plugins/engine/pageControllers/PageController.ts +9 -15
- package/src/server/plugins/engine/pageControllers/QuestionPageController.test.ts +35 -102
- package/src/server/plugins/engine/pageControllers/QuestionPageController.ts +24 -36
- package/src/server/plugins/engine/pageControllers/RepeatPageController.test.ts +4 -6
- package/src/server/plugins/engine/pageControllers/RepeatPageController.ts +8 -11
- package/src/server/plugins/engine/pageControllers/StartPageController.test.ts +4 -4
- package/src/server/plugins/engine/pageControllers/StartPageController.ts +1 -1
- package/src/server/plugins/engine/pageControllers/StatusPageController.test.ts +4 -4
- package/src/server/plugins/engine/pageControllers/StatusPageController.ts +6 -4
- package/src/server/plugins/engine/pageControllers/SummaryPageController.ts +15 -5
- package/src/server/plugins/engine/pageControllers/TerminalController.test.ts +4 -4
- package/src/server/plugins/engine/pageControllers/TerminalPageController.ts +7 -4
- package/src/server/plugins/engine/pageControllers/__stubs__/server.ts +5 -6
- package/src/server/plugins/engine/plugin.ts +7 -13
- package/src/server/plugins/engine/routes/index.ts +9 -12
- package/src/server/plugins/engine/routes/questions.test.ts +29 -53
- package/src/server/plugins/engine/routes/questions.ts +6 -8
- package/src/server/plugins/engine/routes/repeaters/item-delete.ts +5 -14
- package/src/server/plugins/engine/routes/repeaters/summary.ts +5 -14
- package/src/server/plugins/engine/types/index.ts +4 -1
- package/src/server/plugins/engine/types/schema.test.ts +40 -0
- package/src/server/plugins/engine/types/schema.ts +3 -1
- package/src/server/plugins/engine/types.ts +22 -13
- package/src/server/plugins/engine/views/partials/form.html +3 -3
- package/src/server/plugins/engine/views/summary.html +21 -5
- package/src/server/plugins/nunjucks/context.js +3 -3
- package/src/server/routes/types.ts +7 -2
- package/src/server/schemas/index.ts +1 -1
- package/src/server/services/cacheService.test.ts +1 -117
- package/src/server/services/cacheService.ts +22 -73
- package/src/server/types.ts +4 -1
- package/src/typings/hapi/index.d.ts +6 -7
- package/.server/server/plugins/engine/routes/exit.d.ts +0 -46
- package/.server/server/plugins/engine/routes/exit.js +0 -36
- package/.server/server/plugins/engine/routes/exit.js.map +0 -1
- package/src/server/plugins/engine/routes/exit.ts +0 -47
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { ComponentType, type PageFileUpload } from '@defra/forms-model'
|
|
2
2
|
import Boom from '@hapi/boom'
|
|
3
|
-
import { type ResponseToolkit } from '@hapi/hapi'
|
|
4
3
|
import { wait } from '@hapi/hoek'
|
|
5
4
|
import { type ValidationErrorItem } from 'joi'
|
|
6
5
|
|
|
@@ -23,6 +22,7 @@ import {
|
|
|
23
22
|
import {
|
|
24
23
|
FileStatus,
|
|
25
24
|
UploadStatus,
|
|
25
|
+
type AnyFormRequest,
|
|
26
26
|
type FeaturedFormPageViewModel,
|
|
27
27
|
type FileState,
|
|
28
28
|
type FormContext,
|
|
@@ -35,7 +35,8 @@ import {
|
|
|
35
35
|
} from '~/src/server/plugins/engine/types.js'
|
|
36
36
|
import {
|
|
37
37
|
type FormRequest,
|
|
38
|
-
type FormRequestPayload
|
|
38
|
+
type FormRequestPayload,
|
|
39
|
+
type FormResponseToolkit
|
|
39
40
|
} from '~/src/server/routes/types.js'
|
|
40
41
|
|
|
41
42
|
const MAX_UPLOADS = 25
|
|
@@ -111,7 +112,7 @@ export class FileUploadPageController extends QuestionPageController {
|
|
|
111
112
|
return payload
|
|
112
113
|
}
|
|
113
114
|
|
|
114
|
-
async getState(request:
|
|
115
|
+
async getState(request: AnyFormRequest) {
|
|
115
116
|
const { fileUpload } = this
|
|
116
117
|
|
|
117
118
|
// Get the actual state
|
|
@@ -148,7 +149,7 @@ export class FileUploadPageController extends QuestionPageController {
|
|
|
148
149
|
return (
|
|
149
150
|
request: FormRequest,
|
|
150
151
|
context: FormContext,
|
|
151
|
-
h:
|
|
152
|
+
h: FormResponseToolkit
|
|
152
153
|
) => {
|
|
153
154
|
const { viewModel } = this
|
|
154
155
|
const { params } = request
|
|
@@ -183,7 +184,7 @@ export class FileUploadPageController extends QuestionPageController {
|
|
|
183
184
|
return async (
|
|
184
185
|
request: FormRequestPayload,
|
|
185
186
|
context: FormContext,
|
|
186
|
-
h:
|
|
187
|
+
h: FormResponseToolkit
|
|
187
188
|
) => {
|
|
188
189
|
const { path } = this
|
|
189
190
|
const { state } = context
|
|
@@ -279,7 +280,7 @@ export class FileUploadPageController extends QuestionPageController {
|
|
|
279
280
|
* @param state - the form state
|
|
280
281
|
*/
|
|
281
282
|
private async refreshUpload(
|
|
282
|
-
request:
|
|
283
|
+
request: AnyFormRequest,
|
|
283
284
|
state: FormSubmissionState
|
|
284
285
|
) {
|
|
285
286
|
state = await this.checkUploadStatus(request, state)
|
|
@@ -295,7 +296,7 @@ export class FileUploadPageController extends QuestionPageController {
|
|
|
295
296
|
* @param depth - the number of retries so far
|
|
296
297
|
*/
|
|
297
298
|
private async checkUploadStatus(
|
|
298
|
-
request:
|
|
299
|
+
request: AnyFormRequest,
|
|
299
300
|
state: FormSubmissionState,
|
|
300
301
|
depth = 1
|
|
301
302
|
): Promise<FormSubmissionState> {
|
|
@@ -417,7 +418,7 @@ export class FileUploadPageController extends QuestionPageController {
|
|
|
417
418
|
* @param state - the form state
|
|
418
419
|
*/
|
|
419
420
|
private async initiateAndStoreNewUpload(
|
|
420
|
-
request:
|
|
421
|
+
request: AnyFormRequest,
|
|
421
422
|
state: FormSubmissionState
|
|
422
423
|
) {
|
|
423
424
|
const { fileUpload, href, path } = this
|
|
@@ -3,8 +3,11 @@ import { type ResponseToolkit } from '@hapi/hapi'
|
|
|
3
3
|
import { FORM_PREFIX } from '~/src/server/constants.js'
|
|
4
4
|
import { FormModel } from '~/src/server/plugins/engine/models/FormModel.js'
|
|
5
5
|
import { PageController } from '~/src/server/plugins/engine/pageControllers/PageController.js'
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
6
|
+
import { serverWithSaveAndExit } from '~/src/server/plugins/engine/pageControllers/__stubs__/server.js'
|
|
7
|
+
import {
|
|
8
|
+
type FormRequest,
|
|
9
|
+
type FormResponseToolkit
|
|
10
|
+
} from '~/src/server/routes/types.js'
|
|
8
11
|
import definition from '~/test/form/definitions/basic.js'
|
|
9
12
|
|
|
10
13
|
describe('PageController', () => {
|
|
@@ -155,7 +158,7 @@ describe('PageController', () => {
|
|
|
155
158
|
app: { model }
|
|
156
159
|
} as FormRequest
|
|
157
160
|
|
|
158
|
-
const h:
|
|
161
|
+
const h: FormResponseToolkit = {
|
|
159
162
|
redirect: jest.fn(),
|
|
160
163
|
view: jest.fn()
|
|
161
164
|
}
|
|
@@ -206,10 +209,10 @@ describe('PageController', () => {
|
|
|
206
209
|
)
|
|
207
210
|
})
|
|
208
211
|
|
|
209
|
-
it('supports save and
|
|
212
|
+
it('supports save and exit functionality', async () => {
|
|
210
213
|
const mockRequest = {
|
|
211
214
|
...request,
|
|
212
|
-
payload: {
|
|
215
|
+
payload: { saveAndExit: true }
|
|
213
216
|
} as FormRequest
|
|
214
217
|
|
|
215
218
|
const mockResponse = {
|
|
@@ -232,9 +235,9 @@ describe('PageController', () => {
|
|
|
232
235
|
})
|
|
233
236
|
})
|
|
234
237
|
|
|
235
|
-
describe('
|
|
236
|
-
it('should return false (PageController does not allow save and
|
|
237
|
-
expect(controller1.
|
|
238
|
+
describe('shouldShowSaveAndExit', () => {
|
|
239
|
+
it('should return false (PageController does not allow save and exit)', () => {
|
|
240
|
+
expect(controller1.shouldShowSaveAndExit(serverWithSaveAndExit)).toBe(
|
|
238
241
|
false
|
|
239
242
|
)
|
|
240
243
|
})
|
|
@@ -6,17 +6,12 @@ import {
|
|
|
6
6
|
type Section
|
|
7
7
|
} from '@defra/forms-model'
|
|
8
8
|
import Boom from '@hapi/boom'
|
|
9
|
-
import {
|
|
10
|
-
type Lifecycle,
|
|
11
|
-
type ResponseToolkit,
|
|
12
|
-
type RouteOptions,
|
|
13
|
-
type Server
|
|
14
|
-
} from '@hapi/hapi'
|
|
9
|
+
import { type Lifecycle, type RouteOptions, type Server } from '@hapi/hapi'
|
|
15
10
|
|
|
16
11
|
import { type ComponentCollection } from '~/src/server/plugins/engine/components/ComponentCollection.js'
|
|
17
12
|
import {
|
|
18
13
|
encodeUrl,
|
|
19
|
-
|
|
14
|
+
getSaveAndExitHelpers,
|
|
20
15
|
getStartPath,
|
|
21
16
|
normalisePath
|
|
22
17
|
} from '~/src/server/plugins/engine/helpers.js'
|
|
@@ -30,7 +25,8 @@ import {
|
|
|
30
25
|
type FormRequest,
|
|
31
26
|
type FormRequestPayload,
|
|
32
27
|
type FormRequestPayloadRefs,
|
|
33
|
-
type FormRequestRefs
|
|
28
|
+
type FormRequestRefs,
|
|
29
|
+
type FormResponseToolkit
|
|
34
30
|
} from '~/src/server/routes/types.js'
|
|
35
31
|
|
|
36
32
|
export class PageController {
|
|
@@ -47,7 +43,7 @@ export class PageController {
|
|
|
47
43
|
events?: Events
|
|
48
44
|
collection?: ComponentCollection
|
|
49
45
|
viewName = 'index'
|
|
50
|
-
|
|
46
|
+
allowSaveAndExit = false
|
|
51
47
|
|
|
52
48
|
constructor(model: FormModel, pageDef: Page) {
|
|
53
49
|
const { def } = model
|
|
@@ -171,7 +167,7 @@ export class PageController {
|
|
|
171
167
|
makeGetRouteHandler(): (
|
|
172
168
|
request: FormRequest,
|
|
173
169
|
context: FormContext,
|
|
174
|
-
h:
|
|
170
|
+
h: FormResponseToolkit
|
|
175
171
|
) => ReturnType<Lifecycle.Method<FormRequestRefs>> {
|
|
176
172
|
return (request, context, h) => {
|
|
177
173
|
const { viewModel, viewName } = this
|
|
@@ -182,14 +178,12 @@ export class PageController {
|
|
|
182
178
|
makePostRouteHandler(): (
|
|
183
179
|
request: FormRequestPayload,
|
|
184
180
|
context: FormContext,
|
|
185
|
-
h:
|
|
181
|
+
h: FormResponseToolkit
|
|
186
182
|
) => ReturnType<Lifecycle.Method<FormRequestPayloadRefs>> {
|
|
187
183
|
throw Boom.badRequest('Unsupported POST route handler for this page')
|
|
188
184
|
}
|
|
189
185
|
|
|
190
|
-
|
|
191
|
-
return (
|
|
192
|
-
getSaveAndReturnHelpers(server) !== undefined && this.allowSaveAndReturn
|
|
193
|
-
)
|
|
186
|
+
shouldShowSaveAndExit(server: Server): boolean {
|
|
187
|
+
return getSaveAndExitHelpers(server) !== undefined && this.allowSaveAndExit
|
|
194
188
|
}
|
|
195
189
|
}
|
|
@@ -1,14 +1,12 @@
|
|
|
1
1
|
import { type PageQuestion } from '@defra/forms-model'
|
|
2
|
-
import { type ResponseToolkit } from '@hapi/hapi'
|
|
3
2
|
|
|
4
|
-
import { getCacheService } from '~/src/server/plugins/engine/helpers.js'
|
|
5
3
|
import { FormModel } from '~/src/server/plugins/engine/models/FormModel.js'
|
|
6
4
|
import { QuestionPageController } from '~/src/server/plugins/engine/pageControllers/QuestionPageController.js'
|
|
7
5
|
import {
|
|
8
6
|
buildFormContextRequest,
|
|
9
7
|
buildFormRequest
|
|
10
8
|
} from '~/src/server/plugins/engine/pageControllers/__stubs__/request.js'
|
|
11
|
-
import {
|
|
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:
|
|
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:
|
|
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
|
|
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:
|
|
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('
|
|
1326
|
+
describe('shouldShowSaveAndExit', () => {
|
|
1328
1327
|
it('should return true by default', () => {
|
|
1329
|
-
expect(controller1.
|
|
1328
|
+
expect(controller1.shouldShowSaveAndExit(serverWithSaveAndExit)).toBe(
|
|
1330
1329
|
true
|
|
1331
1330
|
)
|
|
1332
1331
|
})
|
|
1333
1332
|
})
|
|
1334
1333
|
|
|
1335
|
-
describe('
|
|
1336
|
-
it('should
|
|
1337
|
-
const
|
|
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
|
-
|
|
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-
|
|
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
|
-
|
|
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(
|
|
1398
|
-
expect(h.redirect).not.toHaveBeenCalled()
|
|
1361
|
+
expect(saveAndExitMock).toHaveBeenCalledWith(request, h, context)
|
|
1399
1362
|
})
|
|
1400
1363
|
|
|
1401
|
-
it('should throw if
|
|
1402
|
-
const
|
|
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
|
|
1375
|
+
// No function
|
|
1376
|
+
saveAndExit: undefined
|
|
1413
1377
|
}
|
|
1414
1378
|
}
|
|
1415
1379
|
},
|
|
1416
1380
|
method: 'post',
|
|
1417
|
-
payload: { yesNoField: true, action: 'save-and-
|
|
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
|
-
|
|
1453
|
-
|
|
1454
|
-
)
|
|
1386
|
+
expect(() => controller1.handleSaveAndExit(request, context, h)).toThrow(
|
|
1387
|
+
'Server misconfigured for save and exit'
|
|
1388
|
+
)
|
|
1455
1389
|
|
|
1456
|
-
expect(
|
|
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-
|
|
1462
|
-
it('should handle FormAction.
|
|
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-
|
|
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, '
|
|
1478
|
-
.
|
|
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.
|
|
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
|
|
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, '
|
|
1506
|
-
.
|
|
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.
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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-
|
|
514
|
+
// Check if this is a save-and-exit action
|
|
519
515
|
const { action } = request.payload
|
|
520
|
-
if (action === FormAction.
|
|
521
|
-
return this.
|
|
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:
|
|
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-
|
|
539
|
+
* Handle save-and-exit action
|
|
544
540
|
*/
|
|
545
|
-
|
|
541
|
+
handleSaveAndExit(
|
|
546
542
|
request: FormRequestPayload,
|
|
547
543
|
context: FormContext,
|
|
548
|
-
h:
|
|
544
|
+
h: FormResponseToolkit
|
|
549
545
|
) {
|
|
550
|
-
const
|
|
546
|
+
const saveAndExit = getSaveAndExitHelpers(request.server)
|
|
551
547
|
|
|
552
|
-
|
|
553
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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('
|
|
275
|
-
it('should return true when save and
|
|
276
|
-
expect(controller.
|
|
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
|
})
|