@defra/forms-engine-plugin 0.0.2 → 0.0.4
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/.public/assets/fonts/bold-affa96571d-v2.woff +0 -0
- package/.public/assets/fonts/bold-b542beb274-v2.woff2 +0 -0
- package/.public/assets/fonts/light-94a07e06a1-v2.woff2 +0 -0
- package/.public/assets/fonts/light-f591b13f7d-v2.woff +0 -0
- package/.public/assets/images/favicon.ico +0 -0
- package/.public/assets/images/favicon.svg +1 -0
- package/.public/assets/images/govuk-crest.svg +1 -0
- package/.public/assets/images/govuk-icon-180.png +0 -0
- package/.public/assets/images/govuk-icon-192.png +0 -0
- package/.public/assets/images/govuk-icon-512.png +0 -0
- package/.public/assets/images/govuk-icon-mask.svg +1 -0
- package/.public/assets/images/govuk-opengraph-image.png +0 -0
- package/.public/assets/manifest.json +39 -0
- package/.public/assets-manifest.json +24 -0
- package/.public/javascripts/application.0fd8c18.min.js +3 -0
- package/.public/javascripts/application.0fd8c18.min.js.LICENSE.txt +58 -0
- package/.public/javascripts/application.0fd8c18.min.js.map +1 -0
- package/.public/javascripts/file-upload.b2f18f5.min.js +2 -0
- package/.public/javascripts/file-upload.b2f18f5.min.js.map +1 -0
- package/.public/javascripts/vendor/accessible-autocomplete.275d332.min.js +2 -0
- package/.public/javascripts/vendor/accessible-autocomplete.275d332.min.js.map +1 -0
- package/.public/stylesheets/application.e340021.min.css +14 -0
- package/.public/stylesheets/application.e340021.min.css.map +1 -0
- package/.server/server/plugins/nunjucks/environment.js +3 -3
- package/.server/server/plugins/nunjucks/environment.js.map +1 -1
- package/package.json +6 -2
- package/.browserslistrc +0 -16
- package/.editorconfig +0 -9
- package/.eslintrc.cjs +0 -266
- package/.github/dependabot.yml +0 -85
- package/.github/workflows/check-pull-request.yml +0 -209
- package/.github/workflows/pr-notifier.yml +0 -98
- package/.github/workflows/publish.yml +0 -111
- package/.husky/pre-commit +0 -1
- package/.lintstagedrc.js +0 -4
- package/.nvmrc +0 -1
- package/.prettierignore +0 -8
- package/.prettierrc.cjs +0 -26
- package/Dockerfile +0 -61
- package/Procfile +0 -1
- package/babel.config.cjs +0 -55
- package/compose/aws.env +0 -4
- package/compose/start-localstack.sh +0 -26
- package/docker-compose.yaml +0 -86
- package/globals.d.ts +0 -1
- package/jest.config.cjs +0 -54
- package/jest.environment.js +0 -4
- package/jest.setup.cjs +0 -14
- package/postcss.config.js +0 -26
- package/sonar-project.properties +0 -17
- package/src/client/javascripts/application.js +0 -87
- package/src/client/javascripts/file-upload.js +0 -386
- package/src/client/stylesheets/_code.scss +0 -33
- package/src/client/stylesheets/_govuk-frontend.scss +0 -4
- package/src/client/stylesheets/_prose.scss +0 -56
- package/src/client/stylesheets/_service-banner.scss +0 -24
- package/src/client/stylesheets/_summary-list.scss +0 -28
- package/src/client/stylesheets/_tag-env.scss +0 -24
- package/src/client/stylesheets/application.scss +0 -14
- package/src/common/cookies.js +0 -58
- package/src/common/cookies.test.js +0 -23
- package/src/common/types.js +0 -5
- package/src/config/index.ts +0 -271
- package/src/index.ts +0 -31
- package/src/server/common/helpers/logging/logger-options.test.ts +0 -50
- package/src/server/common/helpers/logging/logger-options.ts +0 -46
- package/src/server/common/helpers/logging/logger.ts +0 -7
- package/src/server/common/helpers/logging/request-logger.ts +0 -9
- package/src/server/common/helpers/logging/request-tracing.js +0 -10
- package/src/server/common/helpers/redis-client.js +0 -70
- package/src/server/constants.js +0 -1
- package/src/server/forms/README.md +0 -10
- package/src/server/forms/components.json +0 -1015
- package/src/server/forms/report-a-terrorist.json +0 -270
- package/src/server/forms/runner-components-test.json +0 -365
- package/src/server/forms/test.json +0 -581
- package/src/server/index.test.ts +0 -582
- package/src/server/index.ts +0 -140
- package/src/server/plugins/blankie.test.ts +0 -73
- package/src/server/plugins/blankie.ts +0 -48
- package/src/server/plugins/crumb.ts +0 -20
- package/src/server/plugins/engine/README.md +0 -87
- package/src/server/plugins/engine/components/AutocompleteField.test.ts +0 -294
- package/src/server/plugins/engine/components/AutocompleteField.ts +0 -49
- package/src/server/plugins/engine/components/CheckboxesField.test.ts +0 -379
- package/src/server/plugins/engine/components/CheckboxesField.ts +0 -106
- package/src/server/plugins/engine/components/ComponentBase.ts +0 -97
- package/src/server/plugins/engine/components/ComponentCollection.ts +0 -278
- package/src/server/plugins/engine/components/DatePartsField.test.ts +0 -822
- package/src/server/plugins/engine/components/DatePartsField.ts +0 -264
- package/src/server/plugins/engine/components/Details.test.ts +0 -49
- package/src/server/plugins/engine/components/Details.ts +0 -30
- package/src/server/plugins/engine/components/EmailAddressField.test.ts +0 -395
- package/src/server/plugins/engine/components/EmailAddressField.ts +0 -55
- package/src/server/plugins/engine/components/FileUploadField.test.ts +0 -778
- package/src/server/plugins/engine/components/FileUploadField.ts +0 -262
- package/src/server/plugins/engine/components/FormComponent.ts +0 -249
- package/src/server/plugins/engine/components/Html.test.ts +0 -48
- package/src/server/plugins/engine/components/Html.ts +0 -29
- package/src/server/plugins/engine/components/InsetText.test.ts +0 -48
- package/src/server/plugins/engine/components/InsetText.ts +0 -27
- package/src/server/plugins/engine/components/List.test.ts +0 -76
- package/src/server/plugins/engine/components/List.ts +0 -72
- package/src/server/plugins/engine/components/ListFormComponent.ts +0 -140
- package/src/server/plugins/engine/components/MonthYearField.test.ts +0 -567
- package/src/server/plugins/engine/components/MonthYearField.ts +0 -222
- package/src/server/plugins/engine/components/MultilineTextField.test.ts +0 -558
- package/src/server/plugins/engine/components/MultilineTextField.ts +0 -138
- package/src/server/plugins/engine/components/NumberField.test.ts +0 -701
- package/src/server/plugins/engine/components/NumberField.ts +0 -163
- package/src/server/plugins/engine/components/RadiosField.test.ts +0 -288
- package/src/server/plugins/engine/components/RadiosField.ts +0 -24
- package/src/server/plugins/engine/components/SelectField.test.ts +0 -288
- package/src/server/plugins/engine/components/SelectField.ts +0 -47
- package/src/server/plugins/engine/components/SelectionControlField.ts +0 -43
- package/src/server/plugins/engine/components/TelephoneNumberField.test.ts +0 -356
- package/src/server/plugins/engine/components/TelephoneNumberField.ts +0 -67
- package/src/server/plugins/engine/components/TextField.test.ts +0 -489
- package/src/server/plugins/engine/components/TextField.ts +0 -96
- package/src/server/plugins/engine/components/UkAddressField.test.ts +0 -623
- package/src/server/plugins/engine/components/UkAddressField.ts +0 -172
- package/src/server/plugins/engine/components/YesNoField.test.ts +0 -248
- package/src/server/plugins/engine/components/YesNoField.ts +0 -31
- package/src/server/plugins/engine/components/constants.ts +0 -1
- package/src/server/plugins/engine/components/helpers.ts +0 -330
- package/src/server/plugins/engine/components/index.ts +0 -24
- package/src/server/plugins/engine/components/types.ts +0 -117
- package/src/server/plugins/engine/configureEnginePlugin.ts +0 -47
- package/src/server/plugins/engine/helpers.test.ts +0 -791
- package/src/server/plugins/engine/helpers.ts +0 -379
- package/src/server/plugins/engine/index.ts +0 -7
- package/src/server/plugins/engine/models/FormModel.test.ts +0 -42
- package/src/server/plugins/engine/models/FormModel.ts +0 -443
- package/src/server/plugins/engine/models/RepeatingSummaryViewModel.ts +0 -0
- package/src/server/plugins/engine/models/Section.ts +0 -0
- package/src/server/plugins/engine/models/SummaryViewModel.test.ts +0 -209
- package/src/server/plugins/engine/models/SummaryViewModel.ts +0 -220
- package/src/server/plugins/engine/models/index.ts +0 -2
- package/src/server/plugins/engine/models/types.ts +0 -114
- package/src/server/plugins/engine/outputFormatters/human/v1.test.ts +0 -143
- package/src/server/plugins/engine/outputFormatters/human/v1.ts +0 -73
- package/src/server/plugins/engine/outputFormatters/index.test.ts +0 -17
- package/src/server/plugins/engine/outputFormatters/index.ts +0 -44
- package/src/server/plugins/engine/outputFormatters/machine/v1.test.ts +0 -229
- package/src/server/plugins/engine/outputFormatters/machine/v1.ts +0 -140
- package/src/server/plugins/engine/outputFormatters/machine/v2.test.ts +0 -229
- package/src/server/plugins/engine/outputFormatters/machine/v2.ts +0 -153
- package/src/server/plugins/engine/pageControllers/FileUploadPageController.test.ts +0 -1108
- package/src/server/plugins/engine/pageControllers/FileUploadPageController.ts +0 -446
- package/src/server/plugins/engine/pageControllers/PageController.test.ts +0 -205
- package/src/server/plugins/engine/pageControllers/PageController.ts +0 -176
- package/src/server/plugins/engine/pageControllers/QuestionPageController.test.ts +0 -1264
- package/src/server/plugins/engine/pageControllers/QuestionPageController.ts +0 -561
- package/src/server/plugins/engine/pageControllers/README.md +0 -28
- package/src/server/plugins/engine/pageControllers/RepeatPageController.test.ts +0 -264
- package/src/server/plugins/engine/pageControllers/RepeatPageController.ts +0 -458
- package/src/server/plugins/engine/pageControllers/StartPageController.ts +0 -18
- package/src/server/plugins/engine/pageControllers/StatusPageController.ts +0 -50
- package/src/server/plugins/engine/pageControllers/SummaryPageController.ts +0 -261
- package/src/server/plugins/engine/pageControllers/TerminalController.test.ts +0 -28
- package/src/server/plugins/engine/pageControllers/TerminalPageController.ts +0 -19
- package/src/server/plugins/engine/pageControllers/helpers.test.ts +0 -198
- package/src/server/plugins/engine/pageControllers/helpers.ts +0 -101
- package/src/server/plugins/engine/pageControllers/index.ts +0 -10
- package/src/server/plugins/engine/pageControllers/validationOptions.ts +0 -89
- package/src/server/plugins/engine/plugin.ts +0 -673
- package/src/server/plugins/engine/services/formSubmissionService.js +0 -46
- package/src/server/plugins/engine/services/formsService.js +0 -46
- package/src/server/plugins/engine/services/formsService.test.js +0 -90
- package/src/server/plugins/engine/services/index.js +0 -3
- package/src/server/plugins/engine/services/notifyService.test.ts +0 -132
- package/src/server/plugins/engine/services/notifyService.ts +0 -64
- package/src/server/plugins/engine/services/uploadService.js +0 -60
- package/src/server/plugins/engine/types.ts +0 -315
- package/src/server/plugins/engine/views/components/autocompletefield.html +0 -5
- package/src/server/plugins/engine/views/components/checkboxesfield.html +0 -5
- package/src/server/plugins/engine/views/components/datepartsfield.html +0 -5
- package/src/server/plugins/engine/views/components/details.html +0 -6
- package/src/server/plugins/engine/views/components/emailaddressfield.html +0 -5
- package/src/server/plugins/engine/views/components/fileuploadfield-key.html +0 -8
- package/src/server/plugins/engine/views/components/fileuploadfield-value.html +0 -3
- package/src/server/plugins/engine/views/components/fileuploadfield.html +0 -24
- package/src/server/plugins/engine/views/components/html.html +0 -3
- package/src/server/plugins/engine/views/components/insettext.html +0 -7
- package/src/server/plugins/engine/views/components/list.html +0 -36
- package/src/server/plugins/engine/views/components/monthyearfield.html +0 -5
- package/src/server/plugins/engine/views/components/multilinetextfield.html +0 -10
- package/src/server/plugins/engine/views/components/numberfield.html +0 -5
- package/src/server/plugins/engine/views/components/radiosfield.html +0 -5
- package/src/server/plugins/engine/views/components/selectfield.html +0 -5
- package/src/server/plugins/engine/views/components/telephonenumberfield.html +0 -5
- package/src/server/plugins/engine/views/components/textfield.html +0 -5
- package/src/server/plugins/engine/views/components/ukaddressfield.html +0 -25
- package/src/server/plugins/engine/views/components/yesnofield.html +0 -5
- package/src/server/plugins/engine/views/file-upload.html +0 -45
- package/src/server/plugins/engine/views/index.html +0 -39
- package/src/server/plugins/engine/views/item-delete.html +0 -56
- package/src/server/plugins/engine/views/partials/components.html +0 -6
- package/src/server/plugins/engine/views/partials/conditional-components.html +0 -3
- package/src/server/plugins/engine/views/partials/debug.html +0 -44
- package/src/server/plugins/engine/views/partials/form.html +0 -15
- package/src/server/plugins/engine/views/partials/heading.html +0 -16
- package/src/server/plugins/engine/views/partials/preview-banner.html +0 -32
- package/src/server/plugins/engine/views/partials/preview-banner.test.js +0 -122
- package/src/server/plugins/engine/views/partials/warn-missing-notification-email.html +0 -10
- package/src/server/plugins/engine/views/repeat-list-summary.html +0 -53
- package/src/server/plugins/errorPages.ts +0 -58
- package/src/server/plugins/nunjucks/context.js +0 -88
- package/src/server/plugins/nunjucks/context.test.js +0 -142
- package/src/server/plugins/nunjucks/enviroment.test.js +0 -201
- package/src/server/plugins/nunjucks/environment.js +0 -116
- package/src/server/plugins/nunjucks/filters/answer.js +0 -27
- package/src/server/plugins/nunjucks/filters/answer.test.js +0 -89
- package/src/server/plugins/nunjucks/filters/evaluate.js +0 -21
- package/src/server/plugins/nunjucks/filters/field.js +0 -28
- package/src/server/plugins/nunjucks/filters/field.test.js +0 -75
- package/src/server/plugins/nunjucks/filters/highlight.js +0 -11
- package/src/server/plugins/nunjucks/filters/href.js +0 -30
- package/src/server/plugins/nunjucks/filters/href.test.js +0 -80
- package/src/server/plugins/nunjucks/filters/index.js +0 -8
- package/src/server/plugins/nunjucks/filters/inspect.js +0 -15
- package/src/server/plugins/nunjucks/filters/page.js +0 -24
- package/src/server/plugins/nunjucks/filters/page.test.js +0 -65
- package/src/server/plugins/nunjucks/index.js +0 -3
- package/src/server/plugins/nunjucks/plugin.js +0 -40
- package/src/server/plugins/nunjucks/render.js +0 -42
- package/src/server/plugins/nunjucks/types.js +0 -40
- package/src/server/plugins/pulse.ts +0 -11
- package/src/server/plugins/router.ts +0 -201
- package/src/server/plugins/session.ts +0 -28
- package/src/server/routes/health.js +0 -13
- package/src/server/routes/health.test.js +0 -35
- package/src/server/routes/index.test.ts +0 -125
- package/src/server/routes/index.ts +0 -2
- package/src/server/routes/public.ts +0 -47
- package/src/server/routes/types.ts +0 -48
- package/src/server/schemas/index.ts +0 -34
- package/src/server/secure-context.js +0 -43
- package/src/server/services/cacheService.test.ts +0 -276
- package/src/server/services/cacheService.ts +0 -131
- package/src/server/services/httpService.test.js +0 -491
- package/src/server/services/httpService.ts +0 -50
- package/src/server/services/index.ts +0 -1
- package/src/server/types.ts +0 -54
- package/src/server/utils/notify.test.ts +0 -37
- package/src/server/utils/notify.ts +0 -50
- package/src/server/utils/secure-context/get-trust-store-certs.js +0 -11
- package/src/server/utils/secure-context/get-trust-store-certs.test.js +0 -19
- package/src/server/utils/utils.js +0 -24
- package/src/server/utils/utils.test.js +0 -54
- package/src/server/views/404.html +0 -16
- package/src/server/views/500.html +0 -19
- package/src/server/views/components/debug/macro.njk +0 -3
- package/src/server/views/components/debug/template.njk +0 -13
- package/src/server/views/components/service-banner/macro.njk +0 -3
- package/src/server/views/components/service-banner/template.njk +0 -20
- package/src/server/views/components/service-banner/template.test.js +0 -43
- package/src/server/views/components/tag-env/macro.njk +0 -3
- package/src/server/views/components/tag-env/template.njk +0 -30
- package/src/server/views/components/tag-env/template.test.js +0 -66
- package/src/server/views/confirmation.html +0 -19
- package/src/server/views/help/accessibility-statement.html +0 -58
- package/src/server/views/help/cookie-preferences.html +0 -57
- package/src/server/views/help/cookies.html +0 -71
- package/src/server/views/help/get-support.html +0 -37
- package/src/server/views/help/privacy-notice.html +0 -68
- package/src/server/views/help/terms-and-conditions.html +0 -83
- package/src/server/views/layout.html +0 -199
- package/src/server/views/summary.html +0 -50
- package/src/typings/hapi/index.d.ts +0 -95
- package/src/typings/hapi-tracing/index.d.ts +0 -6
- package/src/typings/index.d.ts +0 -3
- package/src/typings/joi/index.d.ts +0 -22
- package/stylelint.config.js +0 -10
- package/test/client/javascripts/file-upload.test.js +0 -1197
- package/test/condition/checkboxes.test.js +0 -112
- package/test/condition/radios.test.js +0 -112
- package/test/condition/text.test.js +0 -103
- package/test/fixtures/assets-manifest.json +0 -4
- package/test/fixtures/form.js +0 -86
- package/test/fixtures/index.js +0 -2
- package/test/fixtures/list.js +0 -92
- package/test/form/cookies.test.js +0 -338
- package/test/form/csrf.test.js +0 -87
- package/test/form/definitions/basic.js +0 -101
- package/test/form/definitions/blank.js +0 -10
- package/test/form/definitions/checkboxes.json +0 -88
- package/test/form/definitions/components.json +0 -452
- package/test/form/definitions/conditional-reveal.js +0 -140
- package/test/form/definitions/conditions-basic.js +0 -187
- package/test/form/definitions/conditions-complex.js +0 -338
- package/test/form/definitions/conditions-dates.js +0 -78
- package/test/form/definitions/conditions-escaping.js +0 -143
- package/test/form/definitions/demo-cph-number.js +0 -3099
- package/test/form/definitions/feedback.json +0 -45
- package/test/form/definitions/fields-optional.js +0 -402
- package/test/form/definitions/fields-required.js +0 -402
- package/test/form/definitions/file-upload-basic.js +0 -44
- package/test/form/definitions/file-upload.js +0 -66
- package/test/form/definitions/minimal.js +0 -39
- package/test/form/definitions/phase-alpha.json +0 -33
- package/test/form/definitions/phase-default.json +0 -26
- package/test/form/definitions/radios.json +0 -88
- package/test/form/definitions/repeat-mixed.js +0 -54
- package/test/form/definitions/repeat.js +0 -70
- package/test/form/definitions/status.json +0 -126
- package/test/form/definitions/templates.js +0 -183
- package/test/form/definitions/test.json +0 -581
- package/test/form/definitions/text.json +0 -75
- package/test/form/definitions/titles.json +0 -170
- package/test/form/definitions.test.js +0 -47
- package/test/form/exit-page.test.js +0 -210
- package/test/form/feedback.test.js +0 -68
- package/test/form/fields-optional.test.js +0 -237
- package/test/form/fields-required.test.js +0 -294
- package/test/form/file-upload.test.js +0 -313
- package/test/form/govuk-notify.test.js +0 -449
- package/test/form/journey-basic.test.js +0 -444
- package/test/form/persist-files.test.js +0 -227
- package/test/form/phase-banner.test.js +0 -71
- package/test/form/repeat.test.js +0 -628
- package/test/form/summary-submission-email.test.js +0 -95
- package/test/form/template.test.js +0 -288
- package/test/form/titles.test.js +0 -204
- package/test/helpers/component-helpers.js +0 -74
- package/test/utils/get-cookie.js +0 -42
- package/test/utils/get-form-definitions.js +0 -18
- package/tmp.pdf +0 -1
- package/tsconfig.json +0 -28
- package/webpack.config.js +0 -208
|
@@ -1,1264 +0,0 @@
|
|
|
1
|
-
import { type PageQuestion } from '@defra/forms-model'
|
|
2
|
-
import { type ResponseToolkit } from '@hapi/hapi'
|
|
3
|
-
|
|
4
|
-
import { FormModel } from '~/src/server/plugins/engine/models/FormModel.js'
|
|
5
|
-
import { QuestionPageController } from '~/src/server/plugins/engine/pageControllers/QuestionPageController.js'
|
|
6
|
-
import {
|
|
7
|
-
type FormContext,
|
|
8
|
-
type FormContextRequest,
|
|
9
|
-
type FormPageViewModel,
|
|
10
|
-
type FormState,
|
|
11
|
-
type FormSubmissionState
|
|
12
|
-
} from '~/src/server/plugins/engine/types.js'
|
|
13
|
-
import { type FormRequest } from '~/src/server/routes/types.js'
|
|
14
|
-
import { CacheService } from '~/src/server/services/cacheService.js'
|
|
15
|
-
import conditionalReveal from '~/test/form/definitions/conditional-reveal.js'
|
|
16
|
-
import definitionConditionsBasic, {
|
|
17
|
-
V2 as definitionConditionsBasicV2
|
|
18
|
-
} from '~/test/form/definitions/conditions-basic.js'
|
|
19
|
-
import definitionConditionsComplex from '~/test/form/definitions/conditions-complex.js'
|
|
20
|
-
import definitionConditionsDates from '~/test/form/definitions/conditions-dates.js'
|
|
21
|
-
|
|
22
|
-
describe('QuestionPageController', () => {
|
|
23
|
-
let page1: PageQuestion
|
|
24
|
-
let page1Url: URL
|
|
25
|
-
|
|
26
|
-
let page2: PageQuestion
|
|
27
|
-
let page2Url: URL
|
|
28
|
-
|
|
29
|
-
let model: FormModel
|
|
30
|
-
let controller1: QuestionPageController
|
|
31
|
-
let controller2: QuestionPageController
|
|
32
|
-
let requestPage1: FormRequest
|
|
33
|
-
let requestPage2: FormRequest
|
|
34
|
-
|
|
35
|
-
beforeEach(() => {
|
|
36
|
-
const { pages } = definitionConditionsBasic
|
|
37
|
-
|
|
38
|
-
page1 = pages[0]
|
|
39
|
-
page1Url = new URL('http://example.com/test/first-page')
|
|
40
|
-
|
|
41
|
-
page2 = pages[1]
|
|
42
|
-
page2Url = new URL('http://example.com/test/second-page')
|
|
43
|
-
|
|
44
|
-
model = new FormModel(definitionConditionsBasic, {
|
|
45
|
-
basePath: 'test'
|
|
46
|
-
})
|
|
47
|
-
|
|
48
|
-
controller1 = new QuestionPageController(model, page1)
|
|
49
|
-
controller2 = new QuestionPageController(model, page2)
|
|
50
|
-
|
|
51
|
-
requestPage1 = {
|
|
52
|
-
method: 'get',
|
|
53
|
-
url: page1Url,
|
|
54
|
-
path: page1Url.pathname,
|
|
55
|
-
params: {
|
|
56
|
-
path: 'first-page',
|
|
57
|
-
slug: 'test'
|
|
58
|
-
},
|
|
59
|
-
query: {},
|
|
60
|
-
app: { model }
|
|
61
|
-
} as FormRequest
|
|
62
|
-
|
|
63
|
-
requestPage2 = {
|
|
64
|
-
method: 'get',
|
|
65
|
-
url: page2Url,
|
|
66
|
-
path: page2Url.pathname,
|
|
67
|
-
params: {
|
|
68
|
-
path: 'second-page',
|
|
69
|
-
slug: 'test'
|
|
70
|
-
},
|
|
71
|
-
query: {},
|
|
72
|
-
app: { model }
|
|
73
|
-
} as FormRequest
|
|
74
|
-
})
|
|
75
|
-
|
|
76
|
-
describe('Properties', () => {
|
|
77
|
-
it('returns path', () => {
|
|
78
|
-
expect(controller1).toHaveProperty('path', '/first-page')
|
|
79
|
-
expect(controller2).toHaveProperty('path', '/second-page')
|
|
80
|
-
})
|
|
81
|
-
|
|
82
|
-
it('returns href', () => {
|
|
83
|
-
expect(controller1).toHaveProperty('href', '/test/first-page')
|
|
84
|
-
expect(controller2).toHaveProperty('href', '/test/second-page')
|
|
85
|
-
})
|
|
86
|
-
|
|
87
|
-
it('returns keys', () => {
|
|
88
|
-
expect(controller1).toHaveProperty('keys', ['yesNoField'])
|
|
89
|
-
expect(controller2).toHaveProperty('keys', [
|
|
90
|
-
'dateField',
|
|
91
|
-
'dateField__day',
|
|
92
|
-
'dateField__month',
|
|
93
|
-
'dateField__year',
|
|
94
|
-
'multilineTextField'
|
|
95
|
-
])
|
|
96
|
-
})
|
|
97
|
-
|
|
98
|
-
it('returns the page section', () => {
|
|
99
|
-
expect(controller1).toHaveProperty('section', undefined)
|
|
100
|
-
expect(controller2).toHaveProperty('section', {
|
|
101
|
-
name: 'marriage',
|
|
102
|
-
title: 'Your marriage',
|
|
103
|
-
hideTitle: false
|
|
104
|
-
})
|
|
105
|
-
})
|
|
106
|
-
})
|
|
107
|
-
|
|
108
|
-
describe('Path methods', () => {
|
|
109
|
-
describe('Next getter', () => {
|
|
110
|
-
it('returns page links', () => {
|
|
111
|
-
expect(controller1).toHaveProperty('next', [
|
|
112
|
-
{ path: '/second-page' },
|
|
113
|
-
{ path: '/summary', condition: 'isPreviouslyMarried' }
|
|
114
|
-
])
|
|
115
|
-
|
|
116
|
-
expect(controller2).toHaveProperty('next', [{ path: '/summary' }])
|
|
117
|
-
})
|
|
118
|
-
|
|
119
|
-
it('returns page links (none found)', () => {
|
|
120
|
-
const pageDef1 = structuredClone(controller1.pageDef)
|
|
121
|
-
const pageDef2 = structuredClone(controller2.pageDef)
|
|
122
|
-
|
|
123
|
-
controller1.pageDef = pageDef1
|
|
124
|
-
controller2.pageDef = pageDef2
|
|
125
|
-
|
|
126
|
-
// @ts-expect-error - Allow invalid property for test
|
|
127
|
-
controller1.pageDef.next = []
|
|
128
|
-
|
|
129
|
-
// @ts-expect-error - Allow invalid property for test
|
|
130
|
-
delete controller2.pageDef.next
|
|
131
|
-
|
|
132
|
-
expect(controller1).toHaveProperty('next', [])
|
|
133
|
-
expect(controller2).toHaveProperty('next', [])
|
|
134
|
-
})
|
|
135
|
-
})
|
|
136
|
-
})
|
|
137
|
-
|
|
138
|
-
describe('Component collection', () => {
|
|
139
|
-
it('returns the components for the page', () => {
|
|
140
|
-
const { components: components1 } = controller1.collection
|
|
141
|
-
const { components: components2 } = controller2.collection
|
|
142
|
-
|
|
143
|
-
expect(components1).toHaveLength(1)
|
|
144
|
-
expect(components1[0].name).toBe('yesNoField')
|
|
145
|
-
|
|
146
|
-
expect(components2).toHaveLength(3)
|
|
147
|
-
expect(components2[0].name).toBe('detailsField')
|
|
148
|
-
expect(components2[1].name).toBe('dateField')
|
|
149
|
-
expect(components2[2].name).toBe('multilineTextField')
|
|
150
|
-
})
|
|
151
|
-
|
|
152
|
-
it('returns the fields for the page', () => {
|
|
153
|
-
const { fields: fields1 } = controller1.collection
|
|
154
|
-
const { fields: fields2 } = controller2.collection
|
|
155
|
-
|
|
156
|
-
expect(fields1).toHaveLength(1)
|
|
157
|
-
expect(fields1[0].name).toBe('yesNoField')
|
|
158
|
-
|
|
159
|
-
expect(fields2).toHaveLength(2)
|
|
160
|
-
expect(fields2[0].name).toBe('dateField')
|
|
161
|
-
expect(fields2[1].name).toBe('multilineTextField')
|
|
162
|
-
})
|
|
163
|
-
|
|
164
|
-
it('returns the guidance for the page', () => {
|
|
165
|
-
const { guidance: guidance1 } = controller1.collection
|
|
166
|
-
const { guidance: guidance2 } = controller2.collection
|
|
167
|
-
|
|
168
|
-
expect(guidance1).toHaveLength(0)
|
|
169
|
-
expect(guidance2).toHaveLength(1)
|
|
170
|
-
expect(guidance2[0].name).toBe('detailsField')
|
|
171
|
-
})
|
|
172
|
-
})
|
|
173
|
-
|
|
174
|
-
describe('Component view models', () => {
|
|
175
|
-
let viewModel1: FormPageViewModel
|
|
176
|
-
let viewModel2: FormPageViewModel
|
|
177
|
-
|
|
178
|
-
beforeEach(() => {
|
|
179
|
-
viewModel1 = controller1.getViewModel(
|
|
180
|
-
requestPage1,
|
|
181
|
-
model.getFormContext(requestPage1, {})
|
|
182
|
-
)
|
|
183
|
-
|
|
184
|
-
viewModel2 = controller2.getViewModel(
|
|
185
|
-
requestPage2,
|
|
186
|
-
model.getFormContext(requestPage2, {})
|
|
187
|
-
)
|
|
188
|
-
})
|
|
189
|
-
|
|
190
|
-
it('hides the page title for single form component pages', () => {
|
|
191
|
-
// Page 1 hides page title (single component)
|
|
192
|
-
expect(viewModel1).toHaveProperty('showTitle', false)
|
|
193
|
-
expect(viewModel1).toHaveProperty('pageTitle', 'Previous marriages')
|
|
194
|
-
|
|
195
|
-
// Page 2 shows page title (multiple components)
|
|
196
|
-
expect(viewModel2).toHaveProperty('showTitle', true)
|
|
197
|
-
expect(viewModel2).toHaveProperty(
|
|
198
|
-
'pageTitle',
|
|
199
|
-
'When will you get married?'
|
|
200
|
-
)
|
|
201
|
-
})
|
|
202
|
-
|
|
203
|
-
it('returns the component view models for the page', () => {
|
|
204
|
-
const { components: components1 } = viewModel1
|
|
205
|
-
const { components: components2 } = viewModel2
|
|
206
|
-
|
|
207
|
-
expect(components1).toHaveLength(1)
|
|
208
|
-
expect(components2).toHaveLength(3)
|
|
209
|
-
|
|
210
|
-
// Page 1, component 1, default label
|
|
211
|
-
expect(components1[0].model).toHaveProperty('label', {
|
|
212
|
-
text: 'Have you previously been married?'
|
|
213
|
-
})
|
|
214
|
-
|
|
215
|
-
// Page 1, component 1, optional legend
|
|
216
|
-
expect(components1[0].model).toHaveProperty('fieldset', {
|
|
217
|
-
legend: {
|
|
218
|
-
text: 'Previous marriages',
|
|
219
|
-
classes: 'govuk-fieldset__legend--l',
|
|
220
|
-
isPageHeading: true
|
|
221
|
-
}
|
|
222
|
-
})
|
|
223
|
-
|
|
224
|
-
// Page 2, component 1, content only
|
|
225
|
-
expect(components2[0].model).toEqual({
|
|
226
|
-
attributes: {},
|
|
227
|
-
summaryHtml: 'Find out more',
|
|
228
|
-
html: 'Some content goes here'
|
|
229
|
-
})
|
|
230
|
-
|
|
231
|
-
// Page 2, component 2, default label
|
|
232
|
-
expect(components2[1].model).toHaveProperty('label', {
|
|
233
|
-
text: 'Date of marriage',
|
|
234
|
-
classes: 'govuk-label--m'
|
|
235
|
-
})
|
|
236
|
-
|
|
237
|
-
// Page 2, component 2, optional legend
|
|
238
|
-
expect(components2[1].model).toHaveProperty('fieldset', {
|
|
239
|
-
legend: {
|
|
240
|
-
text: 'Date of marriage',
|
|
241
|
-
classes: 'govuk-fieldset__legend--m'
|
|
242
|
-
}
|
|
243
|
-
})
|
|
244
|
-
|
|
245
|
-
// Page 2, component 3, default label
|
|
246
|
-
expect(components2[2].model).toHaveProperty('label', {
|
|
247
|
-
text: 'Remarks',
|
|
248
|
-
classes: 'govuk-label--m'
|
|
249
|
-
})
|
|
250
|
-
|
|
251
|
-
// Page 2, component 3, optional legend
|
|
252
|
-
expect(components2[2].model).not.toHaveProperty('fieldset')
|
|
253
|
-
})
|
|
254
|
-
})
|
|
255
|
-
|
|
256
|
-
describe('Condition evaluation context', () => {
|
|
257
|
-
it('filters state by journey pages', () => {
|
|
258
|
-
const { pages } = definitionConditionsComplex
|
|
259
|
-
|
|
260
|
-
const model = new FormModel(definitionConditionsComplex, {
|
|
261
|
-
basePath: 'test'
|
|
262
|
-
})
|
|
263
|
-
|
|
264
|
-
// Selected page appears after convergence and contains a conditional field
|
|
265
|
-
// This is the page we're theoretically browsing to
|
|
266
|
-
const controller = new QuestionPageController(model, pages[7])
|
|
267
|
-
|
|
268
|
-
// The state below shows we said we had a UKPassport and entered details for an applicant
|
|
269
|
-
const state: FormSubmissionState = {
|
|
270
|
-
ukPassport: true,
|
|
271
|
-
numberOfApplicants: 2,
|
|
272
|
-
applicantOneFirstName: 'Enrique',
|
|
273
|
-
applicantOneMiddleName: null,
|
|
274
|
-
applicantOneLastName: 'Chase',
|
|
275
|
-
applicantOneAddress__addressLine1: 'AddressLine1',
|
|
276
|
-
applicantOneAddress__addressLine2: 'AddressLine2',
|
|
277
|
-
applicantOneAddress__town: 'Town',
|
|
278
|
-
applicantOneAddress__postcode: 'Postcode',
|
|
279
|
-
applicantTwoFirstName: 'John',
|
|
280
|
-
applicantTwoMiddleName: null,
|
|
281
|
-
applicantTwoLastName: 'Doe',
|
|
282
|
-
applicantTwoAddress__addressLine1: 'AddressLine1',
|
|
283
|
-
applicantTwoAddress__addressLine2: 'AddressLine2',
|
|
284
|
-
applicantTwoAddress__town: 'Town',
|
|
285
|
-
applicantTwoAddress__postcode: 'Postcode'
|
|
286
|
-
}
|
|
287
|
-
|
|
288
|
-
let request = {
|
|
289
|
-
method: 'get',
|
|
290
|
-
url: new URL('http://example.com/test/applicant-one-address'),
|
|
291
|
-
path: '/test/applicant-one-address',
|
|
292
|
-
params: {
|
|
293
|
-
path: 'applicant-one-address',
|
|
294
|
-
slug: 'test'
|
|
295
|
-
},
|
|
296
|
-
query: {},
|
|
297
|
-
app: { model }
|
|
298
|
-
} satisfies FormContextRequest
|
|
299
|
-
|
|
300
|
-
// Calculate our context based on the page we're attempting to load and the above state we provide
|
|
301
|
-
let context = controller.model.getFormContext(request, state)
|
|
302
|
-
const { evaluationState: stateBefore } = context
|
|
303
|
-
|
|
304
|
-
// Our context should know our first applicant
|
|
305
|
-
expect(stateBefore).toEqual({
|
|
306
|
-
numberOfApplicants: 2,
|
|
307
|
-
ukPassport: true,
|
|
308
|
-
applicantOneFirstName: 'Enrique',
|
|
309
|
-
applicantOneMiddleName: null,
|
|
310
|
-
applicantOneLastName: 'Chase',
|
|
311
|
-
applicantOneAddress: [
|
|
312
|
-
'AddressLine1',
|
|
313
|
-
'AddressLine2',
|
|
314
|
-
'Town',
|
|
315
|
-
'Postcode'
|
|
316
|
-
]
|
|
317
|
-
})
|
|
318
|
-
|
|
319
|
-
// Our context should know which pages are relevant
|
|
320
|
-
expect(context.paths).toEqual([
|
|
321
|
-
'/uk-passport',
|
|
322
|
-
'/how-many-people',
|
|
323
|
-
'/applicant-one-name',
|
|
324
|
-
'/applicant-one-address'
|
|
325
|
-
])
|
|
326
|
-
|
|
327
|
-
// Now mark that we don't have a UK Passport
|
|
328
|
-
state.ukPassport = false
|
|
329
|
-
|
|
330
|
-
request = {
|
|
331
|
-
method: 'get',
|
|
332
|
-
url: new URL('http://example.com/test/summary'),
|
|
333
|
-
path: '/test/summary',
|
|
334
|
-
params: {
|
|
335
|
-
path: 'summary',
|
|
336
|
-
slug: 'test'
|
|
337
|
-
},
|
|
338
|
-
query: {},
|
|
339
|
-
app: { model }
|
|
340
|
-
} satisfies FormContextRequest
|
|
341
|
-
|
|
342
|
-
// And recalculate our context
|
|
343
|
-
context = controller.model.getFormContext(request, state)
|
|
344
|
-
const { evaluationState: stateAfter } = context
|
|
345
|
-
|
|
346
|
-
// Our context should no longer list pages about our applicant
|
|
347
|
-
expect(context.paths).toEqual([
|
|
348
|
-
'/uk-passport',
|
|
349
|
-
'/testconditions',
|
|
350
|
-
'/summary'
|
|
351
|
-
])
|
|
352
|
-
|
|
353
|
-
// Our context should no longer know anything about our applicant
|
|
354
|
-
expect(stateAfter).not.toHaveProperty('numberOfApplicants')
|
|
355
|
-
expect(stateAfter).not.toHaveProperty('applicantOneFirstName')
|
|
356
|
-
expect(stateAfter).not.toHaveProperty('applicantOneMiddleName')
|
|
357
|
-
expect(stateAfter).not.toHaveProperty('applicantOneLastName')
|
|
358
|
-
expect(stateAfter).not.toHaveProperty('applicantOneAddress')
|
|
359
|
-
|
|
360
|
-
expect(stateAfter).toEqual({
|
|
361
|
-
ukPassport: false
|
|
362
|
-
})
|
|
363
|
-
})
|
|
364
|
-
|
|
365
|
-
it('combines state values for date fields', () => {
|
|
366
|
-
const { pages } = definitionConditionsDates
|
|
367
|
-
|
|
368
|
-
const model = new FormModel(definitionConditionsDates, {
|
|
369
|
-
basePath: 'test'
|
|
370
|
-
})
|
|
371
|
-
|
|
372
|
-
const controller = new QuestionPageController(model, pages[0])
|
|
373
|
-
|
|
374
|
-
const request = {
|
|
375
|
-
method: 'get',
|
|
376
|
-
url: new URL('http://example.com/test/page-one'),
|
|
377
|
-
path: '/test/page-one',
|
|
378
|
-
params: {
|
|
379
|
-
path: 'page-one',
|
|
380
|
-
slug: 'test'
|
|
381
|
-
},
|
|
382
|
-
query: {},
|
|
383
|
-
app: { model }
|
|
384
|
-
} satisfies FormContextRequest
|
|
385
|
-
|
|
386
|
-
const context = controller.model.getFormContext(request, {
|
|
387
|
-
dateField__day: 5,
|
|
388
|
-
dateField__month: 1,
|
|
389
|
-
dateField__year: 2024
|
|
390
|
-
})
|
|
391
|
-
|
|
392
|
-
// Ensure dates are transformed to yyyy-MM-dd format
|
|
393
|
-
expect(context.evaluationState).toHaveProperty('dateField', '2024-01-05')
|
|
394
|
-
|
|
395
|
-
// Unlike relevant state which has the individual date parts
|
|
396
|
-
expect(context.relevantState).not.toHaveProperty('dateField')
|
|
397
|
-
expect(context.relevantState).toMatchObject({
|
|
398
|
-
dateField__day: 5,
|
|
399
|
-
dateField__month: 1,
|
|
400
|
-
dateField__year: 2024
|
|
401
|
-
})
|
|
402
|
-
})
|
|
403
|
-
|
|
404
|
-
it('filters on condition A', () => {
|
|
405
|
-
const { pages } = conditionalReveal
|
|
406
|
-
|
|
407
|
-
const model = new FormModel(conditionalReveal, {
|
|
408
|
-
basePath: 'test'
|
|
409
|
-
})
|
|
410
|
-
|
|
411
|
-
const controller = new QuestionPageController(model, pages[0])
|
|
412
|
-
|
|
413
|
-
// The state below shows we said we had a UKPassport and entered details for an applicant
|
|
414
|
-
const state: FormSubmissionState = {}
|
|
415
|
-
|
|
416
|
-
const request = {
|
|
417
|
-
method: 'get',
|
|
418
|
-
url: new URL('http://example.com/test/page-one'),
|
|
419
|
-
path: '/test/first-page',
|
|
420
|
-
params: {
|
|
421
|
-
path: 'first-page',
|
|
422
|
-
slug: 'test'
|
|
423
|
-
},
|
|
424
|
-
query: {},
|
|
425
|
-
app: { model }
|
|
426
|
-
} satisfies FormContextRequest
|
|
427
|
-
|
|
428
|
-
const context = controller.model.getFormContext(request, state)
|
|
429
|
-
const evaluationState = { animalType: 'Barn owl' }
|
|
430
|
-
|
|
431
|
-
const viewModel = controller.getViewModel(request, context)
|
|
432
|
-
|
|
433
|
-
const filtered = controller.filterConditionalComponents(
|
|
434
|
-
viewModel,
|
|
435
|
-
model,
|
|
436
|
-
evaluationState
|
|
437
|
-
)
|
|
438
|
-
|
|
439
|
-
expect(filtered).toHaveLength(2)
|
|
440
|
-
expect(filtered[0].model.content).toBe('This is info for Barn owls')
|
|
441
|
-
expect(filtered[1].model.label?.text).toBe('Select from the list')
|
|
442
|
-
expect(filtered[1].model.items).toEqual([
|
|
443
|
-
{
|
|
444
|
-
checked: false,
|
|
445
|
-
condition: 'isBarnOwl',
|
|
446
|
-
selected: false,
|
|
447
|
-
text: 'Option 1',
|
|
448
|
-
value: '1'
|
|
449
|
-
},
|
|
450
|
-
{
|
|
451
|
-
checked: false,
|
|
452
|
-
condition: 'isBarnOwl',
|
|
453
|
-
selected: false,
|
|
454
|
-
text: 'Option 2',
|
|
455
|
-
value: '2'
|
|
456
|
-
}
|
|
457
|
-
])
|
|
458
|
-
})
|
|
459
|
-
|
|
460
|
-
it('filters on condition B', () => {
|
|
461
|
-
const { pages } = conditionalReveal
|
|
462
|
-
|
|
463
|
-
const model = new FormModel(conditionalReveal, {
|
|
464
|
-
basePath: 'test'
|
|
465
|
-
})
|
|
466
|
-
|
|
467
|
-
const controller = new QuestionPageController(model, pages[0])
|
|
468
|
-
|
|
469
|
-
// The state below shows we said we had a UKPassport and entered details for an applicant
|
|
470
|
-
const state: FormSubmissionState = {}
|
|
471
|
-
|
|
472
|
-
const request = {
|
|
473
|
-
method: 'get',
|
|
474
|
-
url: new URL('http://example.com/test/page-one'),
|
|
475
|
-
path: '/test/first-page',
|
|
476
|
-
params: {
|
|
477
|
-
path: 'first-page',
|
|
478
|
-
slug: 'test'
|
|
479
|
-
},
|
|
480
|
-
query: {},
|
|
481
|
-
app: { model }
|
|
482
|
-
} satisfies FormContextRequest
|
|
483
|
-
|
|
484
|
-
const context = controller.model.getFormContext(request, state)
|
|
485
|
-
const evaluationState = { animalType: 'Swan' }
|
|
486
|
-
|
|
487
|
-
const viewModel = controller.getViewModel(request, context)
|
|
488
|
-
|
|
489
|
-
const filtered = controller.filterConditionalComponents(
|
|
490
|
-
viewModel,
|
|
491
|
-
model,
|
|
492
|
-
evaluationState
|
|
493
|
-
)
|
|
494
|
-
|
|
495
|
-
expect(filtered).toHaveLength(2)
|
|
496
|
-
expect(filtered[0].model.content).toBe('This is info for other breeds')
|
|
497
|
-
expect(filtered[1].model.label?.text).toBe('Select from the list')
|
|
498
|
-
expect(filtered[1].model.items).toEqual([
|
|
499
|
-
{
|
|
500
|
-
checked: false,
|
|
501
|
-
condition: 'notBarnOwl',
|
|
502
|
-
selected: false,
|
|
503
|
-
text: 'Option 3',
|
|
504
|
-
value: '3'
|
|
505
|
-
},
|
|
506
|
-
{
|
|
507
|
-
checked: false,
|
|
508
|
-
condition: 'notBarnOwl',
|
|
509
|
-
selected: false,
|
|
510
|
-
text: 'Option 4',
|
|
511
|
-
value: '4'
|
|
512
|
-
}
|
|
513
|
-
])
|
|
514
|
-
})
|
|
515
|
-
})
|
|
516
|
-
|
|
517
|
-
describe('Form validation', () => {
|
|
518
|
-
it('includes all field errors', () => {
|
|
519
|
-
const result1 = controller1.collection.validate()
|
|
520
|
-
const result2 = controller2.collection.validate()
|
|
521
|
-
|
|
522
|
-
expect(result1.errors).toHaveLength(1)
|
|
523
|
-
expect(result2.errors).toHaveLength(4)
|
|
524
|
-
})
|
|
525
|
-
})
|
|
526
|
-
|
|
527
|
-
describe('Form journey', () => {
|
|
528
|
-
let context: FormContext
|
|
529
|
-
let contextNo: FormContext
|
|
530
|
-
let contextYes: FormContext
|
|
531
|
-
|
|
532
|
-
beforeEach(() => {
|
|
533
|
-
// Empty state
|
|
534
|
-
context = model.getFormContext(requestPage1, {})
|
|
535
|
-
|
|
536
|
-
// Question 1: Selected 'No'
|
|
537
|
-
contextNo = model.getFormContext(requestPage1, {
|
|
538
|
-
yesNoField: false
|
|
539
|
-
})
|
|
540
|
-
|
|
541
|
-
// Question 1: Selected 'Yes'
|
|
542
|
-
contextYes = model.getFormContext(requestPage1, {
|
|
543
|
-
yesNoField: true
|
|
544
|
-
})
|
|
545
|
-
})
|
|
546
|
-
|
|
547
|
-
describe('Next getter', () => {
|
|
548
|
-
it('returns the next page links', () => {
|
|
549
|
-
expect(controller1).toHaveProperty('next', [
|
|
550
|
-
{ path: '/second-page' },
|
|
551
|
-
{ path: '/summary', condition: 'isPreviouslyMarried' }
|
|
552
|
-
])
|
|
553
|
-
|
|
554
|
-
expect(controller2).toHaveProperty('next', [{ path: '/summary' }])
|
|
555
|
-
})
|
|
556
|
-
})
|
|
557
|
-
|
|
558
|
-
describe('Next', () => {
|
|
559
|
-
it('returns the next page path', () => {
|
|
560
|
-
expect(controller1.getNextPath(context)).toBe('/second-page')
|
|
561
|
-
expect(controller1.getNextPath(contextNo)).toBe('/second-page')
|
|
562
|
-
expect(controller1.getNextPath(contextYes)).toBe('/summary')
|
|
563
|
-
|
|
564
|
-
expect(controller2.getNextPath(context)).toBe('/summary')
|
|
565
|
-
expect(controller2.getNextPath(contextNo)).toBe('/summary')
|
|
566
|
-
expect(controller2.getNextPath(contextYes)).toBe('/summary')
|
|
567
|
-
})
|
|
568
|
-
})
|
|
569
|
-
|
|
570
|
-
describe('Summary', () => {
|
|
571
|
-
it('returns the summary path', () => {
|
|
572
|
-
expect(controller1.getSummaryPath()).toBe('/summary')
|
|
573
|
-
expect(controller2.getSummaryPath()).toBe('/summary')
|
|
574
|
-
})
|
|
575
|
-
})
|
|
576
|
-
})
|
|
577
|
-
|
|
578
|
-
describe('Route handlers', () => {
|
|
579
|
-
const response = {
|
|
580
|
-
code: jest.fn().mockImplementation(() => response)
|
|
581
|
-
}
|
|
582
|
-
|
|
583
|
-
const h: Pick<ResponseToolkit, 'redirect' | 'view'> = {
|
|
584
|
-
redirect: jest.fn().mockReturnValue(response),
|
|
585
|
-
view: jest.fn()
|
|
586
|
-
}
|
|
587
|
-
|
|
588
|
-
it('returns default route options', () => {
|
|
589
|
-
expect(controller1.getRouteOptions).toMatchObject({
|
|
590
|
-
ext: {
|
|
591
|
-
onPostHandler: {
|
|
592
|
-
method: expect.any(Function)
|
|
593
|
-
}
|
|
594
|
-
}
|
|
595
|
-
})
|
|
596
|
-
|
|
597
|
-
expect(controller1.postRouteOptions).toMatchObject({
|
|
598
|
-
ext: {
|
|
599
|
-
onPostHandler: {
|
|
600
|
-
method: expect.any(Function)
|
|
601
|
-
}
|
|
602
|
-
}
|
|
603
|
-
})
|
|
604
|
-
})
|
|
605
|
-
|
|
606
|
-
it('supports GET route handler', async () => {
|
|
607
|
-
const state: FormState = { yesNoField: false }
|
|
608
|
-
|
|
609
|
-
for (const controller of [controller1, controller2]) {
|
|
610
|
-
jest
|
|
611
|
-
.spyOn(controller, 'hasMissingNotificationEmail')
|
|
612
|
-
.mockResolvedValue(false)
|
|
613
|
-
|
|
614
|
-
jest.spyOn(controller, 'getState').mockResolvedValue(state)
|
|
615
|
-
}
|
|
616
|
-
|
|
617
|
-
expect(() => controller1.makeGetRouteHandler()).not.toThrow()
|
|
618
|
-
expect(() => controller1.makeGetRouteHandler()).toBeInstanceOf(Function)
|
|
619
|
-
|
|
620
|
-
await controller1.makeGetRouteHandler()(
|
|
621
|
-
requestPage1,
|
|
622
|
-
model.getFormContext(requestPage1, {}),
|
|
623
|
-
h
|
|
624
|
-
)
|
|
625
|
-
|
|
626
|
-
await controller2.makeGetRouteHandler()(
|
|
627
|
-
requestPage2,
|
|
628
|
-
model.getFormContext(requestPage2, {}),
|
|
629
|
-
h
|
|
630
|
-
)
|
|
631
|
-
|
|
632
|
-
expect(h.view).toHaveBeenNthCalledWith(
|
|
633
|
-
1,
|
|
634
|
-
controller1.viewName,
|
|
635
|
-
expect.objectContaining({
|
|
636
|
-
pageTitle: 'Previous marriages',
|
|
637
|
-
sectionTitle: undefined
|
|
638
|
-
})
|
|
639
|
-
)
|
|
640
|
-
|
|
641
|
-
expect(h.view).toHaveBeenNthCalledWith(
|
|
642
|
-
2,
|
|
643
|
-
controller2.viewName,
|
|
644
|
-
expect.objectContaining({
|
|
645
|
-
pageTitle: 'When will you get married?',
|
|
646
|
-
sectionTitle: 'Your marriage'
|
|
647
|
-
})
|
|
648
|
-
)
|
|
649
|
-
})
|
|
650
|
-
})
|
|
651
|
-
|
|
652
|
-
describe('State', () => {
|
|
653
|
-
beforeEach(() => {
|
|
654
|
-
jest.spyOn(CacheService.prototype, 'getState')
|
|
655
|
-
jest.spyOn(CacheService.prototype, 'setState')
|
|
656
|
-
|
|
657
|
-
// Preview URL '?force'
|
|
658
|
-
requestPage1.query.force = ''
|
|
659
|
-
})
|
|
660
|
-
|
|
661
|
-
describe('getState', () => {
|
|
662
|
-
it('should skip get for preview URL direct access', async () => {
|
|
663
|
-
const state = await controller1.getState(requestPage1)
|
|
664
|
-
|
|
665
|
-
expect(state).toEqual({})
|
|
666
|
-
expect(CacheService.prototype.getState).not.toHaveBeenCalled()
|
|
667
|
-
})
|
|
668
|
-
})
|
|
669
|
-
|
|
670
|
-
describe('setState', () => {
|
|
671
|
-
it('should skip set for preview URL direct access', async () => {
|
|
672
|
-
const state: FormSubmissionState = { yesNoField: false }
|
|
673
|
-
const updated = await controller1.setState(requestPage1, state)
|
|
674
|
-
|
|
675
|
-
expect(updated).toBe(state)
|
|
676
|
-
expect(CacheService.prototype.setState).not.toHaveBeenCalled()
|
|
677
|
-
})
|
|
678
|
-
})
|
|
679
|
-
|
|
680
|
-
describe('mergeState', () => {
|
|
681
|
-
it('should skip merge for preview URL direct access', async () => {
|
|
682
|
-
const state: FormSubmissionState = {
|
|
683
|
-
yesNoField: false
|
|
684
|
-
}
|
|
685
|
-
|
|
686
|
-
const update: FormSubmissionState = {
|
|
687
|
-
dateField__day: 5,
|
|
688
|
-
dateField__month: 1,
|
|
689
|
-
dateField__year: 2024
|
|
690
|
-
}
|
|
691
|
-
|
|
692
|
-
const updated = await controller1.mergeState(
|
|
693
|
-
requestPage1,
|
|
694
|
-
state,
|
|
695
|
-
update
|
|
696
|
-
)
|
|
697
|
-
|
|
698
|
-
expect(updated).toEqual({
|
|
699
|
-
yesNoField: false,
|
|
700
|
-
dateField__day: 5,
|
|
701
|
-
dateField__month: 1,
|
|
702
|
-
dateField__year: 2024
|
|
703
|
-
})
|
|
704
|
-
|
|
705
|
-
expect(CacheService.prototype.setState).not.toHaveBeenCalled()
|
|
706
|
-
})
|
|
707
|
-
})
|
|
708
|
-
})
|
|
709
|
-
})
|
|
710
|
-
|
|
711
|
-
describe('QuestionPageController V2', () => {
|
|
712
|
-
let page1Url: URL
|
|
713
|
-
let page2Url: URL
|
|
714
|
-
|
|
715
|
-
let model: FormModel
|
|
716
|
-
let controller1: QuestionPageController
|
|
717
|
-
let controller2: QuestionPageController
|
|
718
|
-
let requestPage1: FormRequest
|
|
719
|
-
let requestPage2: FormRequest
|
|
720
|
-
|
|
721
|
-
beforeEach(() => {
|
|
722
|
-
page1Url = new URL('http://example.com/test/first-page')
|
|
723
|
-
page2Url = new URL('http://example.com/test/second-page')
|
|
724
|
-
|
|
725
|
-
model = new FormModel(definitionConditionsBasicV2, {
|
|
726
|
-
basePath: 'test'
|
|
727
|
-
})
|
|
728
|
-
|
|
729
|
-
controller1 = model.pages[0] // new QuestionPageController(model, page1)
|
|
730
|
-
controller2 = model.pages[1] // new QuestionPageController(model, page2)
|
|
731
|
-
|
|
732
|
-
requestPage1 = {
|
|
733
|
-
method: 'get',
|
|
734
|
-
url: page1Url,
|
|
735
|
-
path: page1Url.pathname,
|
|
736
|
-
params: {
|
|
737
|
-
path: 'first-page',
|
|
738
|
-
slug: 'test'
|
|
739
|
-
},
|
|
740
|
-
query: {},
|
|
741
|
-
app: { model }
|
|
742
|
-
} as FormRequest
|
|
743
|
-
|
|
744
|
-
requestPage2 = {
|
|
745
|
-
method: 'get',
|
|
746
|
-
url: page2Url,
|
|
747
|
-
path: page2Url.pathname,
|
|
748
|
-
params: {
|
|
749
|
-
path: 'second-page',
|
|
750
|
-
slug: 'test'
|
|
751
|
-
},
|
|
752
|
-
query: {},
|
|
753
|
-
app: { model }
|
|
754
|
-
} as FormRequest
|
|
755
|
-
})
|
|
756
|
-
|
|
757
|
-
describe('Properties', () => {
|
|
758
|
-
it('returns path', () => {
|
|
759
|
-
expect(controller1).toHaveProperty('path', '/first-page')
|
|
760
|
-
expect(controller2).toHaveProperty('path', '/second-page')
|
|
761
|
-
})
|
|
762
|
-
|
|
763
|
-
it('returns href', () => {
|
|
764
|
-
expect(controller1).toHaveProperty('href', '/test/first-page')
|
|
765
|
-
expect(controller2).toHaveProperty('href', '/test/second-page')
|
|
766
|
-
})
|
|
767
|
-
|
|
768
|
-
it('returns keys', () => {
|
|
769
|
-
expect(controller1).toHaveProperty('keys', ['yesNoField'])
|
|
770
|
-
expect(controller2).toHaveProperty('keys', [
|
|
771
|
-
'dateField',
|
|
772
|
-
'dateField__day',
|
|
773
|
-
'dateField__month',
|
|
774
|
-
'dateField__year',
|
|
775
|
-
'multilineTextField'
|
|
776
|
-
])
|
|
777
|
-
})
|
|
778
|
-
|
|
779
|
-
it('returns the page section', () => {
|
|
780
|
-
expect(controller1).toHaveProperty('section', undefined)
|
|
781
|
-
expect(controller2).toHaveProperty('section', {
|
|
782
|
-
name: 'marriage',
|
|
783
|
-
title: 'Your marriage',
|
|
784
|
-
hideTitle: false
|
|
785
|
-
})
|
|
786
|
-
})
|
|
787
|
-
})
|
|
788
|
-
|
|
789
|
-
describe('Path methods', () => {
|
|
790
|
-
describe('Next getter', () => {
|
|
791
|
-
it('returns page links', () => {
|
|
792
|
-
expect(controller1).toHaveProperty('next', [])
|
|
793
|
-
|
|
794
|
-
expect(controller2).toHaveProperty('next', [])
|
|
795
|
-
})
|
|
796
|
-
|
|
797
|
-
it('returns page links (none found)', () => {
|
|
798
|
-
const pageDef1 = structuredClone(controller1.pageDef)
|
|
799
|
-
const pageDef2 = structuredClone(controller2.pageDef)
|
|
800
|
-
|
|
801
|
-
controller1.pageDef = pageDef1
|
|
802
|
-
controller2.pageDef = pageDef2
|
|
803
|
-
|
|
804
|
-
// @ts-expect-error - Allow invalid property for test
|
|
805
|
-
controller1.pageDef.next = []
|
|
806
|
-
|
|
807
|
-
// @ts-expect-error - Allow invalid property for test
|
|
808
|
-
delete controller2.pageDef.next
|
|
809
|
-
|
|
810
|
-
expect(controller1).toHaveProperty('next', [])
|
|
811
|
-
expect(controller2).toHaveProperty('next', [])
|
|
812
|
-
})
|
|
813
|
-
})
|
|
814
|
-
})
|
|
815
|
-
|
|
816
|
-
describe('Component collection', () => {
|
|
817
|
-
it('returns the components for the page', () => {
|
|
818
|
-
const { components: components1 } = controller1.collection
|
|
819
|
-
const { components: components2 } = controller2.collection
|
|
820
|
-
|
|
821
|
-
expect(components1).toHaveLength(1)
|
|
822
|
-
expect(components1[0].name).toBe('yesNoField')
|
|
823
|
-
|
|
824
|
-
expect(components2).toHaveLength(3)
|
|
825
|
-
expect(components2[0].name).toBe('detailsField')
|
|
826
|
-
expect(components2[1].name).toBe('dateField')
|
|
827
|
-
expect(components2[2].name).toBe('multilineTextField')
|
|
828
|
-
})
|
|
829
|
-
|
|
830
|
-
it('returns the fields for the page', () => {
|
|
831
|
-
const { fields: fields1 } = controller1.collection
|
|
832
|
-
const { fields: fields2 } = controller2.collection
|
|
833
|
-
|
|
834
|
-
expect(fields1).toHaveLength(1)
|
|
835
|
-
expect(fields1[0].name).toBe('yesNoField')
|
|
836
|
-
|
|
837
|
-
expect(fields2).toHaveLength(2)
|
|
838
|
-
expect(fields2[0].name).toBe('dateField')
|
|
839
|
-
expect(fields2[1].name).toBe('multilineTextField')
|
|
840
|
-
})
|
|
841
|
-
|
|
842
|
-
it('returns the guidance for the page', () => {
|
|
843
|
-
const { guidance: guidance1 } = controller1.collection
|
|
844
|
-
const { guidance: guidance2 } = controller2.collection
|
|
845
|
-
|
|
846
|
-
expect(guidance1).toHaveLength(0)
|
|
847
|
-
expect(guidance2).toHaveLength(1)
|
|
848
|
-
expect(guidance2[0].name).toBe('detailsField')
|
|
849
|
-
})
|
|
850
|
-
})
|
|
851
|
-
|
|
852
|
-
describe('Component view models', () => {
|
|
853
|
-
let viewModel1: FormPageViewModel
|
|
854
|
-
let viewModel2: FormPageViewModel
|
|
855
|
-
|
|
856
|
-
beforeEach(() => {
|
|
857
|
-
viewModel1 = controller1.getViewModel(
|
|
858
|
-
requestPage1,
|
|
859
|
-
model.getFormContext(requestPage1, {})
|
|
860
|
-
)
|
|
861
|
-
|
|
862
|
-
viewModel2 = controller2.getViewModel(
|
|
863
|
-
requestPage2,
|
|
864
|
-
model.getFormContext(requestPage2, {})
|
|
865
|
-
)
|
|
866
|
-
})
|
|
867
|
-
|
|
868
|
-
it('hides the page title for single form component pages', () => {
|
|
869
|
-
// Page 1 hides page title (single component)
|
|
870
|
-
expect(viewModel1).toHaveProperty('showTitle', false)
|
|
871
|
-
expect(viewModel1).toHaveProperty('pageTitle', 'Previous marriages')
|
|
872
|
-
|
|
873
|
-
// Page 2 shows page title (multiple components)
|
|
874
|
-
expect(viewModel2).toHaveProperty('showTitle', true)
|
|
875
|
-
expect(viewModel2).toHaveProperty(
|
|
876
|
-
'pageTitle',
|
|
877
|
-
'When will you get married?'
|
|
878
|
-
)
|
|
879
|
-
})
|
|
880
|
-
|
|
881
|
-
it('returns the component view models for the page', () => {
|
|
882
|
-
const { components: components1 } = viewModel1
|
|
883
|
-
const { components: components2 } = viewModel2
|
|
884
|
-
|
|
885
|
-
expect(components1).toHaveLength(1)
|
|
886
|
-
expect(components2).toHaveLength(3)
|
|
887
|
-
|
|
888
|
-
// Page 1, component 1, default label
|
|
889
|
-
expect(components1[0].model).toHaveProperty('label', {
|
|
890
|
-
text: 'Have you previously been married?'
|
|
891
|
-
})
|
|
892
|
-
|
|
893
|
-
// Page 1, component 1, optional legend
|
|
894
|
-
expect(components1[0].model).toHaveProperty('fieldset', {
|
|
895
|
-
legend: {
|
|
896
|
-
text: 'Previous marriages',
|
|
897
|
-
classes: 'govuk-fieldset__legend--l',
|
|
898
|
-
isPageHeading: true
|
|
899
|
-
}
|
|
900
|
-
})
|
|
901
|
-
|
|
902
|
-
// Page 2, component 1, content only
|
|
903
|
-
expect(components2[0].model).toEqual({
|
|
904
|
-
attributes: {},
|
|
905
|
-
summaryHtml: 'Find out more',
|
|
906
|
-
html: 'Some content goes here'
|
|
907
|
-
})
|
|
908
|
-
|
|
909
|
-
// Page 2, component 2, default label
|
|
910
|
-
expect(components2[1].model).toHaveProperty('label', {
|
|
911
|
-
text: 'Date of marriage',
|
|
912
|
-
classes: 'govuk-label--m'
|
|
913
|
-
})
|
|
914
|
-
|
|
915
|
-
// Page 2, component 2, optional legend
|
|
916
|
-
expect(components2[1].model).toHaveProperty('fieldset', {
|
|
917
|
-
legend: {
|
|
918
|
-
text: 'Date of marriage',
|
|
919
|
-
classes: 'govuk-fieldset__legend--m'
|
|
920
|
-
}
|
|
921
|
-
})
|
|
922
|
-
|
|
923
|
-
// Page 2, component 3, default label
|
|
924
|
-
expect(components2[2].model).toHaveProperty('label', {
|
|
925
|
-
text: 'Remarks',
|
|
926
|
-
classes: 'govuk-label--m'
|
|
927
|
-
})
|
|
928
|
-
|
|
929
|
-
// Page 2, component 3, optional legend
|
|
930
|
-
expect(components2[2].model).not.toHaveProperty('fieldset')
|
|
931
|
-
})
|
|
932
|
-
})
|
|
933
|
-
|
|
934
|
-
describe('Condition evaluation context', () => {
|
|
935
|
-
it('filters state by journey pages', () => {
|
|
936
|
-
const { pages } = definitionConditionsComplex
|
|
937
|
-
|
|
938
|
-
const model = new FormModel(definitionConditionsComplex, {
|
|
939
|
-
basePath: 'test'
|
|
940
|
-
})
|
|
941
|
-
|
|
942
|
-
// Selected page appears after convergence and contains a conditional field
|
|
943
|
-
// This is the page we're theoretically browsing to
|
|
944
|
-
const controller = new QuestionPageController(model, pages[7])
|
|
945
|
-
|
|
946
|
-
// The state below shows we said we had a UKPassport and entered details for an applicant
|
|
947
|
-
const state: FormSubmissionState = {
|
|
948
|
-
ukPassport: true,
|
|
949
|
-
numberOfApplicants: 2,
|
|
950
|
-
applicantOneFirstName: 'Enrique',
|
|
951
|
-
applicantOneMiddleName: null,
|
|
952
|
-
applicantOneLastName: 'Chase',
|
|
953
|
-
applicantOneAddress__addressLine1: 'AddressLine1',
|
|
954
|
-
applicantOneAddress__addressLine2: 'AddressLine2',
|
|
955
|
-
applicantOneAddress__town: 'Town',
|
|
956
|
-
applicantOneAddress__postcode: 'Postcode',
|
|
957
|
-
applicantTwoFirstName: 'John',
|
|
958
|
-
applicantTwoMiddleName: null,
|
|
959
|
-
applicantTwoLastName: 'Doe',
|
|
960
|
-
applicantTwoAddress__addressLine1: 'AddressLine1',
|
|
961
|
-
applicantTwoAddress__addressLine2: 'AddressLine2',
|
|
962
|
-
applicantTwoAddress__town: 'Town',
|
|
963
|
-
applicantTwoAddress__postcode: 'Postcode'
|
|
964
|
-
}
|
|
965
|
-
|
|
966
|
-
let request = {
|
|
967
|
-
method: 'get',
|
|
968
|
-
url: new URL('http://example.com/test/applicant-one-address'),
|
|
969
|
-
path: '/test/applicant-one-address',
|
|
970
|
-
params: {
|
|
971
|
-
path: 'applicant-one-address',
|
|
972
|
-
slug: 'test'
|
|
973
|
-
},
|
|
974
|
-
query: {},
|
|
975
|
-
app: { model }
|
|
976
|
-
} satisfies FormContextRequest
|
|
977
|
-
|
|
978
|
-
// Calculate our context based on the page we're attempting to load and the above state we provide
|
|
979
|
-
let context = controller.model.getFormContext(request, state)
|
|
980
|
-
const { evaluationState: stateBefore } = context
|
|
981
|
-
|
|
982
|
-
// Our context should know our first applicant
|
|
983
|
-
expect(stateBefore).toEqual({
|
|
984
|
-
numberOfApplicants: 2,
|
|
985
|
-
ukPassport: true,
|
|
986
|
-
applicantOneFirstName: 'Enrique',
|
|
987
|
-
applicantOneMiddleName: null,
|
|
988
|
-
applicantOneLastName: 'Chase',
|
|
989
|
-
applicantOneAddress: [
|
|
990
|
-
'AddressLine1',
|
|
991
|
-
'AddressLine2',
|
|
992
|
-
'Town',
|
|
993
|
-
'Postcode'
|
|
994
|
-
]
|
|
995
|
-
})
|
|
996
|
-
|
|
997
|
-
// Our context should know which pages are relevant
|
|
998
|
-
expect(context.paths).toEqual([
|
|
999
|
-
'/uk-passport',
|
|
1000
|
-
'/how-many-people',
|
|
1001
|
-
'/applicant-one-name',
|
|
1002
|
-
'/applicant-one-address'
|
|
1003
|
-
])
|
|
1004
|
-
|
|
1005
|
-
// Now mark that we don't have a UK Passport
|
|
1006
|
-
state.ukPassport = false
|
|
1007
|
-
|
|
1008
|
-
request = {
|
|
1009
|
-
method: 'get',
|
|
1010
|
-
url: new URL('http://example.com/test/summary'),
|
|
1011
|
-
path: '/test/summary',
|
|
1012
|
-
params: {
|
|
1013
|
-
path: 'summary',
|
|
1014
|
-
slug: 'test'
|
|
1015
|
-
},
|
|
1016
|
-
query: {},
|
|
1017
|
-
app: { model }
|
|
1018
|
-
} satisfies FormContextRequest
|
|
1019
|
-
|
|
1020
|
-
// And recalculate our context
|
|
1021
|
-
context = controller.model.getFormContext(request, state)
|
|
1022
|
-
const { evaluationState: stateAfter } = context
|
|
1023
|
-
|
|
1024
|
-
// Our context should no longer list pages about our applicant
|
|
1025
|
-
expect(context.paths).toEqual([
|
|
1026
|
-
'/uk-passport',
|
|
1027
|
-
'/testconditions',
|
|
1028
|
-
'/summary'
|
|
1029
|
-
])
|
|
1030
|
-
|
|
1031
|
-
// Our context should no longer know anything about our applicant
|
|
1032
|
-
expect(stateAfter).not.toHaveProperty('numberOfApplicants')
|
|
1033
|
-
expect(stateAfter).not.toHaveProperty('applicantOneFirstName')
|
|
1034
|
-
expect(stateAfter).not.toHaveProperty('applicantOneMiddleName')
|
|
1035
|
-
expect(stateAfter).not.toHaveProperty('applicantOneLastName')
|
|
1036
|
-
expect(stateAfter).not.toHaveProperty('applicantOneAddress')
|
|
1037
|
-
|
|
1038
|
-
expect(stateAfter).toEqual({
|
|
1039
|
-
ukPassport: false
|
|
1040
|
-
})
|
|
1041
|
-
})
|
|
1042
|
-
|
|
1043
|
-
it('combines state values for date fields', () => {
|
|
1044
|
-
const { pages } = definitionConditionsDates
|
|
1045
|
-
|
|
1046
|
-
const model = new FormModel(definitionConditionsDates, {
|
|
1047
|
-
basePath: 'test'
|
|
1048
|
-
})
|
|
1049
|
-
|
|
1050
|
-
const controller = new QuestionPageController(model, pages[0])
|
|
1051
|
-
|
|
1052
|
-
const request = {
|
|
1053
|
-
method: 'get',
|
|
1054
|
-
url: new URL('http://example.com/test/page-one'),
|
|
1055
|
-
path: '/test/page-one',
|
|
1056
|
-
params: {
|
|
1057
|
-
path: 'page-one',
|
|
1058
|
-
slug: 'test'
|
|
1059
|
-
},
|
|
1060
|
-
query: {},
|
|
1061
|
-
app: { model }
|
|
1062
|
-
} satisfies FormContextRequest
|
|
1063
|
-
|
|
1064
|
-
const context = controller.model.getFormContext(request, {
|
|
1065
|
-
dateField__day: 5,
|
|
1066
|
-
dateField__month: 1,
|
|
1067
|
-
dateField__year: 2024
|
|
1068
|
-
})
|
|
1069
|
-
|
|
1070
|
-
// Ensure dates are transformed to yyyy-MM-dd format
|
|
1071
|
-
expect(context.evaluationState).toHaveProperty('dateField', '2024-01-05')
|
|
1072
|
-
|
|
1073
|
-
// Unlike relevant state which has the individual date parts
|
|
1074
|
-
expect(context.relevantState).not.toHaveProperty('dateField')
|
|
1075
|
-
expect(context.relevantState).toMatchObject({
|
|
1076
|
-
dateField__day: 5,
|
|
1077
|
-
dateField__month: 1,
|
|
1078
|
-
dateField__year: 2024
|
|
1079
|
-
})
|
|
1080
|
-
})
|
|
1081
|
-
})
|
|
1082
|
-
|
|
1083
|
-
describe('Form validation', () => {
|
|
1084
|
-
it('includes all field errors', () => {
|
|
1085
|
-
const result1 = controller1.collection.validate()
|
|
1086
|
-
const result2 = controller2.collection.validate()
|
|
1087
|
-
|
|
1088
|
-
expect(result1.errors).toHaveLength(1)
|
|
1089
|
-
expect(result2.errors).toHaveLength(4)
|
|
1090
|
-
})
|
|
1091
|
-
})
|
|
1092
|
-
|
|
1093
|
-
describe('Form journey', () => {
|
|
1094
|
-
let context: FormContext
|
|
1095
|
-
let contextNo: FormContext
|
|
1096
|
-
let contextYes: FormContext
|
|
1097
|
-
|
|
1098
|
-
beforeEach(() => {
|
|
1099
|
-
// Empty state
|
|
1100
|
-
context = model.getFormContext(requestPage1, {})
|
|
1101
|
-
|
|
1102
|
-
// Question 1: Selected 'No'
|
|
1103
|
-
contextNo = model.getFormContext(requestPage1, {
|
|
1104
|
-
yesNoField: false
|
|
1105
|
-
})
|
|
1106
|
-
|
|
1107
|
-
// Question 1: Selected 'Yes'
|
|
1108
|
-
contextYes = model.getFormContext(requestPage1, {
|
|
1109
|
-
yesNoField: true
|
|
1110
|
-
})
|
|
1111
|
-
})
|
|
1112
|
-
|
|
1113
|
-
describe('Next', () => {
|
|
1114
|
-
it('returns the next page path', () => {
|
|
1115
|
-
expect(controller1.getNextPath(context)).toBe('/summary')
|
|
1116
|
-
expect(controller1.getNextPath(contextNo)).toBe('/second-page')
|
|
1117
|
-
expect(controller1.getNextPath(contextYes)).toBe('/summary')
|
|
1118
|
-
|
|
1119
|
-
expect(controller2.getNextPath(context)).toBe('/summary')
|
|
1120
|
-
expect(controller2.getNextPath(contextNo)).toBe('/summary')
|
|
1121
|
-
expect(controller2.getNextPath(contextYes)).toBe('/summary')
|
|
1122
|
-
})
|
|
1123
|
-
})
|
|
1124
|
-
|
|
1125
|
-
describe('Summary', () => {
|
|
1126
|
-
it('returns the summary path', () => {
|
|
1127
|
-
expect(controller1.getSummaryPath()).toBe('/summary')
|
|
1128
|
-
expect(controller2.getSummaryPath()).toBe('/summary')
|
|
1129
|
-
})
|
|
1130
|
-
})
|
|
1131
|
-
})
|
|
1132
|
-
|
|
1133
|
-
describe('Route handlers', () => {
|
|
1134
|
-
const response = {
|
|
1135
|
-
code: jest.fn().mockImplementation(() => response)
|
|
1136
|
-
}
|
|
1137
|
-
|
|
1138
|
-
const h: Pick<ResponseToolkit, 'redirect' | 'view'> = {
|
|
1139
|
-
redirect: jest.fn().mockReturnValue(response),
|
|
1140
|
-
view: jest.fn()
|
|
1141
|
-
}
|
|
1142
|
-
|
|
1143
|
-
it('returns default route options', () => {
|
|
1144
|
-
expect(controller1.getRouteOptions).toMatchObject({
|
|
1145
|
-
ext: {
|
|
1146
|
-
onPostHandler: {
|
|
1147
|
-
method: expect.any(Function)
|
|
1148
|
-
}
|
|
1149
|
-
}
|
|
1150
|
-
})
|
|
1151
|
-
|
|
1152
|
-
expect(controller1.postRouteOptions).toMatchObject({
|
|
1153
|
-
ext: {
|
|
1154
|
-
onPostHandler: {
|
|
1155
|
-
method: expect.any(Function)
|
|
1156
|
-
}
|
|
1157
|
-
}
|
|
1158
|
-
})
|
|
1159
|
-
})
|
|
1160
|
-
|
|
1161
|
-
it('supports GET route handler', async () => {
|
|
1162
|
-
const state: FormState = { yesNoField: false }
|
|
1163
|
-
|
|
1164
|
-
for (const controller of [controller1, controller2]) {
|
|
1165
|
-
jest
|
|
1166
|
-
.spyOn(controller, 'hasMissingNotificationEmail')
|
|
1167
|
-
.mockResolvedValue(false)
|
|
1168
|
-
|
|
1169
|
-
jest.spyOn(controller, 'getState').mockResolvedValue(state)
|
|
1170
|
-
}
|
|
1171
|
-
|
|
1172
|
-
expect(() => controller1.makeGetRouteHandler()).not.toThrow()
|
|
1173
|
-
expect(() => controller1.makeGetRouteHandler()).toBeInstanceOf(Function)
|
|
1174
|
-
|
|
1175
|
-
await controller1.makeGetRouteHandler()(
|
|
1176
|
-
requestPage1,
|
|
1177
|
-
model.getFormContext(requestPage1, {}),
|
|
1178
|
-
h
|
|
1179
|
-
)
|
|
1180
|
-
|
|
1181
|
-
await controller2.makeGetRouteHandler()(
|
|
1182
|
-
requestPage2,
|
|
1183
|
-
model.getFormContext(requestPage2, {}),
|
|
1184
|
-
h
|
|
1185
|
-
)
|
|
1186
|
-
|
|
1187
|
-
expect(h.view).toHaveBeenNthCalledWith(
|
|
1188
|
-
1,
|
|
1189
|
-
controller1.viewName,
|
|
1190
|
-
expect.objectContaining({
|
|
1191
|
-
pageTitle: 'Previous marriages',
|
|
1192
|
-
sectionTitle: undefined
|
|
1193
|
-
})
|
|
1194
|
-
)
|
|
1195
|
-
|
|
1196
|
-
expect(h.view).toHaveBeenNthCalledWith(
|
|
1197
|
-
2,
|
|
1198
|
-
controller2.viewName,
|
|
1199
|
-
expect.objectContaining({
|
|
1200
|
-
pageTitle: 'When will you get married?',
|
|
1201
|
-
sectionTitle: 'Your marriage'
|
|
1202
|
-
})
|
|
1203
|
-
)
|
|
1204
|
-
})
|
|
1205
|
-
})
|
|
1206
|
-
|
|
1207
|
-
describe('State', () => {
|
|
1208
|
-
beforeEach(() => {
|
|
1209
|
-
jest.spyOn(CacheService.prototype, 'getState')
|
|
1210
|
-
jest.spyOn(CacheService.prototype, 'setState')
|
|
1211
|
-
|
|
1212
|
-
// Preview URL '?force'
|
|
1213
|
-
requestPage1.query.force = ''
|
|
1214
|
-
})
|
|
1215
|
-
|
|
1216
|
-
describe('getState', () => {
|
|
1217
|
-
it('should skip get for preview URL direct access', async () => {
|
|
1218
|
-
const state = await controller1.getState(requestPage1)
|
|
1219
|
-
|
|
1220
|
-
expect(state).toEqual({})
|
|
1221
|
-
expect(CacheService.prototype.getState).not.toHaveBeenCalled()
|
|
1222
|
-
})
|
|
1223
|
-
})
|
|
1224
|
-
|
|
1225
|
-
describe('setState', () => {
|
|
1226
|
-
it('should skip set for preview URL direct access', async () => {
|
|
1227
|
-
const state: FormSubmissionState = { yesNoField: false }
|
|
1228
|
-
const updated = await controller1.setState(requestPage1, state)
|
|
1229
|
-
|
|
1230
|
-
expect(updated).toBe(state)
|
|
1231
|
-
expect(CacheService.prototype.setState).not.toHaveBeenCalled()
|
|
1232
|
-
})
|
|
1233
|
-
})
|
|
1234
|
-
|
|
1235
|
-
describe('mergeState', () => {
|
|
1236
|
-
it('should skip merge for preview URL direct access', async () => {
|
|
1237
|
-
const state: FormSubmissionState = {
|
|
1238
|
-
yesNoField: false
|
|
1239
|
-
}
|
|
1240
|
-
|
|
1241
|
-
const update: FormSubmissionState = {
|
|
1242
|
-
dateField__day: 5,
|
|
1243
|
-
dateField__month: 1,
|
|
1244
|
-
dateField__year: 2024
|
|
1245
|
-
}
|
|
1246
|
-
|
|
1247
|
-
const updated = await controller1.mergeState(
|
|
1248
|
-
requestPage1,
|
|
1249
|
-
state,
|
|
1250
|
-
update
|
|
1251
|
-
)
|
|
1252
|
-
|
|
1253
|
-
expect(updated).toEqual({
|
|
1254
|
-
yesNoField: false,
|
|
1255
|
-
dateField__day: 5,
|
|
1256
|
-
dateField__month: 1,
|
|
1257
|
-
dateField__year: 2024
|
|
1258
|
-
})
|
|
1259
|
-
|
|
1260
|
-
expect(CacheService.prototype.setState).not.toHaveBeenCalled()
|
|
1261
|
-
})
|
|
1262
|
-
})
|
|
1263
|
-
})
|
|
1264
|
-
})
|