@defra/forms-engine-plugin 3.0.0 → 3.0.1

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 (24) 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/routes/index.js +3 -1
  10. package/.server/server/plugins/engine/routes/index.js.map +1 -1
  11. package/.server/server/plugins/engine/types/schema.js +3 -2
  12. package/.server/server/plugins/engine/types/schema.js.map +1 -1
  13. package/.server/server/plugins/engine/types.d.ts +3 -1
  14. package/.server/server/plugins/engine/types.js.map +1 -1
  15. package/package.json +2 -2
  16. package/src/server/plugins/engine/models/FormModel.test.ts +64 -0
  17. package/src/server/plugins/engine/models/FormModel.ts +5 -2
  18. package/src/server/plugins/engine/outputFormatters/adapter/v1.test.ts +446 -13
  19. package/src/server/plugins/engine/outputFormatters/adapter/v1.ts +37 -0
  20. package/src/server/plugins/engine/outputFormatters/machine/v2.ts +8 -6
  21. package/src/server/plugins/engine/routes/index.ts +3 -1
  22. package/src/server/plugins/engine/types/schema.test.ts +40 -0
  23. package/src/server/plugins/engine/types/schema.ts +3 -1
  24. package/src/server/plugins/engine/types.ts +3 -0
@@ -141,6 +141,21 @@ describe('FormModel', () => {
141
141
  expect(model.schemaVersion).toBe(SchemaVersion.V1)
142
142
  })
143
143
 
