@json-editor/json-editor 2.5.4 → 2.7.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.
Files changed (91) hide show
  1. package/.github/workflows/build.yml +12 -3
  2. package/CHANGELOG.md +51 -14
  3. package/README.md +159 -20
  4. package/dist/jsoneditor.js +2 -2
  5. package/dist/nonmin/jsoneditor.js +5483 -3610
  6. package/dist/nonmin/jsoneditor.js.map +1 -1
  7. package/docs/cleave.html +1 -1
  8. package/docs/datetime.html +1 -1
  9. package/docs/describedby.html +1 -1
  10. package/docs/form-submission.html +76 -0
  11. package/docs/index.html +2 -2
  12. package/docs/materialize_css.html +1 -1
  13. package/docs/meta_schema.json +0 -1
  14. package/docs/radio.html +1 -1
  15. package/docs/select2.html +1 -1
  16. package/docs/selectize.html +1 -1
  17. package/docs/starrating.html +1 -1
  18. package/docs/wysiwyg.html +1 -1
  19. package/package.json +28 -26
  20. package/release-notes.md +5 -3
  21. package/src/core.js +36 -37
  22. package/src/defaults.js +9 -2
  23. package/src/editor.js +6 -2
  24. package/src/editors/array.js +12 -1
  25. package/src/editors/autocomplete.js +4 -3
  26. package/src/editors/button.js +1 -1
  27. package/src/editors/multiple.js +2 -0
  28. package/src/editors/multiselect.js +14 -5
  29. package/src/editors/object.js +10 -6
  30. package/src/editors/radio.js +6 -1
  31. package/src/editors/string.js +7 -1
  32. package/src/editors/table.js +21 -2
  33. package/src/editors/upload.js +1 -1
  34. package/src/editors/uuid.js +2 -12
  35. package/src/iconlib.js +1 -1
  36. package/src/schemaloader.js +232 -109
  37. package/src/style.css +3 -0
  38. package/src/style.css.js +1 -1
  39. package/src/theme.js +5 -4
  40. package/src/themes/bootstrap3.js +3 -2
  41. package/src/themes/bootstrap4.js +7 -0
  42. package/src/themes/spectre.js +2 -1
  43. package/src/utilities.js +18 -0
  44. package/src/validator.js +65 -47
  45. package/tests/codeceptjs/codecept.json +1 -1
  46. package/tests/codeceptjs/core_test.js +98 -0
  47. package/tests/codeceptjs/editors/advanced_test.js +1 -1
  48. package/tests/codeceptjs/editors/array_test.js +74 -0
  49. package/tests/codeceptjs/editors/autocomplete_test.js +16 -0
  50. package/tests/codeceptjs/editors/button_test.js +11 -4
  51. package/tests/codeceptjs/editors/integer_test.js +7 -2
  52. package/tests/codeceptjs/editors/jodit_test.js +3 -3
  53. package/tests/codeceptjs/editors/object_test.js +84 -13
  54. package/tests/codeceptjs/editors/range_test.js +12 -0
  55. package/tests/codeceptjs/editors/stepper_test.js +12 -0
  56. package/tests/codeceptjs/editors/uuid_test.js +31 -4
  57. package/tests/codeceptjs/editors/validation_test.js +1 -1
  58. package/tests/docker-compose.yml +1 -1
  59. package/tests/fixtures/definitions.json +22 -0
  60. package/tests/fixtures/properties.json +20 -0
  61. package/tests/fixtures/validation.json +207 -0
  62. package/tests/pages/array-checkboxes-infotext.html +52 -0
  63. package/tests/pages/array-move-events.html +4 -2
  64. package/tests/pages/array-unique-items-sort.html +78 -0
  65. package/tests/pages/autocomplete.html +69 -0
  66. package/tests/pages/button-icons.html +38 -0
  67. package/tests/pages/core.html +4 -2
  68. package/tests/pages/error-messages.html +47 -0
  69. package/tests/pages/grid-strict.html +6 -10
  70. package/tests/pages/grid.html +0 -4
  71. package/tests/pages/issues/issue-gh-812.html +4 -2
  72. package/tests/pages/meta_schema.json +14 -1
  73. package/tests/pages/object-required-properties.html +37 -14
  74. package/tests/pages/object-show-opt-in.html +110 -0
  75. package/tests/pages/object-with-dependencies-array.html +29 -19
  76. package/tests/pages/range.html +60 -0
  77. package/tests/pages/ready.html +43 -0
  78. package/tests/pages/references.html +162 -0
  79. package/tests/pages/stepper-manual.html +57 -0
  80. package/tests/pages/string-simplemde-editor.html +81 -0
  81. package/tests/pages/table-move-events.html +4 -1
  82. package/tests/pages/urn.html +11 -8
  83. package/tests/pages/uuid.html +89 -50
  84. package/tests/pages/validation-messages.json +705 -0
  85. package/tests/unit/core.spec.js +79 -66
  86. package/tests/unit/editor.spec.js +20 -8
  87. package/tests/unit/editors/array.spec.js +3 -2
  88. package/tests/unit/editors/object.spec.js +3 -1
  89. package/tests/unit/editors/table.spec.js +4 -2
  90. package/tests/unit/schemaloader.spec.js +77 -105
  91. package/tests/unit/validator.spec.js +10 -0
