@defra/forms-engine-plugin 0.0.4 → 0.0.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.server/server/index.js +0 -4
- package/.server/server/index.js.map +1 -1
- package/.server/server/plugins/engine/helpers.js +3 -0
- package/.server/server/plugins/engine/helpers.js.map +1 -1
- package/.server/server/plugins/engine/index.js +27 -1
- package/.server/server/plugins/engine/index.js.map +1 -1
- package/.server/server/plugins/engine/pageControllers/FileUploadPageController.js +2 -4
- package/.server/server/plugins/engine/pageControllers/FileUploadPageController.js.map +1 -1
- package/.server/server/plugins/engine/pageControllers/QuestionPageController.js +4 -10
- package/.server/server/plugins/engine/pageControllers/QuestionPageController.js.map +1 -1
- package/.server/server/plugins/engine/pageControllers/StatusPageController.js +2 -3
- package/.server/server/plugins/engine/pageControllers/StatusPageController.js.map +1 -1
- package/.server/server/plugins/engine/pageControllers/SummaryPageController.js +2 -4
- package/.server/server/plugins/engine/pageControllers/SummaryPageController.js.map +1 -1
- package/.server/server/plugins/engine/plugin.js +65 -6
- package/.server/server/plugins/engine/plugin.js.map +1 -1
- package/.server/server/plugins/engine/types.js.map +1 -1
- package/.server/server/{views → plugins/engine/views}/components/service-banner/template.test.js +1 -1
- package/.server/server/plugins/engine/views/components/service-banner/template.test.js.map +1 -0
- package/.server/server/{views → plugins/engine/views}/components/tag-env/template.test.js +1 -1
- package/.server/server/plugins/engine/views/components/tag-env/template.test.js.map +1 -0
- package/.server/server/services/cacheService.js +5 -2
- package/.server/server/services/cacheService.js.map +1 -1
- package/.server/typings/hapi/index.d.js.map +1 -1
- package/README.md +215 -4
- package/package.json +3 -3
- package/src/client/javascripts/application.js +87 -0
- package/src/client/javascripts/file-upload.js +386 -0
- package/src/client/stylesheets/_code.scss +33 -0
- package/src/client/stylesheets/_govuk-frontend.scss +4 -0
- package/src/client/stylesheets/_prose.scss +56 -0
- package/src/client/stylesheets/_service-banner.scss +24 -0
- package/src/client/stylesheets/_summary-list.scss +28 -0
- package/src/client/stylesheets/_tag-env.scss +24 -0
- package/src/client/stylesheets/application.scss +14 -0
- package/src/common/cookies.js +58 -0
- package/src/common/cookies.test.js +23 -0
- package/src/common/types.js +5 -0
- package/src/config/index.ts +271 -0
- package/src/index.ts +31 -0
- package/src/server/common/helpers/logging/logger-options.test.ts +50 -0
- package/src/server/common/helpers/logging/logger-options.ts +46 -0
- package/src/server/common/helpers/logging/logger.ts +7 -0
- package/src/server/common/helpers/logging/request-logger.ts +9 -0
- package/src/server/common/helpers/logging/request-tracing.js +10 -0
- package/src/server/common/helpers/redis-client.js +70 -0
- package/src/server/constants.js +1 -0
- package/src/server/forms/README.md +10 -0
- package/src/server/forms/components.json +1015 -0
- package/src/server/forms/report-a-terrorist.json +270 -0
- package/src/server/forms/runner-components-test.json +365 -0
- package/src/server/forms/test.json +581 -0
- package/src/server/index.test.ts +582 -0
- package/src/server/index.ts +135 -0
- package/src/server/plugins/blankie.test.ts +73 -0
- package/src/server/plugins/blankie.ts +48 -0
- package/src/server/plugins/crumb.ts +20 -0
- package/src/server/plugins/engine/README.md +87 -0
- package/src/server/plugins/engine/components/AutocompleteField.test.ts +294 -0
- package/src/server/plugins/engine/components/AutocompleteField.ts +49 -0
- package/src/server/plugins/engine/components/CheckboxesField.test.ts +379 -0
- package/src/server/plugins/engine/components/CheckboxesField.ts +106 -0
- package/src/server/plugins/engine/components/ComponentBase.ts +97 -0
- package/src/server/plugins/engine/components/ComponentCollection.ts +278 -0
- package/src/server/plugins/engine/components/DatePartsField.test.ts +822 -0
- package/src/server/plugins/engine/components/DatePartsField.ts +264 -0
- package/src/server/plugins/engine/components/Details.test.ts +49 -0
- package/src/server/plugins/engine/components/Details.ts +30 -0
- package/src/server/plugins/engine/components/EmailAddressField.test.ts +395 -0
- package/src/server/plugins/engine/components/EmailAddressField.ts +55 -0
- package/src/server/plugins/engine/components/FileUploadField.test.ts +778 -0
- package/src/server/plugins/engine/components/FileUploadField.ts +262 -0
- package/src/server/plugins/engine/components/FormComponent.ts +249 -0
- package/src/server/plugins/engine/components/Html.test.ts +48 -0
- package/src/server/plugins/engine/components/Html.ts +29 -0
- package/src/server/plugins/engine/components/InsetText.test.ts +48 -0
- package/src/server/plugins/engine/components/InsetText.ts +27 -0
- package/src/server/plugins/engine/components/List.test.ts +76 -0
- package/src/server/plugins/engine/components/List.ts +72 -0
- package/src/server/plugins/engine/components/ListFormComponent.ts +140 -0
- package/src/server/plugins/engine/components/MonthYearField.test.ts +567 -0
- package/src/server/plugins/engine/components/MonthYearField.ts +222 -0
- package/src/server/plugins/engine/components/MultilineTextField.test.ts +558 -0
- package/src/server/plugins/engine/components/MultilineTextField.ts +138 -0
- package/src/server/plugins/engine/components/NumberField.test.ts +701 -0
- package/src/server/plugins/engine/components/NumberField.ts +163 -0
- package/src/server/plugins/engine/components/RadiosField.test.ts +288 -0
- package/src/server/plugins/engine/components/RadiosField.ts +24 -0
- package/src/server/plugins/engine/components/SelectField.test.ts +288 -0
- package/src/server/plugins/engine/components/SelectField.ts +47 -0
- package/src/server/plugins/engine/components/SelectionControlField.ts +43 -0
- package/src/server/plugins/engine/components/TelephoneNumberField.test.ts +356 -0
- package/src/server/plugins/engine/components/TelephoneNumberField.ts +67 -0
- package/src/server/plugins/engine/components/TextField.test.ts +489 -0
- package/src/server/plugins/engine/components/TextField.ts +96 -0
- package/src/server/plugins/engine/components/UkAddressField.test.ts +623 -0
- package/src/server/plugins/engine/components/UkAddressField.ts +172 -0
- package/src/server/plugins/engine/components/YesNoField.test.ts +248 -0
- package/src/server/plugins/engine/components/YesNoField.ts +31 -0
- package/src/server/plugins/engine/components/constants.ts +1 -0
- package/src/server/plugins/engine/components/helpers.ts +330 -0
- package/src/server/plugins/engine/components/index.ts +24 -0
- package/src/server/plugins/engine/components/types.ts +117 -0
- package/src/server/plugins/engine/configureEnginePlugin.ts +47 -0
- package/src/server/plugins/engine/helpers.test.ts +791 -0
- package/src/server/plugins/engine/helpers.ts +384 -0
- package/src/server/plugins/engine/index.ts +47 -0
- package/src/server/plugins/engine/models/FormModel.test.ts +42 -0
- package/src/server/plugins/engine/models/FormModel.ts +443 -0
- 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 +209 -0
- package/src/server/plugins/engine/models/SummaryViewModel.ts +220 -0
- package/src/server/plugins/engine/models/index.ts +2 -0
- package/src/server/plugins/engine/models/types.ts +114 -0
- package/src/server/plugins/engine/outputFormatters/human/v1.test.ts +143 -0
- package/src/server/plugins/engine/outputFormatters/human/v1.ts +73 -0
- package/src/server/plugins/engine/outputFormatters/index.test.ts +17 -0
- package/src/server/plugins/engine/outputFormatters/index.ts +44 -0
- package/src/server/plugins/engine/outputFormatters/machine/v1.test.ts +229 -0
- package/src/server/plugins/engine/outputFormatters/machine/v1.ts +140 -0
- package/src/server/plugins/engine/outputFormatters/machine/v2.test.ts +229 -0
- package/src/server/plugins/engine/outputFormatters/machine/v2.ts +153 -0
- package/src/server/plugins/engine/pageControllers/FileUploadPageController.test.ts +1116 -0
- package/src/server/plugins/engine/pageControllers/FileUploadPageController.ts +447 -0
- package/src/server/plugins/engine/pageControllers/PageController.test.ts +205 -0
- package/src/server/plugins/engine/pageControllers/PageController.ts +176 -0
- package/src/server/plugins/engine/pageControllers/QuestionPageController.test.ts +1264 -0
- package/src/server/plugins/engine/pageControllers/QuestionPageController.ts +565 -0
- package/src/server/plugins/engine/pageControllers/README.md +28 -0
- package/src/server/plugins/engine/pageControllers/RepeatPageController.test.ts +264 -0
- package/src/server/plugins/engine/pageControllers/RepeatPageController.ts +458 -0
- package/src/server/plugins/engine/pageControllers/StartPageController.ts +18 -0
- package/src/server/plugins/engine/pageControllers/StatusPageController.ts +51 -0
- package/src/server/plugins/engine/pageControllers/SummaryPageController.ts +262 -0
- package/src/server/plugins/engine/pageControllers/TerminalController.test.ts +28 -0
- package/src/server/plugins/engine/pageControllers/TerminalPageController.ts +19 -0
- package/src/server/plugins/engine/pageControllers/helpers.test.ts +198 -0
- package/src/server/plugins/engine/pageControllers/helpers.ts +101 -0
- package/src/server/plugins/engine/pageControllers/index.ts +10 -0
- package/src/server/plugins/engine/pageControllers/validationOptions.ts +89 -0
- package/src/server/plugins/engine/plugin.ts +753 -0
- package/src/server/plugins/engine/services/formSubmissionService.js +46 -0
- package/src/server/plugins/engine/services/formsService.js +46 -0
- package/src/server/plugins/engine/services/formsService.test.js +90 -0
- package/src/server/plugins/engine/services/index.js +3 -0
- package/src/server/plugins/engine/services/notifyService.test.ts +132 -0
- package/src/server/plugins/engine/services/notifyService.ts +64 -0
- package/src/server/plugins/engine/services/uploadService.js +60 -0
- package/src/server/plugins/engine/types.ts +317 -0
- package/src/server/plugins/engine/views/components/autocompletefield.html +5 -0
- package/src/server/plugins/engine/views/components/checkboxesfield.html +5 -0
- package/src/server/plugins/engine/views/components/datepartsfield.html +5 -0
- package/src/server/plugins/engine/views/components/debug/macro.njk +3 -0
- package/src/server/plugins/engine/views/components/debug/template.njk +13 -0
- package/src/server/plugins/engine/views/components/details.html +6 -0
- package/src/server/plugins/engine/views/components/emailaddressfield.html +5 -0
- package/src/server/plugins/engine/views/components/fileuploadfield-key.html +8 -0
- package/src/server/plugins/engine/views/components/fileuploadfield-value.html +3 -0
- package/src/server/plugins/engine/views/components/fileuploadfield.html +24 -0
- package/src/server/plugins/engine/views/components/html.html +3 -0
- package/src/server/plugins/engine/views/components/insettext.html +7 -0
- package/src/server/plugins/engine/views/components/list.html +36 -0
- package/src/server/plugins/engine/views/components/monthyearfield.html +5 -0
- package/src/server/plugins/engine/views/components/multilinetextfield.html +10 -0
- package/src/server/plugins/engine/views/components/numberfield.html +5 -0
- package/src/server/plugins/engine/views/components/radiosfield.html +5 -0
- package/src/server/plugins/engine/views/components/selectfield.html +5 -0
- package/src/server/plugins/engine/views/components/service-banner/macro.njk +3 -0
- package/src/server/plugins/engine/views/components/service-banner/template.njk +20 -0
- package/src/server/plugins/engine/views/components/service-banner/template.test.js +43 -0
- package/src/server/plugins/engine/views/components/tag-env/macro.njk +3 -0
- package/src/server/plugins/engine/views/components/tag-env/template.njk +30 -0
- package/src/server/plugins/engine/views/components/tag-env/template.test.js +66 -0
- package/src/server/plugins/engine/views/components/telephonenumberfield.html +5 -0
- package/src/server/plugins/engine/views/components/textfield.html +5 -0
- package/src/server/plugins/engine/views/components/ukaddressfield.html +25 -0
- package/src/server/plugins/engine/views/components/yesnofield.html +5 -0
- package/src/server/plugins/engine/views/confirmation.html +19 -0
- package/src/server/plugins/engine/views/file-upload.html +45 -0
- package/src/server/plugins/engine/views/index.html +39 -0
- package/src/server/plugins/engine/views/item-delete.html +56 -0
- package/src/server/plugins/engine/views/layout.html +199 -0
- package/src/server/plugins/engine/views/partials/components.html +6 -0
- package/src/server/plugins/engine/views/partials/conditional-components.html +3 -0
- package/src/server/plugins/engine/views/partials/debug.html +44 -0
- package/src/server/plugins/engine/views/partials/form.html +15 -0
- package/src/server/plugins/engine/views/partials/heading.html +16 -0
- package/src/server/plugins/engine/views/partials/preview-banner.html +32 -0
- package/src/server/plugins/engine/views/partials/preview-banner.test.js +122 -0
- package/src/server/plugins/engine/views/partials/warn-missing-notification-email.html +10 -0
- package/src/server/plugins/engine/views/repeat-list-summary.html +53 -0
- package/src/server/plugins/engine/views/summary.html +50 -0
- package/src/server/plugins/errorPages.ts +58 -0
- package/src/server/plugins/nunjucks/context.js +88 -0
- package/src/server/plugins/nunjucks/context.test.js +142 -0
- package/src/server/plugins/nunjucks/enviroment.test.js +201 -0
- package/src/server/plugins/nunjucks/environment.js +116 -0
- package/src/server/plugins/nunjucks/filters/answer.js +27 -0
- package/src/server/plugins/nunjucks/filters/answer.test.js +89 -0
- package/src/server/plugins/nunjucks/filters/evaluate.js +21 -0
- package/src/server/plugins/nunjucks/filters/field.js +28 -0
- package/src/server/plugins/nunjucks/filters/field.test.js +75 -0
- package/src/server/plugins/nunjucks/filters/highlight.js +11 -0
- package/src/server/plugins/nunjucks/filters/href.js +30 -0
- package/src/server/plugins/nunjucks/filters/href.test.js +80 -0
- package/src/server/plugins/nunjucks/filters/index.js +8 -0
- package/src/server/plugins/nunjucks/filters/inspect.js +15 -0
- package/src/server/plugins/nunjucks/filters/page.js +24 -0
- package/src/server/plugins/nunjucks/filters/page.test.js +65 -0
- package/src/server/plugins/nunjucks/index.js +3 -0
- package/src/server/plugins/nunjucks/plugin.js +40 -0
- package/src/server/plugins/nunjucks/render.js +42 -0
- package/src/server/plugins/nunjucks/types.js +40 -0
- package/src/server/plugins/pulse.ts +11 -0
- package/src/server/plugins/router.ts +201 -0
- package/src/server/plugins/session.ts +28 -0
- package/src/server/routes/health.js +13 -0
- package/src/server/routes/health.test.js +35 -0
- package/src/server/routes/index.test.ts +125 -0
- package/src/server/routes/index.ts +2 -0
- package/src/server/routes/public.ts +47 -0
- package/src/server/routes/types.ts +48 -0
- package/src/server/schemas/index.ts +34 -0
- package/src/server/secure-context.js +43 -0
- package/src/server/services/cacheService.test.ts +277 -0
- package/src/server/services/cacheService.ts +138 -0
- package/src/server/services/httpService.test.js +491 -0
- package/src/server/services/httpService.ts +50 -0
- package/src/server/services/index.ts +1 -0
- package/src/server/types.ts +54 -0
- package/src/server/utils/notify.test.ts +37 -0
- package/src/server/utils/notify.ts +50 -0
- package/src/server/utils/secure-context/get-trust-store-certs.js +11 -0
- package/src/server/utils/secure-context/get-trust-store-certs.test.js +19 -0
- package/src/server/utils/utils.js +24 -0
- package/src/server/utils/utils.test.js +54 -0
- package/src/server/views/404.html +16 -0
- package/src/server/views/500.html +19 -0
- package/src/server/views/help/accessibility-statement.html +58 -0
- package/src/server/views/help/cookie-preferences.html +57 -0
- package/src/server/views/help/cookies.html +71 -0
- package/src/server/views/help/get-support.html +37 -0
- package/src/server/views/help/privacy-notice.html +68 -0
- package/src/server/views/help/terms-and-conditions.html +83 -0
- package/src/typings/hapi/index.d.ts +87 -0
- package/src/typings/hapi-tracing/index.d.ts +6 -0
- package/src/typings/index.d.ts +3 -0
- package/src/typings/joi/index.d.ts +22 -0
- package/.server/server/views/components/service-banner/template.test.js.map +0 -1
- package/.server/server/views/components/tag-env/template.test.js.map +0 -1
- /package/.server/server/{views → plugins/engine/views}/components/debug/macro.njk +0 -0
- /package/.server/server/{views → plugins/engine/views}/components/debug/template.njk +0 -0
- /package/.server/server/{views → plugins/engine/views}/components/service-banner/macro.njk +0 -0
- /package/.server/server/{views → plugins/engine/views}/components/service-banner/template.njk +0 -0
- /package/.server/server/{views → plugins/engine/views}/components/tag-env/macro.njk +0 -0
- /package/.server/server/{views → plugins/engine/views}/components/tag-env/template.njk +0 -0
- /package/.server/server/{views → plugins/engine/views}/confirmation.html +0 -0
- /package/.server/server/{views → plugins/engine/views}/layout.html +0 -0
- /package/.server/server/{views → plugins/engine/views}/summary.html +0 -0
|
@@ -0,0 +1,791 @@
|
|
|
1
|
+
import Boom from '@hapi/boom'
|
|
2
|
+
import { type ResponseObject, type ResponseToolkit } from '@hapi/hapi'
|
|
3
|
+
import { StatusCodes } from 'http-status-codes'
|
|
4
|
+
import { ValidationError } from 'joi'
|
|
5
|
+
|
|
6
|
+
import { PREVIEW_PATH_PREFIX } from '~/src/server/constants.js'
|
|
7
|
+
import {
|
|
8
|
+
checkEmailAddressForLiveFormSubmission,
|
|
9
|
+
checkFormStatus,
|
|
10
|
+
encodeUrl,
|
|
11
|
+
engine,
|
|
12
|
+
evaluateTemplate,
|
|
13
|
+
getErrors,
|
|
14
|
+
getExponentialBackoffDelay,
|
|
15
|
+
getPageHref,
|
|
16
|
+
proceed,
|
|
17
|
+
safeGenerateCrumb,
|
|
18
|
+
type GlobalScope
|
|
19
|
+
} from '~/src/server/plugins/engine/helpers.js'
|
|
20
|
+
import { FormModel } from '~/src/server/plugins/engine/models/FormModel.js'
|
|
21
|
+
import {
|
|
22
|
+
createPage,
|
|
23
|
+
type PageControllerClass
|
|
24
|
+
} from '~/src/server/plugins/engine/pageControllers/helpers.js'
|
|
25
|
+
import {
|
|
26
|
+
type FormContext,
|
|
27
|
+
type FormContextRequest
|
|
28
|
+
} from '~/src/server/plugins/engine/types.js'
|
|
29
|
+
import {
|
|
30
|
+
FormAction,
|
|
31
|
+
FormStatus,
|
|
32
|
+
type FormRequest
|
|
33
|
+
} from '~/src/server/routes/types.js'
|
|
34
|
+
import definition from '~/test/form/definitions/basic.js'
|
|
35
|
+
import templateDefinition from '~/test/form/definitions/templates.js'
|
|
36
|
+
|
|
37
|
+
interface NunjucksContext {
|
|
38
|
+
context: {
|
|
39
|
+
globals: GlobalScope
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
type EvaluateFilter = (this: NunjucksContext, template: unknown) => unknown
|
|
44
|
+
type HrefFilter = (this: NunjucksContext, path: string) => string | undefined
|
|
45
|
+
|
|
46
|
+
describe('Helpers', () => {
|
|
47
|
+
let page: PageControllerClass
|
|
48
|
+
let request: FormContextRequest
|
|
49
|
+
let h: Pick<ResponseToolkit, 'redirect' | 'view'>
|
|
50
|
+
|
|
51
|
+
beforeEach(() => {
|
|
52
|
+
const model = new FormModel(definition, {
|
|
53
|
+
basePath: 'test'
|
|
54
|
+
})
|
|
55
|
+
|
|
56
|
+
page = createPage(model, definition.pages[0])
|
|
57
|
+
const pageUrl = new URL(page.href, 'http://example.com')
|
|
58
|
+
|
|
59
|
+
request = {
|
|
60
|
+
method: 'get',
|
|
61
|
+
url: pageUrl,
|
|
62
|
+
path: pageUrl.pathname,
|
|
63
|
+
params: {
|
|
64
|
+
path: 'licence',
|
|
65
|
+
slug: 'test'
|
|
66
|
+
},
|
|
67
|
+
query: {},
|
|
68
|
+
app: { model }
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
const response = {
|
|
72
|
+
code: jest.fn().mockImplementation(() => response)
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
h = {
|
|
76
|
+
redirect: jest.fn().mockImplementation(() => response),
|
|
77
|
+
view: jest.fn()
|
|
78
|
+
}
|
|
79
|
+
})
|
|
80
|
+
|
|
81
|
+
describe('proceed', () => {
|
|
82
|
+
it.each([
|
|
83
|
+
{
|
|
84
|
+
href: 'https://www.gov.uk/help/privacy-notice',
|
|
85
|
+
|
|
86
|
+
request: {
|
|
87
|
+
method: 'get'
|
|
88
|
+
} satisfies Partial<FormContextRequest>,
|
|
89
|
+
|
|
90
|
+
redirect: {
|
|
91
|
+
statusCode: StatusCodes.MOVED_TEMPORARILY
|
|
92
|
+
} satisfies Partial<ResponseObject>
|
|
93
|
+
},
|
|
94
|
+
{
|
|
95
|
+
href: '/test/full-name',
|
|
96
|
+
|
|
97
|
+
request: {
|
|
98
|
+
method: 'get'
|
|
99
|
+
} satisfies Partial<FormContextRequest>,
|
|
100
|
+
|
|
101
|
+
redirect: {
|
|
102
|
+
statusCode: StatusCodes.MOVED_TEMPORARILY
|
|
103
|
+
} satisfies Partial<ResponseObject>
|
|
104
|
+
},
|
|
105
|
+
{
|
|
106
|
+
href: '/test/full-name',
|
|
107
|
+
|
|
108
|
+
request: {
|
|
109
|
+
method: 'post',
|
|
110
|
+
payload: {
|
|
111
|
+
action: FormAction.Validate
|
|
112
|
+
}
|
|
113
|
+
} satisfies Partial<FormContextRequest>,
|
|
114
|
+
|
|
115
|
+
redirect: {
|
|
116
|
+
statusCode: StatusCodes.SEE_OTHER
|
|
117
|
+
} satisfies Partial<ResponseObject>
|
|
118
|
+
}
|
|
119
|
+
])(
|
|
120
|
+
'should redirect to the path provided',
|
|
121
|
+
({ href, redirect, ...options }) => {
|
|
122
|
+
request = { ...request, ...options.request }
|
|
123
|
+
|
|
124
|
+
const response = proceed(request, h, href)
|
|
125
|
+
|
|
126
|
+
expect(h.view).not.toHaveBeenCalled()
|
|
127
|
+
expect(h.redirect).toHaveBeenCalledWith(href)
|
|
128
|
+
expect(response.code).toHaveBeenCalledWith(redirect.statusCode)
|
|
129
|
+
}
|
|
130
|
+
)
|
|
131
|
+
|
|
132
|
+
it.each([
|
|
133
|
+
{
|
|
134
|
+
href: '/test/full-name',
|
|
135
|
+
|
|
136
|
+
request: {
|
|
137
|
+
method: 'post',
|
|
138
|
+
payload: {
|
|
139
|
+
action: FormAction.Validate
|
|
140
|
+
},
|
|
141
|
+
query: {
|
|
142
|
+
myParam1: 'myValue1',
|
|
143
|
+
myParam2: 'myValue2',
|
|
144
|
+
returnUrl: '/test/summary'
|
|
145
|
+
}
|
|
146
|
+
} satisfies Partial<FormContextRequest>,
|
|
147
|
+
|
|
148
|
+
redirect: {
|
|
149
|
+
statusCode: StatusCodes.SEE_OTHER
|
|
150
|
+
} satisfies Partial<ResponseObject>
|
|
151
|
+
}
|
|
152
|
+
])(
|
|
153
|
+
"should redirect to the 'returnUrl' query param provided (relative paths)",
|
|
154
|
+
({ href, redirect, ...options }) => {
|
|
155
|
+
request = { ...request, ...options.request }
|
|
156
|
+
|
|
157
|
+
const response = proceed(request, h, href)
|
|
158
|
+
|
|
159
|
+
expect(h.view).not.toHaveBeenCalled()
|
|
160
|
+
expect(h.redirect).toHaveBeenCalledWith(request.query.returnUrl)
|
|
161
|
+
expect(response.code).toHaveBeenCalledWith(redirect.statusCode)
|
|
162
|
+
}
|
|
163
|
+
)
|
|
164
|
+
|
|
165
|
+
it.each([
|
|
166
|
+
{
|
|
167
|
+
href: '/test/full-name',
|
|
168
|
+
|
|
169
|
+
request: {
|
|
170
|
+
method: 'get',
|
|
171
|
+
query: { returnUrl: 'slash-missing' }
|
|
172
|
+
} satisfies Partial<FormContextRequest>,
|
|
173
|
+
|
|
174
|
+
redirect: {
|
|
175
|
+
statusCode: StatusCodes.MOVED_TEMPORARILY
|
|
176
|
+
} satisfies Partial<ResponseObject>
|
|
177
|
+
},
|
|
178
|
+
{
|
|
179
|
+
href: '/test/full-name',
|
|
180
|
+
|
|
181
|
+
request: {
|
|
182
|
+
method: 'post',
|
|
183
|
+
payload: {
|
|
184
|
+
action: FormAction.Validate
|
|
185
|
+
},
|
|
186
|
+
query: { returnUrl: 'https://www.gov.uk/help/privacy-notice' }
|
|
187
|
+
} satisfies Partial<FormContextRequest>,
|
|
188
|
+
|
|
189
|
+
redirect: {
|
|
190
|
+
statusCode: StatusCodes.SEE_OTHER
|
|
191
|
+
} satisfies Partial<ResponseObject>
|
|
192
|
+
},
|
|
193
|
+
{
|
|
194
|
+
href: '/test/repeater/example',
|
|
195
|
+
|
|
196
|
+
request: {
|
|
197
|
+
method: 'post',
|
|
198
|
+
query: {
|
|
199
|
+
myParam1: 'myValue1',
|
|
200
|
+
myParam2: 'myValue2',
|
|
201
|
+
returnUrl: '/test/repeater/summary'
|
|
202
|
+
},
|
|
203
|
+
payload: {
|
|
204
|
+
action: FormAction.AddAnother
|
|
205
|
+
}
|
|
206
|
+
} satisfies Partial<FormContextRequest>,
|
|
207
|
+
|
|
208
|
+
redirect: {
|
|
209
|
+
statusCode: StatusCodes.MOVED_TEMPORARILY
|
|
210
|
+
} satisfies Partial<ResponseObject>
|
|
211
|
+
}
|
|
212
|
+
])(
|
|
213
|
+
"should not redirect to the 'returnUrl' query param provided (other paths)",
|
|
214
|
+
({ href, ...options }) => {
|
|
215
|
+
request = { ...request, ...options.request }
|
|
216
|
+
|
|
217
|
+
proceed(request, h, href)
|
|
218
|
+
expect(h.redirect).not.toHaveBeenCalledWith(request.query.returnUrl)
|
|
219
|
+
}
|
|
220
|
+
)
|
|
221
|
+
})
|
|
222
|
+
|
|
223
|
+
describe('encodeUrl', () => {
|
|
224
|
+
it.each([
|
|
225
|
+
{
|
|
226
|
+
input: 'http://example.com?myParam=has spaces&more£',
|
|
227
|
+
output: 'http://example.com/?myParam=has%20spaces&more%C2%A3'
|
|
228
|
+
},
|
|
229
|
+
{
|
|
230
|
+
input: 'mailto:hello@example.com?subject=has spaces&body=more£',
|
|
231
|
+
output: 'mailto:hello@example.com?subject=has%20spaces&body=more%C2%A3'
|
|
232
|
+
}
|
|
233
|
+
])('should percent encode parameters', ({ input, output }) => {
|
|
234
|
+
const returned = encodeUrl(input)
|
|
235
|
+
expect(returned).toBe(output)
|
|
236
|
+
})
|
|
237
|
+
|
|
238
|
+
it('should return undefined when no url is provided', () => {
|
|
239
|
+
const returned = encodeUrl()
|
|
240
|
+
expect(returned).toBeUndefined()
|
|
241
|
+
})
|
|
242
|
+
|
|
243
|
+
it('should throw when invalid url is provided', () => {
|
|
244
|
+
expect(() => encodeUrl('not a url')).toThrow()
|
|
245
|
+
})
|
|
246
|
+
})
|
|
247
|
+
|
|
248
|
+
describe('getPageHref', () => {
|
|
249
|
+
it('should return page href', () => {
|
|
250
|
+
const returned = getPageHref(page)
|
|
251
|
+
expect(returned).toEqual(page.href)
|
|
252
|
+
})
|
|
253
|
+
|
|
254
|
+
it('should return page href (path override)', () => {
|
|
255
|
+
const nextPath = '/badgers/monkeys'
|
|
256
|
+
const nextHref = '/test/badgers/monkeys'
|
|
257
|
+
|
|
258
|
+
const returned = getPageHref(page, nextPath)
|
|
259
|
+
expect(returned).toEqual(nextHref)
|
|
260
|
+
})
|
|
261
|
+
|
|
262
|
+
it('should return page href without query params', () => {
|
|
263
|
+
request.query.myParam = 'myValue'
|
|
264
|
+
request.query.myParam2 = 'myValue2'
|
|
265
|
+
|
|
266
|
+
const returned = getPageHref(page)
|
|
267
|
+
expect(returned).toEqual(page.href)
|
|
268
|
+
})
|
|
269
|
+
|
|
270
|
+
it('should return page href (path override) without query params', () => {
|
|
271
|
+
request.query.myParam = 'myValue'
|
|
272
|
+
request.query.myParam2 = 'myValue2'
|
|
273
|
+
|
|
274
|
+
const nextPath = '/badgers/monkeys'
|
|
275
|
+
const nextHref = '/test/badgers/monkeys'
|
|
276
|
+
|
|
277
|
+
const returned = getPageHref(page, nextPath)
|
|
278
|
+
expect(returned).toEqual(nextHref)
|
|
279
|
+
})
|
|
280
|
+
|
|
281
|
+
it('should return page href with new query params', () => {
|
|
282
|
+
const returned = getPageHref(page, {
|
|
283
|
+
returnUrl: page.getSummaryPath(),
|
|
284
|
+
badger: 'monkeys'
|
|
285
|
+
})
|
|
286
|
+
|
|
287
|
+
expect(returned).toBe(
|
|
288
|
+
`${page.href}?returnUrl=${encodeURIComponent('/summary')}&badger=monkeys`
|
|
289
|
+
)
|
|
290
|
+
})
|
|
291
|
+
|
|
292
|
+
it('should return page href (path override) with new query params', () => {
|
|
293
|
+
const nextPath = '/badgers/monkeys'
|
|
294
|
+
const nextHref = '/test/badgers/monkeys'
|
|
295
|
+
|
|
296
|
+
const returned = getPageHref(page, nextPath, {
|
|
297
|
+
returnUrl: page.getSummaryPath(),
|
|
298
|
+
badger: 'monkeys'
|
|
299
|
+
})
|
|
300
|
+
|
|
301
|
+
expect(returned).toBe(
|
|
302
|
+
`${nextHref}?returnUrl=${encodeURIComponent('/summary')}&badger=monkeys`
|
|
303
|
+
)
|
|
304
|
+
})
|
|
305
|
+
|
|
306
|
+
it('should throw when absolute URL is provided', () => {
|
|
307
|
+
expect(() =>
|
|
308
|
+
getPageHref(page, 'https://www.gov.uk/help/privacy-notice')
|
|
309
|
+
).toThrow('Only relative URLs are allowed')
|
|
310
|
+
})
|
|
311
|
+
})
|
|
312
|
+
|
|
313
|
+
describe('checkFormStatus', () => {
|
|
314
|
+
it('should return true/live for paths starting with PREVIEW_PATH_PREFIX and form is live', () => {
|
|
315
|
+
const path = `${PREVIEW_PATH_PREFIX}/live/another/segment`
|
|
316
|
+
expect(checkFormStatus(path)).toStrictEqual({
|
|
317
|
+
state: FormStatus.Live,
|
|
318
|
+
isPreview: true
|
|
319
|
+
})
|
|
320
|
+
})
|
|
321
|
+
|
|
322
|
+
it('should return false for paths not starting with PREVIEW_PATH_PREFIX', () => {
|
|
323
|
+
const path = '/some/other/path'
|
|
324
|
+
expect(checkFormStatus(path)).toStrictEqual({
|
|
325
|
+
state: FormStatus.Live,
|
|
326
|
+
isPreview: false
|
|
327
|
+
})
|
|
328
|
+
})
|
|
329
|
+
|
|
330
|
+
it('should be case insensitive and return draft when form is draft', () => {
|
|
331
|
+
const path = `${PREVIEW_PATH_PREFIX.toUpperCase()}/draft/path`
|
|
332
|
+
expect(checkFormStatus(path)).toStrictEqual({
|
|
333
|
+
state: FormStatus.Draft,
|
|
334
|
+
isPreview: true
|
|
335
|
+
})
|
|
336
|
+
})
|
|
337
|
+
|
|
338
|
+
it('should throw an error for invalid form state', () => {
|
|
339
|
+
const path = `${PREVIEW_PATH_PREFIX}/invalid-state`
|
|
340
|
+
expect(() => checkFormStatus(path)).toThrow(
|
|
341
|
+
'Invalid form state: invalid-state'
|
|
342
|
+
)
|
|
343
|
+
})
|
|
344
|
+
})
|
|
345
|
+
|
|
346
|
+
describe('checkEmailAddressForLiveFormSubmission', () => {
|
|
347
|
+
it('should throw an error if emailAddress is undefined and isPreview is false', () => {
|
|
348
|
+
expect(() =>
|
|
349
|
+
checkEmailAddressForLiveFormSubmission(undefined, false)
|
|
350
|
+
).toThrow(
|
|
351
|
+
Boom.internal(
|
|
352
|
+
'An email address is required to complete the form submission'
|
|
353
|
+
)
|
|
354
|
+
)
|
|
355
|
+
})
|
|
356
|
+
|
|
357
|
+
it('should not throw an error if emailAddress is defined and isPreview is false', () => {
|
|
358
|
+
expect(() =>
|
|
359
|
+
checkEmailAddressForLiveFormSubmission('test@example.com', false)
|
|
360
|
+
).not.toThrow()
|
|
361
|
+
})
|
|
362
|
+
|
|
363
|
+
it('should not throw an error if emailAddress is undefined and isPreview is true', () => {
|
|
364
|
+
expect(() =>
|
|
365
|
+
checkEmailAddressForLiveFormSubmission(undefined, true)
|
|
366
|
+
).not.toThrow()
|
|
367
|
+
})
|
|
368
|
+
|
|
369
|
+
it('should not throw an error if emailAddress is defined and isPreview is true', () => {
|
|
370
|
+
expect(() =>
|
|
371
|
+
checkEmailAddressForLiveFormSubmission('test@example.com', true)
|
|
372
|
+
).not.toThrow()
|
|
373
|
+
})
|
|
374
|
+
})
|
|
375
|
+
|
|
376
|
+
describe('getErrors', () => {
|
|
377
|
+
it('formats dates with ISO strings', () => {
|
|
378
|
+
const { details } = new ValidationError(
|
|
379
|
+
'Date of marriage example',
|
|
380
|
+
[
|
|
381
|
+
{
|
|
382
|
+
message:
|
|
383
|
+
'Date of marriage must be on or before 2021-12-25T00:00:00.000Z',
|
|
384
|
+
path: ['dateField'],
|
|
385
|
+
type: 'date.max',
|
|
386
|
+
context: {
|
|
387
|
+
key: 'dateField',
|
|
388
|
+
title: 'date of marriage'
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
],
|
|
392
|
+
undefined
|
|
393
|
+
)
|
|
394
|
+
|
|
395
|
+
expect(getErrors(details)).toEqual([
|
|
396
|
+
{
|
|
397
|
+
path: ['dateField'],
|
|
398
|
+
href: '#dateField',
|
|
399
|
+
name: 'dateField',
|
|
400
|
+
text: 'Date of marriage must be on or before 25 December 2021',
|
|
401
|
+
context: {
|
|
402
|
+
key: 'dateField',
|
|
403
|
+
title: 'date of marriage'
|
|
404
|
+
}
|
|
405
|
+
}
|
|
406
|
+
])
|
|
407
|
+
})
|
|
408
|
+
|
|
409
|
+
it('does not format the first letter to uppercase', () => {
|
|
410
|
+
const { details } = new ValidationError(
|
|
411
|
+
'Date of marriage example',
|
|
412
|
+
[
|
|
413
|
+
{
|
|
414
|
+
message: 'something invalid',
|
|
415
|
+
path: ['yesNoField'],
|
|
416
|
+
type: 'string.pattern.base',
|
|
417
|
+
context: {
|
|
418
|
+
key: 'yesNoField'
|
|
419
|
+
}
|
|
420
|
+
}
|
|
421
|
+
],
|
|
422
|
+
undefined
|
|
423
|
+
)
|
|
424
|
+
|
|
425
|
+
expect(getErrors(details)).toEqual([
|
|
426
|
+
{
|
|
427
|
+
path: ['yesNoField'],
|
|
428
|
+
href: '#yesNoField',
|
|
429
|
+
name: 'yesNoField',
|
|
430
|
+
text: 'something invalid',
|
|
431
|
+
context: {
|
|
432
|
+
key: 'yesNoField'
|
|
433
|
+
}
|
|
434
|
+
}
|
|
435
|
+
])
|
|
436
|
+
})
|
|
437
|
+
})
|
|
438
|
+
|
|
439
|
+
describe('safeGenerateCrumb', () => {
|
|
440
|
+
it('should return undefined when request.state is missing (malformed request)', () => {
|
|
441
|
+
const malformedRequest = {
|
|
442
|
+
server: {
|
|
443
|
+
plugins: {
|
|
444
|
+
crumb: {
|
|
445
|
+
generate: jest.fn()
|
|
446
|
+
}
|
|
447
|
+
}
|
|
448
|
+
},
|
|
449
|
+
plugins: {},
|
|
450
|
+
route: { settings: { plugins: {} } },
|
|
451
|
+
path: '/test',
|
|
452
|
+
url: { search: '' }
|
|
453
|
+
// state intentionally omitted
|
|
454
|
+
} as unknown as FormRequest
|
|
455
|
+
|
|
456
|
+
const crumbToken = safeGenerateCrumb(malformedRequest)
|
|
457
|
+
expect(crumbToken).toBeUndefined()
|
|
458
|
+
expect(
|
|
459
|
+
malformedRequest.server.plugins.crumb.generate
|
|
460
|
+
).not.toHaveBeenCalled()
|
|
461
|
+
})
|
|
462
|
+
|
|
463
|
+
it('should return undefined if crumb is disabled in route settings', () => {
|
|
464
|
+
const requestWithDisabledCrumb = {
|
|
465
|
+
server: {
|
|
466
|
+
plugins: {
|
|
467
|
+
crumb: {
|
|
468
|
+
generate: jest.fn().mockReturnValue('test-token')
|
|
469
|
+
}
|
|
470
|
+
}
|
|
471
|
+
},
|
|
472
|
+
plugins: {},
|
|
473
|
+
route: { settings: { plugins: { crumb: false } } },
|
|
474
|
+
path: '/test',
|
|
475
|
+
url: { search: '' },
|
|
476
|
+
state: {}
|
|
477
|
+
} as unknown as FormRequest
|
|
478
|
+
|
|
479
|
+
const crumbToken = safeGenerateCrumb(requestWithDisabledCrumb)
|
|
480
|
+
expect(crumbToken).toBeUndefined()
|
|
481
|
+
expect(
|
|
482
|
+
requestWithDisabledCrumb.server.plugins.crumb.generate
|
|
483
|
+
).not.toHaveBeenCalled()
|
|
484
|
+
})
|
|
485
|
+
|
|
486
|
+
it('should generate crumb when state exists and crumb plugin is available', () => {
|
|
487
|
+
const mockCrumb = 'generated-crumb-value'
|
|
488
|
+
const validRequest = {
|
|
489
|
+
server: {
|
|
490
|
+
plugins: {
|
|
491
|
+
crumb: {
|
|
492
|
+
generate: jest.fn().mockReturnValue(mockCrumb)
|
|
493
|
+
}
|
|
494
|
+
}
|
|
495
|
+
},
|
|
496
|
+
plugins: {},
|
|
497
|
+
route: { settings: { plugins: {} } },
|
|
498
|
+
path: '/test',
|
|
499
|
+
url: { search: '' },
|
|
500
|
+
state: {}
|
|
501
|
+
} as unknown as FormRequest
|
|
502
|
+
|
|
503
|
+
const crumbToken = safeGenerateCrumb(validRequest)
|
|
504
|
+
expect(crumbToken).toBe(mockCrumb)
|
|
505
|
+
expect(validRequest.server.plugins.crumb.generate).toHaveBeenCalledWith(
|
|
506
|
+
validRequest
|
|
507
|
+
)
|
|
508
|
+
})
|
|
509
|
+
})
|
|
510
|
+
|
|
511
|
+
describe('getExponentialBackoffDelay', () => {
|
|
512
|
+
it.each([
|
|
513
|
+
{ depth: 1, expected: 2000 },
|
|
514
|
+
{ depth: 2, expected: 4000 },
|
|
515
|
+
{ depth: 3, expected: 8000 },
|
|
516
|
+
{ depth: 4, expected: 16000 },
|
|
517
|
+
{ depth: 5, expected: 25000 },
|
|
518
|
+
{ depth: 6, expected: 25000 },
|
|
519
|
+
{ depth: 7, expected: 25000 }
|
|
520
|
+
])(
|
|
521
|
+
'should calculate correct delay for depth $depth',
|
|
522
|
+
({ depth, expected }) => {
|
|
523
|
+
expect(getExponentialBackoffDelay(depth)).toBe(expected)
|
|
524
|
+
}
|
|
525
|
+
)
|
|
526
|
+
|
|
527
|
+
it('should handle depth of 0', () => {
|
|
528
|
+
expect(getExponentialBackoffDelay(0)).toBe(1000)
|
|
529
|
+
})
|
|
530
|
+
|
|
531
|
+
it('should handle negative depth', () => {
|
|
532
|
+
expect(getExponentialBackoffDelay(-1)).toBe(500)
|
|
533
|
+
})
|
|
534
|
+
|
|
535
|
+
it('should cap at 25 seconds (25000ms) even for large depths', () => {
|
|
536
|
+
// For depth 10: 2000 * 2^(9) would be too high, so it should be capped
|
|
537
|
+
expect(getExponentialBackoffDelay(10)).toBe(25000)
|
|
538
|
+
expect(getExponentialBackoffDelay(20)).toBe(25000)
|
|
539
|
+
})
|
|
540
|
+
})
|
|
541
|
+
|
|
542
|
+
describe('evaluateTemplate', () => {
|
|
543
|
+
let model: FormModel
|
|
544
|
+
let formContext: FormContext
|
|
545
|
+
|
|
546
|
+
beforeEach(() => {
|
|
547
|
+
model = new FormModel(templateDefinition, {
|
|
548
|
+
basePath: 'template'
|
|
549
|
+
})
|
|
550
|
+
|
|
551
|
+
formContext = {
|
|
552
|
+
evaluationState: {},
|
|
553
|
+
relevantState: {},
|
|
554
|
+
relevantPages: [],
|
|
555
|
+
payload: {},
|
|
556
|
+
state: {},
|
|
557
|
+
paths: [],
|
|
558
|
+
isForceAccess: false,
|
|
559
|
+
data: {},
|
|
560
|
+
pageDefMap: model.pageDefMap,
|
|
561
|
+
listDefMap: model.listDefMap,
|
|
562
|
+
componentDefMap: model.componentDefMap,
|
|
563
|
+
pageMap: model.pageMap,
|
|
564
|
+
componentMap: model.componentMap
|
|
565
|
+
}
|
|
566
|
+
})
|
|
567
|
+
|
|
568
|
+
it('should replace placeholders with values from form context relevantState', () => {
|
|
569
|
+
Object.assign(formContext.relevantState, {
|
|
570
|
+
WmHfSb: 'Enrique Chase'
|
|
571
|
+
})
|
|
572
|
+
|
|
573
|
+
const areYouInEngland = templateDefinition.pages[2]
|
|
574
|
+
expect(areYouInEngland.title).toBe('Are you in England, {{ WmHfSb }}?')
|
|
575
|
+
|
|
576
|
+
const result = evaluateTemplate(areYouInEngland.title, formContext)
|
|
577
|
+
expect(result).toBe('Are you in England, Enrique Chase?')
|
|
578
|
+
})
|
|
579
|
+
|
|
580
|
+
it('should replace placeholders with values from form context data', () => {
|
|
581
|
+
Object.assign(formContext.data, {
|
|
582
|
+
score: 'Low'
|
|
583
|
+
})
|
|
584
|
+
|
|
585
|
+
const result = evaluateTemplate(
|
|
586
|
+
'Your score is: {{ context.data.score }}',
|
|
587
|
+
formContext
|
|
588
|
+
)
|
|
589
|
+
|
|
590
|
+
expect(result).toBe('Your score is: Low')
|
|
591
|
+
})
|
|
592
|
+
|
|
593
|
+
it('evaluate filter should evaluate a liquid template', () => {
|
|
594
|
+
Object.assign(formContext.relevantState, {
|
|
595
|
+
WmHfSb: 'Enrique Chase'
|
|
596
|
+
})
|
|
597
|
+
|
|
598
|
+
const result = evaluateTemplate(
|
|
599
|
+
'{{ "Hello, {{ WmHfSb }}!" | evaluate }}',
|
|
600
|
+
formContext
|
|
601
|
+
)
|
|
602
|
+
|
|
603
|
+
expect(result).toBe('Hello, Enrique Chase!')
|
|
604
|
+
})
|
|
605
|
+
|
|
606
|
+
it('page filter should return the page definition', () => {
|
|
607
|
+
// @ts-expect-error - spyOn type issue
|
|
608
|
+
const filterSpy = jest.spyOn(engine.filters, 'page')
|
|
609
|
+
const result = evaluateTemplate(
|
|
610
|
+
'{%- assign startPageDef = "/start" | page -%}{{ startPageDef.title }}',
|
|
611
|
+
formContext
|
|
612
|
+
)
|
|
613
|
+
|
|
614
|
+
expect(filterSpy).toHaveBeenCalledWith('/start')
|
|
615
|
+
expect(result).toBe('Start page')
|
|
616
|
+
})
|
|
617
|
+
|
|
618
|
+
it('page filter should return empty when anything but a string is passed', () => {
|
|
619
|
+
// @ts-expect-error - spyOn type issue
|
|
620
|
+
const pageFilterSpy = jest.spyOn(engine.filters, 'page')
|
|
621
|
+
|
|
622
|
+
let result = evaluateTemplate('{{ 0 | page }}', formContext)
|
|
623
|
+
expect(pageFilterSpy).toHaveBeenLastCalledWith(0)
|
|
624
|
+
expect(result).toBe('')
|
|
625
|
+
|
|
626
|
+
result = evaluateTemplate('{{ undefined | page }}', formContext)
|
|
627
|
+
expect(pageFilterSpy).toHaveBeenLastCalledWith(undefined)
|
|
628
|
+
expect(result).toBe('')
|
|
629
|
+
|
|
630
|
+
result = evaluateTemplate('{{ null | page }}', formContext)
|
|
631
|
+
expect(result).toBe('')
|
|
632
|
+
|
|
633
|
+
result = evaluateTemplate('{{ false | page }}', formContext)
|
|
634
|
+
expect(pageFilterSpy).toHaveBeenLastCalledWith(false)
|
|
635
|
+
expect(result).toBe('')
|
|
636
|
+
|
|
637
|
+
result = evaluateTemplate('{{ [] | page }}', formContext)
|
|
638
|
+
expect(result).toBe('')
|
|
639
|
+
})
|
|
640
|
+
|
|
641
|
+
it('href filter should return the page href', () => {
|
|
642
|
+
// @ts-expect-error - spyOn type issue
|
|
643
|
+
const filterSpy = jest.spyOn(engine.filters, 'href')
|
|
644
|
+
const result = evaluateTemplate('{{ "/full-name" | href }}', formContext)
|
|
645
|
+
|
|
646
|
+
expect(filterSpy).toHaveBeenCalledWith('/full-name')
|
|
647
|
+
expect(result).toBe('/template/full-name')
|
|
648
|
+
})
|
|
649
|
+
|
|
650
|
+
it('href filter should return empty when no page passed', () => {
|
|
651
|
+
// @ts-expect-error - spyOn type issue
|
|
652
|
+
const pageFilterSpy = jest.spyOn(engine.filters, 'href')
|
|
653
|
+
|
|
654
|
+
const result = evaluateTemplate('{{ undefined | href }}', formContext)
|
|
655
|
+
expect(pageFilterSpy).toHaveBeenLastCalledWith(undefined)
|
|
656
|
+
expect(result).toBe('')
|
|
657
|
+
})
|
|
658
|
+
|
|
659
|
+
it('field filter should return the component definition', () => {
|
|
660
|
+
// @ts-expect-error - spyOn type issue
|
|
661
|
+
const filterSpy = jest.spyOn(engine.filters, 'field')
|
|
662
|
+
const result = evaluateTemplate(
|
|
663
|
+
'{%- assign fullNameComponentDef = "WmHfSb" | field -%}{{ fullNameComponentDef.title }}',
|
|
664
|
+
formContext
|
|
665
|
+
)
|
|
666
|
+
|
|
667
|
+
expect(filterSpy).toHaveBeenCalledWith('WmHfSb')
|
|
668
|
+
expect(result).toBe('What's your full name?')
|
|
669
|
+
})
|
|
670
|
+
|
|
671
|
+
it('field filter should return empty when anything but a string is passed', () => {
|
|
672
|
+
// @ts-expect-error - spyOn type issue
|
|
673
|
+
const pageFilterSpy = jest.spyOn(engine.filters, 'field')
|
|
674
|
+
|
|
675
|
+
let result = evaluateTemplate('{{ 0 | field }}', formContext)
|
|
676
|
+
expect(pageFilterSpy).toHaveBeenLastCalledWith(0)
|
|
677
|
+
expect(result).toBe('')
|
|
678
|
+
|
|
679
|
+
result = evaluateTemplate('{{ undefined | field }}', formContext)
|
|
680
|
+
expect(pageFilterSpy).toHaveBeenLastCalledWith(undefined)
|
|
681
|
+
expect(result).toBe('')
|
|
682
|
+
|
|
683
|
+
result = evaluateTemplate('{{ null | field }}', formContext)
|
|
684
|
+
expect(result).toBe('')
|
|
685
|
+
|
|
686
|
+
result = evaluateTemplate('{{ false | field }}', formContext)
|
|
687
|
+
expect(pageFilterSpy).toHaveBeenLastCalledWith(false)
|
|
688
|
+
expect(result).toBe('')
|
|
689
|
+
|
|
690
|
+
result = evaluateTemplate('{{ [] | field }}', formContext)
|
|
691
|
+
expect(result).toBe('')
|
|
692
|
+
})
|
|
693
|
+
|
|
694
|
+
it('answer filter should return the formatted submitted answer', () => {
|
|
695
|
+
Object.assign(formContext.relevantState, {
|
|
696
|
+
TKsWbP: true,
|
|
697
|
+
WmHfSb: 'Enrique Chase'
|
|
698
|
+
})
|
|
699
|
+
|
|
700
|
+
// @ts-expect-error - spyOn type issue
|
|
701
|
+
const filterSpy = jest.spyOn(engine.filters, 'answer')
|
|
702
|
+
|
|
703
|
+
let result = evaluateTemplate("{{ 'TKsWbP' | answer }}", formContext)
|
|
704
|
+
expect(filterSpy).toHaveBeenCalledWith('TKsWbP')
|
|
705
|
+
expect(result).toBe('Yes')
|
|
706
|
+
|
|
707
|
+
result = evaluateTemplate("{{ 'WmHfSb' | answer }}", formContext)
|
|
708
|
+
expect(filterSpy).toHaveBeenCalledWith('WmHfSb')
|
|
709
|
+
expect(result).toBe('Enrique Chase')
|
|
710
|
+
})
|
|
711
|
+
|
|
712
|
+
it('answer filter should return empty when anything but a string is passed', () => {
|
|
713
|
+
// @ts-expect-error - spyOn type issue
|
|
714
|
+
const pageFilterSpy = jest.spyOn(engine.filters, 'answer')
|
|
715
|
+
|
|
716
|
+
let result = evaluateTemplate('{{ 0 | answer }}', formContext)
|
|
717
|
+
expect(pageFilterSpy).toHaveBeenLastCalledWith(0)
|
|
718
|
+
expect(result).toBe('')
|
|
719
|
+
|
|
720
|
+
result = evaluateTemplate('{{ undefined | answer }}', formContext)
|
|
721
|
+
expect(pageFilterSpy).toHaveBeenLastCalledWith(undefined)
|
|
722
|
+
expect(result).toBe('')
|
|
723
|
+
|
|
724
|
+
result = evaluateTemplate('{{ null | answer }}', formContext)
|
|
725
|
+
expect(result).toBe('')
|
|
726
|
+
|
|
727
|
+
result = evaluateTemplate('{{ false | answer }}', formContext)
|
|
728
|
+
expect(pageFilterSpy).toHaveBeenLastCalledWith(false)
|
|
729
|
+
expect(result).toBe('')
|
|
730
|
+
|
|
731
|
+
result = evaluateTemplate('{{ [] | answer }}', formContext)
|
|
732
|
+
expect(result).toBe('')
|
|
733
|
+
})
|
|
734
|
+
|
|
735
|
+
it('answer filter should return empty when non-form component name is passed', () => {
|
|
736
|
+
// @ts-expect-error - spyOn type issue
|
|
737
|
+
const pageFilterSpy = jest.spyOn(engine.filters, 'answer')
|
|
738
|
+
|
|
739
|
+
const result = evaluateTemplate('{{ "FGyiLS" | answer }}', formContext)
|
|
740
|
+
expect(pageFilterSpy).toHaveBeenLastCalledWith('FGyiLS')
|
|
741
|
+
expect(result).toBe('')
|
|
742
|
+
})
|
|
743
|
+
})
|
|
744
|
+
|
|
745
|
+
describe('Nunjucks filters', () => {
|
|
746
|
+
describe('evaluate filter', () => {
|
|
747
|
+
it('returns non-string values unchanged', () => {
|
|
748
|
+
const mockContext: NunjucksContext = {
|
|
749
|
+
context: {
|
|
750
|
+
globals: {
|
|
751
|
+
context: { pageMap: new Map() } as FormContext,
|
|
752
|
+
pages: new Map(),
|
|
753
|
+
components: new Map()
|
|
754
|
+
}
|
|
755
|
+
}
|
|
756
|
+
}
|
|
757
|
+
|
|
758
|
+
const numResult = (
|
|
759
|
+
engine.filters.evaluate as unknown as EvaluateFilter
|
|
760
|
+
).call(mockContext, 123)
|
|
761
|
+
expect(numResult).toBe(123)
|
|
762
|
+
|
|
763
|
+
const objResult = (
|
|
764
|
+
engine.filters.evaluate as unknown as EvaluateFilter
|
|
765
|
+
).call(mockContext, { foo: 'bar' })
|
|
766
|
+
expect(objResult).toEqual({ foo: 'bar' })
|
|
767
|
+
})
|
|
768
|
+
})
|
|
769
|
+
|
|
770
|
+
describe('href filter', () => {
|
|
771
|
+
it('returns undefined when page is undefined', () => {
|
|
772
|
+
const mockContext: NunjucksContext = {
|
|
773
|
+
context: {
|
|
774
|
+
globals: {
|
|
775
|
+
context: { pageMap: new Map() } as FormContext,
|
|
776
|
+
pages: new Map(),
|
|
777
|
+
components: new Map()
|
|
778
|
+
}
|
|
779
|
+
}
|
|
780
|
+
}
|
|
781
|
+
|
|
782
|
+
const result = (engine.filters.href as unknown as HrefFilter).call(
|
|
783
|
+
mockContext,
|
|
784
|
+
'/some-page'
|
|
785
|
+
)
|
|
786
|
+
|
|
787
|
+
expect(result).toBeUndefined()
|
|
788
|
+
})
|
|
789
|
+
})
|
|
790
|
+
})
|
|
791
|
+
})
|