@defra/forms-engine-plugin 3.0.0 → 3.0.2

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 (30) hide show
  1. package/.server/server/plugins/engine/models/FormModel.d.ts +2 -0
  2. package/.server/server/plugins/engine/models/FormModel.js +4 -1
  3. package/.server/server/plugins/engine/models/FormModel.js.map +1 -1
  4. package/.server/server/plugins/engine/outputFormatters/adapter/v1.d.ts +4 -0
  5. package/.server/server/plugins/engine/outputFormatters/adapter/v1.js +25 -0
  6. package/.server/server/plugins/engine/outputFormatters/adapter/v1.js.map +1 -1
  7. package/.server/server/plugins/engine/outputFormatters/machine/v2.js +7 -6
  8. package/.server/server/plugins/engine/outputFormatters/machine/v2.js.map +1 -1
  9. package/.server/server/plugins/engine/pageControllers/RepeatPageController.d.ts +1 -0
  10. package/.server/server/plugins/engine/pageControllers/RepeatPageController.js +8 -1
  11. package/.server/server/plugins/engine/pageControllers/RepeatPageController.js.map +1 -1
  12. package/.server/server/plugins/engine/routes/index.js +3 -1
  13. package/.server/server/plugins/engine/routes/index.js.map +1 -1
  14. package/.server/server/plugins/engine/types/schema.js +3 -2
  15. package/.server/server/plugins/engine/types/schema.js.map +1 -1
  16. package/.server/server/plugins/engine/types.d.ts +4 -1
  17. package/.server/server/plugins/engine/types.js.map +1 -1
  18. package/.server/server/plugins/engine/views/repeat-list-summary.html +15 -5
  19. package/package.json +2 -2
  20. package/src/server/plugins/engine/models/FormModel.test.ts +64 -0
  21. package/src/server/plugins/engine/models/FormModel.ts +5 -2
  22. package/src/server/plugins/engine/outputFormatters/adapter/v1.test.ts +446 -13
  23. package/src/server/plugins/engine/outputFormatters/adapter/v1.ts +37 -0
  24. package/src/server/plugins/engine/outputFormatters/machine/v2.ts +8 -6
  25. package/src/server/plugins/engine/pageControllers/RepeatPageController.ts +8 -1
  26. package/src/server/plugins/engine/routes/index.ts +3 -1
  27. package/src/server/plugins/engine/types/schema.test.ts +40 -0
  28. package/src/server/plugins/engine/types/schema.ts +3 -1
  29. package/src/server/plugins/engine/types.ts +4 -0
  30. package/src/server/plugins/engine/views/repeat-list-summary.html +15 -5
@@ -156,5 +156,45 @@ describe('Schema validation', () => {
156
156
  formAdapterSubmissionMessagePayloadSchema.validate(payloadWithoutData)
157
157
  expect(error).toBeDefined()
158
158
  })
159
+
160
+ it('should validate payload with versionMetadata', () => {
161
+ const payloadWithVersion = {
162
+ ...validPayload,
163
+ meta: {
164
+ ...validPayload.meta,
165
+ versionMetadata: {
166
+ versionNumber: 19,
167
+ createdAt: new Date('2025-09-08T09:28:15.576Z')
168
+ }
169
+ }
170
+ }
171
+ const { error } =
172
+ formAdapterSubmissionMessagePayloadSchema.validate(payloadWithVersion)
173
+ expect(error).toBeUndefined()
174
+ })
175
+
176
+ it('should validate payload without versionMetadata', () => {
177
+ const { error } =
178
+ formAdapterSubmissionMessagePayloadSchema.validate(validPayload)
179
+ expect(error).toBeUndefined()
180
+ })
181
+
182
+ it('should reject invalid versionMetadata', () => {
183
+ const payloadWithInvalidVersion = {
184
+ ...validPayload,
185
+ meta: {
186
+ ...validPayload.meta,
187
+ versionMetadata: {
188
+ versionNumber: 'not-a-number', // Invalid - should be number
189
+ createdAt: new Date('2025-09-08T09:28:15.576Z')
190
+ }
191
+ }
192
+ }
193
+ const { error } = formAdapterSubmissionMessagePayloadSchema.validate(
194
+ payloadWithInvalidVersion
195
+ )
196
+ expect(error).toBeDefined()
197
+ expect(error?.message).toContain('must be a number')
198
+ })
159
199
  })
160
200
  })
@@ -1,5 +1,6 @@
1
1
  import {
2
2
  FormStatus,
3
+ formVersionMetadataSchema,
3
4
  idSchema,
4
5
  notificationEmailAddressSchema,
5
6
  slugSchema,
@@ -29,7 +30,8 @@ export const formAdapterSubmissionMessageMetaSchema =
29
30
  .valid(...Object.values(FormStatus))
30
31
  .required(),
31
32
  isPreview: Joi.boolean().required(),
32
- notificationEmail: notificationEmailAddressSchema.required()
33
+ notificationEmail: notificationEmailAddressSchema.required(),
34
+ versionMetadata: formVersionMetadataSchema.optional()
33
35
  })
34
36
 
35
37
  export const formAdapterSubmissionMessageDataSchema =
@@ -3,6 +3,7 @@ import {
3
3
  type Event,
4
4
  type FormDefinition,
5
5
  type FormMetadata,
6
+ type FormVersionMetadata,
6
7
  type Item,
7
8
  type List,
8
9
  type Page
@@ -179,6 +180,7 @@ export interface FormContext {
179
180
  pageMap: Map<string, PageControllerClass>
180
181
  componentMap: Map<string, Component>
181
182
  referenceNumber: string
183
+ submittedVersionNumber?: number
182
184
  }
183
185
 
184
186
  export type FormContextRequest = (
@@ -327,6 +329,7 @@ export interface RepeaterSummaryPageViewModel extends PageViewModelBase {
327
329
  errors?: FormSubmissionError[]
328
330
  checkAnswers: CheckAnswers[]
329
331
  repeatTitle: string
332
+ allowSaveAndExit: boolean
330
333
  }
331
334
 
332
335
  export interface FeaturedFormPageViewModel extends FormPageViewModel {
@@ -405,6 +408,7 @@ export interface FormAdapterSubmissionMessageMeta {
405
408
  status: FormStatus
406
409
  isPreview: boolean
407
410
  notificationEmail: string
411
+ versionMetadata?: FormVersionMetadata
408
412
  }
409
413
 
410
414
  export type FormAdapterSubmissionMessageMetaSerialised = Omit<
@@ -24,10 +24,10 @@
24
24
  {{ govukSummaryList(section.summaryList) }}
25
25
  {% endfor %}
26
26
 
27
- <div class="govuk-button-group">
28
- <form method="post" novalidate>
29
- <input type="hidden" name="crumb" value="{{ crumb }}">
27
+ <form method="post" novalidate>
28
+ <input type="hidden" name="crumb" value="{{ crumb }}">
30
29
 
30
+ <div class="govuk-button-group">
31
31
  {{ govukButton({
32
32
  text: "Continue",
33
33
  name: "action",
@@ -42,8 +42,18 @@
42
42
  classes: "govuk-button--secondary",
43
43
  preventDoubleClick: true
44
44
  }) }}
45
- </form>
46
- </div>
45
+
46
+ {% if allowSaveAndExit %}
47
+ {{ govukButton({
48
+ text: "Save and exit",
49
+ classes: "govuk-button--secondary",
50
+ name: "action",
51
+ value: "save-and-exit",
52
+ preventDoubleClick: true
53
+ }) }}
54
+ {% endif %}
55
+ </div>
56
+ </form>
47
57
  </div>
48
58
  </div>
49
59