144
+ it('sets versionNumber from options', () => {
145
+ const model = new FormModel(definition, {
146
+ basePath: 'test',
147
+ versionNumber: 42
148
+ })
149
+
150
+ expect(model.versionNumber).toBe(42)
151
+ })
152
+
153
+ it('sets versionNumber to undefined when not provided', () => {
154
+ const model = new FormModel(definition, { basePath: 'test' })
155
+
156
+ expect(model.versionNumber).toBeUndefined()
157
+ })
158
+
144
159
  it.each([
145
160
  {
146
161
  input: undefined,
@@ -329,6 +344,55 @@ describe('FormModel', () => {
329
344
  )
330
345
  })
331
346
 
347
+ it('includes submittedVersionNumber in context when versionNumber is set', () => {
348
+ const formModel = new FormModel(fieldsRequiredDefinition, {
349
+ basePath: '/components',
350
+ versionNumber: 123
351
+ })
352
+
353
+ const state = {
354
+ $$__referenceNumber: 'foobar'
355
+ }
356
+ const pageUrl = new URL('http://example.com/components/fields-required')
357
+
358
+ const request: FormContextRequest = buildFormContextRequest({
359
+ method: 'get',
360
+ query: {},
361
+ path: pageUrl.pathname,
362
+ params: { path: 'components', slug: 'fields-required' },
363
+ url: pageUrl,
364
+ app: { model: formModel }
365
+ })
366
+
367
+ const context = formModel.getFormContext(request, state)
368
+
369
+ expect(context.submittedVersionNumber).toBe(123)
370
+ })
371
+
372
+ it('sets submittedVersionNumber to undefined when versionNumber is not set', () => {
373
+ const formModel = new FormModel(fieldsRequiredDefinition, {
374
+ basePath: '/components'
375
+ })
376
+
377
+ const state = {
378
+ $$__referenceNumber: 'foobar'
379
+ }
380
+ const pageUrl = new URL('http://example.com/components/fields-required')
381
+
382
+ const request: FormContextRequest = buildFormContextRequest({
383
+ method: 'get',
384
+ query: {},
385
+ path: pageUrl.pathname,
386
+ params: { path: 'components', slug: 'fields-required' },
387
+ url: pageUrl,
388
+ app: { model: formModel }
389
+ })
390
+
391
+ const context = formModel.getFormContext(request, state)
392
+
393
+ expect(context.submittedVersionNumber).toBeUndefined()
394
+ })
395
+
332
396
  it('redirects to the page if the list field (radio) is invalidated due to list item conditions', () => {
333
397
  const formModel = new FormModel(conditionsListDefinition, {
334
398
  basePath: '/conditional-list-items'
@@ -76,6 +76,7 @@ export class FormModel {
76
76
  name: string
77
77
  values: FormDefinition
78
78
  basePath: string
79
+ versionNumber?: number
79
80
  conditions: Partial<Record<string, ExecutableCondition>>
80
81
  pages: PageControllerClass[]
81
82
  services: Services
@@ -94,7 +95,7 @@ export class FormModel {
94
95
 
95
96
  constructor(
96
97
  def: typeof this.def,
97
- options: { basePath: string },
98
+ options: { basePath: string; versionNumber?: number },
98
99
  services: Services = defaultServices,
99
100
  controllers?: Record<string, typeof PageController>
100
101
  ) {
@@ -148,6 +149,7 @@ export class FormModel {
148
149
  this.name = def.name ?? ''
149
150
  this.values = result.value
150
151
  this.basePath = options.basePath
152
+ this.versionNumber = options.versionNumber
151
153
  this.conditions = {}
152
154
  this.services = services
153
155
  this.controllers = controllers
@@ -344,7 +346,8 @@ export class FormModel {
344
346
  componentDefMap: this.componentDefMap,
345
347
  pageMap: this.pageMap,
346
348
  componentMap: this.componentMap,
347
- referenceNumber: getReferenceNumber(state)
349
+ referenceNumber: getReferenceNumber(state),
350
+ submittedVersionNumber: this.versionNumber
348
351
  }
349
352
 
350
353
  // Validate current page
@@ -11,7 +11,10 @@ import {
11
11
  type DetailItemField,
12
12
  type DetailItemRepeat
13
13
  } from '~/src/server/plugins/engine/models/types.js'
14
- import { format } from '~/src/server/plugins/engine/outputFormatters/adapter/v1.js'
14
+ import {
15
+ format,
16
+ getVersionMetadata
17
+ } from '~/src/server/plugins/engine/outputFormatters/adapter/v1.js'
15
18
  import { buildFormContextRequest } from '~/src/server/plugins/engine/pageControllers/__stubs__/request.js'
16
19
  import { FormAdapterSubmissionSchemaVersion } from '~/src/server/plugins/engine/types/index.js'
17
20
  import {
@@ -207,7 +210,7 @@ describe('Adapter v1 formatter', () => {
207
210
  })
208
211
 
209
212
  it('should return the adapter v1 output with complete formMetadata', () => {
210
- const formMetadata: FormMetadata = {
213
+ const formMetadata: Partial<FormMetadata> = {
211
214
  id: 'form-123',
212
215
  slug: 'test-form',
213
216
  title: 'Test Form',
@@ -225,7 +228,7 @@ describe('Adapter v1 formatter', () => {
225
228
  model,
226
229
  submitResponse,
227
230
  formStatus,
228
- formMetadata
231
+ formMetadata as FormMetadata
229
232
  )
230
233
  const parsedBody = JSON.parse(body) as FormAdapterSubmissionMessagePayload
231
234
 
@@ -284,7 +287,7 @@ describe('Adapter v1 formatter', () => {
284
287
  })
285
288
 
286
289
  it('should handle preview form status correctly', () => {
287
- const formMetadata: FormMetadata = {
290
+ const formMetadata: Partial<FormMetadata> = {
288
291
  id: 'form-123',
289
292
  slug: 'test-form',
290
293
  title: 'Test Form',
@@ -302,7 +305,7 @@ describe('Adapter v1 formatter', () => {
302
305
  model,
303
306
  submitResponse,
304
307
  formStatus,
305
- formMetadata
308
+ formMetadata as FormMetadata
306
309
  )
307
310
  const parsedBody = JSON.parse(body) as FormAdapterSubmissionMessagePayload
308
311
 
@@ -327,7 +330,7 @@ describe('Adapter v1 formatter', () => {
327
330
  })
328
331
 
329
332
  it('should handle partial formMetadata', () => {
330
- const formMetadata: FormMetadata = {
333
+ const formMetadata: Partial<FormMetadata> = {
331
334
  id: 'form-456',
332
335
  slug: 'partial-form',
333
336
  title: 'Partial Form'
@@ -344,7 +347,7 @@ describe('Adapter v1 formatter', () => {
344
347
  model,
345
348
  submitResponse,
346
349
  formStatus,
347
- formMetadata
350
+ formMetadata as FormMetadata
348
351
  )
349
352
  const parsedBody = JSON.parse(body) as FormAdapterSubmissionMessagePayload
350
353
 
@@ -452,7 +455,7 @@ describe('Adapter v1 formatter', () => {
452
455
  })
453
456
 
454
457
  it('should handle formMetadata with only id', () => {
455
- const formMetadata: FormMetadata = {
458
+ const formMetadata: Partial<FormMetadata> = {
456
459
  id: 'only-id-form'
457
460
  } as FormMetadata
458
461
 
@@ -467,7 +470,7 @@ describe('Adapter v1 formatter', () => {
467
470
  model,
468
471
  submitResponse,
469
472
  formStatus,
470
- formMetadata
473
+ formMetadata as FormMetadata
471
474
  )
472
475
  const parsedBody = JSON.parse(body) as FormAdapterSubmissionMessagePayload
473
476
 
@@ -477,7 +480,7 @@ describe('Adapter v1 formatter', () => {
477
480
  })
478
481
 
479
482
  it('should handle formMetadata with only slug', () => {
480
- const formMetadata: FormMetadata = {
483
+ const formMetadata: Partial<FormMetadata> = {
481
484
  slug: 'only-slug-form'
482
485
  } as FormMetadata
483
486
 
@@ -492,7 +495,7 @@ describe('Adapter v1 formatter', () => {
492
495
  model,
493
496
  submitResponse,
494
497
  formStatus,
495
- formMetadata
498
+ formMetadata as FormMetadata
496
499
  )
497
500
  const parsedBody = JSON.parse(body) as FormAdapterSubmissionMessagePayload
498
501
 
@@ -502,7 +505,7 @@ describe('Adapter v1 formatter', () => {
502
505
  })
503
506
 
504
507
  it('should handle formMetadata with only notificationEmail', () => {
505
- const formMetadata: FormMetadata = {
508
+ const formMetadata: Partial<FormMetadata> = {
506
509
  notificationEmail: 'only-email@example.com'
507
510
  } as FormMetadata
508
511
 
@@ -517,7 +520,7 @@ describe('Adapter v1 formatter', () => {
517
520
  model,
518
521
  submitResponse,
519
522
  formStatus,
520
- formMetadata
523
+ formMetadata as FormMetadata
521
524
  )
522
525
  const parsedBody = JSON.parse(body) as FormAdapterSubmissionMessagePayload
523
526
 
@@ -693,4 +696,434 @@ describe('Adapter v1 formatter', () => {
693
696
  }
694
697
  })
695
698
  })
699
+
700
+ it('should handle missing versionMetadata gracefully', () => {
701
+ const formMetadata: Partial<FormMetadata> = {
702
+ id: 'form-123',
703
+ slug: 'test-form',
704
+ title: 'Test Form',
705
+ notificationEmail: 'test@example.com'
706
+ } as FormMetadata
707
+
708
+ const formStatus = {
709
+ isPreview: false,
710
+ state: FormStatus.Live
711
+ }
712
+
713
+ const body = format(
714
+ context,
715
+ items,
716
+ model,
717
+ submitResponse,
718
+ formStatus,
719
+ formMetadata as FormMetadata
720
+ )
721
+ const parsedBody = JSON.parse(body) as FormAdapterSubmissionMessagePayload
722
+
723
+ expect(parsedBody.meta.versionMetadata).toBeUndefined()
724
+ })
725
+
726
+ describe('version metadata handling', () => {
727
+ it('should include versionMetadata when context has submittedVersionNumber and formMetadata has versions', () => {
728
+ const formMetadata: Partial<FormMetadata> = {
729
+ id: 'form-123',
730
+ slug: 'test-form',
731
+ title: 'Test Form',
732
+ notificationEmail: 'test@example.com',
733
+ versions: [
734
+ {
735
+ versionNumber: 1,
736
+ createdAt: new Date('2024-01-01T00:00:00.000Z')
737
+ },
738
+ {
739
+ versionNumber: 2,
740
+ createdAt: new Date('2024-01-15T00:00:00.000Z')
741
+ }
742
+ ]
743
+ }
744
+
745
+ const modelWithVersion = new FormModel(definition, {
746
+ basePath: 'test',
747
+ versionNumber: 2
748
+ })
749
+
750
+ const contextWithVersion = modelWithVersion.getFormContext(request, state)
751
+
752
+ const formStatus = {
753
+ isPreview: false,
754
+ state: FormStatus.Live
755
+ }
756
+
757
+ const body = format(
758
+ contextWithVersion,
759
+ items,
760
+ modelWithVersion,
761
+ submitResponse,
762
+ formStatus,
763
+ formMetadata as FormMetadata
764
+ )
765
+ const parsedBody = JSON.parse(body) as FormAdapterSubmissionMessagePayload
766
+
767
+ expect(parsedBody.meta.versionMetadata).toEqual({
768
+ versionNumber: 2,
769
+ createdAt: '2024-01-15T00:00:00.000Z'
770
+ })
771
+ })
772
+
773
+ it('should use first version as fallback when submittedVersionNumber is undefined', () => {
774
+ const formMetadata: Partial<FormMetadata> = {
775
+ id: 'form-123',
776
+ slug: 'test-form',
777
+ title: 'Test Form',
778
+ notificationEmail: 'test@example.com',
779
+ versions: [
780
+ {
781
+ versionNumber: 1,
782
+ createdAt: new Date('2024-01-01T00:00:00.000Z')
783
+ },
784
+ {
785
+ versionNumber: 2,
786
+ createdAt: new Date('2024-01-15T00:00:00.000Z')
787
+ }
788
+ ]
789
+ }
790
+
791
+ const formStatus = {
792
+ isPreview: false,
793
+ state: FormStatus.Live
794
+ }
795
+
796
+ const body = format(
797
+ context,
798
+ items,
799
+ model,
800
+ submitResponse,
801
+ formStatus,
802
+ formMetadata as FormMetadata
803
+ )
804
+ const parsedBody = JSON.parse(body) as FormAdapterSubmissionMessagePayload
805
+
806
+ expect(parsedBody.meta.versionMetadata).toEqual({
807
+ versionNumber: 1,
808
+ createdAt: '2024-01-01T00:00:00.000Z'
809
+ })
810
+ })
811
+
812
+ it('should not include versionMetadata when submittedVersionNumber is undefined and no versions exist', () => {
813
+ const formMetadata: Partial<FormMetadata> = {
814
+ id: 'form-123',
815
+ slug: 'test-form',
816
+ title: 'Test Form',
817
+ notificationEmail: 'test@example.com'
818
+ }
819
+
820
+ const formStatus = {
821
+ isPreview: false,
822
+ state: FormStatus.Live
823
+ }
824
+
825
+ const body = format(
826
+ context,
827
+ items,
828
+ model,
829
+ submitResponse,
830
+ formStatus,
831
+ formMetadata as FormMetadata
832
+ )
833
+ const parsedBody = JSON.parse(body) as FormAdapterSubmissionMessagePayload
834
+
835
+ expect(parsedBody.meta.versionMetadata).toBeUndefined()
836
+ })
837
+
838
+ it('should not include versionMetadata when submittedVersionNumber is undefined and versions array is empty', () => {
839
+ const formMetadata: Partial<FormMetadata> = {
840
+ id: 'form-123',
841
+ slug: 'test-form',
842
+ title: 'Test Form',
843
+ notificationEmail: 'test@example.com',
844
+ versions: []
845
+ }
846
+
847
+ const formStatus = {
848
+ isPreview: false,
849
+ state: FormStatus.Live
850
+ }
851
+
852
+ const body = format(
853
+ context,
854
+ items,
855
+ model,
856
+ submitResponse,
857
+ formStatus,
858
+ formMetadata as FormMetadata
859
+ )
860
+ const parsedBody = JSON.parse(body) as FormAdapterSubmissionMessagePayload
861
+
862
+ expect(parsedBody.meta.versionMetadata).toBeUndefined()
863
+ })
864
+
865
+ it('should not include versionMetadata when submittedVersionNumber does not match any version', () => {
866
+ const formMetadata: Partial<FormMetadata> = {
867
+ id: 'form-123',
868
+ slug: 'test-form',
869
+ title: 'Test Form',
870
+ notificationEmail: 'test@example.com',
871
+ versions: [
872
+ {
873
+ versionNumber: 1,
874
+ createdAt: new Date('2024-01-01T00:00:00.000Z')
875
+ },
876
+ {
877
+ versionNumber: 2,
878
+ createdAt: new Date('2024-01-15T00:00:00.000Z')
879
+ }
880
+ ]
881
+ }
882
+
883
+ const modelWithVersion = new FormModel(definition, {
884
+ basePath: 'test',
885
+ versionNumber: 99 // Non-existent version
886
+ })
887
+
888
+ const contextWithVersion = modelWithVersion.getFormContext(request, state)
889
+
890
+ const formStatus = {
891
+ isPreview: false,
892
+ state: FormStatus.Live
893
+ }
894
+
895
+ const body = format(
896
+ contextWithVersion,
897
+ items,
898
+ modelWithVersion,
899
+ submitResponse,
900
+ formStatus,
901
+ formMetadata as FormMetadata
902
+ )
903
+ const parsedBody = JSON.parse(body) as FormAdapterSubmissionMessagePayload
904
+
905
+ // Should fall back to first version since submittedVersionNumber doesn't match
906
+ expect(parsedBody.meta.versionMetadata).toEqual({
907
+ versionNumber: 1,
908
+ createdAt: '2024-01-01T00:00:00.000Z'
909
+ })
910
+ })
911
+
912
+ it('should use first version as fallback when submittedVersionNumber does not match any version', () => {
913
+ const formMetadata: Partial<FormMetadata> = {
914
+ id: 'form-123',
915
+ slug: 'test-form',
916
+ title: 'Test Form',
917
+ notificationEmail: 'test@example.com',
918
+ versions: [
919
+ {
920
+ versionNumber: 1,
921
+ createdAt: new Date('2024-01-01T00:00:00.000Z')
922
+ },
923
+ {
924
+ versionNumber: 2,
925
+ createdAt: new Date('2024-01-15T00:00:00.000Z')
926
+ }
927
+ ]
928
+ }
929
+
930
+ const modelWithVersion = new FormModel(definition, {
931
+ basePath: 'test',
932
+ versionNumber: 99 // Non-existent version
933
+ })
934
+
935
+ const contextWithVersion = modelWithVersion.getFormContext(request, state)
936
+
937
+ const formStatus = {
938
+ isPreview: false,
939
+ state: FormStatus.Live
940
+ }
941
+
942
+ const body = format(
943
+ contextWithVersion,
944
+ items,
945
+ modelWithVersion,
946
+ submitResponse,
947
+ formStatus,
948
+ formMetadata as FormMetadata
949
+ )
950
+ const parsedBody = JSON.parse(body) as FormAdapterSubmissionMessagePayload
951
+
952
+ // Should fall back to first version since submittedVersionNumber doesn't match
953
+ expect(parsedBody.meta.versionMetadata).toEqual({
954
+ versionNumber: 1,
955
+ createdAt: '2024-01-01T00:00:00.000Z'
956
+ })
957
+ })
958
+
959
+ it('should handle single version in versions array', () => {
960
+ const formMetadata: Partial<FormMetadata> = {
961
+ id: 'form-123',
962
+ slug: 'test-form',
963
+ title: 'Test Form',
964
+ notificationEmail: 'test@example.com',
965
+ versions: [
966
+ {
967
+ versionNumber: 5,
968
+ createdAt: new Date('2024-02-01T00:00:00.000Z')
969
+ }
970
+ ]
971
+ }
972
+
973
+ const formStatus = {
974
+ isPreview: false,
975
+ state: FormStatus.Live
976
+ }
977
+
978
+ const body = format(
979
+ context,
980
+ items,
981
+ model,
982
+ submitResponse,
983
+ formStatus,
984
+ formMetadata as FormMetadata
985
+ )
986
+ const parsedBody = JSON.parse(body) as FormAdapterSubmissionMessagePayload
987
+
988
+ expect(parsedBody.meta.versionMetadata).toEqual({
989
+ versionNumber: 5,
990
+ createdAt: '2024-02-01T00:00:00.000Z'
991
+ })
992
+ })
993
+ })
994
+
995
+ describe('getVersionMetadata', () => {
996
+ const mockFormMetadata: Partial<FormMetadata> = {
997
+ id: 'form-123',
998
+ slug: 'test-form',
999
+ title: 'Test Form',
1000
+ notificationEmail: 'test@example.com',
1001
+ versions: [
1002
+ {
1003
+ versionNumber: 1,
1004
+ createdAt: new Date('2024-01-01T00:00:00.000Z')
1005
+ },
1006
+ {
1007
+ versionNumber: 2,
1008
+ createdAt: new Date('2024-01-02T00:00:00.000Z')
1009
+ },
1010
+ {
1011
+ versionNumber: 3,
1012
+ createdAt: new Date('2024-01-03T00:00:00.000Z')
1013
+ }
1014
+ ]
1015
+ }
1016
+
1017
+ it('should return undefined when no form metadata provided', () => {
1018
+ const result = getVersionMetadata(1, undefined)
1019
+ expect(result).toBeUndefined()
1020
+ })
1021
+
1022
+ it('should return undefined when form metadata has no versions', () => {
1023
+ const formMetadataWithoutVersions: Partial<FormMetadata> = {
1024
+ ...mockFormMetadata,
1025
+ versions: undefined
1026
+ }
1027
+
1028
+ const result = getVersionMetadata(
1029
+ 1,
1030
+ formMetadataWithoutVersions as FormMetadata
1031
+ )
1032
+ expect(result).toBeUndefined()
1033
+ })
1034
+
1035
+ it('should return undefined when versions array is empty', () => {
1036
+ const formMetadataWithEmptyVersions: Partial<FormMetadata> = {
1037
+ ...mockFormMetadata,
1038
+ versions: []
1039
+ }
1040
+
1041
+ const result = getVersionMetadata(
1042
+ 1,
1043
+ formMetadataWithEmptyVersions as FormMetadata
1044
+ )
1045
+ expect(result).toBeUndefined()
1046
+ })
1047
+
1048
+ it('should return specific version when submittedVersionNumber matches', () => {
1049
+ const result = getVersionMetadata(2, mockFormMetadata as FormMetadata)
1050
+ expect(result).toEqual({
1051
+ versionNumber: 2,
1052
+ createdAt: new Date('2024-01-02T00:00:00.000Z')
1053
+ })
1054
+ })
1055
+
1056
+ it('should return first version when submittedVersionNumber not found', () => {
1057
+ const result = getVersionMetadata(999, mockFormMetadata as FormMetadata)
1058
+ expect(result).toEqual({
1059
+ versionNumber: 1,
1060
+ createdAt: new Date('2024-01-01T00:00:00.000Z')
1061
+ })
1062
+ })
1063
+
1064
+ it('should return first version when no submittedVersionNumber provided', () => {
1065
+ const result = getVersionMetadata(
1066
+ undefined,
1067
+ mockFormMetadata as FormMetadata
1068
+ )
1069
+ expect(result).toEqual({
1070
+ versionNumber: 1,
1071
+ createdAt: new Date('2024-01-01T00:00:00.000Z')
1072
+ })
1073
+ })
1074
+
1075
+ it('should handle single version in versions array', () => {
1076
+ const singleVersionMetadata: Partial<FormMetadata> = {
1077
+ ...mockFormMetadata,
1078
+ versions: [
1079
+ {
1080
+ versionNumber: 5,
1081
+ createdAt: new Date('2024-02-01T00:00:00.000Z')
1082
+ }
1083
+ ]
1084
+ }
1085
+
1086
+ const result = getVersionMetadata(
1087
+ undefined,
1088
+ singleVersionMetadata as FormMetadata
1089
+ )
1090
+ expect(result).toEqual({
1091
+ versionNumber: 5,
1092
+ createdAt: new Date('2024-02-01T00:00:00.000Z')
1093
+ })
1094
+ })
1095
+
1096
+ it('should return correct version when submittedVersionNumber is 0', () => {
1097
+ const metadataWithVersionZero: Partial<FormMetadata> = {
1098
+ ...mockFormMetadata,
1099
+ versions: [
1100
+ {
1101
+ versionNumber: 0,
1102
+ createdAt: new Date('2024-01-01T00:00:00.000Z')
1103
+ },
1104
+ {
1105
+ versionNumber: 1,
1106
+ createdAt: new Date('2024-01-02T00:00:00.000Z')
1107
+ }
1108
+ ]
1109
+ }
1110
+
1111
+ const result = getVersionMetadata(
1112
+ 0,
1113
+ metadataWithVersionZero as FormMetadata
1114
+ )
1115
+ expect(result).toEqual({
1116
+ versionNumber: 0,
1117
+ createdAt: new Date('2024-01-01T00:00:00.000Z')
1118
+ })
1119
+ })
1120
+
1121
+ it('should handle negative submittedVersionNumber by falling back to first version', () => {
1122
+ const result = getVersionMetadata(-1, mockFormMetadata as FormMetadata)
1123
+ expect(result).toEqual({
1124
+ versionNumber: 1,
1125
+ createdAt: new Date('2024-01-01T00:00:00.000Z')
1126
+ })
1127
+ })
1128
+ })
696
1129
  })