@defra/forms-engine-plugin 4.7.2-alpha → 4.7.3

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 (402) hide show
  1. package/.server/client/javascripts/geospatial-map.d.ts +6 -6
  2. package/.server/client/javascripts/geospatial-map.js +3 -3
  3. package/.server/client/javascripts/location-map.d.ts +1 -1
  4. package/.server/client/javascripts/location-map.js +1 -1
  5. package/.server/client/javascripts/map.d.ts +1 -1
  6. package/.server/client/javascripts/map.js +1 -1
  7. package/.server/client/javascripts/shared.d.ts +7 -7
  8. package/.server/server/index.d.ts +1 -1
  9. package/.server/server/plugins/crumb.d.ts +1 -1
  10. package/.server/server/plugins/engine/beta/form-context.d.ts +6 -6
  11. package/.server/server/plugins/engine/components/AutocompleteField.d.ts +4 -4
  12. package/.server/server/plugins/engine/components/CheckboxesField.d.ts +2 -2
  13. package/.server/server/plugins/engine/components/CheckboxesField.js +2 -2
  14. package/.server/server/plugins/engine/components/CheckboxesField.js.map +1 -1
  15. package/.server/server/plugins/engine/components/ComponentBase.d.ts +5 -5
  16. package/.server/server/plugins/engine/components/ComponentCollection.d.ts +8 -8
  17. package/.server/server/plugins/engine/components/DatePartsField.d.ts +11 -11
  18. package/.server/server/plugins/engine/components/DeclarationField.d.ts +3 -3
  19. package/.server/server/plugins/engine/components/Details.d.ts +1 -1
  20. package/.server/server/plugins/engine/components/EastingNorthingField.d.ts +22 -22
  21. package/.server/server/plugins/engine/components/EmailAddressField.d.ts +4 -4
  22. package/.server/server/plugins/engine/components/FileUploadField.d.ts +3 -3
  23. package/.server/server/plugins/engine/components/FormComponent.d.ts +10 -10
  24. package/.server/server/plugins/engine/components/GeospatialField.d.ts +6 -6
  25. package/.server/server/plugins/engine/components/HiddenField.d.ts +2 -2
  26. package/.server/server/plugins/engine/components/Html.d.ts +1 -1
  27. package/.server/server/plugins/engine/components/InsetText.d.ts +1 -1
  28. package/.server/server/plugins/engine/components/LatLongField.d.ts +22 -22
  29. package/.server/server/plugins/engine/components/List.d.ts +8 -8
  30. package/.server/server/plugins/engine/components/ListFormComponent.d.ts +10 -10
  31. package/.server/server/plugins/engine/components/LocationFieldBase.d.ts +6 -6
  32. package/.server/server/plugins/engine/components/LocationFieldHelpers.d.ts +14 -14
  33. package/.server/server/plugins/engine/components/Markdown.d.ts +1 -1
  34. package/.server/server/plugins/engine/components/MonthYearField.d.ts +11 -11
  35. package/.server/server/plugins/engine/components/MultilineTextField.d.ts +5 -5
  36. package/.server/server/plugins/engine/components/NationalGridFieldNumberField.d.ts +1 -1
  37. package/.server/server/plugins/engine/components/NumberField.d.ts +3 -3
  38. package/.server/server/plugins/engine/components/OsGridRefField.d.ts +1 -1
  39. package/.server/server/plugins/engine/components/PaymentField.d.ts +12 -12
  40. package/.server/server/plugins/engine/components/RadiosField.d.ts +1 -1
  41. package/.server/server/plugins/engine/components/SelectField.d.ts +4 -4
  42. package/.server/server/plugins/engine/components/SelectionControlField.d.ts +10 -10
  43. package/.server/server/plugins/engine/components/TelephoneNumberField.d.ts +4 -4
  44. package/.server/server/plugins/engine/components/TextField.d.ts +2 -2
  45. package/.server/server/plugins/engine/components/UkAddressField.d.ts +11 -11
  46. package/.server/server/plugins/engine/components/YesNoField.d.ts +2 -2
  47. package/.server/server/plugins/engine/components/helpers/__stubs__/geospatial.d.ts +1 -1
  48. package/.server/server/plugins/engine/components/helpers/components.d.ts +5 -5
  49. package/.server/server/plugins/engine/components/helpers/geospatial.d.ts +1 -1
  50. package/.server/server/plugins/engine/components/index.d.ts +27 -27
  51. package/.server/server/plugins/engine/components/types.d.ts +1 -1
  52. package/.server/server/plugins/engine/configureEnginePlugin.d.ts +4 -4
  53. package/.server/server/plugins/engine/date-helper.d.ts +1 -1
  54. package/.server/server/plugins/engine/helpers.d.ts +8 -8
  55. package/.server/server/plugins/engine/index.d.ts +5 -5
  56. package/.server/server/plugins/engine/models/FormModel.d.ts +6 -6
  57. package/.server/server/plugins/engine/models/SummaryViewModel.d.ts +6 -6
  58. package/.server/server/plugins/engine/models/index.d.ts +2 -2
  59. package/.server/server/plugins/engine/models/types.d.ts +4 -4
  60. package/.server/server/plugins/engine/options.d.ts +1 -1
  61. package/.server/server/plugins/engine/options.js +1 -1
  62. package/.server/server/plugins/engine/options.test.js +1 -1
  63. package/.server/server/plugins/engine/outputFormatters/adapter/v1.d.ts +4 -4
  64. package/.server/server/plugins/engine/outputFormatters/human/v1.d.ts +4 -4
  65. package/.server/server/plugins/engine/outputFormatters/index.d.ts +5 -5
  66. package/.server/server/plugins/engine/outputFormatters/machine/v1.d.ts +4 -4
  67. package/.server/server/plugins/engine/outputFormatters/machine/v2.d.ts +4 -4
  68. package/.server/server/plugins/engine/pageControllers/FileUploadPageController.d.ts +8 -8
  69. package/.server/server/plugins/engine/pageControllers/PageController.d.ts +6 -6
  70. package/.server/server/plugins/engine/pageControllers/QuestionPageController.d.ts +8 -8
  71. package/.server/server/plugins/engine/pageControllers/RepeatPageController.d.ts +5 -5
  72. package/.server/server/plugins/engine/pageControllers/StartPageController.d.ts +4 -4
  73. package/.server/server/plugins/engine/pageControllers/StatusPageController.d.ts +4 -4
  74. package/.server/server/plugins/engine/pageControllers/SummaryPageController.d.ts +5 -5
  75. package/.server/server/plugins/engine/pageControllers/TerminalPageController.d.ts +3 -3
  76. package/.server/server/plugins/engine/pageControllers/__stubs__/request.d.ts +2 -2
  77. package/.server/server/plugins/engine/pageControllers/errors.d.ts +1 -1
  78. package/.server/server/plugins/engine/pageControllers/helpers/pages.d.ts +2 -2
  79. package/.server/server/plugins/engine/pageControllers/helpers/state.d.ts +5 -5
  80. package/.server/server/plugins/engine/pageControllers/helpers/state.js +1 -1
  81. package/.server/server/plugins/engine/pageControllers/helpers/state.js.map +1 -1
  82. package/.server/server/plugins/engine/pageControllers/helpers/submission.d.ts +1 -1
  83. package/.server/server/plugins/engine/pageControllers/index.d.ts +7 -7
  84. package/.server/server/plugins/engine/plugin.d.ts +1 -1
  85. package/.server/server/plugins/engine/routes/file-upload.d.ts +1 -1
  86. package/.server/server/plugins/engine/routes/index.d.ts +3 -3
  87. package/.server/server/plugins/engine/routes/payment-helper.d.ts +3 -3
  88. package/.server/server/plugins/engine/routes/payment-helper.js +2 -2
  89. package/.server/server/plugins/engine/routes/payment.js +5 -5
  90. package/.server/server/plugins/engine/routes/payment.test.js +1 -1
  91. package/.server/server/plugins/engine/routes/questions.d.ts +2 -2
  92. package/.server/server/plugins/engine/routes/repeaters/item-delete.d.ts +2 -2
  93. package/.server/server/plugins/engine/routes/repeaters/summary.d.ts +2 -2
  94. package/.server/server/plugins/engine/services/index.d.ts +3 -3
  95. package/.server/server/plugins/engine/services/notifyService.d.ts +4 -4
  96. package/.server/server/plugins/engine/services/uploadService.d.ts +2 -2
  97. package/.server/server/plugins/engine/services/uploadService.js +1 -1
  98. package/.server/server/plugins/engine/types/index.d.ts +10 -10
  99. package/.server/server/plugins/engine/types/schema.d.ts +1 -1
  100. package/.server/server/plugins/engine/types.d.ts +25 -16
  101. package/.server/server/plugins/engine/types.js.map +1 -1
  102. package/.server/server/plugins/engine/validationHelpers.d.ts +2 -2
  103. package/.server/server/plugins/engine/vision.d.ts +1 -1
  104. package/.server/server/plugins/map/index.d.ts +1 -1
  105. package/.server/server/plugins/map/index.js +1 -1
  106. package/.server/server/plugins/map/routes/get-os-token.d.ts +11 -1
  107. package/.server/server/plugins/map/routes/get-os-token.js +12 -2
  108. package/.server/server/plugins/map/routes/get-os-token.js.map +1 -1
  109. package/.server/server/plugins/map/routes/index.d.ts +4 -4
  110. package/.server/server/plugins/map/routes/index.js +3 -2
  111. package/.server/server/plugins/map/routes/index.js.map +1 -1
  112. package/.server/server/plugins/map/service.d.ts +1 -1
  113. package/.server/server/plugins/map/service.js +5 -2
  114. package/.server/server/plugins/map/service.js.map +1 -1
  115. package/.server/server/plugins/map/test/__stubs__/find.d.ts +1 -1
  116. package/.server/server/plugins/map/test/__stubs__/find.js +1 -1
  117. package/.server/server/plugins/nunjucks/context.d.ts +2 -2
  118. package/.server/server/plugins/nunjucks/context.js +2 -2
  119. package/.server/server/plugins/nunjucks/context.test.js +1 -1
  120. package/.server/server/plugins/nunjucks/enviroment.test.js +1 -1
  121. package/.server/server/plugins/nunjucks/environment.d.ts +3 -3
  122. package/.server/server/plugins/nunjucks/environment.js +3 -3
  123. package/.server/server/plugins/nunjucks/filters/answer.d.ts +1 -1
  124. package/.server/server/plugins/nunjucks/filters/answer.js +2 -2
  125. package/.server/server/plugins/nunjucks/filters/answer.test.js +1 -1
  126. package/.server/server/plugins/nunjucks/filters/evaluate.d.ts +1 -1
  127. package/.server/server/plugins/nunjucks/filters/evaluate.js +1 -1
  128. package/.server/server/plugins/nunjucks/filters/field.d.ts +1 -1
  129. package/.server/server/plugins/nunjucks/filters/field.js +1 -1
  130. package/.server/server/plugins/nunjucks/filters/field.test.js +1 -1
  131. package/.server/server/plugins/nunjucks/filters/href.d.ts +1 -1
  132. package/.server/server/plugins/nunjucks/filters/href.js +1 -1
  133. package/.server/server/plugins/nunjucks/filters/href.test.js +1 -1
  134. package/.server/server/plugins/nunjucks/filters/index.d.ts +8 -8
  135. package/.server/server/plugins/nunjucks/filters/page.d.ts +1 -1
  136. package/.server/server/plugins/nunjucks/filters/page.js +1 -1
  137. package/.server/server/plugins/nunjucks/filters/page.test.js +1 -1
  138. package/.server/server/plugins/nunjucks/index.d.ts +3 -3
  139. package/.server/server/plugins/nunjucks/render.d.ts +2 -2
  140. package/.server/server/plugins/nunjucks/render.js +1 -1
  141. package/.server/server/plugins/nunjucks/types.d.ts +1 -1
  142. package/.server/server/plugins/nunjucks/types.js +1 -1
  143. package/.server/server/plugins/payment/helper.d.ts +2 -2
  144. package/.server/server/plugins/payment/helper.js +1 -1
  145. package/.server/server/plugins/payment/service.d.ts +3 -3
  146. package/.server/server/plugins/payment/service.js +1 -1
  147. package/.server/server/plugins/postcode-lookup/index.d.ts +1 -1
  148. package/.server/server/plugins/postcode-lookup/index.js +1 -1
  149. package/.server/server/plugins/postcode-lookup/models/index.d.ts +6 -6
  150. package/.server/server/plugins/postcode-lookup/models/index.js +1 -1
  151. package/.server/server/plugins/postcode-lookup/routes/index.d.ts +6 -6
  152. package/.server/server/plugins/postcode-lookup/routes/index.js +15 -15
  153. package/.server/server/plugins/postcode-lookup/routes/index.js.map +1 -1
  154. package/.server/server/plugins/postcode-lookup/service.d.ts +1 -1
  155. package/.server/server/plugins/postcode-lookup/service.js +5 -2
  156. package/.server/server/plugins/postcode-lookup/service.js.map +1 -1
  157. package/.server/server/plugins/postcode-lookup/types.js +1 -1
  158. package/.server/server/routes/index.d.ts +2 -2
  159. package/.server/server/routes/types.d.ts +1 -1
  160. package/.server/server/schemas/index.d.ts +2 -2
  161. package/.server/server/services/cacheService.d.ts +8 -8
  162. package/.server/server/services/cacheService.js +2 -5
  163. package/.server/server/services/cacheService.js.map +1 -1
  164. package/.server/server/services/httpService.test.js +1 -1
  165. package/.server/server/services/index.d.ts +1 -1
  166. package/.server/server/types.d.ts +7 -7
  167. package/.server/server/utils/file-form-service.d.ts +2 -2
  168. package/.server/server/utils/file-form-service.js +1 -1
  169. package/package.json +8 -4
  170. package/src/client/javascripts/application.js +1 -1
  171. package/src/client/javascripts/geospatial-map.js +4 -4
  172. package/src/client/javascripts/location-map.js +2 -2
  173. package/src/client/javascripts/map.js +3 -3
  174. package/src/client/javascripts/shared.js +7 -7
  175. package/src/index.ts +3 -3
  176. package/src/server/common/helpers/logging/logger-options.ts +1 -1
  177. package/src/server/common/helpers/logging/logger.ts +1 -1
  178. package/src/server/common/helpers/logging/request-logger.ts +1 -1
  179. package/src/server/common/helpers/logging/request-tracing.js +1 -1
  180. package/src/server/common/helpers/redis-client.js +2 -2
  181. package/src/server/index.ts +13 -13
  182. package/src/server/plugins/crumb.ts +2 -2
  183. package/src/server/plugins/engine/beta/form-context.ts +9 -9
  184. package/src/server/plugins/engine/components/AutocompleteField.ts +3 -3
  185. package/src/server/plugins/engine/components/CheckboxesField.ts +7 -8
  186. package/src/server/plugins/engine/components/ComponentBase.ts +5 -5
  187. package/src/server/plugins/engine/components/ComponentCollection.ts +9 -9
  188. package/src/server/plugins/engine/components/DatePartsField.ts +8 -8
  189. package/src/server/plugins/engine/components/DeclarationField.ts +3 -3
  190. package/src/server/plugins/engine/components/Details.ts +1 -1
  191. package/src/server/plugins/engine/components/EastingNorthingField.ts +9 -9
  192. package/src/server/plugins/engine/components/EmailAddressField.ts +3 -3
  193. package/src/server/plugins/engine/components/FileUploadField.ts +6 -6
  194. package/src/server/plugins/engine/components/FormComponent.ts +4 -4
  195. package/src/server/plugins/engine/components/GeospatialField.ts +5 -5
  196. package/src/server/plugins/engine/components/HiddenField.ts +4 -4
  197. package/src/server/plugins/engine/components/Html.ts +1 -1
  198. package/src/server/plugins/engine/components/InsetText.ts +1 -1
  199. package/src/server/plugins/engine/components/LatLongField.ts +9 -9
  200. package/src/server/plugins/engine/components/List.ts +2 -2
  201. package/src/server/plugins/engine/components/ListFormComponent.ts +4 -4
  202. package/src/server/plugins/engine/components/LocationFieldBase.ts +5 -5
  203. package/src/server/plugins/engine/components/LocationFieldHelpers.ts +5 -5
  204. package/src/server/plugins/engine/components/Markdown.ts +1 -1
  205. package/src/server/plugins/engine/components/MonthYearField.ts +8 -8
  206. package/src/server/plugins/engine/components/MultilineTextField.ts +4 -4
  207. package/src/server/plugins/engine/components/NationalGridFieldNumberField.ts +2 -2
  208. package/src/server/plugins/engine/components/NumberField.ts +3 -3
  209. package/src/server/plugins/engine/components/OsGridRefField.ts +2 -2
  210. package/src/server/plugins/engine/components/PaymentField.ts +8 -8
  211. package/src/server/plugins/engine/components/RadiosField.ts +1 -1
  212. package/src/server/plugins/engine/components/SelectField.ts +2 -2
  213. package/src/server/plugins/engine/components/SelectionControlField.ts +4 -4
  214. package/src/server/plugins/engine/components/TelephoneNumberField.ts +4 -4
  215. package/src/server/plugins/engine/components/TextField.ts +3 -3
  216. package/src/server/plugins/engine/components/UkAddressField.ts +7 -7
  217. package/src/server/plugins/engine/components/YesNoField.ts +5 -5
  218. package/src/server/plugins/engine/components/helpers/__stubs__/geospatial.ts +1 -1
  219. package/src/server/plugins/engine/components/helpers/components.ts +8 -8
  220. package/src/server/plugins/engine/components/helpers/geospatial.ts +1 -1
  221. package/src/server/plugins/engine/components/index.ts +27 -27
  222. package/src/server/plugins/engine/components/types.ts +1 -1
  223. package/src/server/plugins/engine/configureEnginePlugin.ts +10 -10
  224. package/src/server/plugins/engine/date-helper.ts +1 -1
  225. package/src/server/plugins/engine/helpers.ts +8 -8
  226. package/src/server/plugins/engine/index.ts +8 -8
  227. package/src/server/plugins/engine/models/FormModel.ts +15 -15
  228. package/src/server/plugins/engine/models/SummaryViewModel.ts +10 -10
  229. package/src/server/plugins/engine/models/index.ts +2 -2
  230. package/src/server/plugins/engine/models/types.ts +4 -4
  231. package/src/server/plugins/engine/options.js +3 -3
  232. package/src/server/plugins/engine/outputFormatters/adapter/v1.ts +6 -6
  233. package/src/server/plugins/engine/outputFormatters/human/v1.ts +9 -9
  234. package/src/server/plugins/engine/outputFormatters/index.ts +8 -8
  235. package/src/server/plugins/engine/outputFormatters/machine/v1.ts +7 -7
  236. package/src/server/plugins/engine/outputFormatters/machine/v2.ts +6 -6
  237. package/src/server/plugins/engine/pageControllers/FileUploadPageController.ts +9 -9
  238. package/src/server/plugins/engine/pageControllers/PageController.ts +7 -7
  239. package/src/server/plugins/engine/pageControllers/QuestionPageController.ts +13 -13
  240. package/src/server/plugins/engine/pageControllers/RepeatPageController.ts +6 -6
  241. package/src/server/plugins/engine/pageControllers/StartPageController.ts +3 -3
  242. package/src/server/plugins/engine/pageControllers/StatusPageController.ts +5 -5
  243. package/src/server/plugins/engine/pageControllers/SummaryPageController.ts +12 -12
  244. package/src/server/plugins/engine/pageControllers/TerminalPageController.ts +3 -3
  245. package/src/server/plugins/engine/pageControllers/__stubs__/request.ts +3 -3
  246. package/src/server/plugins/engine/pageControllers/__stubs__/server.ts +2 -2
  247. package/src/server/plugins/engine/pageControllers/errors.ts +1 -1
  248. package/src/server/plugins/engine/pageControllers/helpers/pages.ts +2 -2
  249. package/src/server/plugins/engine/pageControllers/helpers/state.ts +9 -9
  250. package/src/server/plugins/engine/pageControllers/helpers/submission.ts +5 -5
  251. package/src/server/plugins/engine/pageControllers/index.ts +7 -7
  252. package/src/server/plugins/engine/pageControllers/validationOptions.ts +1 -1
  253. package/src/server/plugins/engine/plugin.ts +14 -14
  254. package/src/server/plugins/engine/routes/file-upload.ts +2 -2
  255. package/src/server/plugins/engine/routes/index.ts +10 -10
  256. package/src/server/plugins/engine/routes/payment-helper.js +4 -4
  257. package/src/server/plugins/engine/routes/payment.js +9 -9
  258. package/src/server/plugins/engine/routes/questions.ts +10 -10
  259. package/src/server/plugins/engine/routes/repeaters/item-delete.ts +6 -6
  260. package/src/server/plugins/engine/routes/repeaters/summary.ts +5 -5
  261. package/src/server/plugins/engine/services/formSubmissionService.js +2 -2
  262. package/src/server/plugins/engine/services/index.js +3 -3
  263. package/src/server/plugins/engine/services/localFormsService.js +2 -2
  264. package/src/server/plugins/engine/services/notifyService.ts +9 -9
  265. package/src/server/plugins/engine/services/uploadService.js +3 -3
  266. package/src/server/plugins/engine/types/index.ts +10 -10
  267. package/src/server/plugins/engine/types/schema.ts +2 -2
  268. package/src/server/plugins/engine/types.ts +22 -17
  269. package/src/server/plugins/engine/validationHelpers.ts +3 -3
  270. package/src/server/plugins/engine/vision.ts +3 -3
  271. package/src/server/plugins/map/index.js +2 -2
  272. package/src/server/plugins/map/routes/get-os-token.js +15 -3
  273. package/src/server/plugins/map/routes/index.js +7 -5
  274. package/src/server/plugins/map/service.js +7 -4
  275. package/src/server/plugins/map/test/__stubs__/find.js +1 -1
  276. package/src/server/plugins/nunjucks/context.js +5 -5
  277. package/src/server/plugins/nunjucks/environment.js +6 -6
  278. package/src/server/plugins/nunjucks/filters/answer.js +3 -3
  279. package/src/server/plugins/nunjucks/filters/evaluate.js +2 -2
  280. package/src/server/plugins/nunjucks/filters/field.js +1 -1
  281. package/src/server/plugins/nunjucks/filters/href.js +2 -2
  282. package/src/server/plugins/nunjucks/filters/index.js +8 -8
  283. package/src/server/plugins/nunjucks/filters/page.js +1 -1
  284. package/src/server/plugins/nunjucks/index.js +3 -3
  285. package/src/server/plugins/nunjucks/plugin.js +3 -3
  286. package/src/server/plugins/nunjucks/render.js +2 -2
  287. package/src/server/plugins/nunjucks/types.js +1 -1
  288. package/src/server/plugins/payment/helper.js +2 -2
  289. package/src/server/plugins/payment/service.js +4 -4
  290. package/src/server/plugins/postcode-lookup/index.js +2 -2
  291. package/src/server/plugins/postcode-lookup/models/index.js +3 -3
  292. package/src/server/plugins/postcode-lookup/routes/index.js +24 -14
  293. package/src/server/plugins/postcode-lookup/service.js +7 -4
  294. package/src/server/plugins/postcode-lookup/types.js +1 -1
  295. package/src/server/plugins/session.ts +1 -1
  296. package/src/server/routes/index.ts +2 -2
  297. package/src/server/routes/public.ts +1 -1
  298. package/src/server/routes/types.ts +1 -1
  299. package/src/server/schemas/index.ts +2 -2
  300. package/src/server/secure-context.js +1 -1
  301. package/src/server/services/cacheService.ts +13 -18
  302. package/src/server/services/httpService.ts +1 -1
  303. package/src/server/services/index.ts +1 -1
  304. package/src/server/types.ts +7 -7
  305. package/src/server/utils/notify.ts +2 -2
  306. package/src/server/utils/utils.js +1 -1
  307. package/src/typings/hapi/index.d.ts +4 -4
  308. package/src/typings/joi/index.d.ts +1 -1
  309. package/src/server/common/helpers/logging/logger-options.test.ts +0 -50
  310. package/src/server/index.test.ts +0 -644
  311. package/src/server/plugins/engine/beta/form-context.test.ts +0 -373
  312. package/src/server/plugins/engine/components/AutocompleteField.test.ts +0 -362
  313. package/src/server/plugins/engine/components/CheckboxesField.test.ts +0 -486
  314. package/src/server/plugins/engine/components/DatePartsField.test.ts +0 -927
  315. package/src/server/plugins/engine/components/DeclarationField.test.ts +0 -560
  316. package/src/server/plugins/engine/components/Details.test.ts +0 -49
  317. package/src/server/plugins/engine/components/EastingNorthingField.test.ts +0 -727
  318. package/src/server/plugins/engine/components/EmailAddressField.test.ts +0 -445
  319. package/src/server/plugins/engine/components/FileUploadField.test.ts +0 -1079
  320. package/src/server/plugins/engine/components/GeospatialField.test.ts +0 -380
  321. package/src/server/plugins/engine/components/HiddenField.test.ts +0 -188
  322. package/src/server/plugins/engine/components/Html.test.ts +0 -48
  323. package/src/server/plugins/engine/components/InsetText.test.ts +0 -48
  324. package/src/server/plugins/engine/components/LatLongField.test.ts +0 -898
  325. package/src/server/plugins/engine/components/List.test.ts +0 -79
  326. package/src/server/plugins/engine/components/LocationFieldBase.test.ts +0 -253
  327. package/src/server/plugins/engine/components/LocationFieldHelpers.test.ts +0 -743
  328. package/src/server/plugins/engine/components/Markdown.test.ts +0 -48
  329. package/src/server/plugins/engine/components/MonthYearField.test.ts +0 -617
  330. package/src/server/plugins/engine/components/MultilineTextField.test.ts +0 -647
  331. package/src/server/plugins/engine/components/NationalGridFieldNumberField.test.ts +0 -449
  332. package/src/server/plugins/engine/components/NumberField.test.ts +0 -723
  333. package/src/server/plugins/engine/components/OsGridRefField.test.ts +0 -460
  334. package/src/server/plugins/engine/components/PaymentField.test.ts +0 -745
  335. package/src/server/plugins/engine/components/RadiosField.test.ts +0 -297
  336. package/src/server/plugins/engine/components/SelectField.test.ts +0 -289
  337. package/src/server/plugins/engine/components/TelephoneNumberField.test.ts +0 -384
  338. package/src/server/plugins/engine/components/TextField.test.ts +0 -521
  339. package/src/server/plugins/engine/components/UkAddressField.test.ts +0 -806
  340. package/src/server/plugins/engine/components/YesNoField.test.ts +0 -256
  341. package/src/server/plugins/engine/components/helpers/components.test.ts +0 -399
  342. package/src/server/plugins/engine/components/helpers/geospatial.test.js +0 -55
  343. package/src/server/plugins/engine/components/helpers/helpers.test.ts +0 -219
  344. package/src/server/plugins/engine/date-helper.test.ts +0 -47
  345. package/src/server/plugins/engine/helpers.test.ts +0 -868
  346. package/src/server/plugins/engine/models/FormModel.test.ts +0 -725
  347. package/src/server/plugins/engine/models/SummaryViewModel.test.ts +0 -472
  348. package/src/server/plugins/engine/options.test.js +0 -63
  349. package/src/server/plugins/engine/outputFormatters/adapter/v1.location.test.ts +0 -356
  350. package/src/server/plugins/engine/outputFormatters/adapter/v1.test.ts +0 -871
  351. package/src/server/plugins/engine/outputFormatters/human/v1.payment.test.ts +0 -147
  352. package/src/server/plugins/engine/outputFormatters/human/v1.test.ts +0 -145
  353. package/src/server/plugins/engine/outputFormatters/index.test.ts +0 -17
  354. package/src/server/plugins/engine/outputFormatters/machine/v1.test.ts +0 -268
  355. package/src/server/plugins/engine/outputFormatters/machine/v2.location.test.ts +0 -341
  356. package/src/server/plugins/engine/outputFormatters/machine/v2.payment.test.ts +0 -115
  357. package/src/server/plugins/engine/outputFormatters/machine/v2.test.ts +0 -311
  358. package/src/server/plugins/engine/pageControllers/FileUploadPageController.test.ts +0 -1372
  359. package/src/server/plugins/engine/pageControllers/PageController.test.ts +0 -246
  360. package/src/server/plugins/engine/pageControllers/QuestionPageController.test.ts +0 -1686
  361. package/src/server/plugins/engine/pageControllers/RepeatPageController.test.ts +0 -279
  362. package/src/server/plugins/engine/pageControllers/StartPageController.test.ts +0 -32
  363. package/src/server/plugins/engine/pageControllers/StatusPageController.test.ts +0 -32
  364. package/src/server/plugins/engine/pageControllers/SummaryPageController.test.ts +0 -89
  365. package/src/server/plugins/engine/pageControllers/TerminalController.test.ts +0 -37
  366. package/src/server/plugins/engine/pageControllers/errors.test.ts +0 -78
  367. package/src/server/plugins/engine/pageControllers/helpers/helpers.test.ts +0 -182
  368. package/src/server/plugins/engine/pageControllers/helpers/state.test.ts +0 -359
  369. package/src/server/plugins/engine/pageControllers/helpers/submission.test.ts +0 -373
  370. package/src/server/plugins/engine/referenceNumbers.test.ts +0 -74
  371. package/src/server/plugins/engine/routes/index.test.ts +0 -332
  372. package/src/server/plugins/engine/routes/payment-helper.test.js +0 -136
  373. package/src/server/plugins/engine/routes/payment.test.js +0 -180
  374. package/src/server/plugins/engine/routes/questions.test.ts +0 -502
  375. package/src/server/plugins/engine/routes/repeaters/item-delete.test.ts +0 -83
  376. package/src/server/plugins/engine/routes/repeaters/summary.test.ts +0 -75
  377. package/src/server/plugins/engine/services/formsService.test.js +0 -26
  378. package/src/server/plugins/engine/services/notifyService.test.ts +0 -310
  379. package/src/server/plugins/engine/types/schema.test.ts +0 -234
  380. package/src/server/plugins/engine/views/components/service-banner/template.test.js +0 -43
  381. package/src/server/plugins/engine/views/components/tag-env/template.test.js +0 -28
  382. package/src/server/plugins/engine/views/partials/preview-banner.test.js +0 -122
  383. package/src/server/plugins/map/routes/get-os-token.test.js +0 -55
  384. package/src/server/plugins/map/service.test.js +0 -144
  385. package/src/server/plugins/nunjucks/context.test.js +0 -109
  386. package/src/server/plugins/nunjucks/enviroment.test.js +0 -207
  387. package/src/server/plugins/nunjucks/filters/answer.test.js +0 -92
  388. package/src/server/plugins/nunjucks/filters/field.test.js +0 -75
  389. package/src/server/plugins/nunjucks/filters/href.test.js +0 -80
  390. package/src/server/plugins/nunjucks/filters/merge.test.js +0 -15
  391. package/src/server/plugins/nunjucks/filters/page.test.js +0 -65
  392. package/src/server/plugins/payment/helper.test.js +0 -29
  393. package/src/server/plugins/payment/service.test.js +0 -218
  394. package/src/server/plugins/postcode-lookup/service.test.js +0 -177
  395. package/src/server/postcode-lookup.test.ts +0 -64
  396. package/src/server/routes/dummy-api.test.ts +0 -97
  397. package/src/server/services/cacheService.test.ts +0 -308
  398. package/src/server/services/httpService.test.js +0 -491
  399. package/src/server/utils/file-form-service.test.js +0 -127
  400. package/src/server/utils/notify.test.ts +0 -37
  401. package/src/server/utils/secure-context/get-trust-store-certs.test.js +0 -19
  402. package/src/server/utils/utils.test.js +0 -69
