@zohodesk/library-platform 1.2.0 → 1.2.2-exp.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (22) hide show
  1. package/es/index.js +6 -2
  2. package/es/library/custom-component/frameworks/json-schema-validator/Validator.js +1 -164
  3. package/es/library/custom-component/frameworks/ui/DependencyFactory.js +1 -1
  4. package/es/library/json-schema-validator/Validator.js +338 -0
  5. package/es/library/json-schema-validator/__tests__/Validator.test.js +753 -0
  6. package/es/library/json-schema-validator/index.js +1 -0
  7. package/es/platform/client-actions/behaviour/zclient-actions/adapters/presenters/FilterUtils.js +11 -0
  8. package/es/platform/client-actions/behaviour/zclient-actions/adapters/resources/ClientActionsFetchSDK.js +76 -0
  9. package/es/platform/client-actions/behaviour/zclient-actions/applications/interfaces/ClientActionsAPIGatewayParams.js +1 -0
  10. package/es/platform/client-actions/behaviour/zclient-actions/applications/interfaces/ClientActionsFetchSDKParams.js +10 -0
  11. package/es/platform/client-actions/behaviour/zclient-actions/applications/interfaces/IClientActionsFetchSDK.js +0 -0
  12. package/es/platform/client-scripts/behaviour/zclient-scripts-fetch/adapters/resources/ClientScriptsFetchSDK.js +43 -0
  13. package/es/platform/client-scripts/behaviour/zclient-scripts-fetch/applications/interfaces/ClientScriptsAPIGatewayParams.js +1 -0
  14. package/es/platform/client-scripts/behaviour/zclient-scripts-fetch/applications/interfaces/ClientScriptsSDKFetchParams.js +1 -0
  15. package/es/platform/client-scripts/behaviour/zclient-scripts-fetch/applications/interfaces/IClientScriptsFetchSDK.js +0 -0
  16. package/es/platform/components/smart-action-band/adapters/presenters/ActionBandTranslator.js +7 -4
  17. package/es/platform/data-source/http-template/getPageClientActions.js +23 -0
  18. package/es/platform/sdk/application/interfaces/gateways/AbstractResource.js +1 -1
  19. package/es/platform/zform/adapters/presenter/FormTranslator.js +18 -15
  20. package/es/platform/zform/adapters/presenter/translators/SectionTranslator.js +1 -1
  21. package/es/platform/zlist/adapters/presenters/TableTranslator.js +5 -4
  22. package/package.json +3 -4
