@defra/forms-engine-plugin 0.0.3 → 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/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,446 +0,0 @@
|
|
|
1
|
-
import { ComponentType, type PageFileUpload } from '@defra/forms-model'
|
|
2
|
-
import Boom from '@hapi/boom'
|
|
3
|
-
import { type ResponseToolkit } from '@hapi/hapi'
|
|
4
|
-
import { wait } from '@hapi/hoek'
|
|
5
|
-
import { type ValidationErrorItem } from 'joi'
|
|
6
|
-
|
|
7
|
-
import {
|
|
8
|
-
tempItemSchema,
|
|
9
|
-
type FileUploadField
|
|
10
|
-
} from '~/src/server/plugins/engine/components/FileUploadField.js'
|
|
11
|
-
import {
|
|
12
|
-
getError,
|
|
13
|
-
getExponentialBackoffDelay
|
|
14
|
-
} from '~/src/server/plugins/engine/helpers.js'
|
|
15
|
-
import { type FormModel } from '~/src/server/plugins/engine/models/index.js'
|
|
16
|
-
import { QuestionPageController } from '~/src/server/plugins/engine/pageControllers/QuestionPageController.js'
|
|
17
|
-
import { getProxyUrlForLocalDevelopment } from '~/src/server/plugins/engine/pageControllers/helpers.js'
|
|
18
|
-
import {
|
|
19
|
-
getUploadStatus,
|
|
20
|
-
initiateUpload
|
|
21
|
-
} from '~/src/server/plugins/engine/services/uploadService.js'
|
|
22
|
-
import {
|
|
23
|
-
FileStatus,
|
|
24
|
-
UploadStatus,
|
|
25
|
-
type FeaturedFormPageViewModel,
|
|
26
|
-
type FileState,
|
|
27
|
-
type FormContext,
|
|
28
|
-
type FormContextRequest,
|
|
29
|
-
type FormSubmissionError,
|
|
30
|
-
type FormSubmissionState,
|
|
31
|
-
type ItemDeletePageViewModel,
|
|
32
|
-
type UploadInitiateResponse,
|
|
33
|
-
type UploadStatusFileResponse
|
|
34
|
-
} from '~/src/server/plugins/engine/types.js'
|
|
35
|
-
import {
|
|
36
|
-
type FormRequest,
|
|
37
|
-
type FormRequestPayload
|
|
38
|
-
} from '~/src/server/routes/types.js'
|
|
39
|
-
|
|
40
|
-
const MAX_UPLOADS = 25
|
|
41
|
-
const CDP_UPLOAD_TIMEOUT_MS = 60000 // 1 minute
|
|
42
|
-
|
|
43
|
-
export function prepareStatus(status: UploadStatusFileResponse) {
|
|
44
|
-
const file = status.form.file
|
|
45
|
-
const isPending = file.fileStatus === FileStatus.pending
|
|
46
|
-
|
|
47
|
-
if (!file.errorMessage && isPending) {
|
|
48
|
-
file.errorMessage = 'The selected file has not fully uploaded'
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
return status
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
function prepareFileState(fileState: FileState) {
|
|
55
|
-
prepareStatus(fileState.status)
|
|
56
|
-
|
|
57
|
-
return fileState
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
export class FileUploadPageController extends QuestionPageController {
|
|
61
|
-
declare pageDef: PageFileUpload
|
|
62
|
-
|
|
63
|
-
fileUpload: FileUploadField
|
|
64
|
-
fileDeleteViewName = 'item-delete'
|
|
65
|
-
|
|
66
|
-
constructor(model: FormModel, pageDef: PageFileUpload) {
|
|
67
|
-
super(model, pageDef)
|
|
68
|
-
|
|
69
|
-
const { collection } = this
|
|
70
|
-
|
|
71
|
-
// Get the file upload fields from the collection
|
|
72
|
-
const fileUploads = collection.fields.filter(
|
|
73
|
-
(field): field is FileUploadField =>
|
|
74
|
-
field.type === ComponentType.FileUploadField
|
|
75
|
-
)
|
|
76
|
-
|
|
77
|
-
const fileUpload = fileUploads.at(0)
|
|
78
|
-
|
|
79
|
-
// Assert we have exactly 1 file upload component
|
|
80
|
-
if (!fileUpload || fileUploads.length > 1) {
|
|
81
|
-
throw Boom.badImplementation(
|
|
82
|
-
`Expected 1 FileUploadFieldComponent in FileUploadPageController '${pageDef.path}'`
|
|
83
|
-
)
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
// Assert the file upload component is the first form component
|
|
87
|
-
if (collection.fields.indexOf(fileUpload) !== 0) {
|
|
88
|
-
throw Boom.badImplementation(
|
|
89
|
-
`Expected '${fileUpload.name}' to be the first form component in FileUploadPageController '${pageDef.path}'`
|
|
90
|
-
)
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
// Assign the file upload component to the controller
|
|
94
|
-
this.fileUpload = fileUpload
|
|
95
|
-
this.viewName = 'file-upload'
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
getFormDataFromState(
|
|
99
|
-
request: FormContextRequest | undefined,
|
|
100
|
-
state: FormSubmissionState
|
|
101
|
-
) {
|
|
102
|
-
const { fileUpload } = this
|
|
103
|
-
|
|
104
|
-
const payload = super.getFormDataFromState(request, state)
|
|
105
|
-
const files = this.getFilesFromState(state)
|
|
106
|
-
|
|
107
|
-
// Append the files to the payload
|
|
108
|
-
payload[fileUpload.name] = files.length ? files : undefined
|
|
109
|
-
|
|
110
|
-
return payload
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
async getState(request: FormRequest | FormRequestPayload) {
|
|
114
|
-
const { fileUpload } = this
|
|
115
|
-
|
|
116
|
-
// Get the actual state
|
|
117
|
-
const state = await super.getState(request)
|
|
118
|
-
const files = this.getFilesFromState(state)
|
|
119
|
-
|
|
120
|
-
// Overwrite the files with those in the upload state
|
|
121
|
-
state[fileUpload.name] = files
|
|
122
|
-
|
|
123
|
-
return this.refreshUpload(request, state)
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
/**
|
|
127
|
-
* Get the uploaded files from state.
|
|
128
|
-
*/
|
|
129
|
-
getFilesFromState(state: FormSubmissionState) {
|
|
130
|
-
const { path } = this
|
|
131
|
-
|
|
132
|
-
const uploadState = state.upload?.[path]
|
|
133
|
-
return uploadState?.files ?? []
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
/**
|
|
137
|
-
* Get the initiated upload from state.
|
|
138
|
-
*/
|
|
139
|
-
getUploadFromState(state: FormSubmissionState) {
|
|
140
|
-
const { path } = this
|
|
141
|
-
|
|
142
|
-
const uploadState = state.upload?.[path]
|
|
143
|
-
return uploadState?.upload
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
makeGetItemDeleteRouteHandler() {
|
|
147
|
-
return (
|
|
148
|
-
request: FormRequest,
|
|
149
|
-
context: FormContext,
|
|
150
|
-
h: Pick<ResponseToolkit, 'redirect' | 'view'>
|
|
151
|
-
) => {
|
|
152
|
-
const { viewModel } = this
|
|
153
|
-
const { params } = request
|
|
154
|
-
const { state } = context
|
|
155
|
-
|
|
156
|
-
const files = this.getFilesFromState(state)
|
|
157
|
-
|
|
158
|
-
const fileToRemove = files.find(
|
|
159
|
-
({ uploadId }) => uploadId === params.itemId
|
|
160
|
-
)
|
|
161
|
-
|
|
162
|
-
if (!fileToRemove) {
|
|
163
|
-
throw Boom.notFound('File to delete not found')
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
const { filename } = fileToRemove.status.form.file
|
|
167
|
-
|
|
168
|
-
return h.view(this.fileDeleteViewName, {
|
|
169
|
-
...viewModel,
|
|
170
|
-
context,
|
|
171
|
-
backLink: this.getBackLink(request, context),
|
|
172
|
-
pageTitle: `Are you sure you want to remove this file?`,
|
|
173
|
-
itemTitle: filename,
|
|
174
|
-
confirmation: { text: 'You cannot recover removed files.' },
|
|
175
|
-
buttonConfirm: { text: 'Remove file' },
|
|
176
|
-
buttonCancel: { text: 'Cancel' }
|
|
177
|
-
} satisfies ItemDeletePageViewModel)
|
|
178
|
-
}
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
makePostItemDeleteRouteHandler() {
|
|
182
|
-
return async (
|
|
183
|
-
request: FormRequestPayload,
|
|
184
|
-
context: FormContext,
|
|
185
|
-
h: Pick<ResponseToolkit, 'redirect' | 'view'>
|
|
186
|
-
) => {
|
|
187
|
-
const { path } = this
|
|
188
|
-
const { state } = context
|
|
189
|
-
|
|
190
|
-
const { confirm } = this.getFormParams(request)
|
|
191
|
-
|
|
192
|
-
// Check for any removed files in the POST payload
|
|
193
|
-
if (confirm) {
|
|
194
|
-
await this.checkRemovedFiles(request, state)
|
|
195
|
-
return this.proceed(request, h, path)
|
|
196
|
-
}
|
|
197
|
-
|
|
198
|
-
return this.proceed(request, h)
|
|
199
|
-
}
|
|
200
|
-
}
|
|
201
|
-
|
|
202
|
-
getErrors(details?: ValidationErrorItem[]) {
|
|
203
|
-
const { fileUpload } = this
|
|
204
|
-
|
|
205
|
-
if (details) {
|
|
206
|
-
const errors: FormSubmissionError[] = []
|
|
207
|
-
|
|
208
|
-
details.forEach((error) => {
|
|
209
|
-
const isUploadError = error.path[0] === fileUpload.name
|
|
210
|
-
const isUploadRootError = isUploadError && error.path.length === 1
|
|
211
|
-
|
|
212
|
-
if (!isUploadError || isUploadRootError) {
|
|
213
|
-
// The error is for the root of the upload or another
|
|
214
|
-
// field on the page so defer to the getError helper
|
|
215
|
-
errors.push(getError(error))
|
|
216
|
-
} else {
|
|
217
|
-
const { context, path, type } = error
|
|
218
|
-
|
|
219
|
-
if (type === 'object.unknown' && path.at(-1) === 'errorMessage') {
|
|
220
|
-
const value = context?.value as string | undefined
|
|
221
|
-
|
|
222
|
-
if (value) {
|
|
223
|
-
const name = fileUpload.name
|
|
224
|
-
const text = typeof value === 'string' ? value : 'Unknown error'
|
|
225
|
-
const href = `#${name}`
|
|
226
|
-
|
|
227
|
-
errors.push({ path, href, name, text })
|
|
228
|
-
}
|
|
229
|
-
}
|
|
230
|
-
}
|
|
231
|
-
})
|
|
232
|
-
|
|
233
|
-
return errors
|
|
234
|
-
}
|
|
235
|
-
}
|
|
236
|
-
|
|
237
|
-
getViewModel(
|
|
238
|
-
request: FormContextRequest,
|
|
239
|
-
context: FormContext
|
|
240
|
-
): FeaturedFormPageViewModel {
|
|
241
|
-
const { fileUpload } = this
|
|
242
|
-
const { state } = context
|
|
243
|
-
|
|
244
|
-
const upload = this.getUploadFromState(state)
|
|
245
|
-
|
|
246
|
-
const viewModel = super.getViewModel(request, context)
|
|
247
|
-
const { components } = viewModel
|
|
248
|
-
|
|
249
|
-
// Featured form component
|
|
250
|
-
const [formComponent] = components.filter(
|
|
251
|
-
({ model }) => model.id === fileUpload.name
|
|
252
|
-
)
|
|
253
|
-
|
|
254
|
-
const index = components.indexOf(formComponent)
|
|
255
|
-
|
|
256
|
-
const proxyUrl = getProxyUrlForLocalDevelopment(upload?.uploadUrl)
|
|
257
|
-
|
|
258
|
-
return {
|
|
259
|
-
...viewModel,
|
|
260
|
-
formAction: upload?.uploadUrl,
|
|
261
|
-
uploadId: upload?.uploadId,
|
|
262
|
-
formComponent,
|
|
263
|
-
|
|
264
|
-
// Split out components before/after
|
|
265
|
-
componentsBefore: components.slice(0, index),
|
|
266
|
-
components: components.slice(index),
|
|
267
|
-
proxyUrl
|
|
268
|
-
}
|
|
269
|
-
}
|
|
270
|
-
|
|
271
|
-
/**
|
|
272
|
-
* Refreshes the CDP upload and files in the
|
|
273
|
-
* state and checks for any removed files.
|
|
274
|
-
*
|
|
275
|
-
* If an upload exists and hasn't been consumed
|
|
276
|
-
* it gets re-used, otherwise we initiate a new one.
|
|
277
|
-
* @param request - the hapi request
|
|
278
|
-
* @param state - the form state
|
|
279
|
-
*/
|
|
280
|
-
private async refreshUpload(
|
|
281
|
-
request: FormRequest | FormRequestPayload,
|
|
282
|
-
state: FormSubmissionState
|
|
283
|
-
) {
|
|
284
|
-
state = await this.checkUploadStatus(request, state)
|
|
285
|
-
|
|
286
|
-
return state
|
|
287
|
-
}
|
|
288
|
-
|
|
289
|
-
/**
|
|
290
|
-
* If an upload exists and hasn't been consumed
|
|
291
|
-
* it gets re-used, otherwise a new one is initiated.
|
|
292
|
-
* @param request - the hapi request
|
|
293
|
-
* @param state - the form state
|
|
294
|
-
* @param depth - the number of retries so far
|
|
295
|
-
*/
|
|
296
|
-
private async checkUploadStatus(
|
|
297
|
-
request: FormRequest | FormRequestPayload,
|
|
298
|
-
state: FormSubmissionState,
|
|
299
|
-
depth = 1
|
|
300
|
-
): Promise<FormSubmissionState> {
|
|
301
|
-
const upload = this.getUploadFromState(state)
|
|
302
|
-
const files = this.getFilesFromState(state)
|
|
303
|
-
|
|
304
|
-
// If no upload exists, initiate a new one.
|
|
305
|
-
if (!upload?.uploadId) {
|
|
306
|
-
return this.initiateAndStoreNewUpload(request, state)
|
|
307
|
-
}
|
|
308
|
-
|
|
309
|
-
const uploadId = upload.uploadId
|
|
310
|
-
const statusResponse = await getUploadStatus(uploadId)
|
|
311
|
-
if (!statusResponse) {
|
|
312
|
-
throw Boom.badRequest(
|
|
313
|
-
`Unexpected empty response from getUploadStatus for ${uploadId}`
|
|
314
|
-
)
|
|
315
|
-
}
|
|
316
|
-
|
|
317
|
-
// Re-use the upload if it is still in the "initiated" state.
|
|
318
|
-
if (statusResponse.uploadStatus === UploadStatus.initiated) {
|
|
319
|
-
return state
|
|
320
|
-
}
|
|
321
|
-
|
|
322
|
-
if (statusResponse.uploadStatus === UploadStatus.pending) {
|
|
323
|
-
// Using exponential backoff delays:
|
|
324
|
-
// Depth 1: 2000ms, Depth 2: 4000ms, Depth 3: 8000ms, Depth 4: 16000ms, Depth 5+: 30000ms (capped)
|
|
325
|
-
// A depth of 5 (or more) implies cumulative delays roughly reaching 55 seconds.
|
|
326
|
-
if (depth >= 5) {
|
|
327
|
-
request.logger.error(
|
|
328
|
-
`Exceeded cumulative retry delay for ${uploadId} (depth: ${depth}). Re-initiating a new upload.`
|
|
329
|
-
)
|
|
330
|
-
await this.initiateAndStoreNewUpload(request, state)
|
|
331
|
-
throw Boom.gatewayTimeout(
|
|
332
|
-
`Timed out waiting for ${uploadId} after cumulative retries exceeding ${((CDP_UPLOAD_TIMEOUT_MS - 5000) / 1000).toFixed(0)} seconds`
|
|
333
|
-
)
|
|
334
|
-
}
|
|
335
|
-
const delay = getExponentialBackoffDelay(depth)
|
|
336
|
-
request.logger.info(
|
|
337
|
-
`Waiting ${delay / 1000} seconds for ${uploadId} to complete (depth: ${depth})`
|
|
338
|
-
)
|
|
339
|
-
await wait(delay)
|
|
340
|
-
return this.checkUploadStatus(request, state, depth + 1)
|
|
341
|
-
}
|
|
342
|
-
|
|
343
|
-
// Only add to files state if the file validates.
|
|
344
|
-
// This secures against html tampering of the file input
|
|
345
|
-
// by adding a 'multiple' attribute or it being
|
|
346
|
-
// changed to a simple text field or similar.
|
|
347
|
-
const validationResult = tempItemSchema.validate(
|
|
348
|
-
{ uploadId, status: statusResponse },
|
|
349
|
-
{ stripUnknown: true }
|
|
350
|
-
)
|
|
351
|
-
const error = validationResult.error
|
|
352
|
-
const fileState = validationResult.value as FileState
|
|
353
|
-
|
|
354
|
-
if (error) {
|
|
355
|
-
return this.initiateAndStoreNewUpload(request, state)
|
|
356
|
-
}
|
|
357
|
-
|
|
358
|
-
const file = fileState.status.form.file
|
|
359
|
-
if (file.fileStatus === FileStatus.complete) {
|
|
360
|
-
files.unshift(prepareFileState(fileState))
|
|
361
|
-
await this.mergeState(request, state, {
|
|
362
|
-
upload: { [this.path]: { files, upload } }
|
|
363
|
-
})
|
|
364
|
-
} else {
|
|
365
|
-
// Flash the error message.
|
|
366
|
-
const { fileUpload } = this
|
|
367
|
-
const { cacheService } = request.services([])
|
|
368
|
-
const name = fileUpload.name
|
|
369
|
-
const text = file.errorMessage ?? 'Unknown error'
|
|
370
|
-
const errors: FormSubmissionError[] = [
|
|
371
|
-
{ path: [name], href: `#${name}`, name, text }
|
|
372
|
-
]
|
|
373
|
-
cacheService.setFlash(request, { errors })
|
|
374
|
-
}
|
|
375
|
-
|
|
376
|
-
return this.initiateAndStoreNewUpload(request, state)
|
|
377
|
-
}
|
|
378
|
-
|
|
379
|
-
/**
|
|
380
|
-
* Checks the payload for a file getting removed
|
|
381
|
-
* and removes it from the upload files if found
|
|
382
|
-
* @param request - the hapi request
|
|
383
|
-
* @param state - the form state
|
|
384
|
-
* @returns updated state if any files have been removed
|
|
385
|
-
*/
|
|
386
|
-
private async checkRemovedFiles(
|
|
387
|
-
request: FormRequestPayload,
|
|
388
|
-
state: FormSubmissionState
|
|
389
|
-
) {
|
|
390
|
-
const { path } = this
|
|
391
|
-
const { params } = request
|
|
392
|
-
|
|
393
|
-
const upload = this.getUploadFromState(state)
|
|
394
|
-
const files = this.getFilesFromState(state)
|
|
395
|
-
|
|
396
|
-
const filesUpdated = files.filter(
|
|
397
|
-
({ uploadId }) => uploadId !== params.itemId
|
|
398
|
-
)
|
|
399
|
-
|
|
400
|
-
if (filesUpdated.length === files.length) {
|
|
401
|
-
return
|
|
402
|
-
}
|
|
403
|
-
|
|
404
|
-
await this.mergeState(request, state, {
|
|
405
|
-
upload: { [path]: { files: filesUpdated, upload } }
|
|
406
|
-
})
|
|
407
|
-
}
|
|
408
|
-
|
|
409
|
-
/**
|
|
410
|
-
* Initiates a CDP file upload and stores in the upload state
|
|
411
|
-
* @param request - the hapi request
|
|
412
|
-
* @param state - the form state
|
|
413
|
-
*/
|
|
414
|
-
private async initiateAndStoreNewUpload(
|
|
415
|
-
request: FormRequest | FormRequestPayload,
|
|
416
|
-
state: FormSubmissionState
|
|
417
|
-
) {
|
|
418
|
-
const { fileUpload, href, path } = this
|
|
419
|
-
const { options, schema } = fileUpload
|
|
420
|
-
|
|
421
|
-
const files = this.getFilesFromState(state)
|
|
422
|
-
|
|
423
|
-
// Reset the upload in state
|
|
424
|
-
let upload: UploadInitiateResponse | undefined
|
|
425
|
-
|
|
426
|
-
// Don't initiate anymore after minimum of `schema.max` or MAX_UPLOADS
|
|
427
|
-
const max = Math.min(schema.max ?? MAX_UPLOADS, MAX_UPLOADS)
|
|
428
|
-
|
|
429
|
-
if (files.length < max) {
|
|
430
|
-
const outputEmail =
|
|
431
|
-
this.model.def.outputEmail ?? 'defraforms@defra.gov.uk'
|
|
432
|
-
|
|
433
|
-
const newUpload = await initiateUpload(href, outputEmail, options.accept)
|
|
434
|
-
|
|
435
|
-
if (newUpload === undefined) {
|
|
436
|
-
throw Boom.badRequest('Unexpected empty response from initiateUpload')
|
|
437
|
-
}
|
|
438
|
-
|
|
439
|
-
upload = newUpload
|
|
440
|
-
}
|
|
441
|
-
|
|
442
|
-
return this.mergeState(request, state, {
|
|
443
|
-
upload: { [path]: { files, upload } }
|
|
444
|
-
})
|
|
445
|
-
}
|
|
446
|
-
}
|
|
@@ -1,205 +0,0 @@
|
|
|
1
|
-
import { type ResponseToolkit } from '@hapi/hapi'
|
|
2
|
-
|
|
3
|
-
import { FormModel } from '~/src/server/plugins/engine/models/FormModel.js'
|
|
4
|
-
import { PageController } from '~/src/server/plugins/engine/pageControllers/PageController.js'
|
|
5
|
-
import { type FormRequest } from '~/src/server/routes/types.js'
|
|
6
|
-
import definition from '~/test/form/definitions/basic.js'
|
|
7
|
-
|
|
8
|
-
describe('PageController', () => {
|
|
9
|
-
let model: FormModel
|
|
10
|
-
let controller1: PageController
|
|
11
|
-
let controller2: PageController
|
|
12
|
-
|
|
13
|
-
beforeEach(() => {
|
|
14
|
-
const { pages } = definition
|
|
15
|
-
|
|
16
|
-
const page1 = pages[0]
|
|
17
|
-
const page2 = pages[1]
|
|
18
|
-
|
|
19
|
-
model = new FormModel(definition, {
|
|
20
|
-
basePath: 'test'
|
|
21
|
-
})
|
|
22
|
-
|
|
23
|
-
controller1 = new PageController(model, page1)
|
|
24
|
-
controller2 = new PageController(model, page2)
|
|
25
|
-
})
|
|
26
|
-
|
|
27
|
-
describe('Properties', () => {
|
|
28
|
-
it('returns path', () => {
|
|
29
|
-
expect(controller1).toHaveProperty('path', '/licence')
|
|
30
|
-
expect(controller2).toHaveProperty('path', '/full-name')
|
|
31
|
-
})
|
|
32
|
-
|
|
33
|
-
it('returns href', () => {
|
|
34
|
-
expect(controller1).toHaveProperty('href', '/test/licence')
|
|
35
|
-
expect(controller2).toHaveProperty('href', '/test/full-name')
|
|
36
|
-
})
|
|
37
|
-
|
|
38
|
-
it('returns keys (empty)', () => {
|
|
39
|
-
expect(controller1).toHaveProperty('keys', [])
|
|
40
|
-
expect(controller2).toHaveProperty('keys', [])
|
|
41
|
-
})
|
|
42
|
-
|
|
43
|
-
it('returns the page section', () => {
|
|
44
|
-
expect(controller1).toHaveProperty('section', {
|
|
45
|
-
name: 'licenceDetails',
|
|
46
|
-
title: 'Licence details',
|
|
47
|
-
hideTitle: false
|
|
48
|
-
})
|
|
49
|
-
|
|
50
|
-
expect(controller2).toHaveProperty('section', {
|
|
51
|
-
name: 'personalDetails',
|
|
52
|
-
title: 'Personal details',
|
|
53
|
-
hideTitle: false
|
|
54
|
-
})
|
|
55
|
-
})
|
|
56
|
-
|
|
57
|
-
it('returns feedback link (from form definition)', () => {
|
|
58
|
-
expect(controller1).toHaveProperty('feedbackLink', undefined)
|
|
59
|
-
|
|
60
|
-
const emailAddress = 'test@feedback.cat'
|
|
61
|
-
|
|
62
|
-
model.def.feedback = {
|
|
63
|
-
emailAddress
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
expect(controller1).toHaveProperty(
|
|
67
|
-
'feedbackLink',
|
|
68
|
-
`mailto:${emailAddress}`
|
|
69
|
-
)
|
|
70
|
-
})
|
|
71
|
-
|
|
72
|
-
it('returns phase tag (from form definition)', () => {
|
|
73
|
-
expect(controller1).toHaveProperty('phaseTag', undefined)
|
|
74
|
-
|
|
75
|
-
model.def.phaseBanner = {
|
|
76
|
-
phase: 'alpha'
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
expect(controller1).toHaveProperty('phaseTag', 'alpha')
|
|
80
|
-
})
|
|
81
|
-
|
|
82
|
-
it('sets default viewName to "index"', () => {
|
|
83
|
-
expect(controller1).toHaveProperty('viewName', 'index')
|
|
84
|
-
expect(controller2).toHaveProperty('viewName', 'index')
|
|
85
|
-
})
|
|
86
|
-
|
|
87
|
-
it('overrides viewName when pageDef.view is provided', () => {
|
|
88
|
-
const customPage = {
|
|
89
|
-
...definition.pages[0],
|
|
90
|
-
view: 'custom-view'
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
const customController = new PageController(model, customPage)
|
|
94
|
-
|
|
95
|
-
expect(customController).toHaveProperty('viewName', 'custom-view')
|
|
96
|
-
})
|
|
97
|
-
})
|
|
98
|
-
|
|
99
|
-
describe('Path methods', () => {
|
|
100
|
-
describe('Link href', () => {
|
|
101
|
-
it('prefixes paths into link hrefs', () => {
|
|
102
|
-
const href1 = controller1.getHref('/')
|
|
103
|
-
const href2 = controller1.getHref('/page-one')
|
|
104
|
-
|
|
105
|
-
expect(href1).toBe('/test')
|
|
106
|
-
expect(href2).toBe('/test/page-one')
|
|
107
|
-
})
|
|
108
|
-
})
|
|
109
|
-
|
|
110
|
-
describe('Start path', () => {
|
|
111
|
-
it('returns path to start page', () => {
|
|
112
|
-
const startPath = controller1.getStartPath()
|
|
113
|
-
expect(startPath).toBe('/licence')
|
|
114
|
-
})
|
|
115
|
-
|
|
116
|
-
it('returns path to start page (default)', () => {
|
|
117
|
-
delete model.def.startPage
|
|
118
|
-
|
|
119
|
-
const startPath = controller1.getStartPath()
|
|
120
|
-
expect(startPath).toBe('/start')
|
|
121
|
-
})
|
|
122
|
-
})
|
|
123
|
-
|
|
124
|
-
describe('Summary path', () => {
|
|
125
|
-
it('returns path to summary page', () => {
|
|
126
|
-
const summaryPath = controller1.getSummaryPath()
|
|
127
|
-
expect(summaryPath).toBe('/summary')
|
|
128
|
-
})
|
|
129
|
-
})
|
|
130
|
-
|
|
131
|
-
describe('Status path', () => {
|
|
132
|
-
it('returns path to status page', () => {
|
|
133
|
-
const summaryPath = controller1.getStatusPath()
|
|
134
|
-
expect(summaryPath).toBe('/status')
|
|
135
|
-
})
|
|
136
|
-
})
|
|
137
|
-
})
|
|
138
|
-
|
|
139
|
-
describe('Route handlers', () => {
|
|
140
|
-
const page1Url = new URL('http://example.com/test/licence')
|
|
141
|
-
|
|
142
|
-
const request = {
|
|
143
|
-
method: 'get',
|
|
144
|
-
url: page1Url,
|
|
145
|
-
path: page1Url.pathname,
|
|
146
|
-
params: {
|
|
147
|
-
path: 'licence',
|
|
148
|
-
slug: 'test'
|
|
149
|
-
},
|
|
150
|
-
query: {},
|
|
151
|
-
app: { model }
|
|
152
|
-
} as FormRequest
|
|
153
|
-
|
|
154
|
-
const h: Pick<ResponseToolkit, 'redirect' | 'view'> = {
|
|
155
|
-
redirect: jest.fn(),
|
|
156
|
-
view: jest.fn()
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
it('returns default route options', () => {
|
|
160
|
-
expect(controller1.getRouteOptions).toEqual({})
|
|
161
|
-
expect(controller1.postRouteOptions).toEqual({})
|
|
162
|
-
})
|
|
163
|
-
|
|
164
|
-
it('supports GET route handler', async () => {
|
|
165
|
-
expect(() => controller1.makeGetRouteHandler()).not.toThrow()
|
|
166
|
-
expect(() => controller1.makeGetRouteHandler()).toBeInstanceOf(Function)
|
|
167
|
-
|
|
168
|
-
await controller1.makeGetRouteHandler()(
|
|
169
|
-
request,
|
|
170
|
-
model.getFormContext(request, {}),
|
|
171
|
-
h
|
|
172
|
-
)
|
|
173
|
-
|
|
174
|
-
await controller2.makeGetRouteHandler()(
|
|
175
|
-
request,
|
|
176
|
-
model.getFormContext(request, {}),
|
|
177
|
-
h
|
|
178
|
-
)
|
|
179
|
-
|
|
180
|
-
expect(h.view).toHaveBeenNthCalledWith(
|
|
181
|
-
1,
|
|
182
|
-
controller1.viewName,
|
|
183
|
-
expect.objectContaining({
|
|
184
|
-
pageTitle: 'Buy a rod fishing licence',
|
|
185
|
-
sectionTitle: 'Licence details'
|
|
186
|
-
})
|
|
187
|
-
)
|
|
188
|
-
|
|
189
|
-
expect(h.view).toHaveBeenNthCalledWith(
|
|
190
|
-
2,
|
|
191
|
-
controller1.viewName,
|
|
192
|
-
expect.objectContaining({
|
|
193
|
-
pageTitle: "What's your name?",
|
|
194
|
-
sectionTitle: 'Personal details'
|
|
195
|
-
})
|
|
196
|
-
)
|
|
197
|
-
})
|
|
198
|
-
|
|
199
|
-
it('does not support POST route handler', () => {
|
|
200
|
-
expect(() => controller1.makePostRouteHandler()).toThrow(
|
|
201
|
-
'Unsupported POST route handler for this page'
|
|
202
|
-
)
|
|
203
|
-
})
|
|
204
|
-
})
|
|
205
|
-
})
|