@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.
Files changed (328) hide show
  1. package/.public/assets/fonts/bold-affa96571d-v2.woff +0 -0
  2. package/.public/assets/fonts/bold-b542beb274-v2.woff2 +0 -0
  3. package/.public/assets/fonts/light-94a07e06a1-v2.woff2 +0 -0
  4. package/.public/assets/fonts/light-f591b13f7d-v2.woff +0 -0
  5. package/.public/assets/images/favicon.ico +0 -0
  6. package/.public/assets/images/favicon.svg +1 -0
  7. package/.public/assets/images/govuk-crest.svg +1 -0
  8. package/.public/assets/images/govuk-icon-180.png +0 -0
  9. package/.public/assets/images/govuk-icon-192.png +0 -0
  10. package/.public/assets/images/govuk-icon-512.png +0 -0
  11. package/.public/assets/images/govuk-icon-mask.svg +1 -0
  12. package/.public/assets/images/govuk-opengraph-image.png +0 -0
  13. package/.public/assets/manifest.json +39 -0
  14. package/.public/assets-manifest.json +24 -0
  15. package/.public/javascripts/application.0fd8c18.min.js +3 -0
  16. package/.public/javascripts/application.0fd8c18.min.js.LICENSE.txt +58 -0
  17. package/.public/javascripts/application.0fd8c18.min.js.map +1 -0
  18. package/.public/javascripts/file-upload.b2f18f5.min.js +2 -0
  19. package/.public/javascripts/file-upload.b2f18f5.min.js.map +1 -0
  20. package/.public/javascripts/vendor/accessible-autocomplete.275d332.min.js +2 -0
  21. package/.public/javascripts/vendor/accessible-autocomplete.275d332.min.js.map +1 -0
  22. package/.public/stylesheets/application.e340021.min.css +14 -0
  23. package/.public/stylesheets/application.e340021.min.css.map +1 -0
  24. package/package.json +6 -2
  25. package/.browserslistrc +0 -16
  26. package/.editorconfig +0 -9
  27. package/.eslintrc.cjs +0 -266
  28. package/.github/dependabot.yml +0 -85
  29. package/.github/workflows/check-pull-request.yml +0 -209
  30. package/.github/workflows/pr-notifier.yml +0 -98
  31. package/.github/workflows/publish.yml +0 -111
  32. package/.husky/pre-commit +0 -1
  33. package/.lintstagedrc.js +0 -4
  34. package/.nvmrc +0 -1
  35. package/.prettierignore +0 -8
  36. package/.prettierrc.cjs +0 -26
  37. package/Dockerfile +0 -61
  38. package/Procfile +0 -1
  39. package/babel.config.cjs +0 -55
  40. package/compose/aws.env +0 -4
  41. package/compose/start-localstack.sh +0 -26
  42. package/docker-compose.yaml +0 -86
  43. package/globals.d.ts +0 -1
  44. package/jest.config.cjs +0 -54
  45. package/jest.environment.js +0 -4
  46. package/jest.setup.cjs +0 -14
  47. package/postcss.config.js +0 -26
  48. package/sonar-project.properties +0 -17
  49. package/src/client/javascripts/application.js +0 -87
  50. package/src/client/javascripts/file-upload.js +0 -386
  51. package/src/client/stylesheets/_code.scss +0 -33
  52. package/src/client/stylesheets/_govuk-frontend.scss +0 -4
  53. package/src/client/stylesheets/_prose.scss +0 -56
  54. package/src/client/stylesheets/_service-banner.scss +0 -24
  55. package/src/client/stylesheets/_summary-list.scss +0 -28
  56. package/src/client/stylesheets/_tag-env.scss +0 -24
  57. package/src/client/stylesheets/application.scss +0 -14
  58. package/src/common/cookies.js +0 -58
  59. package/src/common/cookies.test.js +0 -23
  60. package/src/common/types.js +0 -5
  61. package/src/config/index.ts +0 -271
  62. package/src/index.ts +0 -31
  63. package/src/server/common/helpers/logging/logger-options.test.ts +0 -50
  64. package/src/server/common/helpers/logging/logger-options.ts +0 -46
  65. package/src/server/common/helpers/logging/logger.ts +0 -7
  66. package/src/server/common/helpers/logging/request-logger.ts +0 -9
  67. package/src/server/common/helpers/logging/request-tracing.js +0 -10
  68. package/src/server/common/helpers/redis-client.js +0 -70
  69. package/src/server/constants.js +0 -1
  70. package/src/server/forms/README.md +0 -10
  71. package/src/server/forms/components.json +0 -1015
  72. package/src/server/forms/report-a-terrorist.json +0 -270
  73. package/src/server/forms/runner-components-test.json +0 -365
  74. package/src/server/forms/test.json +0 -581
  75. package/src/server/index.test.ts +0 -582
  76. package/src/server/index.ts +0 -140
  77. package/src/server/plugins/blankie.test.ts +0 -73
  78. package/src/server/plugins/blankie.ts +0 -48
  79. package/src/server/plugins/crumb.ts +0 -20
  80. package/src/server/plugins/engine/README.md +0 -87
  81. package/src/server/plugins/engine/components/AutocompleteField.test.ts +0 -294
  82. package/src/server/plugins/engine/components/AutocompleteField.ts +0 -49
  83. package/src/server/plugins/engine/components/CheckboxesField.test.ts +0 -379
  84. package/src/server/plugins/engine/components/CheckboxesField.ts +0 -106
  85. package/src/server/plugins/engine/components/ComponentBase.ts +0 -97
  86. package/src/server/plugins/engine/components/ComponentCollection.ts +0 -278
  87. package/src/server/plugins/engine/components/DatePartsField.test.ts +0 -822
  88. package/src/server/plugins/engine/components/DatePartsField.ts +0 -264
  89. package/src/server/plugins/engine/components/Details.test.ts +0 -49
  90. package/src/server/plugins/engine/components/Details.ts +0 -30
  91. package/src/server/plugins/engine/components/EmailAddressField.test.ts +0 -395
  92. package/src/server/plugins/engine/components/EmailAddressField.ts +0 -55
  93. package/src/server/plugins/engine/components/FileUploadField.test.ts +0 -778
  94. package/src/server/plugins/engine/components/FileUploadField.ts +0 -262
  95. package/src/server/plugins/engine/components/FormComponent.ts +0 -249
  96. package/src/server/plugins/engine/components/Html.test.ts +0 -48
  97. package/src/server/plugins/engine/components/Html.ts +0 -29
  98. package/src/server/plugins/engine/components/InsetText.test.ts +0 -48
  99. package/src/server/plugins/engine/components/InsetText.ts +0 -27
  100. package/src/server/plugins/engine/components/List.test.ts +0 -76
  101. package/src/server/plugins/engine/components/List.ts +0 -72
  102. package/src/server/plugins/engine/components/ListFormComponent.ts +0 -140
  103. package/src/server/plugins/engine/components/MonthYearField.test.ts +0 -567
  104. package/src/server/plugins/engine/components/MonthYearField.ts +0 -222
  105. package/src/server/plugins/engine/components/MultilineTextField.test.ts +0 -558
  106. package/src/server/plugins/engine/components/MultilineTextField.ts +0 -138
  107. package/src/server/plugins/engine/components/NumberField.test.ts +0 -701
  108. package/src/server/plugins/engine/components/NumberField.ts +0 -163
  109. package/src/server/plugins/engine/components/RadiosField.test.ts +0 -288
  110. package/src/server/plugins/engine/components/RadiosField.ts +0 -24
  111. package/src/server/plugins/engine/components/SelectField.test.ts +0 -288
  112. package/src/server/plugins/engine/components/SelectField.ts +0 -47
  113. package/src/server/plugins/engine/components/SelectionControlField.ts +0 -43
  114. package/src/server/plugins/engine/components/TelephoneNumberField.test.ts +0 -356
  115. package/src/server/plugins/engine/components/TelephoneNumberField.ts +0 -67
  116. package/src/server/plugins/engine/components/TextField.test.ts +0 -489
  117. package/src/server/plugins/engine/components/TextField.ts +0 -96
  118. package/src/server/plugins/engine/components/UkAddressField.test.ts +0 -623
  119. package/src/server/plugins/engine/components/UkAddressField.ts +0 -172
  120. package/src/server/plugins/engine/components/YesNoField.test.ts +0 -248
  121. package/src/server/plugins/engine/components/YesNoField.ts +0 -31
  122. package/src/server/plugins/engine/components/constants.ts +0 -1
  123. package/src/server/plugins/engine/components/helpers.ts +0 -330
  124. package/src/server/plugins/engine/components/index.ts +0 -24
  125. package/src/server/plugins/engine/components/types.ts +0 -117
  126. package/src/server/plugins/engine/configureEnginePlugin.ts +0 -47
  127. package/src/server/plugins/engine/helpers.test.ts +0 -791
  128. package/src/server/plugins/engine/helpers.ts +0 -379
  129. package/src/server/plugins/engine/index.ts +0 -7
  130. package/src/server/plugins/engine/models/FormModel.test.ts +0 -42
  131. package/src/server/plugins/engine/models/FormModel.ts +0 -443
  132. package/src/server/plugins/engine/models/RepeatingSummaryViewModel.ts +0 -0
  133. package/src/server/plugins/engine/models/Section.ts +0 -0
  134. package/src/server/plugins/engine/models/SummaryViewModel.test.ts +0 -209
  135. package/src/server/plugins/engine/models/SummaryViewModel.ts +0 -220
  136. package/src/server/plugins/engine/models/index.ts +0 -2
  137. package/src/server/plugins/engine/models/types.ts +0 -114
  138. package/src/server/plugins/engine/outputFormatters/human/v1.test.ts +0 -143
  139. package/src/server/plugins/engine/outputFormatters/human/v1.ts +0 -73
  140. package/src/server/plugins/engine/outputFormatters/index.test.ts +0 -17
  141. package/src/server/plugins/engine/outputFormatters/index.ts +0 -44
  142. package/src/server/plugins/engine/outputFormatters/machine/v1.test.ts +0 -229
  143. package/src/server/plugins/engine/outputFormatters/machine/v1.ts +0 -140
  144. package/src/server/plugins/engine/outputFormatters/machine/v2.test.ts +0 -229
  145. package/src/server/plugins/engine/outputFormatters/machine/v2.ts +0 -153
  146. package/src/server/plugins/engine/pageControllers/FileUploadPageController.test.ts +0 -1108
  147. package/src/server/plugins/engine/pageControllers/FileUploadPageController.ts +0 -446
  148. package/src/server/plugins/engine/pageControllers/PageController.test.ts +0 -205
  149. package/src/server/plugins/engine/pageControllers/PageController.ts +0 -176
  150. package/src/server/plugins/engine/pageControllers/QuestionPageController.test.ts +0 -1264
  151. package/src/server/plugins/engine/pageControllers/QuestionPageController.ts +0 -561
  152. package/src/server/plugins/engine/pageControllers/README.md +0 -28
  153. package/src/server/plugins/engine/pageControllers/RepeatPageController.test.ts +0 -264
  154. package/src/server/plugins/engine/pageControllers/RepeatPageController.ts +0 -458
  155. package/src/server/plugins/engine/pageControllers/StartPageController.ts +0 -18
  156. package/src/server/plugins/engine/pageControllers/StatusPageController.ts +0 -50
  157. package/src/server/plugins/engine/pageControllers/SummaryPageController.ts +0 -261
  158. package/src/server/plugins/engine/pageControllers/TerminalController.test.ts +0 -28
  159. package/src/server/plugins/engine/pageControllers/TerminalPageController.ts +0 -19
  160. package/src/server/plugins/engine/pageControllers/helpers.test.ts +0 -198
  161. package/src/server/plugins/engine/pageControllers/helpers.ts +0 -101
  162. package/src/server/plugins/engine/pageControllers/index.ts +0 -10
  163. package/src/server/plugins/engine/pageControllers/validationOptions.ts +0 -89
  164. package/src/server/plugins/engine/plugin.ts +0 -673
  165. package/src/server/plugins/engine/services/formSubmissionService.js +0 -46
  166. package/src/server/plugins/engine/services/formsService.js +0 -46
  167. package/src/server/plugins/engine/services/formsService.test.js +0 -90
  168. package/src/server/plugins/engine/services/index.js +0 -3
  169. package/src/server/plugins/engine/services/notifyService.test.ts +0 -132
  170. package/src/server/plugins/engine/services/notifyService.ts +0 -64
  171. package/src/server/plugins/engine/services/uploadService.js +0 -60
  172. package/src/server/plugins/engine/types.ts +0 -315
  173. package/src/server/plugins/engine/views/components/autocompletefield.html +0 -5
  174. package/src/server/plugins/engine/views/components/checkboxesfield.html +0 -5
  175. package/src/server/plugins/engine/views/components/datepartsfield.html +0 -5
  176. package/src/server/plugins/engine/views/components/details.html +0 -6
  177. package/src/server/plugins/engine/views/components/emailaddressfield.html +0 -5
  178. package/src/server/plugins/engine/views/components/fileuploadfield-key.html +0 -8
  179. package/src/server/plugins/engine/views/components/fileuploadfield-value.html +0 -3
  180. package/src/server/plugins/engine/views/components/fileuploadfield.html +0 -24
  181. package/src/server/plugins/engine/views/components/html.html +0 -3
  182. package/src/server/plugins/engine/views/components/insettext.html +0 -7
  183. package/src/server/plugins/engine/views/components/list.html +0 -36
  184. package/src/server/plugins/engine/views/components/monthyearfield.html +0 -5
  185. package/src/server/plugins/engine/views/components/multilinetextfield.html +0 -10
  186. package/src/server/plugins/engine/views/components/numberfield.html +0 -5
  187. package/src/server/plugins/engine/views/components/radiosfield.html +0 -5
  188. package/src/server/plugins/engine/views/components/selectfield.html +0 -5
  189. package/src/server/plugins/engine/views/components/telephonenumberfield.html +0 -5
  190. package/src/server/plugins/engine/views/components/textfield.html +0 -5
  191. package/src/server/plugins/engine/views/components/ukaddressfield.html +0 -25
  192. package/src/server/plugins/engine/views/components/yesnofield.html +0 -5
  193. package/src/server/plugins/engine/views/file-upload.html +0 -45
  194. package/src/server/plugins/engine/views/index.html +0 -39
  195. package/src/server/plugins/engine/views/item-delete.html +0 -56
  196. package/src/server/plugins/engine/views/partials/components.html +0 -6
  197. package/src/server/plugins/engine/views/partials/conditional-components.html +0 -3
  198. package/src/server/plugins/engine/views/partials/debug.html +0 -44
  199. package/src/server/plugins/engine/views/partials/form.html +0 -15
  200. package/src/server/plugins/engine/views/partials/heading.html +0 -16
  201. package/src/server/plugins/engine/views/partials/preview-banner.html +0 -32
  202. package/src/server/plugins/engine/views/partials/preview-banner.test.js +0 -122
  203. package/src/server/plugins/engine/views/partials/warn-missing-notification-email.html +0 -10
  204. package/src/server/plugins/engine/views/repeat-list-summary.html +0 -53
  205. package/src/server/plugins/errorPages.ts +0 -58
  206. package/src/server/plugins/nunjucks/context.js +0 -88
  207. package/src/server/plugins/nunjucks/context.test.js +0 -142
  208. package/src/server/plugins/nunjucks/enviroment.test.js +0 -201
  209. package/src/server/plugins/nunjucks/environment.js +0 -116
  210. package/src/server/plugins/nunjucks/filters/answer.js +0 -27
  211. package/src/server/plugins/nunjucks/filters/answer.test.js +0 -89
  212. package/src/server/plugins/nunjucks/filters/evaluate.js +0 -21
  213. package/src/server/plugins/nunjucks/filters/field.js +0 -28
  214. package/src/server/plugins/nunjucks/filters/field.test.js +0 -75
  215. package/src/server/plugins/nunjucks/filters/highlight.js +0 -11
  216. package/src/server/plugins/nunjucks/filters/href.js +0 -30
  217. package/src/server/plugins/nunjucks/filters/href.test.js +0 -80
  218. package/src/server/plugins/nunjucks/filters/index.js +0 -8
  219. package/src/server/plugins/nunjucks/filters/inspect.js +0 -15
  220. package/src/server/plugins/nunjucks/filters/page.js +0 -24
  221. package/src/server/plugins/nunjucks/filters/page.test.js +0 -65
  222. package/src/server/plugins/nunjucks/index.js +0 -3
  223. package/src/server/plugins/nunjucks/plugin.js +0 -40
  224. package/src/server/plugins/nunjucks/render.js +0 -42
  225. package/src/server/plugins/nunjucks/types.js +0 -40
  226. package/src/server/plugins/pulse.ts +0 -11
  227. package/src/server/plugins/router.ts +0 -201
  228. package/src/server/plugins/session.ts +0 -28
  229. package/src/server/routes/health.js +0 -13
  230. package/src/server/routes/health.test.js +0 -35
  231. package/src/server/routes/index.test.ts +0 -125
  232. package/src/server/routes/index.ts +0 -2
  233. package/src/server/routes/public.ts +0 -47
  234. package/src/server/routes/types.ts +0 -48
  235. package/src/server/schemas/index.ts +0 -34
  236. package/src/server/secure-context.js +0 -43
  237. package/src/server/services/cacheService.test.ts +0 -276
  238. package/src/server/services/cacheService.ts +0 -131
  239. package/src/server/services/httpService.test.js +0 -491
  240. package/src/server/services/httpService.ts +0 -50
  241. package/src/server/services/index.ts +0 -1
  242. package/src/server/types.ts +0 -54
  243. package/src/server/utils/notify.test.ts +0 -37
  244. package/src/server/utils/notify.ts +0 -50
  245. package/src/server/utils/secure-context/get-trust-store-certs.js +0 -11
  246. package/src/server/utils/secure-context/get-trust-store-certs.test.js +0 -19
  247. package/src/server/utils/utils.js +0 -24
  248. package/src/server/utils/utils.test.js +0 -54
  249. package/src/server/views/404.html +0 -16
  250. package/src/server/views/500.html +0 -19
  251. package/src/server/views/components/debug/macro.njk +0 -3
  252. package/src/server/views/components/debug/template.njk +0 -13
  253. package/src/server/views/components/service-banner/macro.njk +0 -3
  254. package/src/server/views/components/service-banner/template.njk +0 -20
  255. package/src/server/views/components/service-banner/template.test.js +0 -43
  256. package/src/server/views/components/tag-env/macro.njk +0 -3
  257. package/src/server/views/components/tag-env/template.njk +0 -30
  258. package/src/server/views/components/tag-env/template.test.js +0 -66
  259. package/src/server/views/confirmation.html +0 -19
  260. package/src/server/views/help/accessibility-statement.html +0 -58
  261. package/src/server/views/help/cookie-preferences.html +0 -57
  262. package/src/server/views/help/cookies.html +0 -71
  263. package/src/server/views/help/get-support.html +0 -37
  264. package/src/server/views/help/privacy-notice.html +0 -68
  265. package/src/server/views/help/terms-and-conditions.html +0 -83
  266. package/src/server/views/layout.html +0 -199
  267. package/src/server/views/summary.html +0 -50
  268. package/src/typings/hapi/index.d.ts +0 -95
  269. package/src/typings/hapi-tracing/index.d.ts +0 -6
  270. package/src/typings/index.d.ts +0 -3
  271. package/src/typings/joi/index.d.ts +0 -22
  272. package/stylelint.config.js +0 -10
  273. package/test/client/javascripts/file-upload.test.js +0 -1197
  274. package/test/condition/checkboxes.test.js +0 -112
  275. package/test/condition/radios.test.js +0 -112
  276. package/test/condition/text.test.js +0 -103
  277. package/test/fixtures/assets-manifest.json +0 -4
  278. package/test/fixtures/form.js +0 -86
  279. package/test/fixtures/index.js +0 -2
  280. package/test/fixtures/list.js +0 -92
  281. package/test/form/cookies.test.js +0 -338
  282. package/test/form/csrf.test.js +0 -87
  283. package/test/form/definitions/basic.js +0 -101
  284. package/test/form/definitions/blank.js +0 -10
  285. package/test/form/definitions/checkboxes.json +0 -88
  286. package/test/form/definitions/components.json +0 -452
  287. package/test/form/definitions/conditional-reveal.js +0 -140
  288. package/test/form/definitions/conditions-basic.js +0 -187
  289. package/test/form/definitions/conditions-complex.js +0 -338
  290. package/test/form/definitions/conditions-dates.js +0 -78
  291. package/test/form/definitions/conditions-escaping.js +0 -143
  292. package/test/form/definitions/demo-cph-number.js +0 -3099
  293. package/test/form/definitions/feedback.json +0 -45
  294. package/test/form/definitions/fields-optional.js +0 -402
  295. package/test/form/definitions/fields-required.js +0 -402
  296. package/test/form/definitions/file-upload-basic.js +0 -44
  297. package/test/form/definitions/file-upload.js +0 -66
  298. package/test/form/definitions/minimal.js +0 -39
  299. package/test/form/definitions/phase-alpha.json +0 -33
  300. package/test/form/definitions/phase-default.json +0 -26
  301. package/test/form/definitions/radios.json +0 -88
  302. package/test/form/definitions/repeat-mixed.js +0 -54
  303. package/test/form/definitions/repeat.js +0 -70
  304. package/test/form/definitions/status.json +0 -126
  305. package/test/form/definitions/templates.js +0 -183
  306. package/test/form/definitions/test.json +0 -581
  307. package/test/form/definitions/text.json +0 -75
  308. package/test/form/definitions/titles.json +0 -170
  309. package/test/form/definitions.test.js +0 -47
  310. package/test/form/exit-page.test.js +0 -210
  311. package/test/form/feedback.test.js +0 -68
  312. package/test/form/fields-optional.test.js +0 -237
  313. package/test/form/fields-required.test.js +0 -294
  314. package/test/form/file-upload.test.js +0 -313
  315. package/test/form/govuk-notify.test.js +0 -449
  316. package/test/form/journey-basic.test.js +0 -444
  317. package/test/form/persist-files.test.js +0 -227
  318. package/test/form/phase-banner.test.js +0 -71
  319. package/test/form/repeat.test.js +0 -628
  320. package/test/form/summary-submission-email.test.js +0 -95
  321. package/test/form/template.test.js +0 -288
  322. package/test/form/titles.test.js +0 -204
  323. package/test/helpers/component-helpers.js +0 -74
  324. package/test/utils/get-cookie.js +0 -42
  325. package/test/utils/get-form-definitions.js +0 -18
  326. package/tmp.pdf +0 -1
  327. package/tsconfig.json +0 -28
  328. package/webpack.config.js +0 -208