@@ -0,0 +1,753 @@
1
+ import { Validator } from "./..";
2
+ describe('Validator', () => {
3
+ // --- 1. Type Validation ---
4
+ describe('type validation', () => {
5
+ it('should accept valid string', () => {
6
+ const result = Validator.validate({
7
+ type: 'string'
8
+ }, 'hello');
9
+ expect(result.isValid).toBe(true);
10
+ expect(result.errors).toHaveLength(0);
11
+ });
12
+ it('should reject number when string expected', () => {
13
+ const result = Validator.validate({
14
+ type: 'string'
15
+ }, 123);
16
+ expect(result.isValid).toBe(false);
17
+ expect(result.errors[0]).toMatchObject({
18
+ keyword: 'type',
19
+ message: 'should be string'
20
+ });
21
+ });
22
+ it('should accept valid number', () => {
23
+ const result = Validator.validate({
24
+ type: 'number'
25
+ }, 42);
26
+ expect(result.isValid).toBe(true);
27
+ });
28
+ it('should reject string when number expected', () => {
29
+ const result = Validator.validate({
30
+ type: 'number'
31
+ }, '42');
32
+ expect(result.isValid).toBe(false);
33
+ expect(result.errors[0].keyword).toBe('type');
34
+ });
35
+ it('should accept valid boolean', () => {
36
+ expect(Validator.validate({
37
+ type: 'boolean'
38
+ }, true).isValid).toBe(true);
39
+ expect(Validator.validate({
40
+ type: 'boolean'
41
+ }, false).isValid).toBe(true);
42
+ });
43
+ it('should reject non-boolean', () => {
44
+ expect(Validator.validate({
45
+ type: 'boolean'
46
+ }, 1).isValid).toBe(false);
47
+ });
48
+ it('should accept valid object', () => {
49
+ expect(Validator.validate({
50
+ type: 'object'
51
+ }, {
52
+ a: 1
53
+ }).isValid).toBe(true);
54
+ });
55
+ it('should reject null and array for object type', () => {
56
+ expect(Validator.validate({
57
+ type: 'object'
58
+ }, null).isValid).toBe(false);
59
+ expect(Validator.validate({
60
+ type: 'object'
61
+ }, [1, 2]).isValid).toBe(false);
62
+ });
63
+ it('should accept valid array', () => {
64
+ expect(Validator.validate({
65
+ type: 'array'
66
+ }, [1, 2, 3]).isValid).toBe(true);
67
+ });
68
+ it('should reject object for array type', () => {
69
+ expect(Validator.validate({
70
+ type: 'array'
71
+ }, {
72
+ length: 0
73
+ }).isValid).toBe(false);
74
+ });
75
+ it('should accept null for null type', () => {
76
+ expect(Validator.validate({
77
+ type: 'null'
78
+ }, null).isValid).toBe(true);
79
+ });
80
+ it('should reject undefined for null type', () => {
81
+ // undefined is skipped entirely — no validation error
82
+ expect(Validator.validate({
83
+ type: 'null'
84
+ }, undefined).isValid).toBe(true);
85
+ });
86
+ it('should accept function for function type', () => {
87
+ expect(Validator.validate({
88
+ type: 'function'
89
+ }, () => {}).isValid).toBe(true);
90
+ });
91
+ it('should reject string for function type', () => {
92
+ const result = Validator.validate({
93
+ type: 'function'
94
+ }, 'notAFunction');
95
+ expect(result.isValid).toBe(false);
96
+ expect(result.errors[0].keyword).toBe('type');
97
+ });
98
+ it('should accept anything for any/unknown type', () => {
99
+ expect(Validator.validate({
100
+ type: 'any'
101
+ }, 'hello').isValid).toBe(true);
102
+ expect(Validator.validate({
103
+ type: 'any'
104
+ }, 123).isValid).toBe(true);
105
+ expect(Validator.validate({
106
+ type: 'any'
107
+ }, null).isValid).toBe(true);
108
+ });
109
+ }); // --- 2. Type Arrays ---
110
+
111
+ describe('type arrays', () => {
112
+ const schema = {
113
+ type: ['string', 'number']
114
+ };
115
+ it('should accept string when type is [string, number]', () => {
116
+ expect(Validator.validate(schema, 'hello').isValid).toBe(true);
117
+ });
118
+ it('should accept number when type is [string, number]', () => {
119
+ expect(Validator.validate(schema, 42).isValid).toBe(true);
120
+ });
121
+ it('should reject boolean when type is [string, number]', () => {
122
+ const result = Validator.validate(schema, true);
123
+ expect(result.isValid).toBe(false);
124
+ expect(result.errors[0].message).toBe('should be string,number');
125
+ });
126
+ }); // --- 3. Enum ---
127
+
128
+ describe('enum', () => {
129
+ const schema = {
130
+ type: 'string',
131
+ enum: ['small', 'medium', 'large']
132
+ };
133
+ it('should accept valid enum value', () => {
134
+ expect(Validator.validate(schema, 'medium').isValid).toBe(true);
135
+ });
136
+ it('should reject invalid enum value', () => {
137
+ const result = Validator.validate(schema, 'extra-large');
138
+ expect(result.isValid).toBe(false);
139
+ expect(result.errors[0].keyword).toBe('enum');
140
+ });
141
+ it('should work with runtime Object.values() enum values', () => {
142
+ const SortOrder = {
143
+ ASC: 'ASC',
144
+ DES: 'DES',
145
+ NONE: 'NONE'
146
+ };
147
+ const runtimeSchema = {
148
+ type: 'string',
149
+ enum: Object.values(SortOrder)
150
+ };
151
+ expect(Validator.validate(runtimeSchema, 'ASC').isValid).toBe(true);
152
+ expect(Validator.validate(runtimeSchema, 'INVALID').isValid).toBe(false);
153
+ });
154
+ }); // --- 4. String Constraints ---
155
+
156
+ describe('string constraints', () => {
157
+ it('should pass minLength when string is long enough', () => {
158
+ expect(Validator.validate({
159
+ type: 'string',
160
+ minLength: 3
161
+ }, 'abc').isValid).toBe(true);
162
+ });
163
+ it('should fail minLength when string is too short', () => {
164
+ const result = Validator.validate({
165
+ type: 'string',
166
+ minLength: 3
167
+ }, 'ab');
168
+ expect(result.isValid).toBe(false);
169
+ expect(result.errors[0].keyword).toBe('minLength');
170
+ });
171
+ it('should pass maxLength when string is within limit', () => {
172
+ expect(Validator.validate({
173
+ type: 'string',
174
+ maxLength: 5
175
+ }, 'hello').isValid).toBe(true);
176
+ });
177
+ it('should fail maxLength when string exceeds limit', () => {
178
+ const result = Validator.validate({
179
+ type: 'string',
180
+ maxLength: 5
181
+ }, 'hello!');
182
+ expect(result.isValid).toBe(false);
183
+ expect(result.errors[0].keyword).toBe('maxLength');
184
+ });
185
+ it('should pass pattern when string matches', () => {
186
+ expect(Validator.validate({
187
+ type: 'string',
188
+ pattern: '^[a-z]+$'
189
+ }, 'abc').isValid).toBe(true);
190
+ });
191
+ it('should fail pattern when string does not match', () => {
192
+ const result = Validator.validate({
193
+ type: 'string',
194
+ pattern: '^[a-z]+$'
195
+ }, 'ABC');
196
+ expect(result.isValid).toBe(false);
197
+ expect(result.errors[0].keyword).toBe('pattern');
198
+ });
199
+ }); // --- 5. Array Constraints ---
200
+
201
+ describe('array constraints', () => {
202
+ it('should pass minItems when array has enough items', () => {
203
+ expect(Validator.validate({
204
+ type: 'array',
205
+ minItems: 2
206
+ }, [1, 2]).isValid).toBe(true);
207
+ });
208
+ it('should fail minItems when array has too few items', () => {
209
+ const result = Validator.validate({
210
+ type: 'array',
211
+ minItems: 2
212
+ }, [1]);
213
+ expect(result.isValid).toBe(false);
214
+ expect(result.errors[0].keyword).toBe('minItems');
215
+ });
216
+ it('should pass uniqueItems when items are unique', () => {
217
+ expect(Validator.validate({
218
+ type: 'array',
219
+ uniqueItems: true
220
+ }, ['a', 'b', 'c']).isValid).toBe(true);
221
+ });
222
+ it('should fail uniqueItems when items have duplicates', () => {
223
+ const result = Validator.validate({
224
+ type: 'array',
225
+ uniqueItems: true
226
+ }, ['a', 'b', 'a']);
227
+ expect(result.isValid).toBe(false);
228
+ expect(result.errors[0].keyword).toBe('uniqueItems');
229
+ });
230
+ it('should detect duplicate objects in uniqueItems', () => {
231
+ const result = Validator.validate({
232
+ type: 'array',
233
+ uniqueItems: true
234
+ }, [{
235
+ id: '1'
236
+ }, {
237
+ id: '2'
238
+ }, {
239
+ id: '1'
240
+ }]);
241
+ expect(result.isValid).toBe(false);
242
+ });
243
+ it('should validate each item against items schema', () => {
244
+ const schema = {
245
+ type: 'array',
246
+ items: {
247
+ type: 'string'
248
+ }
249
+ };
250
+ expect(Validator.validate(schema, ['a', 'b']).isValid).toBe(true);
251
+ });
252
+ it('should report invalid item with index in dataPath', () => {
253
+ const schema = {
254
+ type: 'array',
255
+ items: {
256
+ type: 'string'
257
+ }
258
+ };
259
+ const result = Validator.validate(schema, ['a', 42, 'c']);
260
+ expect(result.isValid).toBe(false);
261
+ expect(result.errors[0].dataPath).toBe('[1]');
262
+ expect(result.errors[0].keyword).toBe('type');
263
+ });
264
+ }); // --- 6. Object Constraints ---
265
+
266
+ describe('object constraints', () => {
267
+ it('should pass when all required properties are present', () => {
268
+ const schema = {
269
+ type: 'object',
270
+ properties: {
271
+ id: {
272
+ type: 'string'
273
+ }
274
+ },
275
+ required: ['id']
276
+ };
277
+ expect(Validator.validate(schema, {
278
+ id: '123'
279
+ }).isValid).toBe(true);
280
+ });
281
+ it('should fail when required property is missing', () => {
282
+ const schema = {
283
+ type: 'object',
284
+ properties: {
285
+ id: {
286
+ type: 'string'
287
+ }
288
+ },
289
+ required: ['id']
290
+ };
291
+ const result = Validator.validate(schema, {});
292
+ expect(result.isValid).toBe(false);
293
+ expect(result.errors[0]).toMatchObject({
294
+ keyword: 'required',
295
+ dataPath: '.id'
296
+ });
297
+ });
298
+ it('should validate nested property values', () => {
299
+ const schema = {
300
+ type: 'object',
301
+ properties: {
302
+ name: {
303
+ type: 'string'
304
+ },
305
+ age: {
306
+ type: 'number'
307
+ }
308
+ }
309
+ };
310
+ const result = Validator.validate(schema, {
311
+ name: 'John',
312
+ age: 'thirty'
313
+ });
314
+ expect(result.isValid).toBe(false);
315
+ expect(result.errors[0]).toMatchObject({
316
+ keyword: 'type',
317
+ dataPath: '.age'
318
+ });
319
+ });
320
+ it('should skip properties not present in value', () => {
321
+ const schema = {
322
+ type: 'object',
323
+ properties: {
324
+ name: {
325
+ type: 'string'
326
+ },
327
+ age: {
328
+ type: 'number'
329
+ }
330
+ }
331
+ };
332
+ expect(Validator.validate(schema, {
333
+ name: 'John'
334
+ }).isValid).toBe(true);
335
+ });
336
+ it('should reject extra properties when additionalProperties is false', () => {
337
+ const schema = {
338
+ type: 'object',
339
+ properties: {
340
+ name: {
341
+ type: 'string'
342
+ }
343
+ },
344
+ additionalProperties: false
345
+ };
346
+ const result = Validator.validate(schema, {
347
+ name: 'John',
348
+ extra: true
349
+ });
350
+ expect(result.isValid).toBe(false);
351
+ expect(result.errors[0]).toMatchObject({
352
+ keyword: 'additionalProperties',
353
+ dataPath: '.extra'
354
+ });
355
+ });
356
+ it('should allow extra properties when additionalProperties is true', () => {
357
+ const schema = {
358
+ type: 'object',
359
+ properties: {
360
+ name: {
361
+ type: 'string'
362
+ }
363
+ },
364
+ additionalProperties: true
365
+ };
366
+ expect(Validator.validate(schema, {
367
+ name: 'John',
368
+ extra: true
369
+ }).isValid).toBe(true);
370
+ });
371
+ it('should validate extra properties against additionalProperties schema', () => {
372
+ const schema = {
373
+ type: 'object',
374
+ properties: {
375
+ name: {
376
+ type: 'string'
377
+ }
378
+ },
379
+ additionalProperties: {
380
+ type: 'number'
381
+ }
382
+ };
383
+ expect(Validator.validate(schema, {
384
+ name: 'John',
385
+ score: 100
386
+ }).isValid).toBe(true);
387
+ const result = Validator.validate(schema, {
388
+ name: 'John',
389
+ score: 'high'
390
+ });
391
+ expect(result.isValid).toBe(false);
392
+ expect(result.errors[0]).toMatchObject({
393
+ keyword: 'type',
394
+ dataPath: '.score'
395
+ });
396
+ });
397
+ it('should validate nested objects 2+ levels deep', () => {
398
+ const schema = {
399
+ type: 'object',
400
+ properties: {
401
+ config: {
402
+ type: 'object',
403
+ properties: {
404
+ enabled: {
405
+ type: 'boolean'
406
+ }
407
+ }
408
+ }
409
+ }
410
+ };
411
+ const result = Validator.validate(schema, {
412
+ config: {
413
+ enabled: 'yes'
414
+ }
415
+ });
416
+ expect(result.isValid).toBe(false);
417
+ expect(result.errors[0]).toMatchObject({
418
+ keyword: 'type',
419
+ dataPath: '.config.enabled'
420
+ });
421
+ });
422
+ it('should skip validation for undefined property values', () => {
423
+ const schema = {
424
+ type: 'object',
425
+ properties: {
426
+ isEnabled: {
427
+ type: 'boolean'
428
+ },
429
+ count: {
430
+ type: 'number'
431
+ }
432
+ }
433
+ };
434
+ expect(Validator.validate(schema, {
435
+ isEnabled: undefined,
436
+ count: undefined
437
+ }).isValid).toBe(true);
438
+ });
439
+ }); // --- 7. Composition: anyOf / oneOf / not ---
440
+
441
+ describe('composition', () => {
442
+ describe('anyOf', () => {
443
+ const schema = {
444
+ anyOf: [{
445
+ type: 'string'
446
+ }, {
447
+ type: 'number'
448
+ }]
449
+ };
450
+ it('should pass when value matches one schema', () => {
451
+ expect(Validator.validate(schema, 'hello').isValid).toBe(true);
452
+ expect(Validator.validate(schema, 42).isValid).toBe(true);
453
+ });
454
+ it('should fail when value matches no schema', () => {
455
+ const result = Validator.validate(schema, true);
456
+ expect(result.isValid).toBe(false);
457
+ expect(result.errors[0].keyword).toBe('anyOf');
458
+ });
459
+ });
460
+ describe('oneOf', () => {
461
+ const schema = {
462
+ oneOf: [{
463
+ type: 'string',
464
+ minLength: 1
465
+ }, {
466
+ type: 'string',
467
+ maxLength: 3
468
+ }]
469
+ };
470
+ it('should pass when exactly one schema matches', () => {
471
+ // 'abcd' matches minLength:1 but fails maxLength:3 → exactly 1
472
+ expect(Validator.validate(schema, 'abcd').isValid).toBe(true);
473
+ });
474
+ it('should fail when no schema matches', () => {
475
+ // '' fails minLength:1; passes maxLength:3 → exactly 1 actually. Use number instead.
476
+ const numSchema = {
477
+ oneOf: [{
478
+ type: 'string'
479
+ }, {
480
+ type: 'number'
481
+ }]
482
+ };
483
+ expect(Validator.validate(numSchema, true).isValid).toBe(false);
484
+ });
485
+ it('should fail when more than one schema matches', () => {
486
+ // 'ab' matches both minLength:1 and maxLength:3 → 2 matches
487
+ const result = Validator.validate(schema, 'ab');
488
+ expect(result.isValid).toBe(false);
489
+ expect(result.errors[0].keyword).toBe('oneOf');
490
+ });
491
+ });
492
+ describe('not', () => {
493
+ const schema = {
494
+ not: {
495
+ type: 'string'
496
+ }
497
+ };
498
+ it('should pass when value does NOT match', () => {
499
+ expect(Validator.validate(schema, 42).isValid).toBe(true);
500
+ });
501
+ it('should fail when value matches', () => {
502
+ const result = Validator.validate(schema, 'hello');
503
+ expect(result.isValid).toBe(false);
504
+ expect(result.errors[0].keyword).toBe('not');
505
+ });
506
+ });
507
+ }); // --- 8. $ref + definitions ---
508
+
509
+ describe('$ref and definitions', () => {
510
+ it('should resolve $ref to definition', () => {
511
+ const schema = {
512
+ definitions: {
513
+ Name: {
514
+ type: 'string'
515
+ }
516
+ },
517
+ $ref: '#/definitions/Name'
518
+ };
519
+ expect(Validator.validate(schema, 'John').isValid).toBe(true);
520
+ expect(Validator.validate(schema, 123).isValid).toBe(false);
521
+ });
522
+ it('should resolve $ref in array items', () => {
523
+ const schema = {
524
+ definitions: {
525
+ Item: {
526
+ type: 'object',
527
+ properties: {
528
+ id: {
529
+ type: 'string'
530
+ }
531
+ },
532
+ required: ['id']
533
+ }
534
+ },
535
+ type: 'array',
536
+ items: {
537
+ $ref: '#/definitions/Item'
538
+ }
539
+ };
540
+ expect(Validator.validate(schema, [{
541
+ id: '1'
542
+ }, {
543
+ id: '2'
544
+ }]).isValid).toBe(true);
545
+ const result = Validator.validate(schema, [{
546
+ id: '1'
547
+ }, {}]);
548
+ expect(result.isValid).toBe(false);
549
+ expect(result.errors[0].keyword).toBe('required');
550
+ });
551
+ it('should support $defs as alternative to definitions', () => {
552
+ const schema = {
553
+ $defs: {
554
+ Count: {
555
+ type: 'number'
556
+ }
557
+ },
558
+ $ref: '#/$defs/Count'
559
+ };
560
+ expect(Validator.validate(schema, 5).isValid).toBe(true);
561
+ expect(Validator.validate(schema, 'five').isValid).toBe(false);
562
+ });
563
+ it('should silently skip unresolved $ref', () => {
564
+ const schema = {
565
+ $ref: '#/definitions/NonExistent'
566
+ };
567
+ const result = Validator.validate(schema, 'anything');
568
+ expect(result.isValid).toBe(true);
569
+ expect(result.errors).toHaveLength(0);
570
+ });
571
+ }); // --- 9. Edge Cases ---
572
+
573
+ describe('edge cases', () => {
574
+ it('should skip validation for undefined value', () => {
575
+ expect(Validator.validate({
576
+ type: 'string'
577
+ }, undefined).isValid).toBe(true);
578
+ });
579
+ it('should not error on empty/missing schema properties', () => {
580
+ expect(Validator.validate({}, 'anything').isValid).toBe(true);
581
+ expect(Validator.validate({
582
+ type: undefined
583
+ }, 'anything').isValid).toBe(true);
584
+ });
585
+ it('should accept anything with empty schema', () => {
586
+ expect(Validator.validate({}, 'hello').isValid).toBe(true);
587
+ expect(Validator.validate({}, 42).isValid).toBe(true);
588
+ expect(Validator.validate({}, null).isValid).toBe(true);
589
+ });
590
+ it('should ignore $schema keyword', () => {
591
+ const schema = {
592
+ $schema: 'http://json-schema.org/draft-07/schema#',
593
+ type: 'string'
594
+ };
595
+ expect(Validator.validate(schema, 'hello').isValid).toBe(true);
596
+ });
597
+ it('should hoist nested definitions from property-level schema', () => {
598
+ const schema = {
599
+ type: 'object',
600
+ properties: {
601
+ data: {
602
+ definitions: {
603
+ Tag: {
604
+ type: 'object',
605
+ properties: {
606
+ label: {
607
+ type: 'string'
608
+ }
609
+ },
610
+ required: ['label']
611
+ }
612
+ },
613
+ type: 'array',
614
+ items: {
615
+ $ref: '#/definitions/Tag'
616
+ }
617
+ }
618
+ }
619
+ };
620
+ expect(Validator.validate(schema, {
621
+ data: [{
622
+ label: 'test'
623
+ }]
624
+ }).isValid).toBe(true);
625
+ });
626
+ it('should short-circuit on type mismatch — no further checks', () => {
627
+ const schema = {
628
+ type: 'string',
629
+ minLength: 5
630
+ };
631
+ const result = Validator.validate(schema, 123); // Only type error, no minLength error
632
+
633
+ expect(result.errors).toHaveLength(1);
634
+ expect(result.errors[0].keyword).toBe('type');
635
+ });
636
+ it('should return warnings as empty array for validate()', () => {
637
+ const result = Validator.validate({
638
+ type: 'string'
639
+ }, 'hello');
640
+ expect(result.warnings).toEqual([]);
641
+ });
642
+ }); // --- 10. validatePropertySchema + validateRequired ---
643
+
644
+ describe('validatePropertySchema', () => {
645
+ it('should delegate to validatePropertySchema when schema.schema is present', () => {
646
+ const propertySchema = {
647
+ required: false,
648
+ isValueProvided: true,
649
+ name: 'testProp',
650
+ schema: {
651
+ type: 'string'
652
+ }
653
+ };
654
+ const result = Validator.validate(propertySchema, 'hello');
655
+ expect(result.isValid).toBe(true);
656
+ expect(result.key).toBe('testProp');
657
+ });
658
+ it('should return warning when required + not provided + has default', () => {
659
+ const propertySchema = {
660
+ required: true,
661
+ isValueProvided: false,
662
+ defaultValue: 'fallback',
663
+ name: 'myProp',
664
+ schema: {
665
+ type: 'string'
666
+ }
667
+ };
668
+ const result = Validator.validate(propertySchema, undefined);
669
+ expect(result.isValid).toBe(true);
670
+ expect(result.warnings).toHaveLength(1);
671
+ expect(result.warnings[0].message).toContain('Default value will be used');
672
+ });
673
+ it('should return error when required + not provided + default is undefined', () => {
674
+ const propertySchema = {
675
+ required: true,
676
+ isValueProvided: false,
677
+ defaultValue: undefined,
678
+ name: 'myProp',
679
+ schema: {
680
+ type: 'string'
681
+ }
682
+ };
683
+ const result = Validator.validate(propertySchema, undefined);
684
+ expect(result.isValid).toBe(false);
685
+ expect(result.errors[0].message).toContain('default value is undefined');
686
+ });
687
+ it('should return error when required + not provided + no default', () => {
688
+ const propertySchema = {
689
+ required: true,
690
+ isValueProvided: false,
691
+ name: 'myProp',
692
+ schema: {
693
+ type: 'string'
694
+ }
695
+ };
696
+ const result = Validator.validate(propertySchema, undefined);
697
+ expect(result.isValid).toBe(false);
698
+ expect(result.errors[0].message).toContain('neither a value nor a default value');
699
+ });
700
+ it('should validate schema when required and provided', () => {
701
+ const propertySchema = {
702
+ required: true,
703
+ isValueProvided: true,
704
+ name: 'myProp',
705
+ schema: {
706
+ type: 'number'
707
+ }
708
+ };
709
+ const result = Validator.validate(propertySchema, 'not-a-number');
710
+ expect(result.isValid).toBe(false);
711
+ expect(result.key).toBe('myProp');
712
+ expect(result.errors[0].keyword).toBe('type');
713
+ });
714
+ it('should be valid when not required and not provided', () => {
715
+ const propertySchema = {
716
+ required: false,
717
+ isValueProvided: false,
718
+ name: 'optionalProp',
719
+ schema: {
720
+ type: 'string'
721
+ }
722
+ };
723
+ const result = Validator.validate(propertySchema, undefined);
724
+ expect(result.isValid).toBe(true);
725
+ });
726
+ it('should validate schema when not required but provided', () => {
727
+ const propertySchema = {
728
+ required: false,
729
+ isValueProvided: true,
730
+ name: 'optionalProp',
731
+ schema: {
732
+ type: 'boolean'
733
+ }
734
+ };
735
+ expect(Validator.validate(propertySchema, true).isValid).toBe(true);
736
+ expect(Validator.validate(propertySchema, 'yes').isValid).toBe(false);
737
+ });
738
+ it('should include key in result from propertySchema.name', () => {
739
+ const propertySchema = {
740
+ required: false,
741
+ isValueProvided: true,
742
+ name: 'selectionConfig',
743
+ schema: {
744
+ type: 'object'
745
+ }
746
+ };
747
+ const result = Validator.validate(propertySchema, {
748
+ enabled: true
749
+ });
750
+ expect(result.key).toBe('selectionConfig');
751
+ });
752
+ });
753
+ });