@remoteoss/json-schema-form 0.11.9-dev.20241210213029 → 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.
@@ -1,58 +1,58 @@
1
1
  import { createHeadlessForm } from '../createHeadlessForm';
2
2
 
3
- it('Should allow check of a nested property in a conditional', () => {
4
- const { handleValidation } = createHeadlessForm(
5
- {
6
- additionalProperties: false,
7
- allOf: [
8
- {
9
- if: {
10
- properties: {
11
- parent: {
12
- properties: {
13
- child: {
14
- const: 'yes',
3
+ describe('Conditional attributes updated', () => {
4
+ it('Should allow check of a nested property in a conditional', () => {
5
+ const { handleValidation } = createHeadlessForm(
6
+ {
7
+ additionalProperties: false,
8
+ allOf: [
9
+ {
10
+ if: {
11
+ properties: {
12
+ parent: {
13
+ properties: {
14
+ child: {
15
+ const: 'yes',
16
+ },
15
17
  },
18
+ required: ['child'],
16
19
  },
17
- required: ['child'],
18
20
  },
21
+ required: ['parent'],
19
22
  },
20
- required: ['parent'],
23
+ then: { required: ['parent_sibling'] },
21
24
  },
22
- then: { required: ['parent_sibling'] },
23
- },
24
- ],
25
- properties: {
26
- parent: {
27
- additionalProperties: false,
28
- properties: {
29
- child: {
30
- oneOf: [{ const: 'yes' }, { const: 'no' }],
31
- type: 'string',
25
+ ],
26
+ properties: {
27
+ parent: {
28
+ additionalProperties: false,
29
+ properties: {
30
+ child: {
31
+ oneOf: [{ const: 'yes' }, { const: 'no' }],
32
+ type: 'string',
33
+ },
32
34
  },
35
+ required: ['child'],
36
+ type: 'object',
37
+ },
38
+ parent_sibling: {
39
+ type: 'integer',
33
40
  },
34
- required: ['child'],
35
- type: 'object',
36
- },
37
- parent_sibling: {
38
- type: 'integer',
39
41
  },
42
+ required: ['parent'],
43
+ type: 'object',
40
44
  },
41
- required: ['parent'],
42
- type: 'object',
43
- },
44
- { strictInputType: false }
45
- );
46
- expect(handleValidation({ parent: { child: 'no' } }).formErrors).toEqual(undefined);
47
- expect(handleValidation({ parent: { child: 'yes' } }).formErrors).toEqual({
48
- parent_sibling: 'Required field',
45
+ { strictInputType: false }
46
+ );
47
+ expect(handleValidation({ parent: { child: 'no' } }).formErrors).toEqual(undefined);
48
+ expect(handleValidation({ parent: { child: 'yes' } }).formErrors).toEqual({
49
+ parent_sibling: 'Required field',
50
+ });
51
+ expect(handleValidation({ parent: { child: 'yes' }, parent_sibling: 1 }).formErrors).toEqual(
52
+ undefined
53
+ );
49
54
  });
50
- expect(handleValidation({ parent: { child: 'yes' }, parent_sibling: 1 }).formErrors).toEqual(
51
- undefined
52
- );
53
- });
54
55
 
55
- describe('Conditional attributes updated', () => {
56
56
  it('Update basic case with const, default, maximum', () => {
57
57
  const { fields, handleValidation } = createHeadlessForm(
58
58
  {
@@ -480,116 +480,232 @@ describe('Conditional with a minimum value check', () => {
480
480
  });
481
481
  });
482
482
 
483
- describe('Multiple conditions affecting the same field', () => {
484
- it('Should apply all conditions', () => {
483
+ describe('Conditional with literal booleans', () => {
484
+ it('handles true case', () => {
485
485
  const schema = {
486
486
  properties: {
487
- answer: { type: 'string' },
488
- number: { type: 'number' },
489
- },
490
- allOf: [
491
- {
492
- if: { properties: { answer: { const: 'yes' } } },
493
- then: { properties: { number: { minimum: 10 } } },
487
+ is_full_time: {
488
+ type: 'boolean',
494
489
  },
495
- {
496
- if: { properties: { answer: { const: 'yes' } } },
497
- then: { properties: { number: { maximum: 20 } } },
490
+ salary: {
491
+ type: 'number',
498
492
  },
499
- ],
493
+ },
494
+ required: [],
495
+ if: true,
496
+ then: {
497
+ required: ['is_full_time'],
498
+ },
499
+ else: {
500
+ required: ['salary'],
501
+ },
500
502
  };
503
+ const { fields, handleValidation } = createHeadlessForm(schema, { strictInputType: false });
501
504
 
502
- const { handleValidation } = createHeadlessForm(schema, { strictInputType: false });
503
- expect(handleValidation({ answer: 'yes', number: 15 }).formErrors).toEqual(undefined);
504
- expect(handleValidation({ answer: 'yes', number: 9 }).formErrors).toEqual({
505
- number: 'Must be greater or equal to 10',
505
+ handleValidation({});
506
+
507
+ expect(fields[0]).toMatchObject({
508
+ name: 'is_full_time',
509
+ required: true,
506
510
  });
507
- expect(handleValidation({ answer: 'yes', number: 21 }).formErrors).toEqual({
508
- number: 'Must be smaller or equal to 20',
511
+ expect(fields[1]).toMatchObject({
512
+ name: 'salary',
513
+ required: false,
509
514
  });
510
- expect(handleValidation({ answer: 'no', number: 9 }).formErrors).toBeUndefined();
511
- expect(handleValidation({ answer: 'no', number: 15 }).formErrors).toBeUndefined();
512
- expect(handleValidation({ answer: 'no', number: 21 }).formErrors).toBeUndefined();
513
515
  });
514
516
 
515
- it('Accepts unsatisfiable conflicting conditions', () => {
516
- const unsatisfiableSchema = {
517
+ it('handles false case', () => {
518
+ const schema = {
517
519
  properties: {
518
- has_books: { type: 'string' },
519
- books: {
520
+ is_full_time: {
521
+ type: 'boolean',
522
+ },
523
+ salary: {
520
524
  type: 'number',
521
- const: 4,
522
525
  },
523
526
  },
524
- required: ['books'],
525
- allOf: [
526
- {
527
- if: { properties: { has_books: { const: 'yes' } } },
528
- then: {
529
- properties: {
530
- books: {
531
- minimum: 5,
532
- },
533
- },
527
+ required: [],
528
+ if: false,
529
+ then: {
530
+ required: ['is_full_time'],
531
+ },
532
+ else: {
533
+ required: ['salary'],
534
+ },
535
+ };
536
+ const { fields, handleValidation } = createHeadlessForm(schema, { strictInputType: false });
537
+
538
+ handleValidation({});
539
+
540
+ expect(fields[0]).toMatchObject({
541
+ name: 'is_full_time',
542
+ required: false,
543
+ });
544
+ expect(fields[1]).toMatchObject({
545
+ name: 'salary',
546
+ required: true,
547
+ });
548
+ });
549
+ });
550
+
551
+ describe('Conditionals - bugs and code-smells', () => {
552
+ // Why do we have these bugs?
553
+ // To be honest we never realized it much later later.
554
+ // We will fix them in the next major version.
555
+
556
+ const schemaHasPet = {
557
+ type: 'object',
558
+ additionalProperties: false,
559
+ properties: {
560
+ has_pet: {
561
+ title: 'Has Pet',
562
+ description: 'Do you have a pet?',
563
+ oneOf: [
564
+ { title: 'Yes', const: 'yes' },
565
+ { title: 'No', const: 'no' },
566
+ ],
567
+ type: 'string',
568
+ },
569
+ pet_name: {
570
+ title: "Pet's name",
571
+ type: 'string',
572
+ },
573
+ },
574
+ required: ['has_pet'],
575
+ allOf: [
576
+ {
577
+ if: {
578
+ properties: {
579
+ has_pet: { const: 'yes' },
534
580
  },
581
+ required: ['has_pet'],
535
582
  },
536
- ],
537
- };
583
+ then: {
584
+ required: ['pet_name'],
585
+ },
586
+ else: {
587
+ properties: {
588
+ pet_name: false,
589
+ },
590
+ },
591
+ },
592
+ ],
593
+ };
538
594
 
539
- // This is intentionally not able to be satisfied.
540
- const { handleValidation } = createHeadlessForm(unsatisfiableSchema, {
595
+ it('Given values from hidden fields, it does not thrown an error (@bug)', () => {
596
+ const { fields, handleValidation } = createHeadlessForm(schemaHasPet, {
541
597
  strictInputType: false,
542
598
  });
543
- expect(handleValidation({ has_books: 'yes', books: 4 }).formErrors).toEqual({
544
- books: 'Must be greater or equal to 5',
599
+
600
+ const petNameField = fields[1];
601
+
602
+ const validation = handleValidation({ has_pet: 'no', pet_name: 'Max' });
603
+ expect(petNameField.isVisible).toBe(false);
604
+
605
+ // Bug: 🐛 It does not thrown an error,
606
+ // but it should to be compliant with JSON Schema specs.
607
+ expect(validation.formErrors).toBeUndefined();
608
+ // The error should be something like:
609
+ // expect(validation.formErrors).toEqual({ pet_name: 'Not allowed.'});
610
+ });
611
+
612
+ it('Given values from hidden fields, it mutates the values (@bug)', () => {
613
+ const { handleValidation } = createHeadlessForm(schemaHasPet, {
614
+ strictInputType: false,
545
615
  });
546
- expect(handleValidation({ has_books: 'yes', books: 5 }).formErrors).toEqual({
547
- books: 'The only accepted value is 4.',
616
+
617
+ const newValues = { has_pet: 'no', pet_name: 'Max' };
618
+
619
+ const validation = handleValidation(newValues);
620
+
621
+ expect(newValues).toEqual({
622
+ has_pet: 'no',
623
+ pet_name: null, // BUG! 🐛 Should still be "Max", should not be mutated.
548
624
  });
625
+
626
+ // Same bug as explained in the previous test.
627
+ expect(validation.formErrors).toBeUndefined();
549
628
  });
550
629
 
551
- it('Computed attributes also get the same treatment for unsatisfiable conflicting conditions', () => {
552
- const unsatisfiableSchema = {
553
- properties: {
554
- has_books: { type: 'string' },
555
- books: {
556
- type: 'number',
557
- 'x-jsf-logic-computedAttrs': {
558
- const: 'book_space',
559
- },
630
+ it('Given multiple conditionals to the same field, it only applies the last one (@bug) - case 1', () => {
631
+ const { handleValidation } = createHeadlessForm(
632
+ {
633
+ additionalProperties: false,
634
+ properties: {
635
+ field_a: { type: 'string' },
636
+ field_b: { type: 'string' },
637
+ field_c: { type: 'number' },
560
638
  },
561
- },
562
- required: ['books'],
563
- allOf: [
564
- {
565
- if: { properties: { has_books: { const: 'yes' } } },
566
- then: {
567
- properties: {
568
- books: {
569
- minimum: 5,
570
- },
639
+ allOf: [
640
+ {
641
+ if: {
642
+ properties: { field_a: { const: 'yes' } },
643
+ required: ['field_a'],
571
644
  },
645
+ then: { properties: { field_c: { minimum: 30 } } },
572
646
  },
573
- },
574
- ],
575
- 'x-jsf-logic': {
576
- computedValues: {
577
- book_space: {
578
- rule: {
579
- '*': [2, 2],
647
+ {
648
+ if: {
649
+ properties: { field_b: { const: 'yes' } },
650
+ required: ['field_b'],
580
651
  },
652
+ then: { properties: { field_c: { minimum: 10 } } },
581
653
  },
582
- },
654
+ ],
583
655
  },
584
- };
585
- const { handleValidation } = createHeadlessForm(unsatisfiableSchema, {
586
- strictInputType: false,
587
- });
588
- expect(handleValidation({ has_books: 'yes', books: 4 }).formErrors).toEqual({
589
- books: 'Must be greater or equal to 5',
656
+ {
657
+ strictInputType: false,
658
+ }
659
+ );
660
+
661
+ const validation = handleValidation({ field_a: 'yes', field_b: 'yes', field_c: 5 });
662
+ expect(validation.formErrors).toEqual({
663
+ // BUG: 🐛 it should be "Must be greater or equal to 30"
664
+ field_c: 'Must be greater or equal to 10',
590
665
  });
591
- expect(handleValidation({ has_books: 'yes', books: 5 }).formErrors).toEqual({
592
- books: 'The only accepted value is 4.',
666
+ });
667
+
668
+ it('Given multiple conditionals to the same field, it only applies the last one (@bug) - case 2', () => {
669
+ const { handleValidation } = createHeadlessForm(
670
+ {
671
+ additionalProperties: false,
672
+ properties: {
673
+ field_a: { type: 'string' },
674
+ field_b: { type: 'string' },
675
+ field_c: { type: 'number' },
676
+ },
677
+ allOf: [
678
+ {
679
+ if: {
680
+ properties: { field_a: { const: 'yes' } },
681
+ required: ['field_a'],
682
+ },
683
+ then: { properties: { field_c: { minimum: 5 } } },
684
+ },
685
+ {
686
+ if: {
687
+ properties: { field_b: { const: 'yes' } },
688
+ required: ['field_b'],
689
+ },
690
+ then: { properties: { field_c: { maximum: 10 } } },
691
+ },
692
+ ],
693
+ },
694
+ {
695
+ strictInputType: false,
696
+ }
697
+ );
698
+
699
+ const validation1 = handleValidation({ field_a: 'yes', field_b: 'yes', field_c: 12 });
700
+ expect(validation1.formErrors).toEqual({
701
+ field_c: 'Must be smaller or equal to 10',
593
702
  });
703
+
704
+ const validation2 = handleValidation({ field_a: 'yes', field_b: 'yes', field_c: 3 });
705
+ // BUG: 🐛 it should be "Must be greater or equal to 5"
706
+ expect(validation2.formErrors).toBeUndefined();
707
+ // expect(validation1.formErrors).toEqual({
708
+ // field_c: 'Must be greater or equal to 5',
709
+ // });
594
710
  });
595
711
  });
@@ -140,7 +140,8 @@ afterAll(() => {
140
140
  console.warn.mockRestore();
141
141
  });
142
142
 
143
- describe('createHeadlessForm() - custom validations', () => {
143
+ // @deprecated - customProperties won't be supported in v2.
144
+ describe('createHeadlessForm() - custom validations (deprecated)', () => {
144
145
  describe('simple validation (eg maximum)', () => {
145
146
  it('works as a number', () => {
146
147
  const { fields, validateForm } = createScenario({