@remoteoss/json-schema-form 0.11.9-dev.20241213082826 → 0.11.10-beta.0

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.
@@ -31,6 +31,7 @@ import {
31
31
  schemaInputWithStatement,
32
32
  schemaInputTypeCheckbox,
33
33
  schemaInputTypeCheckboxBooleans,
34
+ schemaInputTypeCheckboxBooleanConditional,
34
35
  schemaInputTypeNull,
35
36
  schemaWithOrderKeyword,
36
37
  schemaWithPositionDeprecated,
@@ -500,450 +501,450 @@ describe('createHeadlessForm', () => {
500
501
  expect(() => fieldValidator.validateSync('')).toThrowError('Required field');
501
502
  });
502
503
 
503
- it('supports both root level "description" and "x-jsf-presentation.description"', () => {
504
- const resultsWithRootDescription = createHeadlessForm(
505
- JSONSchemaBuilder()
506
- .addInput({
507
- username: mockTextInput,
508
- })
509
- .setRequiredFields(['username'])
510
- .build()
511
- );
512
-
513
- expect(resultsWithRootDescription.fields[0].description).toMatch(/your username/i);
504
+ describe('support "select" field type', () => {
505
+ it('support "select" field type @deprecated', () => {
506
+ const { fields, handleValidation } = createHeadlessForm(
507
+ schemaInputTypeSelectSoloDeprecated
508
+ );
514
509
 
515
- const resultsWithPresentationDescription = createHeadlessForm(
516
- JSONSchemaBuilder()
517
- .addInput({
518
- username: {
519
- ...mockTextInput,
520
- 'x-jsf-presentation': {
521
- inputType: 'text',
522
- maskSecret: 2,
523
- // should override the root level description
524
- description: 'a different description with <span>markup</span>',
510
+ expect(fields).toMatchObject([
511
+ {
512
+ description: 'Life Insurance',
513
+ label: 'Benefits (solo)',
514
+ name: 'benefits',
515
+ placeholder: 'Select...',
516
+ type: 'select',
517
+ options: [
518
+ {
519
+ label: 'Medical Insurance',
520
+ value: 'Medical Insurance',
525
521
  },
526
- },
527
- })
528
- .setRequiredFields(['username'])
529
- .build()
530
- );
531
-
532
- expect(resultsWithPresentationDescription.fields[0].description).toMatch(
533
- /a different description /i
534
- );
535
- });
536
-
537
- it('supports both root level "description" and "presentation.description" (deprecated)', () => {
538
- const resultsWithRootDescription = createHeadlessForm(
539
- JSONSchemaBuilder()
540
- .addInput({
541
- username: mockTextInputDeprecated,
542
- })
543
- .setRequiredFields(['username'])
544
- .build()
545
- );
546
-
547
- expect(resultsWithRootDescription.fields[0].description).toMatch(/your username/i);
548
-
549
- const resultsWithPresentationDescription = createHeadlessForm(
550
- JSONSchemaBuilder()
551
- .addInput({
552
- username: {
553
- ...mockTextInputDeprecated,
554
- presentation: {
555
- inputType: 'text',
556
- maskSecret: 2,
557
- // should override the root level description
558
- description: 'a different description with <span>markup</span>',
522
+ {
523
+ label: 'Health Insurance',
524
+ value: 'Health Insurance',
559
525
  },
560
- },
561
- })
562
- .setRequiredFields(['username'])
563
- .build()
564
- );
526
+ {
527
+ label: 'Travel Bonus',
528
+ value: 'Travel Bonus',
529
+ },
530
+ ],
531
+ },
532
+ ]);
565
533
 
566
- expect(resultsWithPresentationDescription.fields[0].description).toMatch(
567
- /a different description /i
568
- );
569
- });
534
+ assertOptionsAllowed({
535
+ handleValidation,
536
+ fieldName: 'benefits',
537
+ validOptions: ['Medical Insurance', 'Health Insurance', 'Travel Bonus'],
538
+ });
539
+ });
570
540
 
571
- it('support "select" field type @deprecated', () => {
572
- const { fields, handleValidation } = createHeadlessForm(schemaInputTypeSelectSoloDeprecated);
541
+ it('support "select" field type', () => {
542
+ const { fields, handleValidation } = createHeadlessForm(schemaInputTypeSelectSolo);
573
543
 
574
- expect(fields).toMatchObject([
575
- {
576
- description: 'Life Insurance',
577
- label: 'Benefits (solo)',
578
- name: 'benefits',
579
- placeholder: 'Select...',
580
- type: 'select',
544
+ const fieldSelect = fields[0];
545
+ expect(fieldSelect).toMatchObject({
546
+ name: 'browsers',
547
+ label: 'Browsers (solo)',
548
+ description: 'This solo select also includes a disabled option.',
581
549
  options: [
582
550
  {
583
- label: 'Medical Insurance',
584
- value: 'Medical Insurance',
551
+ value: 'chr',
552
+ label: 'Chrome',
585
553
  },
586
554
  {
587
- label: 'Health Insurance',
588
- value: 'Health Insurance',
555
+ value: 'ff',
556
+ label: 'Firefox',
589
557
  },
590
558
  {
591
- label: 'Travel Bonus',
592
- value: 'Travel Bonus',
559
+ value: 'ie',
560
+ label: 'Internet Explorer',
561
+ disabled: true,
593
562
  },
594
563
  ],
595
- },
596
- ]);
597
-
598
- assertOptionsAllowed({
599
- handleValidation,
600
- fieldName: 'benefits',
601
- validOptions: ['Medical Insurance', 'Health Insurance', 'Travel Bonus'],
602
- });
603
- });
564
+ });
604
565
 
605
- it('support "select" field type', () => {
606
- const { fields, handleValidation } = createHeadlessForm(schemaInputTypeSelectSolo);
566
+ expect(fieldSelect).not.toHaveProperty('multiple');
607
567
 
608
- const fieldSelect = fields[0];
609
- expect(fieldSelect).toMatchObject({
610
- name: 'browsers',
611
- label: 'Browsers (solo)',
612
- description: 'This solo select also includes a disabled option.',
613
- options: [
614
- {
615
- value: 'chr',
616
- label: 'Chrome',
617
- },
618
- {
619
- value: 'ff',
620
- label: 'Firefox',
621
- },
622
- {
623
- value: 'ie',
624
- label: 'Internet Explorer',
625
- disabled: true,
626
- },
627
- ],
568
+ assertOptionsAllowed({
569
+ handleValidation,
570
+ fieldName: 'browsers',
571
+ validOptions: ['chr', 'ff', 'ie'],
572
+ });
628
573
  });
629
574
 
630
- expect(fieldSelect).not.toHaveProperty('multiple');
575
+ it('supports "select" field type with multiple options @deprecated', () => {
576
+ const result = createHeadlessForm(schemaInputTypeSelectMultipleDeprecated);
577
+ expect(result).toMatchObject({
578
+ fields: [
579
+ {
580
+ description: 'Life Insurance',
581
+ label: 'Benefits (multiple)',
582
+ name: 'benefits_multi',
583
+ placeholder: 'Select...',
584
+ type: 'select',
585
+ options: [
586
+ {
587
+ label: 'Medical Insurance',
588
+ value: 'Medical Insurance',
589
+ },
590
+ {
591
+ label: 'Health Insurance',
592
+ value: 'Health Insurance',
593
+ },
594
+ {
595
+ label: 'Travel Bonus',
596
+ value: 'Travel Bonus',
597
+ },
598
+ ],
599
+ multiple: true,
600
+ },
601
+ ],
602
+ });
603
+ });
604
+ it('supports "select" field type with multiple options', () => {
605
+ const result = createHeadlessForm(schemaInputTypeSelectMultiple);
606
+ expect(result).toMatchObject({
607
+ fields: [
608
+ {
609
+ name: 'browsers_multi',
610
+ label: 'Browsers (multiple)',
611
+ description: 'This multi-select also includes a disabled option.',
612
+ options: [
613
+ {
614
+ value: 'chr',
615
+ label: 'Chrome',
616
+ },
617
+ {
618
+ value: 'ff',
619
+ label: 'Firefox',
620
+ },
621
+ {
622
+ value: 'ie',
623
+ label: 'Internet Explorer',
624
+ disabled: true,
625
+ },
626
+ ],
627
+ multiple: true,
628
+ },
629
+ ],
630
+ });
631
+ });
631
632
 
632
- assertOptionsAllowed({
633
- handleValidation,
634
- fieldName: 'browsers',
635
- validOptions: ['chr', 'ff', 'ie'],
633
+ it('supports "select" field type with multiple options and optional', () => {
634
+ const result = createHeadlessForm(schemaInputTypeSelectMultipleOptional);
635
+ expect(result).toMatchObject({
636
+ fields: [
637
+ {
638
+ name: 'browsers_multi_optional',
639
+ label: 'Browsers (multiple) (optional)',
640
+ description: 'This optional multi-select also includes a disabled option.',
641
+ options: [
642
+ {
643
+ value: 'chr',
644
+ label: 'Chrome',
645
+ },
646
+ {
647
+ value: 'ff',
648
+ label: 'Firefox',
649
+ },
650
+ {
651
+ value: 'ie',
652
+ label: 'Internet Explorer',
653
+ disabled: true,
654
+ },
655
+ ],
656
+ multiple: true,
657
+ },
658
+ ],
659
+ });
636
660
  });
637
661
  });
662
+ describe('support "radio" field type', () => {
663
+ it('support "radio" field type @deprecated', () => {
664
+ const { fields, handleValidation } = createHeadlessForm(schemaInputTypeRadioDeprecated);
638
665
 
639
- it('supports "select" field type with multiple options @deprecated', () => {
640
- const result = createHeadlessForm(schemaInputTypeSelectMultipleDeprecated);
641
- expect(result).toMatchObject({
642
- fields: [
666
+ expect(fields).toMatchObject([
643
667
  {
644
- description: 'Life Insurance',
645
- label: 'Benefits (multiple)',
646
- name: 'benefits_multi',
647
- placeholder: 'Select...',
648
- type: 'select',
668
+ description: 'Do you have any siblings?',
669
+ label: 'Has siblings',
670
+ name: 'has_siblings',
649
671
  options: [
650
672
  {
651
- label: 'Medical Insurance',
652
- value: 'Medical Insurance',
673
+ label: 'Yes',
674
+ value: 'yes',
653
675
  },
654
676
  {
655
- label: 'Health Insurance',
656
- value: 'Health Insurance',
657
- },
658
- {
659
- label: 'Travel Bonus',
660
- value: 'Travel Bonus',
677
+ label: 'No',
678
+ value: 'no',
661
679
  },
662
680
  ],
663
- multiple: true,
681
+ required: true,
682
+ schema: expect.any(Object),
683
+ type: 'radio',
664
684
  },
665
- ],
685
+ ]);
686
+
687
+ assertOptionsAllowed({
688
+ handleValidation,
689
+ fieldName: 'has_siblings',
690
+ validOptions: ['yes', 'no'],
691
+ });
666
692
  });
667
- });
668
- it('supports "select" field type with multiple options', () => {
669
- const result = createHeadlessForm(schemaInputTypeSelectMultiple);
670
- expect(result).toMatchObject({
671
- fields: [
693
+ it('support "radio" field string type', () => {
694
+ const { fields, handleValidation } = createHeadlessForm(schemaInputTypeRadioString);
695
+
696
+ expect(fields).toMatchObject([
672
697
  {
673
- name: 'browsers_multi',
674
- label: 'Browsers (multiple)',
675
- description: 'This multi-select also includes a disabled option.',
698
+ description: 'Do you have any siblings?',
699
+ label: 'Has siblings',
700
+ name: 'has_siblings',
676
701
  options: [
677
702
  {
678
- value: 'chr',
679
- label: 'Chrome',
680
- },
681
- {
682
- value: 'ff',
683
- label: 'Firefox',
703
+ label: 'Yes',
704
+ value: 'yes',
684
705
  },
685
706
  {
686
- value: 'ie',
687
- label: 'Internet Explorer',
688
- disabled: true,
707
+ label: 'No',
708
+ value: 'no',
689
709
  },
690
710
  ],
691
- multiple: true,
711
+ required: true,
712
+ schema: expect.any(Object),
713
+ type: 'radio',
692
714
  },
693
- ],
715
+ ]);
716
+
717
+ assertOptionsAllowed({
718
+ handleValidation,
719
+ fieldName: 'has_siblings',
720
+ validOptions: ['yes', 'no'],
721
+ });
694
722
  });
695
- });
696
723
 
697
- it('supports "select" field type with multiple options and optional', () => {
698
- const result = createHeadlessForm(schemaInputTypeSelectMultipleOptional);
699
- expect(result).toMatchObject({
700
- fields: [
724
+ it('support "radio" field boolean type', () => {
725
+ const { fields, handleValidation } = createHeadlessForm(schemaInputTypeRadioBoolean);
726
+
727
+ const validateForm = (vals) => friendlyError(handleValidation(vals));
728
+
729
+ expect(fields).toMatchObject([
701
730
  {
702
- name: 'browsers_multi_optional',
703
- label: 'Browsers (multiple) (optional)',
704
- description: 'This optional multi-select also includes a disabled option.',
731
+ description: 'Are you over 18 years old?',
732
+ label: 'Over 18',
733
+ name: 'over_18',
705
734
  options: [
706
735
  {
707
- value: 'chr',
708
- label: 'Chrome',
709
- },
710
- {
711
- value: 'ff',
712
- label: 'Firefox',
736
+ label: 'Yes',
737
+ value: true,
713
738
  },
714
739
  {
715
- value: 'ie',
716
- label: 'Internet Explorer',
717
- disabled: true,
740
+ label: 'No',
741
+ value: false,
718
742
  },
719
743
  ],
720
- multiple: true,
744
+ required: true,
745
+ schema: expect.any(Object),
746
+ type: 'radio',
721
747
  },
722
- ],
723
- });
724
- });
725
-
726
- it('support "radio" field type @deprecated', () => {
727
- const { fields, handleValidation } = createHeadlessForm(schemaInputTypeRadioDeprecated);
728
-
729
- expect(fields).toMatchObject([
730
- {
731
- description: 'Do you have any siblings?',
732
- label: 'Has siblings',
733
- name: 'has_siblings',
734
- options: [
735
- {
736
- label: 'Yes',
737
- value: 'yes',
738
- },
739
- {
740
- label: 'No',
741
- value: 'no',
742
- },
743
- ],
744
- required: true,
745
- schema: expect.any(Object),
746
- type: 'radio',
747
- },
748
- ]);
749
-
750
- assertOptionsAllowed({
751
- handleValidation,
752
- fieldName: 'has_siblings',
753
- validOptions: ['yes', 'no'],
754
- });
755
- });
756
- it('support "radio" field string type', () => {
757
- const { fields, handleValidation } = createHeadlessForm(schemaInputTypeRadioString);
748
+ ]);
758
749
 
759
- expect(fields).toMatchObject([
760
- {
761
- description: 'Do you have any siblings?',
762
- label: 'Has siblings',
763
- name: 'has_siblings',
764
- options: [
765
- {
766
- label: 'Yes',
767
- value: 'yes',
768
- },
769
- {
770
- label: 'No',
771
- value: 'no',
772
- },
773
- ],
774
- required: true,
775
- schema: expect.any(Object),
776
- type: 'radio',
777
- },
778
- ]);
750
+ assertOptionsAllowed({
751
+ handleValidation,
752
+ fieldName: 'over_18',
753
+ validOptions: [true, false],
754
+ type: schemaInputTypeRadioBoolean.properties.over_18.type,
755
+ });
779
756
 
780
- assertOptionsAllowed({
781
- handleValidation,
782
- fieldName: 'has_siblings',
783
- validOptions: ['yes', 'no'],
757
+ expect(validateForm({ over_18: 'true' })).toEqual({
758
+ over_18: 'The option "true" is not valid.',
759
+ });
784
760
  });
785
- });
786
761
 
787
- it('support "radio" field boolean type', () => {
788
- const { fields, handleValidation } = createHeadlessForm(schemaInputTypeRadioBoolean);
789
-
790
- const validateForm = (vals) => friendlyError(handleValidation(vals));
762
+ it('supports "radio" field type with its "card" and "card-expandable" variants', () => {
763
+ const result = createHeadlessForm(
764
+ JSONSchemaBuilder()
765
+ .addInput({
766
+ experience_level: mockRadioCardExpandableInput,
767
+ payment_method: mockRadioCardInput,
768
+ })
769
+ .build()
770
+ );
791
771
 
792
- expect(fields).toMatchObject([
793
- {
794
- description: 'Are you over 18 years old?',
795
- label: 'Over 18',
796
- name: 'over_18',
797
- options: [
772
+ expect(result).toMatchObject({
773
+ fields: [
798
774
  {
799
- label: 'Yes',
800
- value: true,
775
+ description:
776
+ 'Please select the experience level that aligns with this role based on the job description (not the employees overall experience)',
777
+ label: 'Experience level',
778
+ name: 'experience_level',
779
+ type: 'radio',
780
+ required: false,
781
+ variant: 'card-expandable',
782
+ options: [
783
+ {
784
+ label: 'Junior level',
785
+ value: 'junior',
786
+ description:
787
+ 'Entry level employees who perform tasks under the supervision of a more experienced employee.',
788
+ },
789
+ {
790
+ label: 'Mid level',
791
+ value: 'mid',
792
+ description:
793
+ 'Employees who perform tasks with a good degree of autonomy and/or with coordination and control functions.',
794
+ },
795
+ {
796
+ label: 'Senior level',
797
+ value: 'senior',
798
+ description:
799
+ 'Employees who perform tasks with a high degree of autonomy and/or with coordination and control functions.',
800
+ },
801
+ ],
801
802
  },
802
803
  {
803
- label: 'No',
804
- value: false,
804
+ description: 'Chose how you want to be paid',
805
+ label: 'Payment method',
806
+ name: 'payment_method',
807
+ type: 'radio',
808
+ variant: 'card',
809
+ required: false,
810
+ options: [
811
+ {
812
+ label: 'Credit Card',
813
+ value: 'cc',
814
+ description: 'Plastic money, which is still money',
815
+ },
816
+ {
817
+ label: 'Cash',
818
+ value: 'cash',
819
+ description: 'Rules Everything Around Me',
820
+ },
821
+ ],
805
822
  },
806
823
  ],
807
- required: true,
808
- schema: expect.any(Object),
809
- type: 'radio',
810
- },
811
- ]);
812
-
813
- assertOptionsAllowed({
814
- handleValidation,
815
- fieldName: 'over_18',
816
- validOptions: [true, false],
817
- type: schemaInputTypeRadioBoolean.properties.over_18.type,
824
+ });
818
825
  });
819
826
 
820
- expect(validateForm({ over_18: 'true' })).toEqual({
821
- over_18: 'The option "true" is not valid.',
822
- });
823
- });
827
+ // @BUG COD-1859
828
+ // it should validate when type is string but value is not a boolean
829
+ it('support "radio" field string-y type', () => {
830
+ const { fields, handleValidation } = createHeadlessForm(schemaInputTypeRadioStringY);
824
831
 
825
- // @BUG COD-1859
826
- // it should validate when type is string but value is not a boolean
827
- it('support "radio" field string-y type', () => {
828
- const { fields, handleValidation } = createHeadlessForm(schemaInputTypeRadioStringY);
829
-
830
- const validateForm = (vals) => friendlyError(handleValidation(vals));
832
+ const validateForm = (vals) => friendlyError(handleValidation(vals));
831
833
 
832
- expect(fields).toMatchObject([
833
- {
834
- description: 'Do you have any siblings?',
835
- label: 'Has siblings',
836
- name: 'has_siblings',
837
- options: [
838
- {
839
- label: 'Yes',
840
- value: 'true',
841
- },
842
- {
843
- label: 'No',
844
- value: 'false',
845
- },
846
- ],
847
- required: true,
848
- schema: expect.any(Object),
849
- type: 'radio',
850
- },
851
- ]);
834
+ expect(fields).toMatchObject([
835
+ {
836
+ description: 'Do you have any siblings?',
837
+ label: 'Has siblings',
838
+ name: 'has_siblings',
839
+ options: [
840
+ {
841
+ label: 'Yes',
842
+ value: 'true',
843
+ },
844
+ {
845
+ label: 'No',
846
+ value: 'false',
847
+ },
848
+ ],
849
+ required: true,
850
+ schema: expect.any(Object),
851
+ type: 'radio',
852
+ },
853
+ ]);
852
854
 
853
- assertOptionsAllowed({
854
- handleValidation,
855
- fieldName: 'has_siblings',
856
- validOptions: ['true', 'false'],
857
- });
855
+ assertOptionsAllowed({
856
+ handleValidation,
857
+ fieldName: 'has_siblings',
858
+ validOptions: ['true', 'false'],
859
+ });
858
860
 
859
- expect(validateForm({ has_siblings: false })).toEqual({
860
- has_siblings: 'The option "false" is not valid.',
861
+ expect(validateForm({ has_siblings: false })).toEqual({
862
+ has_siblings: 'The option "false" is not valid.',
863
+ });
861
864
  });
862
- });
863
865
 
864
- it('support "radio" field number type', () => {
865
- const { fields, handleValidation } = createHeadlessForm(schemaInputTypeRadioNumber);
866
+ it('support "radio" field number type', () => {
867
+ const { fields, handleValidation } = createHeadlessForm(schemaInputTypeRadioNumber);
866
868
 
867
- const validateForm = (vals) => friendlyError(handleValidation(vals));
869
+ const validateForm = (vals) => friendlyError(handleValidation(vals));
868
870
 
869
- expect(fields).toMatchObject([
870
- {
871
- description: 'How many siblings do you have?',
872
- label: 'Number of siblings',
873
- name: 'siblings_count',
874
- options: [
875
- {
876
- label: 'One',
877
- value: 1,
878
- },
879
- {
880
- label: 'Two',
881
- value: 2,
882
- },
883
- {
884
- label: 'Three',
885
- value: 3,
886
- },
887
- ],
888
- required: true,
889
- schema: expect.any(Object),
890
- type: 'radio',
891
- },
892
- ]);
871
+ expect(fields).toMatchObject([
872
+ {
873
+ description: 'How many siblings do you have?',
874
+ label: 'Number of siblings',
875
+ name: 'siblings_count',
876
+ options: [
877
+ {
878
+ label: 'One',
879
+ value: 1,
880
+ },
881
+ {
882
+ label: 'Two',
883
+ value: 2,
884
+ },
885
+ {
886
+ label: 'Three',
887
+ value: 3,
888
+ },
889
+ ],
890
+ required: true,
891
+ schema: expect.any(Object),
892
+ type: 'radio',
893
+ },
894
+ ]);
893
895
 
894
- assertOptionsAllowed({
895
- handleValidation,
896
- fieldName: 'siblings_count',
897
- validOptions: [1, 2, 3],
898
- type: schemaInputTypeRadioNumber.properties.siblings_count.type,
899
- });
896
+ assertOptionsAllowed({
897
+ handleValidation,
898
+ fieldName: 'siblings_count',
899
+ validOptions: [1, 2, 3],
900
+ type: schemaInputTypeRadioNumber.properties.siblings_count.type,
901
+ });
900
902
 
901
- expect(validateForm({ siblings_count: '3' })).toEqual({
902
- siblings_count: 'The option "3" is not valid.',
903
+ expect(validateForm({ siblings_count: '3' })).toEqual({
904
+ siblings_count: 'The option "3" is not valid.',
905
+ });
903
906
  });
904
- });
905
907
 
906
- it('support "radio" optional field', () => {
907
- const { fields, handleValidation } = createHeadlessForm(
908
- schemaInputTypeRadioRequiredAndOptional
909
- );
910
- const validateForm = (vals) => friendlyError(handleValidation(vals));
908
+ it('support "radio" optional field', () => {
909
+ const { fields, handleValidation } = createHeadlessForm(
910
+ schemaInputTypeRadioRequiredAndOptional
911
+ );
912
+ const validateForm = (vals) => friendlyError(handleValidation(vals));
911
913
 
912
- expect(fields).toMatchObject([
913
- {},
914
- {
915
- name: 'has_car',
916
- label: 'Has car',
917
- description: 'Do you have a car? (optional field, check oneOf)',
918
- options: [
919
- {
920
- label: 'Yes',
921
- value: 'yes',
922
- },
923
- {
924
- label: 'No',
925
- value: 'no',
926
- },
927
- ],
928
- required: false,
929
- schema: expect.any(Object),
930
- type: 'radio',
931
- },
932
- ]);
914
+ expect(fields).toMatchObject([
915
+ {},
916
+ {
917
+ name: 'has_car',
918
+ label: 'Has car',
919
+ description: 'Do you have a car? (optional field, check oneOf)',
920
+ options: [
921
+ {
922
+ label: 'Yes',
923
+ value: 'yes',
924
+ },
925
+ {
926
+ label: 'No',
927
+ value: 'no',
928
+ },
929
+ ],
930
+ required: false,
931
+ schema: expect.any(Object),
932
+ type: 'radio',
933
+ },
934
+ ]);
933
935
 
934
- expect(
935
- validateForm({
936
- has_siblings: 'yes',
937
- has_car: 'yes',
938
- })
939
- ).toBeUndefined();
936
+ expect(
937
+ validateForm({
938
+ has_siblings: 'yes',
939
+ has_car: 'yes',
940
+ })
941
+ ).toBeUndefined();
940
942
 
941
- expect(validateForm({})).toEqual({
942
- has_siblings: 'Required field',
943
+ expect(validateForm({})).toEqual({
944
+ has_siblings: 'Required field',
945
+ });
943
946
  });
944
- });
945
947
 
946
- describe('support "radio" optional field - more examples @BUG RMT-518', () => {
947
948
  function assertCommonBehavior(validateForm) {
948
949
  // Note: Very similar to assertOptionsAllowed()
949
950
  // We could reuse it in a next iteration.
@@ -969,13 +970,13 @@ describe('createHeadlessForm', () => {
969
970
  expect(validateForm({ has_car: '' })).toBeUndefined();
970
971
  }
971
972
 
972
- it('normal optional (conventional way)', () => {
973
+ it('support "radio" optional field - optional (conventional way) - @BUG RMT-518', () => {
973
974
  const { handleValidation } = createHeadlessForm(schemaInputRadioOptionalConventional);
974
975
  const validateForm = (vals) => friendlyError(handleValidation(vals));
975
976
 
976
977
  assertCommonBehavior(validateForm);
977
978
 
978
- // Accepts null, even though it shoudln't @BUG RMT-518
979
+ // Accepts null, even though it shouldn't @BUG RMT-518
979
980
  // This is for cases where we (Remote) still have incorrect
980
981
  // JSON Schemas in our Platform.
981
982
  expect(validateForm({ has_car: null })).toBeUndefined();
@@ -986,53 +987,98 @@ describe('createHeadlessForm', () => {
986
987
  // });
987
988
  });
988
989
 
989
- it('with null option (as Remote does)', () => {
990
+ it('support "radio" optional field - optional with null option (as Remote does) - @BUG RMT-518', () => {
990
991
  const { handleValidation } = createHeadlessForm(schemaInputRadioOptionalNull);
991
992
  const validateForm = (vals) => friendlyError(handleValidation(vals));
992
993
 
993
- assertCommonBehavior(validateForm);
994
+ assertCommonBehavior(validateForm);
995
+
996
+ // Accepts null value
997
+ expect(validateForm({ has_car: null })).toBeUndefined();
998
+ });
999
+
1000
+ it('support "radio" field type with extra info inside each option', () => {
1001
+ const result = createHeadlessForm(schemaInputTypeRadioOptionsWithDetails);
1002
+
1003
+ expect(result.fields).toHaveLength(1);
1004
+
1005
+ const fieldOptions = result.fields[0].options;
1006
+
1007
+ // The x-jsf-presentation content was spread to the root:
1008
+ expect(fieldOptions[0]).not.toHaveProperty('x-jsf-presentation');
1009
+ expect(fieldOptions).toEqual([
1010
+ {
1011
+ label: 'Basic',
1012
+ value: 'basic',
1013
+ meta: {
1014
+ displayCost: '$30.00/mo',
1015
+ },
1016
+ // Other x-* keywords are kept as it is.
1017
+ 'x-another': 'extra-thing',
1018
+ },
1019
+ {
1020
+ label: 'Standard',
1021
+ value: 'standard',
1022
+ meta: {
1023
+ displayCost: '$50.00/mo',
1024
+ },
1025
+ },
1026
+ ]);
1027
+ });
1028
+
1029
+ it('supports oneOf pattern validation', () => {
1030
+ const result = createHeadlessForm(mockTelWithPattern);
1031
+
1032
+ expect(result).toMatchObject({
1033
+ fields: [
1034
+ {
1035
+ label: 'Phone number',
1036
+ name: 'phone_number',
1037
+ type: 'tel',
1038
+ required: false,
1039
+ options: [
1040
+ {
1041
+ label: 'Portugal',
1042
+ pattern: '^(\\+351)[0-9]{9,}$',
1043
+ },
1044
+ {
1045
+ label: 'United Kingdom (UK)',
1046
+ pattern: '^(\\+44)[0-9]{1,}$',
1047
+ },
1048
+ {
1049
+ label: 'Bolivia',
1050
+ pattern: '^(\\+591)[0-9]{9,}$',
1051
+ },
1052
+ {
1053
+ label: 'Canada',
1054
+ pattern: '^(\\+1)(206|224)[0-9]{1,}$',
1055
+ },
1056
+ {
1057
+ label: 'United States',
1058
+ pattern: '^(\\+1)[0-9]{1,}$',
1059
+ },
1060
+ ],
1061
+ },
1062
+ ],
1063
+ });
1064
+
1065
+ const fieldValidator = result.fields[0].schema;
994
1066
 
995
- // Accepts null value
996
- expect(validateForm({ has_car: null })).toBeUndefined();
1067
+ expect(fieldValidator.isValidSync('+351123123123')).toBe(true);
1068
+ expect(() => fieldValidator.validateSync('+35100')).toThrowError(
1069
+ 'The option "+35100" is not valid.'
1070
+ );
1071
+ expect(fieldValidator.isValidSync(undefined)).toBe(true);
997
1072
  });
998
- });
999
-
1000
- it('support "radio" field type with extra info inside each option', () => {
1001
- const result = createHeadlessForm(schemaInputTypeRadioOptionsWithDetails);
1002
-
1003
- expect(result.fields).toHaveLength(1);
1004
-
1005
- const fieldOptions = result.fields[0].options;
1006
-
1007
- // The x-jsf-presentation content was spread to the root:
1008
- expect(fieldOptions[0]).not.toHaveProperty('x-jsf-presentation');
1009
- expect(fieldOptions).toEqual([
1010
- {
1011
- label: 'Basic',
1012
- value: 'basic',
1013
- meta: {
1014
- displayCost: '$30.00/mo',
1015
- },
1016
- // Other x-* keywords are kept as it is.
1017
- 'x-another': 'extra-thing',
1018
- },
1019
- {
1020
- label: 'Standard',
1021
- value: 'standard',
1022
- meta: {
1023
- displayCost: '$50.00/mo',
1024
- },
1025
- },
1026
- ]);
1027
- });
1028
1073
 
1029
- it('support "radio" field type without oneOf options', () => {
1030
- const result = createHeadlessForm(schemaInputTypeRadioWithoutOptions);
1074
+ it('support "radio" field type without oneOf options', () => {
1075
+ const result = createHeadlessForm(schemaInputTypeRadioWithoutOptions);
1031
1076
 
1032
- expect(result.fields).toHaveLength(1);
1077
+ expect(result.fields).toHaveLength(1);
1033
1078
 
1034
- const fieldOptions = result.fields[0].options;
1035
- expect(fieldOptions).toEqual([]);
1079
+ const fieldOptions = result.fields[0].options;
1080
+ expect(fieldOptions).toEqual([]);
1081
+ });
1036
1082
  });
1037
1083
 
1038
1084
  it('support "integer" field type', () => {
@@ -1201,110 +1247,298 @@ describe('createHeadlessForm', () => {
1201
1247
  });
1202
1248
  });
1203
1249
 
1204
- it('support "date" field type with a minDate', () => {
1205
- const { fields, handleValidation } = createHeadlessForm(schemaInputTypeDate);
1250
+ describe('support "date" field type', () => {
1251
+ it('support "date" field type with a minDate', () => {
1252
+ const { fields, handleValidation } = createHeadlessForm(schemaInputTypeDate);
1206
1253
 
1207
- const validateForm = (vals) => friendlyError(handleValidation(vals));
1254
+ const validateForm = (vals) => friendlyError(handleValidation(vals));
1208
1255
 
1209
- expect(fields[0]).toMatchObject({
1210
- label: 'Birthdate',
1211
- name: 'birthdate',
1212
- required: true,
1213
- schema: expect.any(Object),
1214
- type: 'date',
1215
- minDate: '1922-03-01',
1216
- maxDate: '2022-03-17',
1256
+ expect(fields[0]).toMatchObject({
1257
+ label: 'Birthdate',
1258
+ name: 'birthdate',
1259
+ required: true,
1260
+ schema: expect.any(Object),
1261
+ type: 'date',
1262
+ minDate: '1922-03-01',
1263
+ maxDate: '2022-03-17',
1264
+ });
1265
+
1266
+ expect(validateForm({})).toEqual({
1267
+ birthdate: 'Required field',
1268
+ });
1269
+
1270
+ expect(validateForm({ birthdate: '' })).toEqual({
1271
+ birthdate: `Required field`,
1272
+ });
1273
+
1274
+ expect(validateForm({ birthdate: '1922-02-01' })).toEqual({
1275
+ birthdate: 'The date must be 1922-03-01 or after.',
1276
+ });
1277
+
1278
+ expect(validateForm({ birthdate: '1922-03-01' })).toBeUndefined();
1279
+
1280
+ expect(validateForm({ birthdate: '2021-03-01' })).toBeUndefined();
1217
1281
  });
1218
1282
 
1219
- expect(validateForm({})).toEqual({
1220
- birthdate: 'Required field',
1283
+ it('support "date" field type with a maxDate', () => {
1284
+ const { fields, handleValidation } = createHeadlessForm(schemaInputTypeDate);
1285
+
1286
+ const validateForm = (vals) => friendlyError(handleValidation(vals));
1287
+
1288
+ expect(fields[0]).toMatchObject({
1289
+ label: 'Birthdate',
1290
+ name: 'birthdate',
1291
+ required: true,
1292
+ schema: expect.any(Object),
1293
+ type: 'date',
1294
+ minDate: '1922-03-01',
1295
+ maxDate: '2022-03-17',
1296
+ });
1297
+
1298
+ expect(validateForm({ birthdate: '' })).toEqual({
1299
+ birthdate: `Required field`,
1300
+ });
1301
+
1302
+ expect(validateForm({ birthdate: '2022-02-01' })).toBeUndefined();
1303
+ expect(validateForm({ birthdate: '2022-03-01' })).toBeUndefined();
1304
+ expect(validateForm({ birthdate: '2022-04-01' })).toEqual({
1305
+ birthdate: 'The date must be 2022-03-17 or before.',
1306
+ });
1221
1307
  });
1222
1308
 
1223
- expect(validateForm({ birthdate: '' })).toEqual({
1224
- birthdate: `Required field`,
1309
+ it('support format date with minDate and maxDate', () => {
1310
+ const schemaFormatDate = {
1311
+ properties: {
1312
+ birthdate: {
1313
+ title: 'Birthdate',
1314
+ type: 'string',
1315
+ format: 'date',
1316
+ 'x-jsf-presentation': {
1317
+ inputType: 'myDateType',
1318
+ maxDate: '2022-03-01',
1319
+ minDate: '1922-03-01',
1320
+ },
1321
+ },
1322
+ },
1323
+ };
1324
+
1325
+ const { handleValidation } = createHeadlessForm(schemaFormatDate);
1326
+ const validateForm = (vals) => friendlyError(handleValidation(vals));
1327
+
1328
+ expect(validateForm({ birthdate: '1922-02-01' })).toEqual({
1329
+ birthdate: 'The date must be 1922-03-01 or after.',
1330
+ });
1225
1331
  });
1332
+ });
1333
+
1334
+ describe('supports "file" field type', () => {
1335
+ it('supports "file" field type', () => {
1336
+ const result = createHeadlessForm(
1337
+ JSONSchemaBuilder()
1338
+ .addInput({
1339
+ fileInput: mockFileInput,
1340
+ })
1341
+ .build()
1342
+ );
1226
1343
 
1227
- expect(validateForm({ birthdate: '1922-02-01' })).toEqual({
1228
- birthdate: 'The date must be 1922-03-01 or after.',
1344
+ expect(result).toMatchObject({
1345
+ fields: [
1346
+ {
1347
+ type: 'file',
1348
+ fileDownload: 'http://some.domain.com/file-name.pdf',
1349
+ description: 'File Input Description',
1350
+ fileName: 'My File',
1351
+ label: 'File Input',
1352
+ name: 'fileInput',
1353
+ required: false,
1354
+ accept: '.png,.jpg,.jpeg,.pdf',
1355
+ },
1356
+ ],
1357
+ });
1229
1358
  });
1230
1359
 
1231
- expect(validateForm({ birthdate: '1922-03-01' })).toBeUndefined();
1360
+ describe('when a field has accepted extensions', () => {
1361
+ let fields;
1362
+ beforeEach(() => {
1363
+ const result = createHeadlessForm(
1364
+ JSONSchemaBuilder().addInput({ fileInput: mockFileInput }).build()
1365
+ );
1366
+ fields = result.fields;
1367
+ });
1232
1368
 
1233
- expect(validateForm({ birthdate: '2021-03-01' })).toBeUndefined();
1234
- });
1369
+ describe('and file is of incorrect format', () => {
1370
+ const file = new File(['foo'], 'file.txt', {
1371
+ type: 'text/plain',
1372
+ });
1235
1373
 
1236
- it('support "date" field type with a maxDate', () => {
1237
- const { fields, handleValidation } = createHeadlessForm(schemaInputTypeDate);
1374
+ it('should throw an error', async () =>
1375
+ expect(
1376
+ object()
1377
+ .shape({
1378
+ fileInput: fields[0].schema,
1379
+ })
1380
+ .validate({ fileInput: [file] })
1381
+ ).rejects.toMatchObject({
1382
+ errors: ['Unsupported file format. The acceptable formats are .png,.jpg,.jpeg,.pdf.'],
1383
+ }));
1384
+ });
1238
1385
 
1239
- const validateForm = (vals) => friendlyError(handleValidation(vals));
1386
+ describe('and file is of correct format', () => {
1387
+ const file = new File(['foo'], 'file.png', {
1388
+ type: 'image/png',
1389
+ });
1390
+ Object.defineProperty(file, 'size', { value: 1024 * 1024 });
1391
+
1392
+ const assertObj = { fileInput: [file] };
1393
+ it('should validate field', async () =>
1394
+ expect(
1395
+ object()
1396
+ .shape({
1397
+ fileInput: fields[0].schema,
1398
+ })
1399
+ .validate({ fileInput: [file] })
1400
+ ).resolves.toEqual(assertObj));
1401
+ });
1240
1402
 
1241
- expect(fields[0]).toMatchObject({
1242
- label: 'Birthdate',
1243
- name: 'birthdate',
1244
- required: true,
1245
- schema: expect.any(Object),
1246
- type: 'date',
1247
- minDate: '1922-03-01',
1248
- maxDate: '2022-03-17',
1249
- });
1403
+ describe('and file is of correct but uppercase format ', () => {
1404
+ const file = new File(['foo'], 'file.PNG', {
1405
+ type: 'image/png',
1406
+ });
1407
+ Object.defineProperty(file, 'size', { value: 1024 * 1024 });
1408
+
1409
+ const assertObj = { fileInput: [file] };
1410
+ it('should validate field', async () =>
1411
+ expect(
1412
+ object()
1413
+ .shape({
1414
+ fileInput: fields[0].schema,
1415
+ })
1416
+ .validate({ fileInput: [file] })
1417
+ ).resolves.toEqual(assertObj));
1418
+ });
1419
+
1420
+ describe('and file is not instance of a File', () => {
1421
+ it('accepts if file object has name property', async () => {
1422
+ expect(
1423
+ object()
1424
+ .shape({
1425
+ fileInput: fields[0].schema,
1426
+ })
1427
+ .validate({ fileInput: [{ name: 'foo.pdf' }] })
1428
+ ).resolves.toEqual({ fileInput: [{ name: 'foo.pdf' }] });
1429
+ });
1250
1430
 
1251
- expect(validateForm({ birthdate: '' })).toEqual({
1252
- birthdate: `Required field`,
1431
+ it('should validate format', async () =>
1432
+ expect(
1433
+ object()
1434
+ .shape({
1435
+ fileInput: fields[0].schema,
1436
+ })
1437
+ .validate({ fileInput: [{ name: 'foo.txt' }] })
1438
+ ).rejects.toMatchObject({
1439
+ errors: ['Unsupported file format. The acceptable formats are .png,.jpg,.jpeg,.pdf.'],
1440
+ }));
1441
+
1442
+ it('should validate max size', async () =>
1443
+ expect(
1444
+ object()
1445
+ .shape({
1446
+ fileInput: fields[0].schema,
1447
+ })
1448
+ .validate({ fileInput: [{ name: 'foo.txt', size: 1024 * 1024 * 1024 }] })
1449
+ ).rejects.toMatchObject({ errors: ['File size too large. The limit is 20 MB.'] }));
1450
+
1451
+ it('throw an error if invalid file object', async () =>
1452
+ expect(
1453
+ object()
1454
+ .shape({
1455
+ fileInput: fields[0].schema,
1456
+ })
1457
+ .validate({ fileInput: [{ path: 'foo.txt' }] })
1458
+ ).rejects.toMatchObject({ errors: ['Not a valid file.'] }));
1459
+ });
1253
1460
  });
1254
1461
 
1255
- expect(validateForm({ birthdate: '2022-02-01' })).toBeUndefined();
1256
- expect(validateForm({ birthdate: '2022-03-01' })).toBeUndefined();
1257
- expect(validateForm({ birthdate: '2022-04-01' })).toEqual({
1258
- birthdate: 'The date must be 2022-03-17 or before.',
1462
+ describe('when a field has max file size', () => {
1463
+ let fields;
1464
+ beforeEach(() => {
1465
+ const result = createHeadlessForm(
1466
+ JSONSchemaBuilder().addInput({ fileInput: mockFileInput }).build()
1467
+ );
1468
+ fields = result.fields;
1469
+ });
1470
+ describe('and file is greater than that', () => {
1471
+ const file = new File([''], 'file.png');
1472
+ Object.defineProperty(file, 'size', { value: 1024 * 1024 * 1024 });
1473
+
1474
+ it('should throw an error', async () =>
1475
+ expect(
1476
+ object()
1477
+ .shape({
1478
+ fileInput: fields[0].schema,
1479
+ })
1480
+ .validate({ fileInput: [file] })
1481
+ ).rejects.toMatchObject({ errors: ['File size too large. The limit is 20 MB.'] }));
1482
+ });
1483
+ describe('and file is smaller than that', () => {
1484
+ const file = new File([''], 'file.png');
1485
+ Object.defineProperty(file, 'size', { value: 1024 * 1024 });
1486
+
1487
+ const assertObj = { fileInput: [file] };
1488
+ it('should validate field', async () =>
1489
+ expect(
1490
+ object()
1491
+ .shape({
1492
+ fileInput: fields[0].schema,
1493
+ })
1494
+ .validate({ fileInput: [file] })
1495
+ ).resolves.toEqual(assertObj));
1496
+ });
1259
1497
  });
1260
- });
1261
1498
 
1262
- it('support format date with minDate and maxDate', () => {
1263
- const schemaFormatDate = {
1264
- properties: {
1265
- birthdate: {
1266
- title: 'Birthdate',
1267
- type: 'string',
1268
- format: 'date',
1269
- 'x-jsf-presentation': {
1270
- inputType: 'myDateType',
1271
- maxDate: '2022-03-01',
1272
- minDate: '1922-03-01',
1273
- },
1274
- },
1275
- },
1276
- };
1499
+ describe('when a field file is optional', () => {
1500
+ it('it accepts an empty array', () => {
1501
+ const result = createHeadlessForm(
1502
+ JSONSchemaBuilder().addInput({ fileInput: mockFileInput }).build()
1503
+ );
1504
+ const emptyFile = { fileInput: [] };
1505
+ expect(
1506
+ object()
1507
+ .shape({
1508
+ fileInput: result.fields[0].schema,
1509
+ })
1510
+ .validate(emptyFile)
1511
+ ).resolves.toEqual(emptyFile);
1512
+ });
1277
1513
 
1278
- const { handleValidation } = createHeadlessForm(schemaFormatDate);
1279
- const validateForm = (vals) => friendlyError(handleValidation(vals));
1514
+ it('it validates missing file correctly', () => {
1515
+ const { handleValidation } = createHeadlessForm(
1516
+ JSONSchemaBuilder().addInput({ fileInput: mockFileInput }).build()
1517
+ );
1518
+ const validateForm = (vals) => friendlyError(handleValidation(vals));
1280
1519
 
1281
- expect(validateForm({ birthdate: '1922-02-01' })).toEqual({
1282
- birthdate: 'The date must be 1922-03-01 or after.',
1520
+ expect(validateForm({})).toBeUndefined();
1521
+ expect(validateForm({ fileInput: null })).toBeUndefined();
1522
+ });
1283
1523
  });
1284
- });
1285
1524
 
1286
- it('supports "file" field type', () => {
1287
- const result = createHeadlessForm(
1288
- JSONSchemaBuilder()
1289
- .addInput({
1290
- fileInput: mockFileInput,
1291
- })
1292
- .build()
1293
- );
1525
+ describe('when a field file is required', () => {
1526
+ it('it validates missing file correctly', () => {
1527
+ const { handleValidation } = createHeadlessForm(schemaInputTypeFile);
1528
+ const validateForm = (vals) => friendlyError(handleValidation(vals));
1294
1529
 
1295
- expect(result).toMatchObject({
1296
- fields: [
1297
- {
1298
- type: 'file',
1299
- fileDownload: 'http://some.domain.com/file-name.pdf',
1300
- description: 'File Input Description',
1301
- fileName: 'My File',
1302
- label: 'File Input',
1303
- name: 'fileInput',
1304
- required: false,
1305
- accept: '.png,.jpg,.jpeg,.pdf',
1306
- },
1307
- ],
1530
+ expect(validateForm({})).toEqual({
1531
+ a_file: 'Required field',
1532
+ });
1533
+
1534
+ expect(
1535
+ validateForm({
1536
+ a_file: null,
1537
+ })
1538
+ ).toEqual({
1539
+ a_file: 'Required field',
1540
+ });
1541
+ });
1308
1542
  });
1309
1543
  });
1310
1544
 
@@ -1591,71 +1825,6 @@ describe('createHeadlessForm', () => {
1591
1825
  });
1592
1826
  });
1593
1827
 
1594
- it('supports "radio" field type with its "card" and "card-expandable" variants', () => {
1595
- const result = createHeadlessForm(
1596
- JSONSchemaBuilder()
1597
- .addInput({
1598
- experience_level: mockRadioCardExpandableInput,
1599
- payment_method: mockRadioCardInput,
1600
- })
1601
- .build()
1602
- );
1603
-
1604
- expect(result).toMatchObject({
1605
- fields: [
1606
- {
1607
- description:
1608
- 'Please select the experience level that aligns with this role based on the job description (not the employees overall experience)',
1609
- label: 'Experience level',
1610
- name: 'experience_level',
1611
- type: 'radio',
1612
- required: false,
1613
- variant: 'card-expandable',
1614
- options: [
1615
- {
1616
- label: 'Junior level',
1617
- value: 'junior',
1618
- description:
1619
- 'Entry level employees who perform tasks under the supervision of a more experienced employee.',
1620
- },
1621
- {
1622
- label: 'Mid level',
1623
- value: 'mid',
1624
- description:
1625
- 'Employees who perform tasks with a good degree of autonomy and/or with coordination and control functions.',
1626
- },
1627
- {
1628
- label: 'Senior level',
1629
- value: 'senior',
1630
- description:
1631
- 'Employees who perform tasks with a high degree of autonomy and/or with coordination and control functions.',
1632
- },
1633
- ],
1634
- },
1635
- {
1636
- description: 'Chose how you want to be paid',
1637
- label: 'Payment method',
1638
- name: 'payment_method',
1639
- type: 'radio',
1640
- variant: 'card',
1641
- required: false,
1642
- options: [
1643
- {
1644
- label: 'Credit Card',
1645
- value: 'cc',
1646
- description: 'Plastic money, which is still money',
1647
- },
1648
- {
1649
- label: 'Cash',
1650
- value: 'cash',
1651
- description: 'Rules Everything Around Me',
1652
- },
1653
- ],
1654
- },
1655
- ],
1656
- });
1657
- });
1658
-
1659
1828
  it('supports "null" field type', () => {
1660
1829
  const { handleValidation, fields } = createHeadlessForm(schemaInputTypeNull, {
1661
1830
  strictInputType: false,
@@ -1698,51 +1867,6 @@ describe('createHeadlessForm', () => {
1698
1867
  expect(validateForm({ username: 'john', name: null })).toBeUndefined();
1699
1868
  });
1700
1869
 
1701
- it('supports oneOf pattern validation', () => {
1702
- const result = createHeadlessForm(mockTelWithPattern);
1703
-
1704
- expect(result).toMatchObject({
1705
- fields: [
1706
- {
1707
- label: 'Phone number',
1708
- name: 'phone_number',
1709
- type: 'tel',
1710
- required: false,
1711
- options: [
1712
- {
1713
- label: 'Portugal',
1714
- pattern: '^(\\+351)[0-9]{9,}$',
1715
- },
1716
- {
1717
- label: 'United Kingdom (UK)',
1718
- pattern: '^(\\+44)[0-9]{1,}$',
1719
- },
1720
- {
1721
- label: 'Bolivia',
1722
- pattern: '^(\\+591)[0-9]{9,}$',
1723
- },
1724
- {
1725
- label: 'Canada',
1726
- pattern: '^(\\+1)(206|224)[0-9]{1,}$',
1727
- },
1728
- {
1729
- label: 'United States',
1730
- pattern: '^(\\+1)[0-9]{1,}$',
1731
- },
1732
- ],
1733
- },
1734
- ],
1735
- });
1736
-
1737
- const fieldValidator = result.fields[0].schema;
1738
-
1739
- expect(fieldValidator.isValidSync('+351123123123')).toBe(true);
1740
- expect(() => fieldValidator.validateSync('+35100')).toThrowError(
1741
- 'The option "+35100" is not valid.'
1742
- );
1743
- expect(fieldValidator.isValidSync(undefined)).toBe(true);
1744
- });
1745
-
1746
1870
  describe('supports "fieldset" field type', () => {
1747
1871
  it('supports basic case', () => {
1748
1872
  const result = createHeadlessForm({
@@ -2117,7 +2241,7 @@ describe('createHeadlessForm', () => {
2117
2241
  expect(fieldValidator.isValidSync(false)).toBe(true);
2118
2242
  expect(fieldValidator.isValidSync(undefined)).toBe(true);
2119
2243
  expect(() => fieldValidator.validateSync('foo')).toThrowError(
2120
- 'this must be a `boolean` type, but the final value was: `"foo"`.'
2244
+ 'The value must be a boolean, but received "foo"'
2121
2245
  );
2122
2246
  });
2123
2247
 
@@ -2145,6 +2269,27 @@ describe('createHeadlessForm', () => {
2145
2269
  default: true,
2146
2270
  });
2147
2271
  });
2272
+
2273
+ it('conditional: it works as undefined value', () => {
2274
+ const { fields, handleValidation } = createHeadlessForm(
2275
+ schemaInputTypeCheckboxBooleanConditional
2276
+ );
2277
+ const checkboxField = fields.find((field) => field.name === 'pet_is_cat');
2278
+
2279
+ expect(handleValidation({ has_pet: false }).formErrors).toBeUndefined();
2280
+ expect(checkboxField.isVisible).toBe(false);
2281
+
2282
+ expect(handleValidation({ has_pet: true, pet_is_cat: true }).formErrors).toBeUndefined();
2283
+ expect(checkboxField.isVisible).toBe(true);
2284
+
2285
+ expect(handleValidation({ has_pet: true, pet_is_cat: 'foo' }).formErrors).toEqual({
2286
+ pet_is_cat: 'The value must be a boolean, but received "foo"',
2287
+ });
2288
+
2289
+ // Bug: It should throw an error saying pet_is_cat is not allowed, but it doesn't.
2290
+ // Explained at "Given values from hidden fields, it does not thrown an error"
2291
+ expect(handleValidation({ has_pet: false, pet_is_cat: true }).formErrors).toBeUndefined();
2292
+ });
2148
2293
  });
2149
2294
  });
2150
2295
 
@@ -2266,43 +2411,12 @@ describe('createHeadlessForm', () => {
2266
2411
  'x-jsf-order': ['pets'],
2267
2412
  });
2268
2413
 
2269
- const fieldValidator = result.fields[0].schema;
2270
-
2271
- expect(fieldValidator.isValidSync(0)).toBe(true);
2272
- expect(fieldValidator.isValidSync(1)).toBe(true);
2273
- expect(() => fieldValidator.validateSync('2')).toThrowError('The option "2" is not valid.');
2274
- expect(fieldValidator.isValidSync(null)).toBe(true);
2275
- });
2276
-
2277
- describe('x-jsf-presentation attribute', () => {
2278
- it('support field with "x-jsf-presentation.statement"', () => {
2279
- const result = createHeadlessForm(schemaInputWithStatement);
2280
-
2281
- expect(result).toMatchObject({
2282
- fields: [
2283
- {
2284
- name: 'bonus',
2285
- label: 'Bonus',
2286
- type: 'text',
2287
- statement: {
2288
- description: 'This is a custom statement message.',
2289
- inputType: 'statement',
2290
- severity: 'info',
2291
- },
2292
- },
2293
- {
2294
- name: 'role',
2295
- label: 'Role',
2296
- type: 'text',
2297
- statement: {
2298
- description: 'This is another statement message, but more severe.',
2299
- inputType: 'statement',
2300
- severity: 'warning',
2301
- },
2302
- },
2303
- ],
2304
- });
2305
- });
2414
+ const fieldValidator = result.fields[0].schema;
2415
+
2416
+ expect(fieldValidator.isValidSync(0)).toBe(true);
2417
+ expect(fieldValidator.isValidSync(1)).toBe(true);
2418
+ expect(() => fieldValidator.validateSync('2')).toThrowError('The option "2" is not valid.');
2419
+ expect(fieldValidator.isValidSync(null)).toBe(true);
2306
2420
  });
2307
2421
 
2308
2422
  describe('property misc attributes', () => {
@@ -2364,27 +2478,93 @@ describe('createHeadlessForm', () => {
2364
2478
  });
2365
2479
  });
2366
2480
 
2367
- it('pass "description" to field', () => {
2368
- const result = createHeadlessForm({
2481
+ it('pass both root level "description" and "x-jsf-presentation.description"', () => {
2482
+ const resultsWithRootDescription = createHeadlessForm({
2369
2483
  properties: {
2370
- plain: {
2371
- title: 'Regular',
2372
- description: 'I am regular',
2373
- presentation: { inputType: 'text' },
2374
- },
2375
- html: {
2376
- title: 'Name',
2377
- description: 'I am regular',
2378
- presentation: {
2379
- description: 'I am <b>bold</b>.',
2484
+ username: mockTextInput,
2485
+ },
2486
+ required: ['username'],
2487
+ });
2488
+
2489
+ expect(resultsWithRootDescription.fields[0].description).toMatch(/your username/i);
2490
+
2491
+ const resultsWithPresentationDescription = createHeadlessForm({
2492
+ properties: {
2493
+ username: {
2494
+ ...mockTextInput,
2495
+ 'x-jsf-presentation': {
2380
2496
  inputType: 'text',
2497
+ // should override the root level description
2498
+ description: 'a different description with <span>markup</span>',
2381
2499
  },
2382
2500
  },
2383
2501
  },
2502
+ required: ['username'],
2384
2503
  });
2385
2504
 
2505
+ expect(resultsWithPresentationDescription.fields[0].description).toMatch(
2506
+ /a different description /i
2507
+ );
2508
+ });
2509
+
2510
+ it('pass both root level "description" and "presentation.description" (deprecated)', () => {
2511
+ const resultsWithRootDescription = createHeadlessForm({
2512
+ properties: {
2513
+ username: mockTextInputDeprecated,
2514
+ },
2515
+ required: ['username'],
2516
+ });
2517
+
2518
+ expect(resultsWithRootDescription.fields[0].description).toMatch(/your username/i);
2519
+
2520
+ const resultsWithPresentationDescription = createHeadlessForm(
2521
+ JSONSchemaBuilder()
2522
+ .addInput({
2523
+ username: {
2524
+ ...mockTextInputDeprecated,
2525
+ presentation: {
2526
+ inputType: 'text',
2527
+ maskSecret: 2,
2528
+ // should override the root level description
2529
+ description: 'a different description with <span>markup</span>',
2530
+ },
2531
+ },
2532
+ })
2533
+ .setRequiredFields(['username'])
2534
+ .build()
2535
+ );
2536
+
2537
+ expect(resultsWithPresentationDescription.fields[0].description).toMatch(
2538
+ /a different description /i
2539
+ );
2540
+ });
2541
+
2542
+ it('support field with "x-jsf-presentation.statement"', () => {
2543
+ const result = createHeadlessForm(schemaInputWithStatement);
2544
+
2386
2545
  expect(result).toMatchObject({
2387
- fields: [{ description: 'I am regular' }, { description: 'I am <b>bold</b>.' }],
2546
+ fields: [
2547
+ {
2548
+ name: 'bonus',
2549
+ label: 'Bonus',
2550
+ type: 'text',
2551
+ statement: {
2552
+ description: 'This is a custom statement message.',
2553
+ inputType: 'statement',
2554
+ severity: 'info',
2555
+ },
2556
+ },
2557
+ {
2558
+ name: 'role',
2559
+ label: 'Role',
2560
+ type: 'text',
2561
+ statement: {
2562
+ description: 'This is another statement message, but more severe.',
2563
+ inputType: 'statement',
2564
+ severity: 'warning',
2565
+ },
2566
+ },
2567
+ ],
2388
2568
  });
2389
2569
  });
2390
2570
 
@@ -2457,7 +2637,7 @@ describe('createHeadlessForm', () => {
2457
2637
  });
2458
2638
  });
2459
2639
 
2460
- it('passes scopedJsonSchema to each field', () => {
2640
+ it('pass scopedJsonSchema to each field', () => {
2461
2641
  const { fields } = createHeadlessForm(schemaWithoutInputTypes, {
2462
2642
  strictInputType: false,
2463
2643
  });
@@ -2522,7 +2702,7 @@ describe('createHeadlessForm', () => {
2522
2702
  expect(fieldsetByName).toEqual(['line_one', 'number', 'postal_code']);
2523
2703
  });
2524
2704
 
2525
- it('sorts fields based on original properties (wihout x-jsf-order)', () => {
2705
+ it('sorts fields based on original properties (without x-jsf-order)', () => {
2526
2706
  // Assert the sample schema has x-jsf-order
2527
2707
  expect(schemaWithOrderKeyword['x-jsf-order']).toBeDefined();
2528
2708
 
@@ -2543,467 +2723,276 @@ describe('createHeadlessForm', () => {
2543
2723
  });
2544
2724
  });
2545
2725
 
2546
- describe('when a field is required', () => {
2547
- let fields;
2548
- beforeEach(() => {
2549
- const result = createHeadlessForm(
2550
- buildJSONSchemaInput({ presentationFields: { inputType: 'text' }, required: true })
2551
- );
2552
- fields = result.fields;
2553
- });
2554
- describe('and value is empty', () => {
2555
- it('should throw an error', async () =>
2556
- expect(
2557
- object()
2558
- .shape({
2559
- test: fields[0].schema,
2560
- })
2561
- .validate({ test: '' })
2562
- ).rejects.toMatchObject({ errors: ['Required field'] }));
2563
- });
2564
- describe('and value is defined', () => {
2565
- it('should validate field', async () => {
2566
- const assertObj = { test: 'Hello' };
2567
- return expect(
2568
- object()
2569
- .shape({
2570
- test: fields[0].schema,
2571
- })
2572
- .validate(assertObj)
2573
- ).resolves.toEqual(assertObj);
2726
+ describe('more validations', () => {
2727
+ describe('when a field is required', () => {
2728
+ let fields;
2729
+ beforeEach(() => {
2730
+ const result = createHeadlessForm(
2731
+ buildJSONSchemaInput({ presentationFields: { inputType: 'text' }, required: true })
2732
+ );
2733
+ fields = result.fields;
2574
2734
  });
2575
- });
2576
- });
2577
-
2578
- describe('when a field is number', () => {
2579
- let fields;
2580
- beforeEach(() => {
2581
- const result = createHeadlessForm(
2582
- buildJSONSchemaInput({ presentationFields: { inputType: 'number' } })
2583
- );
2584
- fields = result.fields;
2585
- });
2586
- describe('and value is a string', () => {
2587
- it('should throw an error', async () =>
2588
- expect(
2589
- object()
2590
- .shape({
2591
- test: fields[0].schema,
2592
- })
2593
- .validate({ test: 'Hello' })
2594
- ).rejects.toThrow());
2595
- });
2596
- describe('and value is a number', () => {
2597
- it('should validate field', async () => {
2598
- const assertObj = { test: 3 };
2599
- return expect(
2600
- object()
2601
- .shape({
2602
- test: fields[0].schema,
2603
- })
2604
- .validate(assertObj)
2605
- ).resolves.toEqual(assertObj);
2735
+ describe('and value is empty', () => {
2736
+ it('should throw an error', async () =>
2737
+ expect(
2738
+ object()
2739
+ .shape({
2740
+ test: fields[0].schema,
2741
+ })
2742
+ .validate({ test: '' })
2743
+ ).rejects.toMatchObject({ errors: ['Required field'] }));
2606
2744
  });
2607
- });
2608
- describe('and maximum is set to zero', () => {
2609
- it('shows the correct validation', () => {
2610
- const { handleValidation } = createHeadlessForm(schemaInputTypeNumberZeroMaximum);
2611
- const validateForm = (vals) => friendlyError(handleValidation(vals));
2612
-
2613
- expect(validateForm({ tabs: '0' })).toBeUndefined();
2614
- expect(validateForm({ tabs: '-10' })).toBeUndefined();
2615
-
2616
- expect(validateForm({ tabs: 1 })).toEqual({
2617
- tabs: 'Must be smaller or equal to 0',
2745
+ describe('and value is defined', () => {
2746
+ it('should validate field', async () => {
2747
+ const assertObj = { test: 'Hello' };
2748
+ return expect(
2749
+ object()
2750
+ .shape({
2751
+ test: fields[0].schema,
2752
+ })
2753
+ .validate(assertObj)
2754
+ ).resolves.toEqual(assertObj);
2618
2755
  });
2619
2756
  });
2620
2757
  });
2621
- });
2622
2758
 
2623
- describe('when a field has a maxLength of 10', () => {
2624
- let fields;
2625
- beforeEach(() => {
2626
- const result = createHeadlessForm(
2627
- buildJSONSchemaInput({
2628
- presentationFields: { inputType: 'text' },
2629
- inputFields: { maxLength: 10 },
2630
- })
2631
- );
2632
- fields = result.fields;
2633
- });
2634
- describe('and value is greater than that', () => {
2635
- it('should throw an error', async () =>
2636
- expect(
2637
- object()
2638
- .shape({
2639
- test: fields[0].schema,
2640
- })
2641
- .validate({ test: 'Hello Mr John Doe' })
2642
- ).rejects.toMatchObject({ errors: ['Please insert up to 10 characters'] }));
2643
- });
2644
- describe('and value is less than that', () => {
2645
- it('should validate field', async () => {
2646
- const assertObj = { test: 'Hello John' };
2647
- return expect(
2648
- object()
2649
- .shape({
2650
- test: fields[0].schema,
2651
- })
2652
- .validate(assertObj)
2653
- ).resolves.toEqual(assertObj);
2759
+ describe('when a field is number', () => {
2760
+ let fields;
2761
+ beforeEach(() => {
2762
+ const result = createHeadlessForm(
2763
+ buildJSONSchemaInput({ presentationFields: { inputType: 'number' } })
2764
+ );
2765
+ fields = result.fields;
2654
2766
  });
2655
- });
2656
- });
2657
-
2658
- describe('when a field has a minLength of 2', () => {
2659
- let fields;
2660
- beforeEach(() => {
2661
- const result = createHeadlessForm(
2662
- buildJSONSchemaInput({
2663
- presentationFields: { inputType: 'text' },
2664
- inputFields: { minLength: 2 },
2665
- })
2666
- );
2667
- fields = result.fields;
2668
- });
2669
- describe('and value is smaller than that', () => {
2670
- it('should throw an error', async () =>
2671
- expect(
2672
- object()
2673
- .shape({
2674
- test: fields[0].schema,
2675
- })
2676
- .validate({ test: 'H' })
2677
- ).rejects.toMatchObject({ errors: ['Please insert at least 2 characters'] }));
2678
- });
2679
- describe('and value is greater than that', () => {
2680
- it('should validate field', async () => {
2681
- const assertObj = { test: 'Hello John' };
2682
- return expect(
2683
- object()
2684
- .shape({
2685
- test: fields[0].schema,
2686
- })
2687
- .validate(assertObj)
2688
- ).resolves.toEqual(assertObj);
2767
+ describe('and value is a string', () => {
2768
+ it('should throw an error', async () =>
2769
+ expect(
2770
+ object()
2771
+ .shape({
2772
+ test: fields[0].schema,
2773
+ })
2774
+ .validate({ test: 'Hello' })
2775
+ ).rejects.toThrow());
2689
2776
  });
2690
- });
2691
- });
2692
-
2693
- describe('when a field has a minimum of 0', () => {
2694
- let fields;
2695
- beforeEach(() => {
2696
- const result = createHeadlessForm(
2697
- buildJSONSchemaInput({
2698
- presentationFields: { inputType: 'number' },
2699
- inputFields: { minimum: 0 },
2700
- })
2701
- );
2702
- fields = result.fields;
2703
- });
2704
-
2705
- describe('and value is less than that', () => {
2706
- it('should throw an error', async () =>
2707
- expect(
2708
- object()
2709
- .shape({
2710
- test: fields[0].schema,
2711
- })
2712
- .validate({ test: -1 })
2713
- ).rejects.toMatchObject({ errors: ['Must be greater or equal to 0'] }));
2714
- });
2715
-
2716
- describe('and value is greater than that', () => {
2717
- it('should validate field', async () => {
2718
- const assertObj = { test: 4 };
2719
- return expect(
2720
- object()
2721
- .shape({
2722
- test: fields[0].schema,
2723
- })
2724
- .validate(assertObj)
2725
- ).resolves.toEqual(assertObj);
2777
+ describe('and value is a number', () => {
2778
+ it('should validate field', async () => {
2779
+ const assertObj = { test: 3 };
2780
+ return expect(
2781
+ object()
2782
+ .shape({
2783
+ test: fields[0].schema,
2784
+ })
2785
+ .validate(assertObj)
2786
+ ).resolves.toEqual(assertObj);
2787
+ });
2726
2788
  });
2727
- });
2728
- });
2789
+ describe('and maximum is set to zero', () => {
2790
+ it('shows the correct validation', () => {
2791
+ const { handleValidation } = createHeadlessForm(schemaInputTypeNumberZeroMaximum);
2792
+ const validateForm = (vals) => friendlyError(handleValidation(vals));
2729
2793
 
2730
- describe('when a field has a maximum of 10', () => {
2731
- let fields;
2732
- beforeEach(() => {
2733
- const result = createHeadlessForm(
2734
- buildJSONSchemaInput({
2735
- presentationFields: { inputType: 'number' },
2736
- inputFields: { maximum: 10 },
2737
- })
2738
- );
2739
- fields = result.fields;
2740
- });
2741
-
2742
- describe('and value is greater than that', () => {
2743
- it('should throw an error', async () =>
2744
- expect(
2745
- object()
2746
- .shape({
2747
- test: fields[0].schema,
2748
- })
2749
- .validate({ test: 11 })
2750
- ).rejects.toMatchObject({ errors: ['Must be smaller or equal to 10'] }));
2751
- });
2794
+ expect(validateForm({ tabs: '0' })).toBeUndefined();
2795
+ expect(validateForm({ tabs: '-10' })).toBeUndefined();
2752
2796
 
2753
- describe('and value is greater than that', () => {
2754
- it('should validate field', async () => {
2755
- const assertObj = { test: 4 };
2756
- return expect(
2757
- object()
2758
- .shape({
2759
- test: fields[0].schema,
2760
- })
2761
- .validate(assertObj)
2762
- ).resolves.toEqual(assertObj);
2797
+ expect(validateForm({ tabs: 1 })).toEqual({
2798
+ tabs: 'Must be smaller or equal to 0',
2799
+ });
2800
+ });
2763
2801
  });
2764
2802
  });
2765
- });
2766
2803
 
2767
- describe('when a field has a pattern', () => {
2768
- let fields;
2769
- beforeEach(() => {
2770
- const result = createHeadlessForm(
2771
- buildJSONSchemaInput({
2772
- presentationFields: { inputType: 'text' },
2773
- inputFields: { pattern: '^[0-9]{3}-[0-9]{2}-(?!0{4})[0-9]{4}$' },
2774
- })
2775
- );
2776
- fields = result.fields;
2777
- });
2778
- describe('and value does not match the pattern', () => {
2779
- it('should throw an error', async () =>
2780
- expect(
2781
- object()
2782
- .shape({
2783
- test: fields[0].schema,
2784
- })
2785
- .validate({ test: 'Hello' })
2786
- ).rejects.toMatchObject({ errors: [expect.any(String)] }));
2787
- });
2788
- describe('and value matches the pattern', () => {
2789
- it('should validate field', async () => {
2790
- const assertObj = { test: '401-85-1950' };
2791
- return expect(
2792
- object()
2793
- .shape({
2794
- test: fields[0].schema,
2795
- })
2796
- .validate(assertObj)
2797
- ).resolves.toEqual(assertObj);
2804
+ describe('when a field has a maxLength of 10', () => {
2805
+ let fields;
2806
+ beforeEach(() => {
2807
+ const result = createHeadlessForm(
2808
+ buildJSONSchemaInput({
2809
+ presentationFields: { inputType: 'text' },
2810
+ inputFields: { maxLength: 10 },
2811
+ })
2812
+ );
2813
+ fields = result.fields;
2814
+ });
2815
+ describe('and value is greater than that', () => {
2816
+ it('should throw an error', async () =>
2817
+ expect(
2818
+ object()
2819
+ .shape({
2820
+ test: fields[0].schema,
2821
+ })
2822
+ .validate({ test: 'Hello Mr John Doe' })
2823
+ ).rejects.toMatchObject({ errors: ['Please insert up to 10 characters'] }));
2824
+ });
2825
+ describe('and value is less than that', () => {
2826
+ it('should validate field', async () => {
2827
+ const assertObj = { test: 'Hello John' };
2828
+ return expect(
2829
+ object()
2830
+ .shape({
2831
+ test: fields[0].schema,
2832
+ })
2833
+ .validate(assertObj)
2834
+ ).resolves.toEqual(assertObj);
2835
+ });
2798
2836
  });
2799
2837
  });
2800
- });
2801
-
2802
- describe('when a field has max file size', () => {
2803
- let fields;
2804
- beforeEach(() => {
2805
- const result = createHeadlessForm(
2806
- JSONSchemaBuilder().addInput({ fileInput: mockFileInput }).build()
2807
- );
2808
- fields = result.fields;
2809
- });
2810
- describe('and file is greater than that', () => {
2811
- const file = new File([''], 'file.png');
2812
- Object.defineProperty(file, 'size', { value: 1024 * 1024 * 1024 });
2813
-
2814
- it('should throw an error', async () =>
2815
- expect(
2816
- object()
2817
- .shape({
2818
- fileInput: fields[0].schema,
2819
- })
2820
- .validate({ fileInput: [file] })
2821
- ).rejects.toMatchObject({ errors: ['File size too large. The limit is 20 MB.'] }));
2822
- });
2823
- describe('and file is smaller than that', () => {
2824
- const file = new File([''], 'file.png');
2825
- Object.defineProperty(file, 'size', { value: 1024 * 1024 });
2826
-
2827
- const assertObj = { fileInput: [file] };
2828
- it('should validate field', async () =>
2829
- expect(
2830
- object()
2831
- .shape({
2832
- fileInput: fields[0].schema,
2833
- })
2834
- .validate({ fileInput: [file] })
2835
- ).resolves.toEqual(assertObj));
2836
- });
2837
- });
2838
2838
 
2839
- describe('when a field file is optional', () => {
2840
- it('it accepts an empty array', () => {
2841
- const result = createHeadlessForm(
2842
- JSONSchemaBuilder().addInput({ fileInput: mockFileInput }).build()
2843
- );
2844
- const emptyFile = { fileInput: [] };
2845
- expect(
2846
- object()
2847
- .shape({
2848
- fileInput: result.fields[0].schema,
2839
+ describe('when a field has a minLength of 2', () => {
2840
+ let fields;
2841
+ beforeEach(() => {
2842
+ const result = createHeadlessForm(
2843
+ buildJSONSchemaInput({
2844
+ presentationFields: { inputType: 'text' },
2845
+ inputFields: { minLength: 2 },
2849
2846
  })
2850
- .validate(emptyFile)
2851
- ).resolves.toEqual(emptyFile);
2852
- });
2853
-
2854
- it('it validates missing file correctly', () => {
2855
- const { handleValidation } = createHeadlessForm(
2856
- JSONSchemaBuilder().addInput({ fileInput: mockFileInput }).build()
2857
- );
2858
- const validateForm = (vals) => friendlyError(handleValidation(vals));
2859
-
2860
- expect(validateForm({})).toBeUndefined();
2861
- expect(validateForm({ fileInput: null })).toBeUndefined();
2862
- });
2863
- });
2864
-
2865
- describe('when a field file is required', () => {
2866
- it('it validates missing file correctly', () => {
2867
- const { handleValidation } = createHeadlessForm(schemaInputTypeFile);
2868
- const validateForm = (vals) => friendlyError(handleValidation(vals));
2869
-
2870
- expect(validateForm({})).toEqual({
2871
- a_file: 'Required field',
2847
+ );
2848
+ fields = result.fields;
2872
2849
  });
2873
-
2874
- expect(
2875
- validateForm({
2876
- a_file: null,
2877
- })
2878
- ).toEqual({
2879
- a_file: 'Required field',
2850
+ describe('and value is smaller than that', () => {
2851
+ it('should throw an error', async () =>
2852
+ expect(
2853
+ object()
2854
+ .shape({
2855
+ test: fields[0].schema,
2856
+ })
2857
+ .validate({ test: 'H' })
2858
+ ).rejects.toMatchObject({ errors: ['Please insert at least 2 characters'] }));
2859
+ });
2860
+ describe('and value is greater than that', () => {
2861
+ it('should validate field', async () => {
2862
+ const assertObj = { test: 'Hello John' };
2863
+ return expect(
2864
+ object()
2865
+ .shape({
2866
+ test: fields[0].schema,
2867
+ })
2868
+ .validate(assertObj)
2869
+ ).resolves.toEqual(assertObj);
2870
+ });
2880
2871
  });
2881
2872
  });
2882
- });
2883
2873
 
2884
- describe('when a field has accepted extensions', () => {
2885
- let fields;
2886
- beforeEach(() => {
2887
- const result = createHeadlessForm(
2888
- JSONSchemaBuilder().addInput({ fileInput: mockFileInput }).build()
2889
- );
2890
- fields = result.fields;
2891
- });
2892
- describe('and file is of incorrect format', () => {
2893
- const file = new File(['foo'], 'file.txt', {
2894
- type: 'text/plain',
2874
+ describe('when a field has a minimum of 0', () => {
2875
+ let fields;
2876
+ beforeEach(() => {
2877
+ const result = createHeadlessForm(
2878
+ buildJSONSchemaInput({
2879
+ presentationFields: { inputType: 'number' },
2880
+ inputFields: { minimum: 0 },
2881
+ })
2882
+ );
2883
+ fields = result.fields;
2895
2884
  });
2896
2885
 
2897
- it('should throw an error', async () =>
2898
- expect(
2899
- object()
2900
- .shape({
2901
- fileInput: fields[0].schema,
2902
- })
2903
- .validate({ fileInput: [file] })
2904
- ).rejects.toMatchObject({
2905
- errors: ['Unsupported file format. The acceptable formats are .png,.jpg,.jpeg,.pdf.'],
2906
- }));
2907
- });
2908
- describe('and file is of correct format', () => {
2909
- const file = new File(['foo'], 'file.png', {
2910
- type: 'image/png',
2886
+ describe('and value is less than that', () => {
2887
+ it('should throw an error', async () =>
2888
+ expect(
2889
+ object()
2890
+ .shape({
2891
+ test: fields[0].schema,
2892
+ })
2893
+ .validate({ test: -1 })
2894
+ ).rejects.toMatchObject({ errors: ['Must be greater or equal to 0'] }));
2911
2895
  });
2912
- Object.defineProperty(file, 'size', { value: 1024 * 1024 });
2913
2896
 
2914
- const assertObj = { fileInput: [file] };
2915
- it('should validate field', async () =>
2916
- expect(
2917
- object()
2918
- .shape({
2919
- fileInput: fields[0].schema,
2920
- })
2921
- .validate({ fileInput: [file] })
2922
- ).resolves.toEqual(assertObj));
2923
- });
2924
-
2925
- describe('and file is of correct but uppercase format ', () => {
2926
- const file = new File(['foo'], 'file.PNG', {
2927
- type: 'image/png',
2897
+ describe('and value is greater than that', () => {
2898
+ it('should validate field', async () => {
2899
+ const assertObj = { test: 4 };
2900
+ return expect(
2901
+ object()
2902
+ .shape({
2903
+ test: fields[0].schema,
2904
+ })
2905
+ .validate(assertObj)
2906
+ ).resolves.toEqual(assertObj);
2907
+ });
2928
2908
  });
2929
- Object.defineProperty(file, 'size', { value: 1024 * 1024 });
2930
-
2931
- const assertObj = { fileInput: [file] };
2932
- it('should validate field', async () =>
2933
- expect(
2934
- object()
2935
- .shape({
2936
- fileInput: fields[0].schema,
2937
- })
2938
- .validate({ fileInput: [file] })
2939
- ).resolves.toEqual(assertObj));
2940
2909
  });
2941
2910
 
2942
- describe('and file is not instance of a File', () => {
2943
- it('accepts if file object has name property', async () => {
2944
- expect(
2945
- object()
2946
- .shape({
2947
- fileInput: fields[0].schema,
2948
- })
2949
- .validate({ fileInput: [{ name: 'foo.pdf' }] })
2950
- ).resolves.toEqual({ fileInput: [{ name: 'foo.pdf' }] });
2911
+ describe('when a field has a maximum of 10', () => {
2912
+ let fields;
2913
+ beforeEach(() => {
2914
+ const result = createHeadlessForm(
2915
+ buildJSONSchemaInput({
2916
+ presentationFields: { inputType: 'number' },
2917
+ inputFields: { maximum: 10 },
2918
+ })
2919
+ );
2920
+ fields = result.fields;
2951
2921
  });
2952
2922
 
2953
- it('should validate format', async () =>
2954
- expect(
2955
- object()
2956
- .shape({
2957
- fileInput: fields[0].schema,
2958
- })
2959
- .validate({ fileInput: [{ name: 'foo.txt' }] })
2960
- ).rejects.toMatchObject({
2961
- errors: ['Unsupported file format. The acceptable formats are .png,.jpg,.jpeg,.pdf.'],
2962
- }));
2963
-
2964
- it('should validate max size', async () =>
2965
- expect(
2966
- object()
2967
- .shape({
2968
- fileInput: fields[0].schema,
2969
- })
2970
- .validate({ fileInput: [{ name: 'foo.txt', size: 1024 * 1024 * 1024 }] })
2971
- ).rejects.toMatchObject({ errors: ['File size too large. The limit is 20 MB.'] }));
2923
+ describe('and value is greater than that', () => {
2924
+ it('should throw an error', async () =>
2925
+ expect(
2926
+ object()
2927
+ .shape({
2928
+ test: fields[0].schema,
2929
+ })
2930
+ .validate({ test: 11 })
2931
+ ).rejects.toMatchObject({ errors: ['Must be smaller or equal to 10'] }));
2932
+ });
2972
2933
 
2973
- it('throw an error if invalid file object', async () =>
2974
- expect(
2975
- object()
2976
- .shape({
2977
- fileInput: fields[0].schema,
2978
- })
2979
- .validate({ fileInput: [{ path: 'foo.txt' }] })
2980
- ).rejects.toMatchObject({ errors: ['Not a valid file.'] }));
2934
+ describe('and value is greater than that', () => {
2935
+ it('should validate field', async () => {
2936
+ const assertObj = { test: 4 };
2937
+ return expect(
2938
+ object()
2939
+ .shape({
2940
+ test: fields[0].schema,
2941
+ })
2942
+ .validate(assertObj)
2943
+ ).resolves.toEqual(assertObj);
2944
+ });
2945
+ });
2981
2946
  });
2982
- });
2983
2947
 
2984
- describe('when a field has conditional presentation properties', () => {
2985
- it('returns the nested properties when the conditional matches', () => {
2986
- const { fields } = createHeadlessForm(schemaWithConditionalPresentationProperties, {
2987
- initialValues: {
2988
- // show the hidden statement
2989
- mock_radio: 'no',
2990
- },
2948
+ describe('when a field has a pattern', () => {
2949
+ let fields;
2950
+ beforeEach(() => {
2951
+ const result = createHeadlessForm(
2952
+ buildJSONSchemaInput({
2953
+ presentationFields: { inputType: 'text' },
2954
+ inputFields: { pattern: '^[0-9]{3}-[0-9]{2}-(?!0{4})[0-9]{4}$' },
2955
+ })
2956
+ );
2957
+ fields = result.fields;
2958
+ });
2959
+ describe('and value does not match the pattern', () => {
2960
+ it('should throw an error', async () =>
2961
+ expect(
2962
+ object()
2963
+ .shape({
2964
+ test: fields[0].schema,
2965
+ })
2966
+ .validate({ test: 'Hello' })
2967
+ ).rejects.toMatchObject({ errors: [expect.any(String)] }));
2968
+ });
2969
+ describe('and value matches the pattern', () => {
2970
+ it('should validate field', async () => {
2971
+ const assertObj = { test: '401-85-1950' };
2972
+ return expect(
2973
+ object()
2974
+ .shape({
2975
+ test: fields[0].schema,
2976
+ })
2977
+ .validate(assertObj)
2978
+ ).resolves.toEqual(assertObj);
2979
+ });
2991
2980
  });
2992
-
2993
- expect(fields[0].statement.description).toBe(`<a href="">conditional statement markup</a>`);
2994
2981
  });
2995
2982
  });
2996
2983
 
2997
- describe('when a JSON Schema is provided', () => {
2984
+ describe('even more validations', () => {
2998
2985
  describe('and all fields are optional', () => {
2999
2986
  let handleValidation;
3000
2987
  const validateForm = (vals) => friendlyError(handleValidation(vals));
3001
2988
 
3002
2989
  beforeEach(() => {
3003
- const result = JSONSchemaBuilder()
3004
- .addInput({ textInput: mockTextInput })
3005
- .addInput({ numberInput: mockNumberInput })
3006
- .build();
2990
+ const result = {
2991
+ properties: {
2992
+ textInput: mockTextInput,
2993
+ numberInput: mockNumberInput,
2994
+ },
2995
+ };
3007
2996
  const { handleValidation: handleValidationEach } = createHeadlessForm(result);
3008
2997
  handleValidation = handleValidationEach;
3009
2998
  });
@@ -3034,11 +3023,13 @@ describe('createHeadlessForm', () => {
3034
3023
  const validateForm = (vals) => friendlyError(handleValidation(vals));
3035
3024
 
3036
3025
  beforeEach(() => {
3037
- const result = JSONSchemaBuilder()
3038
- .addInput({ textInput: mockTextInput })
3039
- .addInput({ numberInput: mockNumberInput })
3040
- .setRequiredFields(['numberInput', 'textInput'])
3041
- .build();
3026
+ const result = {
3027
+ properties: {
3028
+ textInput: mockTextInput,
3029
+ numberInput: mockNumberInput,
3030
+ },
3031
+ required: ['numberInput', 'textInput'],
3032
+ };
3042
3033
  const { handleValidation: handleValidationEach } = createHeadlessForm(result);
3043
3034
  handleValidation = handleValidationEach;
3044
3035
  });
@@ -3865,7 +3856,19 @@ describe('createHeadlessForm', () => {
3865
3856
  });
3866
3857
  });
3867
3858
 
3868
- describe('when default values are provided', () => {
3859
+ describe('given default values', () => {
3860
+ describe('and a field with conditional presentation properties', () => {
3861
+ it('returns the nested properties when the conditional matches', () => {
3862
+ const { fields } = createHeadlessForm(schemaWithConditionalPresentationProperties, {
3863
+ initialValues: {
3864
+ // show the hidden statement
3865
+ mock_radio: 'no',
3866
+ },
3867
+ });
3868
+
3869
+ expect(fields[0].statement.description).toBe(`<a href="">conditional statement markup</a>`);
3870
+ });
3871
+ });
3869
3872
  describe('and "fieldset" has scoped conditionals', () => {
3870
3873
  it('should show conditionals fields when values fullfil conditions', () => {
3871
3874
  const result = createHeadlessForm(schemaFieldsetScopedCondition, {