@@ -1,264 +0,0 @@
1
- import { FormModel } from '~/src/server/plugins/engine/models/FormModel.js'
2
- import { RepeatPageController } from '~/src/server/plugins/engine/pageControllers/RepeatPageController.js'
3
- import {
4
- type FormContextRequest,
5
- type FormPageViewModel,
6
- type FormSubmissionError,
7
- type RepeatListState,
8
- type RepeaterSummaryPageViewModel
9
- } from '~/src/server/plugins/engine/types.js'
10
- import definition from '~/test/form/definitions/repeat.js'
11
-
12
- describe('RepeatPageController', () => {
13
- const itemId1 = 'abc-123'
14
- const itemId2 = 'xyz-987'
15
-
16
- let pageUrl: URL
17
- let pageItemUrl: URL
18
- let pageSummaryUrl: URL
19
-
20
- let model: FormModel
21
- let controller: RepeatPageController
22
- let requestPage: FormContextRequest
23
- let requestPageItem: FormContextRequest
24
- let requestPageSummary: FormContextRequest
25
-
26
- beforeEach(() => {
27
- const { pages } = definition
28
-
29
- pageUrl = new URL('/repeat/pizza-order', 'http://example.com')
30
-
31
- pageItemUrl = new URL(
32
- `${pageUrl.pathname}/${itemId1}`,
33
- 'http://example.com'
34
- )
35
-
36
- pageSummaryUrl = new URL(
37
- `${pageUrl.pathname}/summary`,
38
- 'http://example.com'
39
- )
40
-
41
- model = new FormModel(definition, {
42
- basePath: 'test'
43
- })
44
-
45
- controller = new RepeatPageController(model, pages[0])
46
-
47
- requestPage = {
48
- method: 'get',
49
- url: pageUrl,
50
- path: pageUrl.pathname,
51
- params: {
52
- path: 'pizza-order',
53
- slug: 'repeat'
54
- },
55
- query: {},
56
- app: { model }
57
- }
58
-
59
- requestPageItem = {
60
- method: 'get',
61
- url: pageItemUrl,
62
- path: pageItemUrl.pathname,
63
- params: {
64
- path: 'pizza-order',
65
- slug: 'repeat',
66
- itemId: itemId1
67
- },
68
- query: {},
69
- app: { model }
70
- }
71
-
72
- requestPageSummary = {
73
- method: 'get',
74
- url: pageSummaryUrl,
75
- path: pageSummaryUrl.pathname,
76
- params: {
77
- path: 'pizza-order',
78
- slug: 'repeat'
79
- },
80
- query: {},
81
- app: { model }
82
- }
83
- })
84
-
85
- describe('Properties', () => {
86
- it('returns summary view name', () => {
87
- expect(controller).toHaveProperty(
88
- 'listSummaryViewName',
89
- 'repeat-list-summary'
90
- )
91
- })
92
-
93
- it('returns delete view name', () => {
94
- expect(controller).toHaveProperty('listDeleteViewName', 'item-delete')
95
- })
96
-
97
- it('returns repeater config', () => {
98
- expect(controller).toHaveProperty('repeat', {
99
- options: {
100
- name: 'pizza',
101
- title: 'Pizza'
102
- },
103
- schema: {
104
- max: 3,
105
- min: 2
106
- }
107
- })
108
- })
109
- })
110
-
111
- describe('Path methods', () => {
112
- describe('Summary path', () => {
113
- it('returns path to summary page', () => {
114
- expect(controller.getSummaryPath()).toBe('/summary')
115
- })
116
-
117
- it('returns path to repeater summary page', () => {
118
- expect(controller.getSummaryPath(requestPage)).toBe(
119
- '/pizza-order/summary'
120
- )
121
- })
122
- })
123
- })
124
-
125
- describe('Item view model', () => {
126
- let viewModel: FormPageViewModel
127
-
128
- beforeEach(() => {
129
- viewModel = controller.getViewModel(
130
- requestPageItem,
131
- model.getFormContext(requestPageItem, {})
132
- )
133
- })
134
-
135
- it('updates section title with repeater title and count', () => {
136
- expect(viewModel).toHaveProperty('sectionTitle', 'Food: Pizza 1')
137
- })
138
- })
139
-
140
- describe.each([
141
- {
142
- description: 'No items',
143
- list: [] satisfies RepeatListState,
144
- viewModel: {
145
- pageTitle: 'You have added 0 Pizzas',
146
- showTitle: true,
147
- sectionTitle: 'Food'
148
- }
149
- },
150
- {
151
- description: '1 item',
152
- list: [
153
- {
154
- itemId: itemId1,
155
- toppings: 'Ham',
156
- quantity: 2
157
- }
158
- ] satisfies RepeatListState,
159
- viewModel: {
160
- pageTitle: 'You have added 1 Pizza',
161
- showTitle: true,
162
- sectionTitle: 'Food'
163
- }
164
- },
165
- {
166
- description: '2 items',
167
- list: [
168
- {
169
- itemId: itemId1,
170
- toppings: 'Ham',
171
- quantity: 2
172
- },
173
- {
174
- itemId: itemId2,
175
- toppings: 'Cheese',
176
- quantity: 1
177
- }
178
- ] satisfies RepeatListState,
179
- viewModel: {
180
- pageTitle: 'You have added 2 Pizzas',
181
- showTitle: true,
182
- sectionTitle: 'Food'
183
- }
184
- }
185
- ])(
186
- 'List summary view model ($description)',
187
- ({ list, viewModel: expected }) => {
188
- let viewModel: RepeaterSummaryPageViewModel
189
-
190
- beforeEach(() => {
191
- viewModel = controller.getListSummaryViewModel(
192
- requestPageSummary,
193
- model.getFormContext(requestPageSummary, {}),
194
- list
195
- )
196
- })
197
-
198
- it('should customise page title', () => {
199
- expect(viewModel).toHaveProperty('pageTitle', expected.pageTitle)
200
- expect(viewModel).toHaveProperty('showTitle', expected.showTitle)
201
- })
202
-
203
- it('should extend default view model', () => {
204
- const defaults = controller.viewModel
205
-
206
- expect(viewModel).toHaveProperty('name', defaults.name)
207
- expect(viewModel).toHaveProperty('page', defaults.page)
208
- expect(viewModel).toHaveProperty('sectionTitle', expected.sectionTitle)
209
- expect(viewModel).toHaveProperty('isStartPage', defaults.isStartPage)
210
- expect(viewModel).toHaveProperty('serviceUrl', defaults.serviceUrl)
211
- expect(viewModel).toHaveProperty('feedbackLink', defaults.feedbackLink)
212
-
213
- // Unless overridden
214
- expect(viewModel).not.toHaveProperty('pageTitle', defaults.pageTitle)
215
- })
216
- }
217
- )
218
-
219
- describe('Form validation', () => {
220
- it('includes title text and errors', () => {
221
- const result = controller.collection.validate()
222
-
223
- expect(result.errors).toEqual<FormSubmissionError[]>([
224
- {
225
- path: ['toppings'],
226
- href: '#toppings',
227
- name: 'toppings',
228
- text: 'Select toppings',
229
- context: {
230
- key: 'toppings',
231
- label: 'Toppings',
232
- title: 'Toppings'
233
- }
234
- },
235
- {
236
- path: ['quantity'],
237
- href: '#quantity',
238
- name: 'quantity',
239
- text: 'Enter quantity',
240
- context: {
241
- key: 'quantity',
242
- label: 'Quantity',
243
- title: 'Quantity'
244
- }
245
- },
246
- {
247
- path: ['itemId'],
248
- href: '#itemId',
249
- name: 'itemId',
250
- text: 'Select itemId',
251
- context: {
252
- key: 'itemId',
253
- label: 'itemId'
254
- }
255
- }
256
- ])
257
- })
258
-
259
- it('includes all field errors', () => {
260
- const result = controller.collection.validate()
261
- expect(result.errors).toHaveLength(3)
262
- })
263
- })
264
- })
@@ -1,458 +0,0 @@
1
- import { randomUUID } from 'crypto'
2
-
3
- import { type PageRepeat, type Repeat } from '@defra/forms-model'
4
- import Boom from '@hapi/boom'
5
- import { type ResponseToolkit } from '@hapi/hapi'
6
- import Joi from 'joi'
7
-
8
- import { isRepeatState } from '~/src/server/plugins/engine/components/FormComponent.js'
9
- import { redirectPath } from '~/src/server/plugins/engine/helpers.js'
10
- import { type FormModel } from '~/src/server/plugins/engine/models/index.js'
11
- import { QuestionPageController } from '~/src/server/plugins/engine/pageControllers/QuestionPageController.js'
12
- import {
13
- type FormContext,
14
- type FormContextRequest,
15
- type FormPageViewModel,
16
- type FormPayload,
17
- type FormSubmissionState,
18
- type ItemDeletePageViewModel,
19
- type RepeatItemState,
20
- type RepeatListState,
21
- type RepeaterSummaryPageViewModel,
22
- type SummaryList,
23
- type SummaryListAction
24
- } from '~/src/server/plugins/engine/types.js'
25
- import {
26
- FormAction,
27
- type FormRequest,
28
- type FormRequestPayload
29
- } from '~/src/server/routes/types.js'
30
-
31
- export class RepeatPageController extends QuestionPageController {
32
- declare pageDef: PageRepeat
33
-
34
- listSummaryViewName = 'repeat-list-summary'
35
- listDeleteViewName = 'item-delete'
36
- repeat: Repeat
37
-
38
- constructor(model: FormModel, pageDef: PageRepeat) {
39
- super(model, pageDef)
40
-
41
- this.repeat = pageDef.repeat
42
-
43
- const { options, schema } = this.repeat
44
- const itemId = Joi.string().uuid().required()
45
-
46
- this.collection.formSchema = this.collection.formSchema.append({ itemId })
47
- this.collection.stateSchema = Joi.object<RepeatItemState>().keys({
48
- [options.name]: Joi.array()
49
- .items(this.collection.stateSchema.append({ itemId }))
50
- .min(schema.min)
51
- .max(schema.max)
52
- .label(`${options.title} list`)
53
- .required()
54
- })
55
- }
56
-
57
- get keys() {
58
- const { repeat } = this
59
- return [repeat.options.name, ...super.keys]
60
- }
61
-
62
- getFormParams(request?: FormContextRequest) {
63
- const params = super.getFormParams(request)
64
-
65
- // Apply an itemId to the form payload
66
- if (request?.payload) {
67
- params.itemId = request.params.itemId ?? randomUUID()
68
- }
69
-
70
- return params
71
- }
72
-
73
- getFormDataFromState(
74
- request: FormContextRequest | undefined,
75
- state: FormSubmissionState
76
- ) {
77
- const { repeat } = this
78
-
79
- const params = this.getFormParams(request)
80
- const list = this.getListFromState(state)
81
- const itemId = this.getItemId(request)
82
-
83
- // Create payload with repeater list state
84
- if (!itemId) {
85
- return {
86
- ...params,
87
- [repeat.options.name]: list
88
- }
89
- }
90
-
91
- // Create payload with repeater item state
92
- const item = this.getItemFromList(list, itemId)
93
-
94
- return {
95
- ...params,
96
- ...item
97
- }
98
- }
99
-
100
- getStateFromValidForm(
101
- request: FormContextRequest,
102
- state: FormSubmissionState,
103
- payload: FormPayload
104
- ) {
105
- const itemId = this.getItemId(request)
106
-
107
- if (!itemId) {
108
- throw Boom.badRequest('No item ID found')
109
- }
110
-
111
- const list = this.getListFromState(state)
112
- const item = this.getItemFromList(list, itemId)
113
-
114
- const itemState = super.getStateFromValidForm(request, state, payload)
115
- const updated: RepeatItemState = { ...itemState, itemId }
116
- const newList = [...list]
117
-
118
- if (!item) {
119
- // Adding a new item
120
- newList.push(updated)
121
- } else {
122
- // Update an existing item
123
- newList[list.indexOf(item)] = updated
124
- }
125
-
126
- return {
127
- [this.repeat.options.name]: newList
128
- }
129
- }
130
-
131
- proceed(
132
- request: FormContextRequest,
133
- h: Pick<ResponseToolkit, 'redirect' | 'view'>
134
- ) {
135
- const nextPath = this.getSummaryPath(request)
136
- return super.proceed(request, h, nextPath)
137
- }
138
-
139
- getItemFromList(list: RepeatListState, itemId?: string) {
140
- return list.find((item) => item.itemId === itemId)
141
- }
142
-
143
- getListFromState(state: FormSubmissionState) {
144
- const { name } = this.repeat.options
145
- const values = state[name]
146
-
147
- return isRepeatState(values) ? values : []
148
- }
149
-
150
- makeGetRouteHandler() {
151
- return async (
152
- request: FormRequest,
153
- context: FormContext,
154
- h: Pick<ResponseToolkit, 'redirect' | 'view'>
155
- ) => {
156
- const { path } = this
157
- const { query } = request
158
- const { state } = context
159
-
160
- const itemId = this.getItemId(request)
161
- const list = this.getListFromState(state)
162
-
163
- if (!itemId) {
164
- const summaryPath = this.getSummaryPath(request)
165
- const nextPath = redirectPath(`${path}/${randomUUID()}`, {
166
- returnUrl: query.returnUrl,
167
- force: query.force
168
- })
169
-
170
- // Only redirect to new item when list is empty
171
- return super.proceed(request, h, list.length ? summaryPath : nextPath)
172
- }
173
-
174
- return super.makeGetRouteHandler()(request, context, h)
175
- }
176
- }
177
-
178
- makeGetListSummaryRouteHandler() {
179
- return (
180
- request: FormRequest,
181
- context: FormContext,
182
- h: Pick<ResponseToolkit, 'redirect' | 'view'>
183
- ) => {
184
- const { path } = this
185
- const { query } = request
186
- const { state } = context
187
-
188
- const list = this.getListFromState(state)
189
-
190
- if (!list.length) {
191
- const nextPath = redirectPath(`${path}/${randomUUID()}`, {
192
- returnUrl: query.returnUrl
193
- })
194
-
195
- return super.proceed(request, h, nextPath)
196
- }
197
-
198
- const viewModel = this.getListSummaryViewModel(request, context, list)
199
-
200
- return h.view(this.listSummaryViewName, viewModel)
201
- }
202
- }
203
-
204
- makePostListSummaryRouteHandler() {
205
- return (
206
- request: FormRequestPayload,
207
- context: FormContext,
208
- h: Pick<ResponseToolkit, 'redirect' | 'view'>
209
- ) => {
210
- const { path, repeat } = this
211
- const { query } = request
212
- const { schema, options } = repeat
213
- const { state } = context
214
-
215
- const list = this.getListFromState(state)
216
-
217
- if (!list.length) {
218
- const nextPath = redirectPath(`${path}/${randomUUID()}`, {
219
- returnUrl: query.returnUrl
220
- })
221
-
222
- return super.proceed(request, h, nextPath)
223
- }
224
-
225
- const { action } = this.getFormParams(request)
226
-
227
- const hasErrorMin =
228
- action === FormAction.Continue && list.length < schema.min
229
-
230
- const hasErrorMax =
231
- (action === FormAction.AddAnother && list.length >= schema.max) ||
232
- (action === FormAction.Continue && list.length > schema.max)
233
-
234
- // Show error if repeat limits apply
235
- if (hasErrorMin || hasErrorMax) {
236
- const count = hasErrorMax ? schema.max : schema.min
237
- const itemTitle = `${options.title}${count === 1 ? '' : 's'}`
238
-
239
- context.errors = [
240
- {
241
- path: [],
242
- href: '',
243
- name: '',
244
- text: hasErrorMax
245
- ? `You can only add up to ${count} ${itemTitle}`
246
- : `You must add at least ${count} ${itemTitle}`
247
- }
248
- ]
249
-
250
- const viewModel = this.getListSummaryViewModel(request, context, list)
251
-
252
- return h.view(this.listSummaryViewName, viewModel)
253
- }
254
-
255
- if (action === FormAction.AddAnother) {
256
- const nextPath = redirectPath(`${path}/${randomUUID()}`, {
257
- returnUrl: query.returnUrl
258
- })
259
-
260
- return super.proceed(request, h, nextPath)
261
- }
262
-
263
- const nextPath = this.getNextPath(context)
264
- return super.proceed(request, h, nextPath)
265
- }
266
- }
267
-
268
- makeGetItemDeleteRouteHandler() {
269
- return (
270
- request: FormRequest,
271
- context: FormContext,
272
- h: Pick<ResponseToolkit, 'redirect' | 'view'>
273
- ) => {
274
- const { viewModel } = this
275
- const { state } = context
276
-
277
- const list = this.getListFromState(state)
278
- const itemId = this.getItemId(request)
279
- const item = this.getItemFromList(list, itemId)
280
-
281
- if (!item || list.length === 1) {
282
- throw Boom.notFound(
283
- item
284
- ? 'Last list item cannot be removed'
285
- : 'List item to remove not found'
286
- )
287
- }
288
-
289
- const { title } = this.repeat.options
290
-
291
- return h.view(this.listDeleteViewName, {
292
- ...viewModel,
293
- context,
294
- backLink: this.getBackLink(request, context),
295
- pageTitle: `Are you sure you want to remove this ${title}?`,
296
- itemTitle: `${title} ${list.indexOf(item) + 1}`,
297
- buttonConfirm: { text: `Remove ${title}` },
298
- buttonCancel: { text: 'Cancel' }
299
- } satisfies ItemDeletePageViewModel)
300
- }
301
- }
302
-
303
- makePostItemDeleteRouteHandler() {
304
- return async (
305
- request: FormRequestPayload,
306
- context: FormContext,
307
- h: Pick<ResponseToolkit, 'redirect' | 'view'>
308
- ) => {
309
- const { repeat } = this
310
- const { state } = context
311
-
312
- const { confirm } = this.getFormParams(request)
313
-
314
- const list = this.getListFromState(state)
315
- const itemId = this.getItemId(request)
316
- const item = this.getItemFromList(list, itemId)
317
-
318
- if (!item || list.length === 1) {
319
- throw Boom.notFound(
320
- item
321
- ? 'Last list item cannot be removed'
322
- : 'List item to remove not found'
323
- )
324
- }
325
-
326
- // Remove the item from the list
327
- if (confirm) {
328
- list.splice(list.indexOf(item), 1)
329
-
330
- const update = {
331
- [repeat.options.name]: list
332
- }
333
-
334
- await this.mergeState(request, state, update)
335
- }
336
-
337
- return this.proceed(request, h)
338
- }
339
- }
340
-
341
- getViewModel(
342
- request: FormContextRequest,
343
- context: FormContext
344
- ): FormPageViewModel {
345
- const { state } = context
346
-
347
- const list = this.getListFromState(state)
348
- const itemId = this.getItemId(request)
349
- const item = this.getItemFromList(list, itemId)
350
-
351
- const viewModel = super.getViewModel(request, context)
352
- const itemNumber = item ? list.indexOf(item) + 1 : list.length + 1
353
- const repeatCaption = `${this.repeat.options.title} ${itemNumber}`
354
-
355
- return {
356
- ...viewModel,
357
-
358
- sectionTitle: viewModel.sectionTitle
359
- ? `${viewModel.sectionTitle}: ${repeatCaption}`
360
- : repeatCaption
361
- }
362
- }
363
-
364
- getListSummaryViewModel(
365
- request: FormContextRequest,
366
- context: FormContext,
367
- list: RepeatListState
368
- ): RepeaterSummaryPageViewModel {
369
- const { collection, href, repeat } = this
370
- const { query } = request
371
- const { isForceAccess, errors } = context
372
-
373
- const { title } = repeat.options
374
-
375
- const summaryList: SummaryList = {
376
- classes: 'govuk-summary-list--long-actions',
377
- rows: []
378
- }
379
-
380
- let count = 0
381
-
382
- if (Array.isArray(list)) {
383
- count = list.length
384
-
385
- const summaryPath = this.getSummaryPath(request)
386
-
387
- list.forEach((item, index) => {
388
- const items: SummaryListAction[] = []
389
-
390
- // Remove summary list actions from previews
391
- if (!isForceAccess) {
392
- items.push({
393
- href: redirectPath(`${href}/${item.itemId}`, {
394
- returnUrl: query.returnUrl ?? this.getHref(summaryPath)
395
- }),
396
- text: 'Change',
397
- classes: 'govuk-link--no-visited-state',
398
- visuallyHiddenText: `item ${index + 1}`
399
- })
400
-
401
- if (count > 1) {
402
- items.push({
403
- href: redirectPath(`${href}/${item.itemId}/confirm-delete`, {
404
- returnUrl: query.returnUrl
405
- }),
406
- text: 'Remove',
407
- classes: 'govuk-link--no-visited-state',
408
- visuallyHiddenText: `item ${index + 1}`
409
- })
410
- }
411
- }
412
-
413
- const itemDisplayText = collection.fields.length
414
- ? collection.fields[0].getDisplayStringFromState(item)
415
- : ''
416
-
417
- summaryList.rows.push({
418
- key: {
419
- text: `${title} ${index + 1}`
420
- },
421
- value: {
422
- text: itemDisplayText || 'Not supplied'
423
- },
424
- actions: {
425
- items
426
- }
427
- })
428
- })
429
- }
430
-
431
- return {
432
- ...this.viewModel,
433
- backLink: this.getBackLink(request, context),
434
- repeatTitle: title,
435
- pageTitle: `You have added ${count} ${title}${count === 1 ? '' : 's'}`,
436
- showTitle: true,
437
- context,
438
- errors,
439
- checkAnswers: [{ summaryList }]
440
- }
441
- }
442
-
443
- getSummaryPath(request?: FormContextRequest) {
444
- const { path } = this
445
-
446
- const summaryPath = super.getSummaryPath()
447
-
448
- if (!request) {
449
- return summaryPath
450
- }
451
-
452
- const { query } = request
453
-
454
- return redirectPath(`${path}${summaryPath}`, {
455
- returnUrl: query.returnUrl
456
- })
457
- }
458
- }