@@ -1,1686 +0,0 @@
1
- import { type PageQuestion } from '@defra/forms-model'
2
-
3
- import { getForm } from '~/src/server/plugins/engine/configureEnginePlugin.js'
4
- import { FormModel } from '~/src/server/plugins/engine/models/FormModel.js'
5
- import { QuestionPageController } from '~/src/server/plugins/engine/pageControllers/QuestionPageController.js'
6
- import {
7
- buildFormContextRequest,
8
- buildFormRequest
9
- } from '~/src/server/plugins/engine/pageControllers/__stubs__/request.js'
10
- import { serverWithSaveAndExit } from '~/src/server/plugins/engine/pageControllers/__stubs__/server.js'
11
- import {
12
- FileStatus,
13
- UploadStatus,
14
- type FormContext,
15
- type FormPageViewModel,
16
- type FormState,
17
- type FormSubmissionState
18
- } from '~/src/server/plugins/engine/types.js'
19
- import {
20
- type FormRequest,
21
- type FormRequestPayload,
22
- type FormResponseToolkit
23
- } from '~/src/server/routes/types.js'
24
- import { CacheService } from '~/src/server/services/cacheService.js'
25
- import conditionalReveal from '~/test/form/definitions/conditional-reveal.js'
26
- import definitionConditionsBasic, {
27
- V2 as definitionConditionsBasicV2
28
- } from '~/test/form/definitions/conditions-basic.js'
29
- import definitionConditionsComplex from '~/test/form/definitions/conditions-complex.js'
30
- import definitionConditionsDates from '~/test/form/definitions/conditions-dates.js'
31
-
32
- describe('QuestionPageController', () => {
33
- let page1: PageQuestion
34
- let page1Url: URL
35
-
36
- let page2: PageQuestion
37
- let page2Url: URL
38
-
39
- let model: FormModel
40
- let controller1: QuestionPageController
41
- let controller2: QuestionPageController
42
- let requestPage1: FormRequest
43
- let requestPage2: FormRequest
44
-
45
- beforeEach(() => {
46
- const { pages } = definitionConditionsBasic
47
-
48
- page1 = pages[0]
49
- page1Url = new URL('http://example.com/test/first-page')
50
-
51
- page2 = pages[1]
52
- page2Url = new URL('http://example.com/test/second-page')
53
-
54
- model = new FormModel(definitionConditionsBasic, {
55
- basePath: 'test'
56
- })
57
-
58
- controller1 = new QuestionPageController(model, page1)
59
- controller2 = new QuestionPageController(model, page2)
60
-
61
- requestPage1 = buildFormRequest({
62
- method: 'get',
63
- url: page1Url,
64
- path: page1Url.pathname,
65
- params: {
66
- path: 'first-page',
67
- slug: 'test'
68
- },
69
- query: {},
70
- app: { model }
71
- } as FormRequest)
72
-
73
- requestPage2 = buildFormRequest({
74
- method: 'get',
75
- url: page2Url,
76
- path: page2Url.pathname,
77
- params: {
78
- path: 'second-page',
79
- slug: 'test'
80
- },
81
- query: {},
82
- app: { model }
83
- } as FormRequest)
84
- })
85
-
86
- describe('Properties', () => {
87
- it('returns path', () => {
88
- expect(controller1).toHaveProperty('path', '/first-page')
89
- expect(controller2).toHaveProperty('path', '/second-page')
90
- })
91
-
92
- it('returns href', () => {
93
- expect(controller1).toHaveProperty('href', '/test/first-page')
94
- expect(controller2).toHaveProperty('href', '/test/second-page')
95
- })
96
-
97
- it('returns keys', () => {
98
- expect(controller1).toHaveProperty('keys', ['yesNoField'])
99
- expect(controller2).toHaveProperty('keys', [
100
- 'dateField',
101
- 'dateField__day',
102
- 'dateField__month',
103
- 'dateField__year',
104
- 'multilineTextField'
105
- ])
106
- })
107
-
108
- it('returns the page section', () => {
109
- expect(controller1).toHaveProperty('section', undefined)
110
- expect(controller2).toHaveProperty('section', {
111
- name: 'marriage',
112
- title: 'Your marriage',
113
- hideTitle: false
114
- })
115
- })
116
- })
117
-
118
- describe('Path methods', () => {
119
- describe('Next getter', () => {
120
- it('returns page links', () => {
121
- expect(controller1).toHaveProperty('next', [
122
- { path: '/second-page' },
123
- { path: '/summary', condition: 'isPreviouslyMarried' }
124
- ])
125
-
126
- expect(controller2).toHaveProperty('next', [{ path: '/summary' }])
127
- })
128
-
129
- it('returns page links (none found)', () => {
130
- const pageDef1 = structuredClone(controller1.pageDef)
131
- const pageDef2 = structuredClone(controller2.pageDef)
132
-
133
- controller1.pageDef = pageDef1
134
- controller2.pageDef = pageDef2
135
-
136
- // @ts-expect-error - Allow invalid property for test
137
- controller1.pageDef.next = []
138
-
139
- // @ts-expect-error - Allow invalid property for test
140
- delete controller2.pageDef.next
141
-
142
- expect(controller1).toHaveProperty('next', [])
143
- expect(controller2).toHaveProperty('next', [])
144
- })
145
- })
146
- })
147
-
148
- describe('Component collection', () => {
149
- it('returns the components for the page', () => {
150
- const { components: components1 } = controller1.collection
151
- const { components: components2 } = controller2.collection
152
-
153
- expect(components1).toHaveLength(1)
154
- expect(components1[0].name).toBe('yesNoField')
155
-
156
- expect(components2).toHaveLength(3)
157
- expect(components2[0].name).toBe('detailsField')
158
- expect(components2[1].name).toBe('dateField')
159
- expect(components2[2].name).toBe('multilineTextField')
160
- })
161
-
162
- it('returns the fields for the page', () => {
163
- const { fields: fields1 } = controller1.collection
164
- const { fields: fields2 } = controller2.collection
165
-
166
- expect(fields1).toHaveLength(1)
167
- expect(fields1[0].name).toBe('yesNoField')
168
-
169
- expect(fields2).toHaveLength(2)
170
- expect(fields2[0].name).toBe('dateField')
171
- expect(fields2[1].name).toBe('multilineTextField')
172
- })
173
-
174
- it('returns the guidance for the page', () => {
175
- const { guidance: guidance1 } = controller1.collection
176
- const { guidance: guidance2 } = controller2.collection
177
-
178
- expect(guidance1).toHaveLength(0)
179
- expect(guidance2).toHaveLength(1)
180
- expect(guidance2[0].name).toBe('detailsField')
181
- })
182
- })
183
-
184
- describe('Component view models', () => {
185
- let viewModel1: FormPageViewModel
186
- let viewModel2: FormPageViewModel
187
-
188
- beforeEach(() => {
189
- viewModel1 = controller1.getViewModel(
190
- requestPage1,
191
- model.getFormContext(requestPage1, { $$__referenceNumber: 'foobar' })
192
- )
193
-
194
- viewModel2 = controller2.getViewModel(
195
- requestPage2,
196
- model.getFormContext(requestPage2, { $$__referenceNumber: 'foobar' })
197
- )
198
- })
199
-
200
- it('hides the page title for single form component pages', () => {
201
- // Page 1 hides page title (single component)
202
- expect(viewModel1).toHaveProperty('showTitle', false)
203
- expect(viewModel1).toHaveProperty('pageTitle', 'Previous marriages')
204
-
205
- // Page 2 shows page title (multiple components)
206
- expect(viewModel2).toHaveProperty('showTitle', true)
207
- expect(viewModel2).toHaveProperty(
208
- 'pageTitle',
209
- 'When will you get married?'
210
- )
211
- })
212
-
213
- it('returns the component view models for the page', () => {
214
- const { components: components1 } = viewModel1
215
- const { components: components2 } = viewModel2
216
-
217
- expect(components1).toHaveLength(1)
218
- expect(components2).toHaveLength(3)
219
-
220
- // Page 1, component 1, default label
221
- expect(components1[0].model).toHaveProperty('label', {
222
- text: 'Have you previously been married?'
223
- })
224
-
225
- // Page 1, component 1, optional legend
226
- expect(components1[0].model).toHaveProperty('fieldset', {
227
- legend: {
228
- text: 'Previous marriages',
229
- classes: 'govuk-fieldset__legend--l',
230
- isPageHeading: true
231
- }
232
- })
233
-
234
- // Page 2, component 1, content only
235
- expect(components2[0].model).toEqual({
236
- attributes: {},
237
- summaryHtml: 'Find out more',
238
- html: 'Some content goes here'
239
- })
240
-
241
- // Page 2, component 2, default label
242
- expect(components2[1].model).toHaveProperty('label', {
243
- text: 'Date of marriage',
244
- classes: 'govuk-label--m'
245
- })
246
-
247
- // Page 2, component 2, optional legend
248
- expect(components2[1].model).toHaveProperty('fieldset', {
249
- legend: {
250
- text: 'Date of marriage',
251
- classes: 'govuk-fieldset__legend--m'
252
- }
253
- })
254
-
255
- // Page 2, component 3, default label
256
- expect(components2[2].model).toHaveProperty('label', {
257
- text: 'Remarks',
258
- classes: 'govuk-label--m'
259
- })
260
-
261
- // Page 2, component 3, optional legend
262
- expect(components2[2].model).not.toHaveProperty('fieldset')
263
- })
264
- })
265
-
266
- describe('Condition evaluation context', () => {
267
- it('filters state by journey pages', () => {
268
- const { pages } = definitionConditionsComplex
269
-
270
- const model = new FormModel(definitionConditionsComplex, {
271
- basePath: 'test'
272
- })
273
-
274
- // Selected page appears after convergence and contains a conditional field
275
- // This is the page we're theoretically browsing to
276
- const controller = new QuestionPageController(model, pages[7])
277
-
278
- // The state below shows we said we had a UKPassport and entered details for an applicant
279
- const state: FormSubmissionState = {
280
- $$__referenceNumber: 'foobar',
281
- ukPassport: true,
282
- numberOfApplicants: 2,
283
- applicantOneFirstName: 'Enrique',
284
- applicantOneMiddleName: null,
285
- applicantOneLastName: 'Chase',
286
- applicantOneAddress__addressLine1: 'AddressLine1',
287
- applicantOneAddress__addressLine2: 'AddressLine2',
288
- applicantOneAddress__town: 'Town',
289
- applicantOneAddress__postcode: 'Postcode',
290
- applicantTwoFirstName: 'John',
291
- applicantTwoMiddleName: null,
292
- applicantTwoLastName: 'Doe',
293
- applicantTwoAddress__addressLine1: 'AddressLine1',
294
- applicantTwoAddress__addressLine2: 'AddressLine2',
295
- applicantTwoAddress__town: 'Town',
296
- applicantTwoAddress__postcode: 'Postcode'
297
- }
298
-
299
- let request = buildFormContextRequest({
300
- method: 'get',
301
- url: new URL('http://example.com/test/applicant-one-address'),
302
- path: '/test/applicant-one-address',
303
- params: {
304
- path: 'applicant-one-address',
305
- slug: 'test'
306
- },
307
- query: {},
308
- app: { model }
309
- })
310
-
311
- // Calculate our context based on the page we're attempting to load and the above state we provide
312
- let context = controller.model.getFormContext(request, state)
313
- const { evaluationState: stateBefore } = context
314
-
315
- // Our context should know our first applicant
316
- expect(stateBefore).toEqual({
317
- numberOfApplicants: 2,
318
- ukPassport: true,
319
- applicantOneFirstName: 'Enrique',
320
- applicantOneMiddleName: null,
321
- applicantOneLastName: 'Chase',
322
- applicantOneAddress: [
323
- 'AddressLine1',
324
- 'AddressLine2',
325
- 'Town',
326
- 'Postcode'
327
- ],
328
- applicantTwoAddress: null,
329
- applicantTwoFirstName: null,
330
- applicantTwoLastName: null,
331
- applicantTwoMiddleName: null
332
- })
333
-
334
- // Our context should know which pages are relevant
335
- expect(context.paths).toEqual([
336
- '/uk-passport',
337
- '/how-many-people',
338
- '/applicant-one-name',
339
- '/applicant-one-address'
340
- ])
341
-
342
- // Now mark that we don't have a UK Passport
343
- state.ukPassport = false
344
-
345
- request = buildFormContextRequest({
346
- method: 'get',
347
- url: new URL('http://example.com/test/summary'),
348
- path: '/test/summary',
349
- params: {
350
- path: 'summary',
351
- slug: 'test'
352
- },
353
- query: {},
354
- app: { model }
355
- })
356
-
357
- // And recalculate our context
358
- context = controller.model.getFormContext(request, state)
359
- const { evaluationState: stateAfter } = context
360
-
361
- // Our context should no longer list pages about our applicant
362
- expect(context.paths).toEqual([
363
- '/uk-passport',
364
- '/testconditions',
365
- '/summary'
366
- ])
367
-
368
- // Our evaluation context should have default values for irrelevant fields
369
- expect(stateAfter).toEqual({
370
- ukPassport: false,
371
- numberOfApplicants: null,
372
- applicantOneFirstName: null,
373
- applicantOneMiddleName: null,
374
- applicantOneLastName: null,
375
- applicantOneAddress: null,
376
- applicantTwoAddress: null,
377
- applicantTwoFirstName: null,
378
- applicantTwoLastName: null,
379
- applicantTwoMiddleName: null
380
- })
381
- })
382
-
383
- it('combines state values for date fields', () => {
384
- const { pages } = definitionConditionsDates
385
-
386
- const model = new FormModel(definitionConditionsDates, {
387
- basePath: 'test'
388
- })
389
-
390
- const controller = new QuestionPageController(model, pages[0])
391
-
392
- const request = buildFormContextRequest({
393
- method: 'get',
394
- url: new URL('http://example.com/test/page-one'),
395
- path: '/test/page-one',
396
- params: {
397
- path: 'page-one',
398
- slug: 'test'
399
- },
400
- query: {},
401
- app: { model }
402
- })
403
-
404
- const context = controller.model.getFormContext(request, {
405
- $$__referenceNumber: 'foobar',
406
- dateField__day: 5,
407
- dateField__month: 1,
408
- dateField__year: 2024
409
- })
410
-
411
- // Ensure dates are transformed to yyyy-MM-dd format
412
- expect(context.evaluationState).toHaveProperty('dateField', '2024-01-05')
413
-
414
- // Unlike relevant state which has the individual date parts
415
- expect(context.relevantState).not.toHaveProperty('dateField')
416
- expect(context.relevantState).toMatchObject({
417
- dateField__day: 5,
418
- dateField__month: 1,
419
- dateField__year: 2024
420
- })
421
- })
422
-
423
- it('filters on condition A', () => {
424
- const { pages } = conditionalReveal
425
-
426
- const model = new FormModel(conditionalReveal, {
427
- basePath: 'test'
428
- })
429
-
430
- const controller = new QuestionPageController(model, pages[0])
431
-
432
- // The state below shows we said we had a UKPassport and entered details for an applicant
433
- const state: FormSubmissionState = { $$__referenceNumber: 'foobar' }
434
-
435
- const request = buildFormContextRequest({
436
- method: 'get',
437
- url: new URL('http://example.com/test/page-one'),
438
- path: '/test/first-page',
439
- params: {
440
- path: 'first-page',
441
- slug: 'test'
442
- },
443
- query: {},
444
- app: { model }
445
- })
446
-
447
- const context = controller.model.getFormContext(request, state)
448
- const evaluationState = { animalType: 'Barn owl' }
449
-
450
- const viewModel = controller.getViewModel(request, context)
451
-
452
- const filtered = controller.filterConditionalComponents(
453
- viewModel,
454
- model,
455
- evaluationState
456
- )
457
-
458
- expect(filtered).toHaveLength(2)
459
- expect(filtered[0].model.content).toBe('This is info for Barn owls')
460
- expect(filtered[1].model.label?.text).toBe('Select from the list')
461
- expect(filtered[1].model.items).toEqual([
462
- {
463
- checked: false,
464
- condition: 'isBarnOwl',
465
- selected: false,
466
- text: 'Option 1',
467
- value: '1'
468
- },
469
- {
470
- checked: false,
471
- condition: 'isBarnOwl',
472
- selected: false,
473
- text: 'Option 2',
474
- value: '2'
475
- }
476
- ])
477
- })
478
-
479
- it('filters on condition B', () => {
480
- const { pages } = conditionalReveal
481
-
482
- const model = new FormModel(conditionalReveal, {
483
- basePath: 'test'
484
- })
485
-
486
- const controller = new QuestionPageController(model, pages[0])
487
-
488
- // The state below shows we said we had a UKPassport and entered details for an applicant
489
- const state: FormSubmissionState = { $$__referenceNumber: 'foobar' }
490
-
491
- const request = buildFormContextRequest({
492
- method: 'get',
493
- url: new URL('http://example.com/test/page-one'),
494
- path: '/test/first-page',
495
- params: {
496
- path: 'first-page',
497
- slug: 'test'
498
- },
499
- query: {},
500
- app: { model }
501
- })
502
-
503
- const context = controller.model.getFormContext(request, state)
504
- const evaluationState = { animalType: 'Swan' }
505
-
506
- const viewModel = controller.getViewModel(request, context)
507
-
508
- const filtered = controller.filterConditionalComponents(
509
- viewModel,
510
- model,
511
- evaluationState
512
- )
513
-
514
- expect(filtered).toHaveLength(2)
515
- expect(filtered[0].model.content).toBe('This is info for other breeds')
516
- expect(filtered[1].model.label?.text).toBe('Select from the list')
517
- expect(filtered[1].model.items).toEqual([
518
- {
519
- checked: false,
520
- condition: 'notBarnOwl',
521
- selected: false,
522
- text: 'Option 3',
523
- value: '3'
524
- },
525
- {
526
- checked: false,
527
- condition: 'notBarnOwl',
528
- selected: false,
529
- text: 'Option 4',
530
- value: '4'
531
- }
532
- ])
533
- })
534
-
535
- it('correctly initialises default values', async () => {
536
- const components = await getForm(
537
- '../../../../test/form/definitions/components.json'
538
- )
539
- const { pages } = components
540
-
541
- const model = new FormModel(components, {
542
- basePath: 'test'
543
- })
544
-
545
- const controller = new QuestionPageController(model, pages[0])
546
-
547
- const state: FormSubmissionState = { $$__referenceNumber: 'foobar' }
548
-
549
- let request = buildFormContextRequest({
550
- method: 'get',
551
- url: new URL('http://example.com/test/all-components'),
552
- path: '/test/all-components',
553
- params: {
554
- path: 'all-components',
555
- slug: 'test'
556
- },
557
- query: {},
558
- app: { model }
559
- })
560
-
561
- // Calculate our context based on the page we're attempting to load and the above state we provide
562
- let context = controller.model.getFormContext(request, state)
563
-
564
- // Context paths should be all pages up to the one we requested
565
- expect(context.paths).toEqual(['/all-components'])
566
-
567
- // Our context should have default values for all input fields
568
- expect(context.evaluationState).toEqual({
569
- textField: null,
570
- multilineTextField: null,
571
- numberField: null,
572
- datePartsField: null,
573
- monthYearField: null,
574
- yesNoField: null,
575
- emailAddressField: null,
576
- telephoneNumberField: null,
577
- addressField: null,
578
- radiosField: null,
579
- selectField: null,
580
- autocompleteField: null,
581
- checkboxesSingle: [],
582
- checkboxesMultiple: [],
583
- checkboxesSingleNumber: [],
584
- checkboxesMultipleNumber: [],
585
- fileUpload: null,
586
- declaration: false
587
- })
588
-
589
- Object.assign(state, {
590
- textField: 'Text field',
591
- multilineTextField: 'Multiline text field',
592
- numberField: 1,
593
- datePartsField__day: 12,
594
- datePartsField__month: 12,
595
- datePartsField__year: 2012,
596
- monthYearField__month: 12,
597
- monthYearField__year: 2012,
598
- yesNoField: 'true',
599
- emailAddressField: 'user@email.com',
600
- telephoneNumberField: '+447900000000',
601
- addressField__addressLine1: 'Address line 1',
602
- addressField__addressLine2: 'Address line 2',
603
- addressField__town: 'Town or city',
604
- addressField__county: 'Cheshire',
605
- addressField__postcode: 'CW1 1AB',
606
- addressField__uprn: '',
607
- radiosField: 'privateLimitedCompany',
608
- selectField: 910400000,
609
- autocompleteField: 910400044,
610
- checkboxesSingle: ['Shetland'],
611
- checkboxesMultiple: ['Arabian', 'Shire', 'Race'],
612
- checkboxesSingleNumber: [1],
613
- checkboxesMultipleNumber: [0, 1],
614
- declaration: true
615
- })
616
-
617
- request = buildFormContextRequest({
618
- method: 'get',
619
- url: new URL('http://example.com/test/methodology-statement'),
620
- path: '/test/methodology-statement',
621
- params: {
622
- path: 'methodology-statement',
623
- slug: 'test'
624
- },
625
- query: {},
626
- app: { model }
627
- })
628
-
629
- // Calculate our context based on the page we're attempting to load and the above state we provide
630
- context = controller.model.getFormContext(request, state)
631
-
632
- // Context paths should be all pages up to the one we requested
633
- expect(context.paths).toEqual([
634
- '/all-components',
635
- '/methodology-statement'
636
- ])
637
-
638
- // Our context should have evaluation state values for relevant fields and default values for everything else
639
- expect(context.evaluationState).toEqual({
640
- textField: 'Text field',
641
- multilineTextField: 'Multiline text field',
642
- numberField: 1,
643
- datePartsField: '2012-12-12',
644
- monthYearField: '2012-12',
645
- yesNoField: null,
646
- emailAddressField: 'user@email.com',
647
- telephoneNumberField: '+447900000000',
648
- addressField: [
649
- 'Address line 1',
650
- 'Address line 2',
651
- 'Town or city',
652
- 'Cheshire',
653
- 'CW1 1AB'
654
- ],
655
- radiosField: 'privateLimitedCompany',
656
- selectField: 910400000,
657
- autocompleteField: 910400044,
658
- checkboxesSingle: ['Shetland'],
659
- checkboxesMultiple: ['Arabian', 'Shire', 'Race'],
660
- checkboxesSingleNumber: [1],
661
- checkboxesMultipleNumber: [0, 1],
662
- fileUpload: null,
663
- declaration: true
664
- })
665
-
666
- Object.assign(state, {
667
- fileUpload: [
668
- {
669
- uploadId: '348e1878-59c0-4d2e-a52b-e5042ad729f0',
670
- status: {
671
- uploadStatus: UploadStatus.ready,
672
- metadata: {
673
- retrievalKey: 'enrique.chase@defra.gov.uk'
674
- },
675
- form: {
676
- file: {
677
- fileId: 'fd5db541-179c-4107-a4d0-149d09672ffc',
678
- filename: 'test.jpg',
679
- fileStatus: FileStatus.complete,
680
- contentLength: 3671
681
- }
682
- },
683
- numberOfRejectedFiles: 0
684
- }
685
- }
686
- ]
687
- })
688
-
689
- request = buildFormContextRequest({
690
- method: 'get',
691
- url: new URL('http://example.com/test/summary'),
692
- path: '/test/summary',
693
- params: {
694
- path: 'summary',
695
- slug: 'test'
696
- },
697
- query: {},
698
- app: { model }
699
- })
700
-
701
- // Calculate our context based on the page we're attempting to load and the above state we provide
702
- context = controller.model.getFormContext(request, state)
703
-
704
- // Context paths should be all pages up to the one we requested
705
- expect(context.paths).toEqual([
706
- '/all-components',
707
- '/methodology-statement',
708
- '/summary'
709
- ])
710
-
711
- // Our context should now have evaluation state values all fields
712
- expect(context.evaluationState).toEqual({
713
- textField: 'Text field',
714
- multilineTextField: 'Multiline text field',
715
- numberField: 1,
716
- datePartsField: '2012-12-12',
717
- monthYearField: '2012-12',
718
- yesNoField: null,
719
- emailAddressField: 'user@email.com',
720
- telephoneNumberField: '+447900000000',
721
- addressField: [
722
- 'Address line 1',
723
- 'Address line 2',
724
- 'Town or city',
725
- 'Cheshire',
726
- 'CW1 1AB'
727
- ],
728
- radiosField: 'privateLimitedCompany',
729
- selectField: 910400000,
730
- autocompleteField: 910400044,
731
- checkboxesSingle: ['Shetland'],
732
- checkboxesMultiple: ['Arabian', 'Shire', 'Race'],
733
- checkboxesSingleNumber: [1],
734
- checkboxesMultipleNumber: [0, 1],
735
- fileUpload: ['fd5db541-179c-4107-a4d0-149d09672ffc'],
736
- declaration: true
737
- })
738
- })
739
- })
740
-
741
- describe('Form validation', () => {
742
- it('includes all field errors', () => {
743
- const result1 = controller1.collection.validate()
744
- const result2 = controller2.collection.validate()
745
-
746
- expect(result1.errors).toHaveLength(1)
747
- expect(result2.errors).toHaveLength(4)
748
- })
749
- })
750
-
751
- describe('Form journey', () => {
752
- let context: FormContext
753
- let contextNo: FormContext
754
- let contextYes: FormContext
755
-
756
- beforeEach(() => {
757
- // Empty state
758
- context = model.getFormContext(requestPage1, {
759
- $$__referenceNumber: 'foobar'
760
- })
761
-
762
- // Question 1: Selected 'No'
763
- contextNo = model.getFormContext(requestPage1, {
764
- $$__referenceNumber: 'foobar',
765
- yesNoField: false
766
- })
767
-
768
- // Question 1: Selected 'Yes'
769
- contextYes = model.getFormContext(requestPage1, {
770
- $$__referenceNumber: 'foobar',
771
- yesNoField: true
772
- })
773
- })
774
-
775
- describe('Next getter', () => {
776
- it('returns the next page links', () => {
777
- expect(controller1).toHaveProperty('next', [
778
- { path: '/second-page' },
779
- { path: '/summary', condition: 'isPreviouslyMarried' }
780
- ])
781
-
782
- expect(controller2).toHaveProperty('next', [{ path: '/summary' }])
783
- })
784
- })
785
-
786
- describe('Next', () => {
787
- it('returns the next page path', () => {
788
- expect(controller1.getNextPath(context)).toBe('/second-page')
789
- expect(controller1.getNextPath(contextNo)).toBe('/second-page')
790
- expect(controller1.getNextPath(contextYes)).toBe('/summary')
791
-
792
- expect(controller2.getNextPath(context)).toBe('/summary')
793
- expect(controller2.getNextPath(contextNo)).toBe('/summary')
794
- expect(controller2.getNextPath(contextYes)).toBe('/summary')
795
- })
796
- })
797
-
798
- describe('Summary', () => {
799
- it('returns the summary path', () => {
800
- expect(controller1.getSummaryPath()).toBe('/summary')
801
- expect(controller2.getSummaryPath()).toBe('/summary')
802
- })
803
- })
804
- })
805
-
806
- describe('Route handlers', () => {
807
- const response = {
808
- code: jest.fn().mockImplementation(() => response)
809
- }
810
-
811
- const h: FormResponseToolkit = {
812
- redirect: jest.fn().mockReturnValue(response),
813
- view: jest.fn(),
814
- continue: Symbol('continue')
815
- }
816
-
817
- it('returns default route options', () => {
818
- expect(controller1.getRouteOptions).toMatchObject({
819
- ext: {
820
- onPostHandler: {
821
- method: expect.any(Function)
822
- }
823
- }
824
- })
825
-
826
- expect(controller1.postRouteOptions).toMatchObject({
827
- ext: {
828
- onPostHandler: {
829
- method: expect.any(Function)
830
- }
831
- }
832
- })
833
- })
834
-
835
- it('supports GET route handler', async () => {
836
- const state: FormState = { yesNoField: false }
837
-
838
- for (const controller of [controller1, controller2]) {
839
- jest
840
- .spyOn(controller, 'hasMissingNotificationEmail')
841
- .mockResolvedValue(false)
842
-
843
- jest.spyOn(controller, 'getState').mockResolvedValue(state)
844
- }
845
-
846
- expect(() => controller1.makeGetRouteHandler()).not.toThrow()
847
- expect(() => controller1.makeGetRouteHandler()).toBeInstanceOf(Function)
848
-
849
- await controller1.makeGetRouteHandler()(
850
- requestPage1,
851
- model.getFormContext(requestPage1, { $$__referenceNumber: 'foobar' }),
852
- h
853
- )
854
-
855
- await controller2.makeGetRouteHandler()(
856
- requestPage2,
857
- model.getFormContext(requestPage2, { $$__referenceNumber: 'foobar' }),
858
- h
859
- )
860
-
861
- expect(h.view).toHaveBeenNthCalledWith(
862
- 1,
863
- controller1.viewName,
864
- expect.objectContaining({
865
- pageTitle: 'Previous marriages',
866
- sectionTitle: undefined
867
- })
868
- )
869
-
870
- expect(h.view).toHaveBeenNthCalledWith(
871
- 2,
872
- controller2.viewName,
873
- expect.objectContaining({
874
- pageTitle: 'When will you get married?',
875
- sectionTitle: 'Your marriage'
876
- })
877
- )
878
- })
879
- })
880
-
881
- describe('State', () => {
882
- beforeEach(() => {
883
- jest.spyOn(CacheService.prototype, 'getState')
884
- jest.spyOn(CacheService.prototype, 'setState')
885
-
886
- // Preview URL '?force'
887
- requestPage1.query.force = ''
888
- })
889
-
890
- describe('getState', () => {
891
- it('should skip get for preview URL direct access', async () => {
892
- const state = await controller1.getState(requestPage1)
893
-
894
- expect(state).toEqual({})
895
- expect(CacheService.prototype.getState).not.toHaveBeenCalled()
896
- })
897
- })
898
-
899
- describe('setState', () => {
900
- it('should skip set for preview URL direct access', async () => {
901
- const state: FormSubmissionState = { yesNoField: false }
902
- const updated = await controller1.setState(requestPage1, state)
903
-
904
- expect(updated).toBe(state)
905
- expect(CacheService.prototype.setState).not.toHaveBeenCalled()
906
- })
907
- })
908
-
909
- describe('mergeState', () => {
910
- it('should skip merge for preview URL direct access', async () => {
911
- const state: FormSubmissionState = {
912
- yesNoField: false
913
- }
914
-
915
- const update: FormSubmissionState = {
916
- dateField__day: 5,
917
- dateField__month: 1,
918
- dateField__year: 2024
919
- }
920
-
921
- const updated = await controller1.mergeState(
922
- requestPage1,
923
- state,
924
- update
925
- )
926
-
927
- expect(updated).toEqual({
928
- yesNoField: false,
929
- dateField__day: 5,
930
- dateField__month: 1,
931
- dateField__year: 2024
932
- })
933
-
934
- expect(CacheService.prototype.setState).not.toHaveBeenCalled()
935
- })
936
- })
937
- })
938
- })
939
-
940
- describe('QuestionPageController V2', () => {
941
- let page1Url: URL
942
- let page2Url: URL
943
-
944
- let model: FormModel
945
- let controller1: QuestionPageController
946
- let controller2: QuestionPageController
947
- let requestPage1: FormRequest
948
- let requestPage2: FormRequest
949
-
950
- beforeEach(() => {
951
- page1Url = new URL('http://example.com/test/first-page')
952
- page2Url = new URL('http://example.com/test/second-page')
953
-
954
- model = new FormModel(definitionConditionsBasicV2, {
955
- basePath: 'test'
956
- })
957
-
958
- controller1 = model.pages[0] // new QuestionPageController(model, page1)
959
- controller2 = model.pages[1] // new QuestionPageController(model, page2)
960
-
961
- requestPage1 = buildFormRequest({
962
- method: 'get',
963
- url: page1Url,
964
- path: page1Url.pathname,
965
- params: {
966
- path: 'first-page',
967
- slug: 'test'
968
- },
969
- query: {},
970
- app: { model }
971
- } as FormRequest)
972
-
973
- requestPage2 = buildFormRequest({
974
- method: 'get',
975
- url: page2Url,
976
- path: page2Url.pathname,
977
- params: {
978
- path: 'second-page',
979
- slug: 'test'
980
- },
981
- query: {},
982
- app: { model }
983
- } as FormRequest)
984
- })
985
-
986
- describe('Properties', () => {
987
- it('returns path', () => {
988
- expect(controller1).toHaveProperty('path', '/first-page')
989
- expect(controller2).toHaveProperty('path', '/second-page')
990
- })
991
-
992
- it('returns href', () => {
993
- expect(controller1).toHaveProperty('href', '/test/first-page')
994
- expect(controller2).toHaveProperty('href', '/test/second-page')
995
- })
996
-
997
- it('returns keys', () => {
998
- expect(controller1).toHaveProperty('keys', ['yesNoField'])
999
- expect(controller2).toHaveProperty('keys', [
1000
- 'dateField',
1001
- 'dateField__day',
1002
- 'dateField__month',
1003
- 'dateField__year',
1004
- 'multilineTextField'
1005
- ])
1006
- })
1007
-
1008
- it('returns the page section', () => {
1009
- expect(controller1).toHaveProperty('section', undefined)
1010
- expect(controller2.section).toEqual(
1011
- expect.objectContaining({
1012
- name: 'marriage',
1013
- title: 'Your marriage',
1014
- hideTitle: false
1015
- })
1016
- )
1017
- })
1018
- })
1019
-
1020
- describe('Path methods', () => {
1021
- describe('Next getter', () => {
1022
- it('returns page links', () => {
1023
- expect(controller1).toHaveProperty('next', [])
1024
-
1025
- expect(controller2).toHaveProperty('next', [])
1026
- })
1027
-
1028
- it('returns page links (none found)', () => {
1029
- const pageDef1 = structuredClone(controller1.pageDef)
1030
- const pageDef2 = structuredClone(controller2.pageDef)
1031
-
1032
- controller1.pageDef = pageDef1
1033
- controller2.pageDef = pageDef2
1034
-
1035
- // @ts-expect-error - Allow invalid property for test
1036
- controller1.pageDef.next = []
1037
-
1038
- // @ts-expect-error - Allow invalid property for test
1039
- delete controller2.pageDef.next
1040
-
1041
- expect(controller1).toHaveProperty('next', [])
1042
- expect(controller2).toHaveProperty('next', [])
1043
- })
1044
- })
1045
- })
1046
-
1047
- describe('Component collection', () => {
1048
- it('returns the components for the page', () => {
1049
- const { components: components1 } = controller1.collection
1050
- const { components: components2 } = controller2.collection
1051
-
1052
- expect(components1).toHaveLength(1)
1053
- expect(components1[0].name).toBe('yesNoField')
1054
-
1055
- expect(components2).toHaveLength(3)
1056
- expect(components2[0].name).toBe('detailsField')
1057
- expect(components2[1].name).toBe('dateField')
1058
- expect(components2[2].name).toBe('multilineTextField')
1059
- })
1060
-
1061
- it('returns the fields for the page', () => {
1062
- const { fields: fields1 } = controller1.collection
1063
- const { fields: fields2 } = controller2.collection
1064
-
1065
- expect(fields1).toHaveLength(1)
1066
- expect(fields1[0].name).toBe('yesNoField')
1067
-
1068
- expect(fields2).toHaveLength(2)
1069
- expect(fields2[0].name).toBe('dateField')
1070
- expect(fields2[1].name).toBe('multilineTextField')
1071
- })
1072
-
1073
- it('returns the guidance for the page', () => {
1074
- const { guidance: guidance1 } = controller1.collection
1075
- const { guidance: guidance2 } = controller2.collection
1076
-
1077
- expect(guidance1).toHaveLength(0)
1078
- expect(guidance2).toHaveLength(1)
1079
- expect(guidance2[0].name).toBe('detailsField')
1080
- })
1081
- })
1082
-
1083
- describe('Component view models', () => {
1084
- let viewModel1: FormPageViewModel
1085
- let viewModel2: FormPageViewModel
1086
-
1087
- beforeEach(() => {
1088
- viewModel1 = controller1.getViewModel(
1089
- requestPage1,
1090
- model.getFormContext(requestPage1, { $$__referenceNumber: 'foobar' })
1091
- )
1092
-
1093
- viewModel2 = controller2.getViewModel(
1094
- requestPage2,
1095
- model.getFormContext(requestPage2, { $$__referenceNumber: 'foobar' })
1096
- )
1097
- })
1098
-
1099
- it('hides the page title for single form component pages', () => {
1100
- // Page 1 hides page title (single component)
1101
- expect(viewModel1).toHaveProperty('showTitle', false)
1102
- expect(viewModel1).toHaveProperty('pageTitle', 'Previous marriages')
1103
-
1104
- // Page 2 shows page title (multiple components)
1105
- expect(viewModel2).toHaveProperty('showTitle', true)
1106
- expect(viewModel2).toHaveProperty(
1107
- 'pageTitle',
1108
- 'When will you get married?'
1109
- )
1110
- })
1111
-
1112
- it('returns the component view models for the page', () => {
1113
- const { components: components1 } = viewModel1
1114
- const { components: components2 } = viewModel2
1115
-
1116
- expect(components1).toHaveLength(1)
1117
- expect(components2).toHaveLength(3)
1118
-
1119
- // Page 1, component 1, default label
1120
- expect(components1[0].model).toHaveProperty('label', {
1121
- text: 'Have you previously been married?'
1122
- })
1123
-
1124
- // Page 1, component 1, optional legend
1125
- expect(components1[0].model).toHaveProperty('fieldset', {
1126
- legend: {
1127
- text: 'Previous marriages',
1128
- classes: 'govuk-fieldset__legend--l',
1129
- isPageHeading: true
1130
- }
1131
- })
1132
-
1133
- // Page 2, component 1, content only
1134
- expect(components2[0].model).toEqual({
1135
- attributes: {},
1136
- summaryHtml: 'Find out more',
1137
- html: 'Some content goes here'
1138
- })
1139
-
1140
- // Page 2, component 2, default label
1141
- expect(components2[1].model).toHaveProperty('label', {
1142
- text: 'Date of marriage',
1143
- classes: 'govuk-label--m'
1144
- })
1145
-
1146
- // Page 2, component 2, optional legend
1147
- expect(components2[1].model).toHaveProperty('fieldset', {
1148
- legend: {
1149
- text: 'Date of marriage',
1150
- classes: 'govuk-fieldset__legend--m'
1151
- }
1152
- })
1153
-
1154
- // Page 2, component 3, default label
1155
- expect(components2[2].model).toHaveProperty('label', {
1156
- text: 'Remarks',
1157
- classes: 'govuk-label--m'
1158
- })
1159
-
1160
- // Page 2, component 3, optional legend
1161
- expect(components2[2].model).not.toHaveProperty('fieldset')
1162
- })
1163
- })
1164
-
1165
- describe('Condition evaluation context', () => {
1166
- it('filters state by journey pages', () => {
1167
- const { pages } = definitionConditionsComplex
1168
-
1169
- const model = new FormModel(definitionConditionsComplex, {
1170
- basePath: 'test'
1171
- })
1172
-
1173
- // Selected page appears after convergence and contains a conditional field
1174
- // This is the page we're theoretically browsing to
1175
- const controller = new QuestionPageController(model, pages[7])
1176
-
1177
- // The state below shows we said we had a UKPassport and entered details for an applicant
1178
- const state: FormSubmissionState = {
1179
- $$__referenceNumber: 'foobar',
1180
- ukPassport: true,
1181
- numberOfApplicants: 2,
1182
- applicantOneFirstName: 'Enrique',
1183
- applicantOneMiddleName: null,
1184
- applicantOneLastName: 'Chase',
1185
- applicantOneAddress__addressLine1: 'AddressLine1',
1186
- applicantOneAddress__addressLine2: 'AddressLine2',
1187
- applicantOneAddress__town: 'Town',
1188
- applicantOneAddress__postcode: 'Postcode',
1189
- applicantTwoFirstName: 'John',
1190
- applicantTwoMiddleName: null,
1191
- applicantTwoLastName: 'Doe',
1192
- applicantTwoAddress__addressLine1: 'AddressLine1',
1193
- applicantTwoAddress__addressLine2: 'AddressLine2',
1194
- applicantTwoAddress__town: 'Town',
1195
- applicantTwoAddress__postcode: 'Postcode'
1196
- }
1197
-
1198
- let request = buildFormContextRequest({
1199
- method: 'get',
1200
- url: new URL('http://example.com/test/applicant-one-address'),
1201
- path: '/test/applicant-one-address',
1202
- params: {
1203
- path: 'applicant-one-address',
1204
- slug: 'test'
1205
- },
1206
- query: {},
1207
- app: { model }
1208
- })
1209
-
1210
- // Calculate our context based on the page we're attempting to load and the above state we provide
1211
- let context = controller.model.getFormContext(request, state)
1212
- const { evaluationState: stateBefore } = context
1213
-
1214
- // Our context should know our first applicant
1215
- expect(stateBefore).toEqual({
1216
- numberOfApplicants: 2,
1217
- ukPassport: true,
1218
- applicantOneFirstName: 'Enrique',
1219
- applicantOneMiddleName: null,
1220
- applicantOneLastName: 'Chase',
1221
- applicantOneAddress: [
1222
- 'AddressLine1',
1223
- 'AddressLine2',
1224
- 'Town',
1225
- 'Postcode'
1226
- ],
1227
- applicantTwoAddress: null,
1228
- applicantTwoFirstName: null,
1229
- applicantTwoLastName: null,
1230
- applicantTwoMiddleName: null
1231
- })
1232
-
1233
- // Our context should know which pages are relevant
1234
- expect(context.paths).toEqual([
1235
- '/uk-passport',
1236
- '/how-many-people',
1237
- '/applicant-one-name',
1238
- '/applicant-one-address'
1239
- ])
1240
-
1241
- // Now mark that we don't have a UK Passport
1242
- state.ukPassport = false
1243
-
1244
- request = buildFormContextRequest({
1245
- method: 'get',
1246
- url: new URL('http://example.com/test/summary'),
1247
- path: '/test/summary',
1248
- params: {
1249
- path: 'summary',
1250
- slug: 'test'
1251
- },
1252
- query: {},
1253
- app: { model }
1254
- })
1255
-
1256
- // And recalculate our context
1257
- context = controller.model.getFormContext(request, state)
1258
- const { evaluationState: stateAfter } = context
1259
-
1260
- // Our context should no longer list pages about our applicant
1261
- expect(context.paths).toEqual([
1262
- '/uk-passport',
1263
- '/testconditions',
1264
- '/summary'
1265
- ])
1266
-
1267
- // Our evaluation context should have default values for irrelevant fields
1268
- expect(stateAfter).toEqual({
1269
- ukPassport: false,
1270
- numberOfApplicants: null,
1271
- applicantOneFirstName: null,
1272
- applicantOneMiddleName: null,
1273
- applicantOneLastName: null,
1274
- applicantOneAddress: null,
1275
- applicantTwoAddress: null,
1276
- applicantTwoFirstName: null,
1277
- applicantTwoLastName: null,
1278
- applicantTwoMiddleName: null
1279
- })
1280
- })
1281
-
1282
- it('combines state values for date fields', () => {
1283
- const { pages } = definitionConditionsDates
1284
-
1285
- const model = new FormModel(definitionConditionsDates, {
1286
- basePath: 'test'
1287
- })
1288
-
1289
- const controller = new QuestionPageController(model, pages[0])
1290
-
1291
- const request = buildFormContextRequest({
1292
- method: 'get',
1293
- url: new URL('http://example.com/test/page-one'),
1294
- path: '/test/page-one',
1295
- params: {
1296
- path: 'page-one',
1297
- slug: 'test'
1298
- },
1299
- query: {},
1300
- app: { model }
1301
- })
1302
-
1303
- const context = controller.model.getFormContext(request, {
1304
- $$__referenceNumber: 'foobar',
1305
- dateField__day: 5,
1306
- dateField__month: 1,
1307
- dateField__year: 2024
1308
- })
1309
-
1310
- // Ensure dates are transformed to yyyy-MM-dd format
1311
- expect(context.evaluationState).toHaveProperty('dateField', '2024-01-05')
1312
-
1313
- // Unlike relevant state which has the individual date parts
1314
- expect(context.relevantState).not.toHaveProperty('dateField')
1315
- expect(context.relevantState).toMatchObject({
1316
- dateField__day: 5,
1317
- dateField__month: 1,
1318
- dateField__year: 2024
1319
- })
1320
- })
1321
- })
1322
-
1323
- describe('Form validation', () => {
1324
- it('includes all field errors', () => {
1325
- const result1 = controller1.collection.validate()
1326
- const result2 = controller2.collection.validate()
1327
-
1328
- expect(result1.errors).toHaveLength(1)
1329
- expect(result2.errors).toHaveLength(4)
1330
- })
1331
- })
1332
-
1333
- describe('Form journey', () => {
1334
- let context: FormContext
1335
- let contextNo: FormContext
1336
- let contextYes: FormContext
1337
-
1338
- beforeEach(() => {
1339
- // Empty state
1340
- context = model.getFormContext(requestPage1, {
1341
- $$__referenceNumber: 'foobar'
1342
- })
1343
-
1344
- // Question 1: Selected 'No'
1345
- contextNo = model.getFormContext(requestPage1, {
1346
- $$__referenceNumber: 'foobar',
1347
- yesNoField: false
1348
- })
1349
-
1350
- // Question 1: Selected 'Yes'
1351
- contextYes = model.getFormContext(requestPage1, {
1352
- $$__referenceNumber: 'foobar',
1353
- yesNoField: true
1354
- })
1355
- })
1356
-
1357
- describe('Next', () => {
1358
- it('returns the next page path', () => {
1359
- expect(controller1.getNextPath(context)).toBe('/summary')
1360
- expect(controller1.getNextPath(contextNo)).toBe('/second-page')
1361
- expect(controller1.getNextPath(contextYes)).toBe('/summary')
1362
-
1363
- expect(controller2.getNextPath(context)).toBe('/summary')
1364
- expect(controller2.getNextPath(contextNo)).toBe('/summary')
1365
- expect(controller2.getNextPath(contextYes)).toBe('/summary')
1366
- })
1367
- })
1368
-
1369
- describe('Summary', () => {
1370
- it('returns the summary path', () => {
1371
- expect(controller1.getSummaryPath()).toBe('/summary')
1372
- expect(controller2.getSummaryPath()).toBe('/summary')
1373
- })
1374
- })
1375
- })
1376
-
1377
- describe('Route handlers', () => {
1378
- const response = {
1379
- code: jest.fn().mockImplementation(() => response)
1380
- }
1381
-
1382
- const h: FormResponseToolkit = {
1383
- redirect: jest.fn().mockReturnValue(response),
1384
- view: jest.fn(),
1385
- continue: Symbol('continue')
1386
- }
1387
-
1388
- it('returns default route options', () => {
1389
- expect(controller1.getRouteOptions).toMatchObject({
1390
- ext: {
1391
- onPostHandler: {
1392
- method: expect.any(Function)
1393
- }
1394
- }
1395
- })
1396
-
1397
- expect(controller1.postRouteOptions).toMatchObject({
1398
- ext: {
1399
- onPostHandler: {
1400
- method: expect.any(Function)
1401
- }
1402
- }
1403
- })
1404
- })
1405
-
1406
- it('supports GET route handler', async () => {
1407
- const state: FormState = { yesNoField: false }
1408
-
1409
- for (const controller of [controller1, controller2]) {
1410
- jest
1411
- .spyOn(controller, 'hasMissingNotificationEmail')
1412
- .mockResolvedValue(false)
1413
-
1414
- jest.spyOn(controller, 'getState').mockResolvedValue(state)
1415
- }
1416
-
1417
- expect(() => controller1.makeGetRouteHandler()).not.toThrow()
1418
- expect(() => controller1.makeGetRouteHandler()).toBeInstanceOf(Function)
1419
-
1420
- await controller1.makeGetRouteHandler()(
1421
- requestPage1,
1422
- model.getFormContext(requestPage1, { $$__referenceNumber: 'foobar' }),
1423
- h
1424
- )
1425
-
1426
- await controller2.makeGetRouteHandler()(
1427
- requestPage2,
1428
- model.getFormContext(requestPage2, { $$__referenceNumber: 'foobar' }),
1429
- h
1430
- )
1431
-
1432
- expect(h.view).toHaveBeenNthCalledWith(
1433
- 1,
1434
- controller1.viewName,
1435
- expect.objectContaining({
1436
- pageTitle: 'Previous marriages',
1437
- sectionTitle: undefined
1438
- })
1439
- )
1440
-
1441
- expect(h.view).toHaveBeenNthCalledWith(
1442
- 2,
1443
- controller2.viewName,
1444
- expect.objectContaining({
1445
- pageTitle: 'When will you get married?',
1446
- sectionTitle: 'Your marriage'
1447
- })
1448
- )
1449
- })
1450
- })
1451
-
1452
- describe('State', () => {
1453
- beforeEach(() => {
1454
- jest.spyOn(CacheService.prototype, 'getState')
1455
- jest.spyOn(CacheService.prototype, 'setState')
1456
-
1457
- // Preview URL '?force'
1458
- requestPage1.query.force = ''
1459
- })
1460
-
1461
- describe('getState', () => {
1462
- it('should skip get for preview URL direct access', async () => {
1463
- const state = await controller1.getState(requestPage1)
1464
-
1465
- expect(state).toEqual({})
1466
- expect(CacheService.prototype.getState).not.toHaveBeenCalled()
1467
- })
1468
- })
1469
-
1470
- describe('setState', () => {
1471
- it('should skip set for preview URL direct access', async () => {
1472
- const state: FormSubmissionState = { yesNoField: false }
1473
- const updated = await controller1.setState(requestPage1, state)
1474
-
1475
- expect(updated).toBe(state)
1476
- expect(CacheService.prototype.setState).not.toHaveBeenCalled()
1477
- })
1478
- })
1479
-
1480
- describe('mergeState', () => {
1481
- it('should skip merge for preview URL direct access', async () => {
1482
- const state: FormSubmissionState = {
1483
- yesNoField: false
1484
- }
1485
-
1486
- const update: FormSubmissionState = {
1487
- dateField__day: 5,
1488
- dateField__month: 1,
1489
- dateField__year: 2024
1490
- }
1491
-
1492
- const updated = await controller1.mergeState(
1493
- requestPage1,
1494
- state,
1495
- update
1496
- )
1497
-
1498
- expect(updated).toEqual({
1499
- yesNoField: false,
1500
- dateField__day: 5,
1501
- dateField__month: 1,
1502
- dateField__year: 2024
1503
- })
1504
-
1505
- expect(CacheService.prototype.setState).not.toHaveBeenCalled()
1506
- })
1507
- })
1508
- })
1509
- })
1510
-
1511
- describe('Save and Exit functionality', () => {
1512
- let model: FormModel
1513
- let controller1: QuestionPageController
1514
- let requestPage1: FormRequest
1515
-
1516
- beforeEach(() => {
1517
- const { pages } = definitionConditionsBasic
1518
-
1519
- model = new FormModel(definitionConditionsBasic, {
1520
- basePath: 'test'
1521
- })
1522
-
1523
- controller1 = new QuestionPageController(model, pages[0])
1524
-
1525
- requestPage1 = buildFormRequest({
1526
- method: 'get',
1527
- url: new URL('http://example.com/test/first-page'),
1528
- path: '/test/first-page',
1529
- params: {
1530
- path: 'first-page',
1531
- slug: 'test'
1532
- },
1533
- query: {},
1534
- app: { model }
1535
- } as FormRequest)
1536
- })
1537
-
1538
- const response = {
1539
- code: jest.fn().mockImplementation(() => response)
1540
- }
1541
-
1542
- const h: FormResponseToolkit = {
1543
- redirect: jest.fn().mockReturnValue(response),
1544
- view: jest.fn(),
1545
- continue: Symbol('continue')
1546
- }
1547
-
1548
- beforeEach(() => {
1549
- jest.clearAllMocks()
1550
- jest.spyOn(CacheService.prototype, 'setState')
1551
- })
1552
-
1553
- describe('shouldShowSaveAndExit', () => {
1554
- it('should return true by default', () => {
1555
- expect(controller1.shouldShowSaveAndExit(serverWithSaveAndExit)).toBe(
1556
- true
1557
- )
1558
- })
1559
- })
1560
-
1561
- describe('handleSaveAndExit', () => {
1562
- it('should invoke saveAndExit plugin option', () => {
1563
- const saveAndExitMock = jest.fn(() => ({}))
1564
- const state: FormSubmissionState = {
1565
- $$__referenceNumber: 'foobar',
1566
- yesNoField: true
1567
- }
1568
- const request = {
1569
- ...requestPage1,
1570
- server: {
1571
- plugins: {
1572
- 'forms-engine-plugin': {
1573
- saveAndExit: saveAndExitMock,
1574
- cacheService: {
1575
- clearState: jest.fn()
1576
- } as unknown as CacheService
1577
- }
1578
- }
1579
- },
1580
- method: 'post',
1581
- payload: { yesNoField: true, action: 'save-and-exit' }
1582
- } as unknown as FormRequestPayload
1583
-
1584
- const context = model.getFormContext(request, state)
1585
-
1586
- controller1.handleSaveAndExit(request, context, h)
1587
-
1588
- expect(saveAndExitMock).toHaveBeenCalledWith(request, h, context)
1589
- })
1590
-
1591
- it('should throw if saveAndExit option not provided', () => {
1592
- const saveAndExitMock = jest.fn()
1593
- const state: FormSubmissionState = {
1594
- $$__referenceNumber: 'foobar',
1595
- yesNoField: true
1596
- }
1597
- const request = {
1598
- ...requestPage1,
1599
- server: {
1600
- plugins: {
1601
- 'forms-engine-plugin': {
1602
- // No function
1603
- saveAndExit: undefined
1604
- }
1605
- }
1606
- },
1607
- method: 'post',
1608
- payload: { yesNoField: true, action: 'save-and-exit' }
1609
- } as unknown as FormRequestPayload
1610
-
1611
- const context = model.getFormContext(request, state)
1612
-
1613
- expect(() => controller1.handleSaveAndExit(request, context, h)).toThrow(
1614
- 'Server misconfigured for save and exit'
1615
- )
1616
-
1617
- expect(saveAndExitMock).not.toHaveBeenCalled()
1618
- })
1619
- })
1620
-
1621
- describe('POST handler with save-and-exit action', () => {
1622
- it('should handle FormAction.SaveAndExit', async () => {
1623
- const state: FormSubmissionState = {
1624
- $$__referenceNumber: 'foobar',
1625
- yesNoField: true
1626
- }
1627
- const request = {
1628
- ...requestPage1,
1629
- method: 'post',
1630
- payload: { yesNoField: true, action: 'save-and-exit' }
1631
- } as unknown as FormRequestPayload
1632
-
1633
- const context = model.getFormContext(request, state)
1634
-
1635
- jest.spyOn(controller1, 'getState').mockResolvedValue({})
1636
- jest
1637
- .spyOn(controller1, 'handleSaveAndExit')
1638
- .mockReturnValue(h.redirect('/custom-save-and-exit'))
1639
- jest.spyOn(controller1, 'setState').mockResolvedValue(state)
1640
-
1641
- const postHandler = controller1.makePostRouteHandler()
1642
- await postHandler(request, context, h)
1643
-
1644
- expect(controller1.handleSaveAndExit).toHaveBeenCalledWith(
1645
- request,
1646
- context,
1647
- h
1648
- )
1649
- })
1650
-
1651
- it('should not call handleSaveAndExit for continue action', async () => {
1652
- const state: FormSubmissionState = {
1653
- $$__referenceNumber: 'foobar',
1654
- yesNoField: true
1655
- }
1656
- const request = {
1657
- ...requestPage1,
1658
- method: 'post',
1659
- payload: { yesNoField: true, action: 'continue' }
1660
- } as unknown as FormRequestPayload
1661
-
1662
- const context = model.getFormContext(request, state)
1663
-
1664
- jest.spyOn(controller1, 'getState').mockResolvedValue({})
1665
- jest
1666
- .spyOn(controller1, 'handleSaveAndExit')
1667
- .mockReturnValue(h.redirect('/custom-save-and-exit'))
1668
- jest.spyOn(controller1, 'setState').mockResolvedValue(state)
1669
-
1670
- const mockResponse = {
1671
- code: jest.fn().mockReturnValue({ redirect: jest.fn() })
1672
- }
1673
-
1674
- const mockH = {
1675
- redirect: jest.fn().mockReturnValue(mockResponse),
1676
- view: jest.fn(),
1677
- continue: Symbol('continue')
1678
- }
1679
-
1680
- const postHandler = controller1.makePostRouteHandler()
1681
- await postHandler(request, context, mockH)
1682
-
1683
- expect(controller1.handleSaveAndExit).not.toHaveBeenCalled()
1684
- })
1685
- })
1686
- })