@@ -55,6 +55,7 @@ export class bootstrap3Theme extends AbstractTheme {
55
55
 
56
56
  if (label && (input.type === 'checkbox' || input.type === 'radio')) {
57
57
  group.classList.add(input.type)
58
+ if (infoText) label.appendChild(infoText)
58
59
  label.insertBefore(input, label.firstChild)
59
60
  group.appendChild(label)
60
61
  } else {
@@ -172,14 +173,14 @@ export class bootstrap3Theme extends AbstractTheme {
172
173
  getTabHolder (propertyName) {
173
174
  const pName = (typeof propertyName === 'undefined') ? '' : propertyName
174
175
  const el = document.createElement('div')
175
- el.innerHTML = `<ul class='col-md-2 nav nav-pills nav-stacked' id='${pName}' role='tablist'></ul><div class='col-md-10 tab-content well well-small' id='${pName}'></div>`
176
+ el.innerHTML = `<ul class='col-md-2 nav nav-pills nav-stacked' id='${pName}' role='tablist'></ul><div class='col-md-10 tab-content active well well-small' id='${pName}'></div>`
176
177
  return el
177
178
  }
178
179
 
179
180
  getTopTabHolder (propertyName) {
180
181
  const pName = (typeof propertyName === 'undefined') ? '' : propertyName
181
182
  const el = document.createElement('div')
182
- el.innerHTML = `<ul class='nav nav-tabs' id='${pName}' role='tablist'></ul><div class='tab-content well well-small' id='${pName}'></div>`
183
+ el.innerHTML = `<ul class='nav nav-tabs' id='${pName}' role='tablist'></ul><div class='tab-content active well well-small' id='${pName}'></div>`
183
184
  return el
184
185
  }
185
186
 
@@ -135,6 +135,12 @@ export class bootstrap4Theme extends AbstractTheme {
135
135
  const min = input.getAttribute('min')
136
136
  const max = input.getAttribute('max')
137
137
 
138
+ input.addEventListener('change', () => {
139
+ if (!input.getAttribute('initialized')) {
140
+ input.setAttribute('initialized', '1')
141
+ }
142
+ })
143
+
138
144
  minusBtn.addEventListener('click', () => {
139
145
  if (!input.getAttribute('initialized')) {
140
146
  initialize(input, min)
@@ -405,6 +411,7 @@ export class bootstrap4Theme extends AbstractTheme {
405
411
 
406
412
  const el = document.createElement('h3')
407
413
  el.classList.add('card-title')
414
+ el.classList.add('level-' + pathDepth)
408
415
 
409
416
  if (typeof text === 'string') {
410
417
  el.textContent = text
@@ -134,7 +134,7 @@ export class spectreTheme extends AbstractTheme {
134
134
  getCheckboxLabel (text, req) {
135
135
  const el = super.getCheckboxLabel(text, req); const icon = document.createElement('i')
136
136
  icon.classList.add('form-icon')
137
- el.classList.add('form-checkbox', 'mr-5')
137
+ el.classList.add('form-checkbox', 'pr-0')
138
138
  el.insertBefore(icon, el.firstChild)
139
139
  return el
140
140
  }
@@ -219,6 +219,7 @@ export class spectreTheme extends AbstractTheme {
219
219
 
220
220
  if (label && (input.type === 'checkbox' || input.type === 'radio')) {
221
221
  group.classList.add(input.type)
222
+ if (infoText) label.appendChild(infoText)
222
223
  label.insertBefore(input, label.firstChild)
223
224
  group.appendChild(label)
224
225
  } else {
package/src/utilities.js CHANGED
@@ -86,3 +86,21 @@ export function isInteger (value) {
86
86
  const v = parseInt(value)
87
87
  return match !== null && !isNaN(v) && isFinite(v)
88
88
  }
89
+
90
+ /* This function generates a uuid.
91
+ https://stackoverflow.com/questions/105034/create-guid-uuid-in-javascript
92
+ TODO: It will be probably better to move to: https://www.npmjs.com/package/uuid
93
+ */
94
+ export function generateUUID () {
95
+ let d = new Date().getTime()
96
+
97
+ if (typeof performance !== 'undefined' && typeof performance.now === 'function') {
98
+ d += performance.now() /* use high-precision timer if available */
99
+ }
100
+
101
+ return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, (c) => {
102
+ const r = (d + Math.random() * 16) % 16 | 0
103
+ d = Math.floor(d / 16)
104
+ return (c === 'x' ? r : (r & 0x3 | 0x8)).toString(16)
105
+ })
106
+ }
package/src/validator.js CHANGED
@@ -17,7 +17,7 @@ export class Validator {
17
17
  return [{
18
18
  path,
19
19
  property: 'const',
20
- message: this.translate('error_const')
20
+ message: this.translate('error_const', null, schema)
21
21
  }]
22
22
  }
23
23
  return []
@@ -29,7 +29,7 @@ export class Validator {
29
29
  return [{
30
30
  path,
31
31
  property: 'enum',
32
- message: this.translate('error_enum')
32
+ message: this.translate('error_enum', null, schema)
33
33
  }]
34
34
  }
35
35
  return []
@@ -54,7 +54,7 @@ export class Validator {
54
54
  return [{
55
55
  path,
56
56
  property: 'anyOf',
57
- message: this.translate('error_anyOf')
57
+ message: this.translate('error_anyOf', null, schema)
58
58
  }]
59
59
  }
60
60
  return []
@@ -79,7 +79,7 @@ export class Validator {
79
79
  errors.push({
80
80
  path,
81
81
  property: 'oneOf',
82
- message: this.translate('error_oneOf', [valid])
82
+ message: this.translate('error_oneOf', [valid], schema)
83
83
  })
84
84
  errors.push(...oneofErrors)
85
85
  }
@@ -90,7 +90,7 @@ export class Validator {
90
90
  return [{
91
91
  path,
92
92
  property: 'not',
93
- message: this.translate('error_not')
93
+ message: this.translate('error_not', null, schema)
94
94
  }]
95
95
  }
96
96
  return []
@@ -103,7 +103,7 @@ export class Validator {
103
103
  return [{
104
104
  path,
105
105
  property: 'type',
106
- message: this.translate('error_type_union')
106
+ message: this.translate('error_type_union', null, schema)
107
107
  }]
108
108
  }
109
109
  } else {
@@ -115,14 +115,14 @@ export class Validator {
115
115
  return [{
116
116
  path,
117
117
  property: 'type',
118
- message: this.translate('error_type', [schema.format])
118
+ message: this.translate('error_type', [schema.format], schema)
119
119
  }]
120
120
  }
121
121
  } else if (!this._checkType(schema.type, value)) {
122
122
  return [{
123
123
  path,
124
124
  property: 'type',
125
- message: this.translate('error_type', [schema.type])
125
+ message: this.translate('error_type', [schema.type], schema)
126
126
  }]
127
127
  }
128
128
  }
@@ -136,7 +136,7 @@ export class Validator {
136
136
  return [{
137
137
  path,
138
138
  property: 'disallow',
139
- message: this.translate('error_disallow_union')
139
+ message: this.translate('error_disallow_union', null, schema)
140
140
  }]
141
141
  }
142
142
  } else {
@@ -145,7 +145,7 @@ export class Validator {
145
145
  return [{
146
146
  path,
147
147
  property: 'disallow',
148
- message: this.translate('error_disallow', [schema.disallow])
148
+ message: this.translate('error_disallow', [schema.disallow], schema)
149
149
  }]
150
150
  }
151
151
  }
@@ -177,7 +177,8 @@ export class Validator {
177
177
  property: 'maximum',
178
178
  message: this.translate(
179
179
  (schema.exclusiveMaximum ? 'error_maximum_excl' : 'error_maximum_incl'),
180
- [schema.maximum]
180
+ [schema.maximum],
181
+ schema
181
182
  )
182
183
  }]
183
184
  }
@@ -204,7 +205,8 @@ export class Validator {
204
205
  property: 'minimum',
205
206
  message: this.translate(
206
207
  (schema.exclusiveMinimum ? 'error_minimum_excl' : 'error_minimum_incl'),
207
- [schema.minimum]
208
+ [schema.minimum],
209
+ schema
208
210
  )
209
211
  }]
210
212
  }
@@ -219,7 +221,7 @@ export class Validator {
219
221
  errors.push({
220
222
  path,
221
223
  property: 'maxLength',
222
- message: this.translate('error_maxLength', [schema.maxLength])
224
+ message: this.translate('error_maxLength', [schema.maxLength], schema)
223
225
  })
224
226
  }
225
227
  return errors
@@ -230,7 +232,7 @@ export class Validator {
230
232
  return [{
231
233
  path,
232
234
  property: 'minLength',
233
- message: this.translate((schema.minLength === 1 ? 'error_notempty' : 'error_minLength'), [schema.minLength])
235
+ message: this.translate((schema.minLength === 1 ? 'error_notempty' : 'error_minLength'), [schema.minLength], schema)
234
236
  }]
235
237
  }
236
238
  return []
@@ -241,7 +243,7 @@ export class Validator {
241
243
  return [{
242
244
  path,
243
245
  property: 'pattern',
244
- message: (schema.options && schema.options.patternmessage) ? schema.options.patternmessage : this.translate('error_pattern', [schema.pattern])
246
+ message: (schema.options && schema.options.patternmessage) ? schema.options.patternmessage : this.translate('error_pattern', [schema.pattern], schema)
245
247
  }]
246
248
  }
247
249
  return []
@@ -269,7 +271,7 @@ export class Validator {
269
271
  errors.push({
270
272
  path,
271
273
  property: 'additionalItems',
272
- message: this.translate('error_additionalItems')
274
+ message: this.translate('error_additionalItems', null, schema)
273
275
  })
274
276
  break
275
277
  /* Default for `additionalItems` is an empty schema */
@@ -291,7 +293,7 @@ export class Validator {
291
293
  return [{
292
294
  path,
293
295
  property: 'maxItems',
294
- message: this.translate('error_maxItems', [schema.maxItems])
296
+ message: this.translate('error_maxItems', [schema.maxItems], schema)
295
297
  }]
296
298
  }
297
299
  return []
@@ -301,7 +303,7 @@ export class Validator {
301
303
  return [{
302
304
  path,
303
305
  property: 'minItems',
304
- message: this.translate('error_minItems', [schema.minItems])
306
+ message: this.translate('error_minItems', [schema.minItems], schema)
305
307
  }]
306
308
  }
307
309
  return []
@@ -314,7 +316,7 @@ export class Validator {
314
316
  return [{
315
317
  path,
316
318
  property: 'uniqueItems',
317
- message: this.translate('error_uniqueItems')
319
+ message: this.translate('error_uniqueItems', null, schema)
318
320
  }]
319
321
  }
320
322
  seen[valid] = true
@@ -329,7 +331,7 @@ export class Validator {
329
331
  return [{
330
332
  path,
331
333
  property: 'maxProperties',
332
- message: this.translate('error_maxProperties', [schema.maxProperties])
334
+ message: this.translate('error_maxProperties', [schema.maxProperties], schema)
333
335
  }]
334
336
  }
335
337
  return []
@@ -339,7 +341,7 @@ export class Validator {
339
341
  return [{
340
342
  path,
341
343
  property: 'minProperties',
342
- message: this.translate('error_minProperties', [schema.minProperties])
344
+ message: this.translate('error_minProperties', [schema.minProperties], schema)
343
345
  }]
344
346
  }
345
347
  return []
@@ -350,12 +352,13 @@ export class Validator {
350
352
  schema.required.forEach(e => {
351
353
  if (typeof value[e] !== 'undefined') return
352
354
  const editor = this.jsoneditor.getEditor(`${path}.${e}`)
355
+ if (editor && editor.dependenciesFulfilled === false) return
353
356
  /* Ignore required error if editor is of type "button" or "info" */
354
357
  if (editor && ['button', 'info'].includes(editor.schema.format || editor.schema.type)) return
355
358
  errors.push({
356
359
  path,
357
360
  property: 'required',
358
- message: this.translate('error_required', [schema && schema.properties && schema.properties[e] && schema.properties[e].title ? schema.properties[e].title : e])
361
+ message: this.translate('error_required', [schema && schema.properties && schema.properties[e] && schema.properties[e].title ? schema.properties[e].title : e], schema)
359
362
  })
360
363
  })
361
364
  }
@@ -402,7 +405,7 @@ export class Validator {
402
405
  errors.push({
403
406
  path,
404
407
  property: 'propertyNames',
405
- message: this.translate('error_property_names_false', [k])
408
+ message: this.translate('error_property_names_false', [k], schema)
406
409
  })
407
410
  break
408
411
  }
@@ -456,14 +459,14 @@ export class Validator {
456
459
  errors.push({
457
460
  path,
458
461
  property: 'propertyNames',
459
- message: this.translate('error_property_names_unsupported', [j])
462
+ message: this.translate('error_property_names_unsupported', [j], schema)
460
463
  })
461
464
  return false
462
465
  }
463
466
  errors.push({
464
467
  path,
465
468
  property: 'propertyNames',
466
- message: this.translate(msg, [k])
469
+ message: this.translate(msg, [k], schema)
467
470
  })
468
471
  return false
469
472
  })
@@ -482,7 +485,7 @@ export class Validator {
482
485
  errors.push({
483
486
  path,
484
487
  property: 'additionalProperties',
485
- message: this.translate('error_additional_properties', [k])
488
+ message: this.translate('error_additional_properties', [k], schema)
486
489
  })
487
490
  break
488
491
  /* Allowed */
@@ -509,7 +512,7 @@ export class Validator {
509
512
  errors.push({
510
513
  path,
511
514
  property: 'dependencies',
512
- message: this.translate('error_dependency', [d])
515
+ message: this.translate('error_dependency', [d], schema)
513
516
  })
514
517
  }
515
518
  })
@@ -527,20 +530,35 @@ export class Validator {
527
530
  const fit = { match: 0, extra: 0 }
528
531
  if (typeof value === 'object' && value !== null) {
529
532
  /* Work on a copy of the schema */
530
- const properties = this._getSchema(givenSchema).properties
531
-
532
- for (const i in properties) {
533
- if (!hasOwnProperty(properties, i)) {
534
- fit.extra += weight
535
- continue
536
- }
537
- if (typeof value[i] === 'object' && typeof properties[i] === 'object' && typeof properties[i].properties === 'object') {
538
- const result = this.fitTest(value[i], properties[i], weight / 100)
539
- fit.match += result.match
540
- fit.extra += result.extra
533
+ const schema = this._getSchema(givenSchema)
534
+ /* If the schema is an anyOf declaration, do use the properties of the allowed sub schemata instead.
535
+ Of these sub schemata, the best fit is selected */
536
+ if (schema.anyOf) {
537
+ let bestFit = { ...fit }
538
+ for (const subSchema of schema.anyOf) {
539
+ const subFit = this.fitTest(value, subSchema, weight)
540
+ /* The best fit is the one with the best value for match. If there are multiple results
541
+ with the same match value, use the one with the least number of extra properties */
542
+ if ((subFit.match > bestFit.match) || (subFit.match === bestFit.match && subFit.extra < bestFit.extra)) {
543
+ bestFit = subFit
544
+ }
541
545
  }
542
- if (typeof value[i] !== 'undefined') {
543
- fit.match += weight
546
+ return bestFit
547
+ } else {
548
+ const properties = this._getSchema(givenSchema).properties
549
+ for (const i in properties) {
550
+ if (!hasOwnProperty(properties, i)) {
551
+ fit.extra += weight
552
+ continue
553
+ }
554
+ if (typeof value[i] === 'object' && typeof properties[i] === 'object' && typeof properties[i].properties === 'object') {
555
+ const result = this.fitTest(value[i], properties[i], weight / 100)
556
+ fit.match += result.match
557
+ fit.extra += result.extra
558
+ }
559
+ if (typeof value[i] !== 'undefined') {
560
+ fit.match += weight
561
+ }
544
562
  }
545
563
  }
546
564
  }
@@ -622,7 +640,7 @@ export class Validator {
622
640
  return [{
623
641
  path,
624
642
  property: 'required',
625
- message: this.translate('error_notset')
643
+ message: this.translate('error_notset', null, schema)
626
644
  }]
627
645
  }
628
646
  return []
@@ -697,7 +715,7 @@ export class Validator {
697
715
  return [{
698
716
  path,
699
717
  property: 'format',
700
- message: this.translate('error_pattern', ['^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-5][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}$'])
718
+ message: this.translate('error_pattern', ['^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-5][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}$'], schema)
701
719
  }]
702
720
  }
703
721
  return []
@@ -720,7 +738,7 @@ export class Validator {
720
738
  return [{
721
739
  path,
722
740
  property: schema.multipleOf ? 'multipleOf' : 'divisibleBy',
723
- message: this.translate('error_multipleOf', [divisor])
741
+ message: this.translate('error_multipleOf', [divisor], schema)
724
742
  }]
725
743
  }
726
744
  return []
@@ -734,14 +752,14 @@ export class Validator {
734
752
  return [{
735
753
  path,
736
754
  property: 'format',
737
- message: this.translate('error_invalid_epoch')
755
+ message: this.translate('error_invalid_epoch', null, schema)
738
756
  }]
739
757
  } else if (value !== Math.abs(parseInt(value))) {
740
758
  /* not much to check for, so we assume value is ok if it's a positive number */
741
759
  return [{
742
760
  path,
743
761
  property: 'format',
744
- message: this.translate(`error_${schema.format.replace(/-/g, '_')}`, [dateFormat])
762
+ message: this.translate(`error_${schema.format.replace(/-/g, '_')}`, [dateFormat], schema)
745
763
  }]
746
764
  }
747
765
  return []
@@ -770,7 +788,7 @@ export class Validator {
770
788
  return [{
771
789
  path,
772
790
  property: 'format',
773
- message: this.translate(`error_${editor.format.replace(/-/g, '_')}`, [errorDateFormat])
791
+ message: this.translate(`error_${editor.format.replace(/-/g, '_')}`, [errorDateFormat], schema)
774
792
  }]
775
793
  }
776
794
  }
@@ -799,7 +817,7 @@ export class Validator {
799
817
  return [{
800
818
  path,
801
819
  property: 'format',
802
- message: this.translate(`error_${schema.format.replace(/-/g, '_')}`, [dateFormat])
820
+ message: this.translate(`error_${schema.format.replace(/-/g, '_')}`, [dateFormat], schema)
803
821
  }]
804
822
  }
805
823
  } else if (editor) {
@@ -11,7 +11,7 @@
11
11
  "browser": "firefox",
12
12
  "restart": false,
13
13
  "host": "firefox",
14
- "smartWait": 5000
14
+ "smartWait": 10000
15
15
  },
16
16
  "customHelpers": {
17
17
  "require": "./../../config/codeceptjs_helpers.js"
@@ -4,6 +4,11 @@ var assert = require('assert')
4
4
 
5
5
  Feature('core')
6
6
 
7
+ Scenario('should have class je-ready when ready @core @ready', async (I) => {
8
+ I.amOnPage('ready.html')
9
+ I.waitForElement('.je-ready')
10
+ })
11
+
7
12
  Scenario('should Disable and enable entire form', async (I) => {
8
13
  I.amOnPage('core.html')
9
14
  I.seeElement('[data-schemapath="root.name"] input')
@@ -215,3 +220,96 @@ Scenario('should validate against oneOf schemas and display single oneOf and edi
215
220
  I.waitForText('Rating Description (but in german)')
216
221
  I.seeInSource('Rating Info Text (but in german)')
217
222
  })
223
+
224
+ Scenario('should load internal schema definitions, external schema definitions and external schema properties @core @references', async (I) => {
225
+ I.amOnPage('references.html')
226
+ I.waitForText('References JSON Editor Example')
227
+
228
+ // internal schema definitions
229
+ I.waitForElement('[data-schemapath="root.external"]')
230
+ I.waitForElement('[data-schemapath="root.orgid"]')
231
+
232
+ // external schema definitions
233
+ I.click('Add Person')
234
+ I.waitForElement('[data-schemapath="root.people.0.name"]')
235
+ I.waitForElement('[data-schemapath="root.people.0.age"]')
236
+ I.waitForElement('[data-schemapath="root.people.0.gender"]')
237
+ I.selectOption('.je-switcher', 'Complex Person')
238
+ I.waitForElement('[data-schemapath="root.people.0.location.city"]')
239
+ I.waitForElement('[data-schemapath="root.people.0.location.state"]')
240
+ I.waitForElement('[data-schemapath="root.people.0.location.citystate"]')
241
+
242
+ // external schema properties
243
+ I.waitForElement('[data-schemapath="root.link.street_address"]')
244
+ })
245
+
246
+ Scenario('should override error messages if specified in schema options @core @errors-messages', async (I) => {
247
+ I.amOnPage('error-messages.html')
248
+ I.waitForText('Error Messages')
249
+
250
+ I.waitForText('CUSTOM EN: Value required')
251
+ I.waitForText('CUSTOM EN: Value must be the constant value')
252
+ I.waitForText('CUSTOM EN: Value must be at least 6 characters long')
253
+ I.waitForText('CUSTOM EN: Value must be at most 6 characters long')
254
+ I.waitForText('CUSTOM EN: Value must validate against at least one of the provided schemas')
255
+ I.waitForText('CUSTOM EN: Value must validate against exactly one of the provided schemas. It currently validates against 0 of the schemas')
256
+ I.waitForText('CUSTOM EN: Value must be one of the provided types')
257
+ I.waitForText('CUSTOM EN: Value must not be one of the provided disallowed types')
258
+ I.waitForText('CUSTOM EN: Value must not be of type string')
259
+ I.waitForText('CUSTOM EN: Value must be of type integer')
260
+ I.waitForText('CUSTOM EN: Value must not validate against the provided schema')
261
+ I.waitForText('CUSTOM EN: Date must be greater than 1 January 1970')
262
+ I.waitForText('CUSTOM EN: Value must match the pattern ^[a-zA-Z0-9_]+$')
263
+ I.waitForText('CUSTOM EN: Value must be a multiple of 5')
264
+ I.waitForText('CUSTOM EN: Value must be at least 5')
265
+ I.waitForText('CUSTOM EN: Value must be greater than 5')
266
+ I.waitForText('CUSTOM EN: Value must be less than 10')
267
+ I.waitForText('CUSTOM EN: Value must be less than 15')
268
+ I.waitForText('CUSTOM EN: Object must have at least 1 properties')
269
+ I.waitForText('CUSTOM EN: Object must have at most 1 properties')
270
+ I.waitForText('CUSTOM EN: Object is missing the required property \'name\'')
271
+ I.waitForText('CUSTOM EN: Unsupported propertyName UNSOPPORTED')
272
+ I.waitForText('CUSTOM EN: Property name fullname cannot match invalid pattern')
273
+ I.waitForText('CUSTOM EN: Property name fullname cannot match invalid enum')
274
+ I.waitForText('CUSTOM EN: Property name fullname cannot match invalid maxLength')
275
+ I.waitForText('CUSTOM EN: Property name fullname exceeds maxLength')
276
+ I.waitForText('CUSTOM EN: Property name pets does not match pattern')
277
+ I.waitForText('CUSTOM EN: Property name $pets does not match pattern')
278
+ I.waitForText('CUSTOM EN: Property name pets does not match the const value')
279
+ I.waitForText('CUSTOM EN: Property name pets does not match any enum values')
280
+ I.waitForText('CUSTOM EN: Array must have unique items')
281
+
282
+ I.click('Switch to ES')
283
+
284
+ I.waitForText('CUSTOM ES: Value required')
285
+ I.waitForText('CUSTOM ES: Value must be the constant value')
286
+ I.waitForText('CUSTOM ES: Value must be at least 6 characters long')
287
+ I.waitForText('CUSTOM ES: Value must be at most 6 characters long')
288
+ I.waitForText('CUSTOM ES: Value must validate against at least one of the provided schemas')
289
+ I.waitForText('CUSTOM ES: Value must validate against exactly one of the provided schemas. It currently validates against 0 of the schemas')
290
+ I.waitForText('CUSTOM ES: Value must be one of the provided types')
291
+ I.waitForText('CUSTOM ES: Value must not be one of the provided disallowed types')
292
+ I.waitForText('CUSTOM ES: Value must not be of type string')
293
+ I.waitForText('CUSTOM ES: Value must be of type integer')
294
+ I.waitForText('CUSTOM ES: Value must not validate against the provided schema')
295
+ I.waitForText('CUSTOM ES: Date must be greater than 1 January 1970')
296
+ I.waitForText('CUSTOM ES: Value must match the pattern ^[a-zA-Z0-9_]+$')
297
+ I.waitForText('CUSTOM ES: Value must be a multiple of 5')
298
+ I.waitForText('CUSTOM ES: Value must be at least 5')
299
+ I.waitForText('CUSTOM ES: Value must be greater than 5')
300
+ I.waitForText('CUSTOM ES: Value must be less than 10')
301
+ I.waitForText('CUSTOM ES: Value must be less than 15')
302
+ I.waitForText('CUSTOM ES: Object must have at least 1 properties')
303
+ I.waitForText('CUSTOM ES: Object must have at most 1 properties')
304
+ I.waitForText('CUSTOM ES: Object is missing the required property \'name\'')
305
+ I.waitForText('CUSTOM ES: Unsupported propertyName UNSOPPORTED')
306
+ I.waitForText('CUSTOM ES: Property name fullname cannot match invalid pattern')
307
+ I.waitForText('CUSTOM ES: Property name fullname cannot match invalid enum')
308
+ I.waitForText('CUSTOM ES: Property name fullname cannot match invalid maxLength')
309
+ I.waitForText('CUSTOM ES: Property name fullname exceeds maxLength')
310
+ I.waitForText('CUSTOM ES: Property name pets does not match pattern')
311
+ I.waitForText('CUSTOM ES: Property name $pets does not match pattern')
312
+ I.waitForText('CUSTOM ES: Property name pets does not match the const value')
313
+ I.waitForText('CUSTOM ES: Property name pets does not match any enum values')
314
+ I.waitForText('CUSTOM ES: Array must have unique items')
315
+ })
@@ -3,7 +3,7 @@ Feature('Advanced Editor');
3
3
 
4
4
  Scenario('test validation & delete', (I) => {
5
5
  I.amOnPage('advanced.html');
6
- I.seeElement('#valid_indicator');
6
+ I.retry({ retries: 5, minTimeout: 500 }).seeElement('#valid_indicator');
7
7
  I.see('valid', '#valid_indicator');
8
8
  I.fillField('root[0][location][city]', 'Stuttgart');
9
9
  I.waitForValue("[name='root[0][location][city]']", 'Stuttgart');
@@ -10,6 +10,56 @@ Scenario('should have correct initial value', async (I) => {
10
10
  assert.equal(await I.grabValueFrom('.debug'), '[]');
11
11
  });
12
12
 
13
+ Scenario('unique items arrays sorting @unique-itmes-sorting', async (I) => {
14
+ I.amOnPage('array-unique-items-sort.html')
15
+ I.waitForElement('.je-ready', 10)
16
+ I.click('[data-schemapath="root.items"] .form-control:nth-of-type(1)')
17
+ I.click('.get-value')
18
+ assert.equal(await I.grabValueFrom('.debug'), '{"items":["01"]}')
19
+ I.click('[data-schemapath="root.items"] .form-control:nth-of-type(2)')
20
+ I.click('.get-value')
21
+ assert.equal(await I.grabValueFrom('.debug'), '{"items":["01","02"]}')
22
+ I.click('[data-schemapath="root.items"] .form-control:nth-of-type(3)')
23
+ I.click('.get-value')
24
+ assert.equal(await I.grabValueFrom('.debug'), '{"items":["01","02","03"]}')
25
+ I.click('[data-schemapath="root.items"] .form-control:nth-of-type(4)')
26
+ I.click('.get-value')
27
+ assert.equal(await I.grabValueFrom('.debug'), '{"items":["01","02","03","04"]}')
28
+ I.click('[data-schemapath="root.items"] .form-control:nth-of-type(5)')
29
+ I.click('.get-value')
30
+ assert.equal(await I.grabValueFrom('.debug'), '{"items":["01","02","03","04","05"]}')
31
+ I.click('[data-schemapath="root.items"] .form-control:nth-of-type(6)')
32
+ I.click('.get-value')
33
+ assert.equal(await I.grabValueFrom('.debug'), '{"items":["01","02","03","04","05","06"]}')
34
+ I.click('[data-schemapath="root.items"] .form-control:nth-of-type(7)')
35
+ I.click('.get-value')
36
+ assert.equal(await I.grabValueFrom('.debug'), '{"items":["01","02","03","04","05","06","07"]}')
37
+ I.click('[data-schemapath="root.items"] .form-control:nth-of-type(8)')
38
+ I.click('.get-value')
39
+ assert.equal(await I.grabValueFrom('.debug'), '{"items":["01","02","03","04","05","06","07","08"]}')
40
+ I.click('[data-schemapath="root.items"] .form-control:nth-of-type(9)')
41
+ I.click('.get-value')
42
+ assert.equal(await I.grabValueFrom('.debug'), '{"items":["01","02","03","04","05","06","07","08","09"]}')
43
+ I.click('[data-schemapath="root.items"] .form-control:nth-of-type(10)')
44
+ I.click('.get-value')
45
+ assert.equal(await I.grabValueFrom('.debug'), '{"items":["01","02","03","04","05","06","07","08","09","10"]}')
46
+ I.click('[data-schemapath="root.items"] .form-control:nth-of-type(11)')
47
+ I.click('.get-value')
48
+ assert.equal(await I.grabValueFrom('.debug'), '{"items":["01","02","03","04","05","06","07","08","09","10","11"]}')
49
+ I.click('[data-schemapath="root.items"] .form-control:nth-of-type(12)')
50
+ I.click('.get-value')
51
+ assert.equal(await I.grabValueFrom('.debug'), '{"items":["01","02","03","04","05","06","07","08","09","10","11","12"]}')
52
+ I.click('[data-schemapath="root.items"] .form-control:nth-of-type(13)')
53
+ I.click('.get-value')
54
+ assert.equal(await I.grabValueFrom('.debug'), '{"items":["01","02","03","04","05","06","07","08","09","10","11","12","13"]}')
55
+ I.click('[data-schemapath="root.items"] .form-control:nth-of-type(14)')
56
+ I.click('.get-value')
57
+ assert.equal(await I.grabValueFrom('.debug'), '{"items":["01","02","03","04","05","06","07","08","09","10","11","12","13","14"]}')
58
+ I.click('[data-schemapath="root.items"] .form-control:nth-of-type(15)')
59
+ I.click('.get-value')
60
+ assert.equal(await I.grabValueFrom('.debug'), '{"items":["01","02","03","04","05","06","07","08","09","10","11","12","13","14","15"]}')
61
+ });
62
+
13
63
  Scenario('should trigger array (table) editing triggers @retry', async (I) => {
14
64
  I.amOnPage('table-move-events.html');
15
65
  I.seeElement('[data-schemapath="root.0"]');
@@ -478,6 +528,30 @@ Scenario('should work well with checkbox editors', async (I) => {
478
528
  I.dontSee('Checkbox 3');
479
529
  });
480
530
 
531
+ Scenario('should work well with checkbox editors with infoText', async (I) => {
532
+ I.amOnPage('array-checkboxes-infotext.html')
533
+
534
+ function check (checkboxId, title, infoText) {
535
+ const label = '//label[@for="' + checkboxId + '"]'
536
+ I.see(title, label)
537
+ const infoTextIcon = label + '/span[@class="je-infobutton-icon"]'
538
+
539
+ if (infoText) {
540
+ I.seeElement(infoTextIcon)
541
+ I.moveCursorTo(infoTextIcon)
542
+ I.see(infoText, label + '//span[@class="je-infobutton-tooltip"]')
543
+ } else {
544
+ I.dontSeeElement(infoTextIcon)
545
+ }
546
+ }
547
+
548
+ check('root0', 'old a')
549
+ check('root1', 'b')
550
+ check('root2', '3')
551
+ check('root3', '4', 'dd')
552
+ check('root4', 'e', 'ee')
553
+ })
554
+
481
555
  Scenario('should work well with rating editors', async (I) => {
482
556
  I.amOnPage('array-ratings.html');
483
557
  I.seeElement('[data-schemapath="root.0"]');