@json-editor/json-editor 2.9.1 → 2.10.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 (87) hide show
  1. package/CHANGELOG.md +18 -0
  2. package/README.md +47 -1
  3. package/dist/jsoneditor.js +1 -1
  4. package/dist/jsoneditor.js.LICENSE.txt +1 -1
  5. package/dist/nonmin/jsoneditor.js +445 -124
  6. package/dist/nonmin/jsoneditor.js.map +1 -1
  7. package/docs/css_integration.html +17 -15
  8. package/docs/custom-editor.html +92 -0
  9. package/docs/index.html +4 -1
  10. package/docs/meta_schema.json +426 -398
  11. package/package.json +1 -1
  12. package/src/defaults.js +15 -1
  13. package/src/editor.js +23 -6
  14. package/src/editors/multiple.js +64 -7
  15. package/src/editors/object.js +0 -1
  16. package/src/iconlibs/bootstrap.js +28 -0
  17. package/src/iconlibs/index.js +2 -0
  18. package/src/resolvers.js +5 -2
  19. package/src/schemaloader.js +3 -1
  20. package/src/themes/bootstrap3.js +1 -1
  21. package/src/utilities.js +22 -0
  22. package/src/validator.js +93 -0
  23. package/tests/codeceptjs/constrains/contains_test.js +36 -0
  24. package/tests/codeceptjs/constrains/dependentSchemas_test.js +15 -0
  25. package/tests/codeceptjs/constrains/if-then-else_test.js +143 -0
  26. package/tests/codeceptjs/core_test.js +27 -27
  27. package/tests/codeceptjs/editors/advanced_test.js +11 -10
  28. package/tests/codeceptjs/editors/array_any_of_test.js +35 -35
  29. package/tests/codeceptjs/editors/array_test.js +757 -767
  30. package/tests/codeceptjs/editors/autocomplete_test.js +1 -3
  31. package/tests/codeceptjs/editors/button_test.js +25 -24
  32. package/tests/codeceptjs/editors/checkbox_test.js +17 -16
  33. package/tests/codeceptjs/editors/colorpicker_test.js +18 -16
  34. package/tests/codeceptjs/editors/datetime_test.js +7 -7
  35. package/tests/codeceptjs/editors/inheritance_test.js +7 -8
  36. package/tests/codeceptjs/editors/integer_test.js +71 -72
  37. package/tests/codeceptjs/editors/jodit_test.js +16 -17
  38. package/tests/codeceptjs/editors/multiselect_test.js +5 -5
  39. package/tests/codeceptjs/editors/number_test.js +64 -65
  40. package/tests/codeceptjs/editors/object_test.js +39 -13
  41. package/tests/codeceptjs/editors/option-no_default_values_test.js +4 -4
  42. package/tests/codeceptjs/editors/programmatic-changes_test.js +2 -3
  43. package/tests/codeceptjs/editors/range_test.js +1 -3
  44. package/tests/codeceptjs/editors/select_test.js +3 -5
  45. package/tests/codeceptjs/editors/stepper_test.js +5 -7
  46. package/tests/codeceptjs/editors/string_test.js +8 -8
  47. package/tests/codeceptjs/editors/table-confirm-delete_test.js +7 -9
  48. package/tests/codeceptjs/editors/uuid_test.js +10 -10
  49. package/tests/codeceptjs/editors/validation_test.js +1 -1
  50. package/tests/codeceptjs/{editors/issues → issues}/issue-gh-1133_test.js +1 -3
  51. package/tests/codeceptjs/issues/issue-gh-1158-2_test.js +10 -0
  52. package/tests/codeceptjs/{editors/issues → issues}/issue-gh-1158_test.js +0 -2
  53. package/tests/codeceptjs/issues/issue-gh-1164_test.js +10 -0
  54. package/tests/codeceptjs/issues/issue-gh-1211_test.js +17 -0
  55. package/tests/codeceptjs/{editors/issues → issues}/issue-gh-1257_test.js +2 -4
  56. package/tests/codeceptjs/issues/issue-gh-1338_test.js +16 -0
  57. package/tests/codeceptjs/issues/issue-gh-1347_test.js +8 -0
  58. package/tests/codeceptjs/issues/issue-gh-795_test.js +13 -0
  59. package/tests/codeceptjs/issues/issue-gh-810_test.js +52 -0
  60. package/tests/codeceptjs/issues/issue-gh-812_test.js +25 -0
  61. package/tests/codeceptjs/meta-schema_test.js +10 -10
  62. package/tests/codeceptjs/schemaloader_test.js +7 -7
  63. package/tests/codeceptjs/themes_test.js +31 -0
  64. package/tests/pages/autocomplete.html +1 -0
  65. package/tests/pages/contains.html +38 -0
  66. package/tests/pages/dependentSchemas.html +52 -0
  67. package/tests/pages/if-else.html +57 -0
  68. package/tests/pages/if-then-else-allOf.html +117 -0
  69. package/tests/pages/if-then-else.html +64 -0
  70. package/tests/pages/if-then.html +57 -0
  71. package/tests/pages/issues/issue-gh-1158-2.html +189 -0
  72. package/tests/pages/issues/issue-gh-1165.html +63 -0
  73. package/tests/pages/issues/issue-gh-1165.json +8 -0
  74. package/tests/pages/issues/issue-gh-1211.html +1022 -0
  75. package/tests/pages/issues/issue-gh-1338.html +74 -0
  76. package/tests/pages/issues/issue-gh-1347.html +142 -0
  77. package/tests/pages/issues/issue-gh-795.html +58 -0
  78. package/tests/pages/issues/issue-gh-810.html +149 -0
  79. package/tests/pages/maxContains.html +39 -0
  80. package/tests/pages/meta-schema.html +28 -0
  81. package/tests/pages/meta_schema.json +426 -398
  82. package/tests/pages/minContains.html +39 -0
  83. package/tests/pages/option-dependencies.html +106 -0
  84. package/tests/pages/themes.html +3 -1
  85. package/tests/unit/core.spec.js +2 -5
  86. package/tests/codeceptjs/editors/issues/issue-gh-1164_test.js +0 -12
  87. package/tests/codeceptjs/editors/issues/issue-gh-812_test.js +0 -32
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "@json-editor/json-editor",
3
3
  "title": "JSONEditor",
4
4
  "description": "JSON Schema based editor",
5
- "version": "2.9.1",
5
+ "version": "2.10.0",
6
6
  "main": "dist/jsoneditor.js",
7
7
  "author": {
8
8
  "name": "Jeremy Dorn",
package/src/defaults.js CHANGED
@@ -101,6 +101,20 @@ languages.en = {
101
101
  * @variables This key takes one variable: The maximum character count
102
102
  */
103
103
  error_maxLength: 'Value must be at most {{0}} characters long',
104
+ /**
105
+ * When no array items validates the contains schema
106
+ */
107
+ error_contains: 'No items match contains',
108
+ /**
109
+ * When an array have too few items that validate agaist contains schema
110
+ * @variables This key takes two variable: The valid items count and the minContains value
111
+ */
112
+ error_minContains: 'Contains match count {{0}} is less than minimum contains count of {{1}}',
113
+ /**
114
+ * When an array have too many items that validate agaist contains schema
115
+ * @variables This key takes two variable: The valid items count and the maxContains value
116
+ */
117
+ error_maxContains: 'Contains match count {{0}} exceeds maximum contains count of {{1}}',
104
118
  /**
105
119
  * When a value does not have enough characters
106
120
  * @variables This key takes one variable: The minimum character count
@@ -345,7 +359,7 @@ languages.en = {
345
359
  /**
346
360
  * Warning when deleting a node
347
361
  */
348
- button_delete_node_warning: 'Are you sure you want to remove this node?'
362
+ button_delete_node_warning: 'Are you sure you want to remove this item?'
349
363
  }
350
364
 
351
365
  /* Default per-editor options */
package/src/editor.js CHANGED
@@ -95,9 +95,17 @@ export class AbstractEditor {
95
95
  }
96
96
 
97
97
  Object.keys(deps).forEach(dependency => {
98
- let path = this.path.split('.')
99
- path[path.length - 1] = dependency
100
- path = path.join('.')
98
+ let path
99
+ const isFullPath = dependency.startsWith(this.jsoneditor.root.path)
100
+
101
+ if (isFullPath) {
102
+ path = dependency
103
+ } else {
104
+ path = this.path.split('.')
105
+ path[path.length - 1] = dependency
106
+ path = path.join('.')
107
+ }
108
+
101
109
  this.jsoneditor.watch(path, () => {
102
110
  this.evaluateDependencies()
103
111
  })
@@ -114,14 +122,23 @@ export class AbstractEditor {
114
122
  if (!deps) {
115
123
  return
116
124
  }
125
+
117
126
  // Assume true and set to false if any unmet dependencies are found
118
127
  const previousStatus = this.dependenciesFulfilled
119
128
  this.dependenciesFulfilled = true
120
129
 
121
130
  Object.keys(deps).forEach(dependency => {
122
- let path = this.path.split('.')
123
- path[path.length - 1] = dependency
124
- path = path.join('.')
131
+ let path
132
+ const isFullPath = dependency.startsWith(this.jsoneditor.root.path)
133
+
134
+ if (isFullPath) {
135
+ path = dependency
136
+ } else {
137
+ path = this.path.split('.')
138
+ path[path.length - 1] = dependency
139
+ path = path.join('.')
140
+ }
141
+
125
142
  const choices = deps[dependency]
126
143
  this.checkDependency(path, choices)
127
144
  })
@@ -1,7 +1,7 @@
1
1
  /* Multiple Editor (for when `type` is an array, also when `oneOf` is present) */
2
2
  import { AbstractEditor } from '../editor.js'
3
3
  import { Validator } from '../validator.js'
4
- import { extend } from '../utilities.js'
4
+ import { extend, mergeDeep } from '../utilities.js'
5
5
 
6
6
  export class MultipleEditor extends AbstractEditor {
7
7
  register () {
@@ -56,6 +56,8 @@ export class MultipleEditor extends AbstractEditor {
56
56
  }
57
57
 
58
58
  switchEditor (i) {
59
+ this.lastType = this.type
60
+
59
61
  if (!this.editors[i]) {
60
62
  this.buildChildEditor(i)
61
63
  }
@@ -68,11 +70,15 @@ export class MultipleEditor extends AbstractEditor {
68
70
 
69
71
  this.editors.forEach((editor, type) => {
70
72
  if (!editor) return
73
+
71
74
  if (this.type === type) {
72
- if (this.keep_values) editor.setValue(currentValue, true)
75
+ if (this.keep_values || this.if) editor.setValue(currentValue, true)
73
76
  editor.container.style.display = ''
74
- } else editor.container.style.display = 'none'
77
+ } else {
78
+ editor.container.style.display = 'none'
79
+ }
75
80
  })
81
+
76
82
  this.refreshValue()
77
83
  this.refreshHeaderText()
78
84
  }
@@ -140,6 +146,31 @@ export class MultipleEditor extends AbstractEditor {
140
146
  this.anyOf = true
141
147
  this.types = this.schema.anyOf
142
148
  delete this.schema.anyOf
149
+ } else if (this.schema.if) {
150
+ this.if = true
151
+ this.ifSchema = JSON.parse(JSON.stringify(this.schema.if))
152
+ this.thenSchema = { title: 'then' }
153
+ this.elseSchema = { title: 'else' }
154
+ this.types = []
155
+
156
+ if (this.schema.then) {
157
+ mergeDeep(this.thenSchema, this.schema, this.schema.then)
158
+ }
159
+
160
+ if (this.schema.else) {
161
+ mergeDeep(this.elseSchema, this.schema, this.schema.else)
162
+ }
163
+
164
+ this.types.push(this.thenSchema)
165
+ this.types.push(this.elseSchema)
166
+
167
+ this.types.forEach((schema) => {
168
+ delete schema.if
169
+ delete schema.then
170
+ delete schema.else
171
+ })
172
+
173
+ delete this.schema.if
143
174
  } else {
144
175
  if (!this.schema.type || this.schema.type === 'any') {
145
176
  this.types = ['string', 'number', 'integer', 'boolean', 'object', 'array', 'null']
@@ -169,12 +200,14 @@ export class MultipleEditor extends AbstractEditor {
169
200
 
170
201
  build () {
171
202
  const { container } = this
172
-
173
203
  this.header = this.label = this.theme.getFormInputLabel(this.getTitle(), this.isRequired())
174
- this.container.appendChild(this.header)
175
-
176
204
  this.switcher = this.theme.getSwitcher(this.display_text)
177
- container.appendChild(this.switcher)
205
+
206
+ if (!this.if) {
207
+ this.container.appendChild(this.header)
208
+ container.appendChild(this.switcher)
209
+ }
210
+
178
211
  this.switcher.addEventListener('change', e => {
179
212
  e.preventDefault()
180
213
  e.stopPropagation()
@@ -220,6 +253,7 @@ export class MultipleEditor extends AbstractEditor {
220
253
  this.refreshHeaderText()
221
254
  }
222
255
 
256
+ this.switchIf()
223
257
  super.onChildEditorChange()
224
258
  }
225
259
 
@@ -234,6 +268,24 @@ export class MultipleEditor extends AbstractEditor {
234
268
  this.value = this.editors[this.type].getValue()
235
269
  }
236
270
 
271
+ switchIf () {
272
+ if (this.ifSchema && this.value) {
273
+ const type = this.getIfType(this.value)
274
+
275
+ if (this.lastType !== type) {
276
+ this.switchEditor(type)
277
+ this.editors[this.type].setValue(this.value, true)
278
+ }
279
+
280
+ this.switcher.value = this.display_text[this.type]
281
+ }
282
+ }
283
+
284
+ getIfType (value) {
285
+ const errors = this.jsoneditor.validator._validateSchema(this.ifSchema, value)
286
+ return errors.length === 0 ? 0 : 1
287
+ }
288
+
237
289
  setValue (val, initial) {
238
290
  /* Determine type by getting the first one that validates */
239
291
 
@@ -279,6 +331,9 @@ export class MultipleEditor extends AbstractEditor {
279
331
  finalI = fitTestVal.i
280
332
  }
281
333
  }
334
+ if (this.if) {
335
+ finalI = this.getIfType(val)
336
+ }
282
337
  if (finalI === null) {
283
338
  finalI = this.type
284
339
  }
@@ -286,8 +341,10 @@ export class MultipleEditor extends AbstractEditor {
286
341
  this.switcher.value = this.display_text[finalI]
287
342
 
288
343
  const typeChanged = this.type !== prevType
344
+
289
345
  if (typeChanged) {
290
346
  this.switchEditor(this.type)
347
+ this.editors[this.type].setValue(val, initial)
291
348
  }
292
349
 
293
350
  if (typeof val !== 'undefined') {
@@ -893,7 +893,6 @@ export class ObjectEditor extends AbstractEditor {
893
893
  let labelText
894
894
 
895
895
  const checkbox = this.theme.getCheckbox()
896
- checkbox.style.width = 'auto'
897
896
 
898
897
  if (this.schema.properties[key] && this.schema.properties[key].title) { labelText = this.schema.properties[key].title } else { labelText = key }
899
898
 
@@ -0,0 +1,28 @@
1
+ import { AbstractIconLib } from '../iconlib.js'
2
+
3
+ const iconPrefix = 'bi bi-'
4
+ const mapping = {
5
+ collapse: 'chevron-down',
6
+ expand: 'chevron-right',
7
+ delete: 'trash',
8
+ edit: 'pencil',
9
+ add: 'plus',
10
+ subtract: 'dash',
11
+ cancel: 'x-circle',
12
+ save: 'save',
13
+ moveup: 'arrow-up',
14
+ moveright: 'arrow-right',
15
+ movedown: 'arrow-down',
16
+ moveleft: 'arrow-left',
17
+ copy: 'clipboard',
18
+ clear: 'x-circle',
19
+ time: 'clock',
20
+ calendar: 'calendar',
21
+ edit_properties: 'list-ul'
22
+ }
23
+
24
+ export class bootstrapIconlib extends AbstractIconLib {
25
+ constructor () {
26
+ super(iconPrefix, mapping)
27
+ }
28
+ }
@@ -9,9 +9,11 @@ import { jqueryuiIconlib } from './jqueryui.js'
9
9
  // import { materialiconsIconlib } from './materialicons.js'
10
10
  import { openiconicIconlib } from './openiconic.js'
11
11
  import { spectreIconlib } from './spectre.js'
12
+ import { bootstrapIconlib } from './bootstrap'
12
13
 
13
14
  export const iconlibs = {
14
15
  // bootstrap2: bootstrap2Iconlib,
16
+ bootstrap: bootstrapIconlib,
15
17
  bootstrap3: bootstrap3Iconlib,
16
18
  fontawesome3: fontawesome3Iconlib,
17
19
  fontawesome4: fontawesome4Iconlib,
package/src/resolvers.js CHANGED
@@ -76,6 +76,9 @@ const arraysOfStrings = schema => {
76
76
  /* Use the multiple editor for schemas with `oneOf` or `anyOf` set */
77
77
  const oneOf = schema => (schema.oneOf || schema.anyOf) && 'multiple'
78
78
 
79
+ /* Use the multiple editor for schemas with `if` set */
80
+ const ifThenElse = schema => (schema.if) && 'multiple'
81
+
79
82
  /* Specialized editor for date, time and datetime-local formats */
80
83
  const date = schema => ['string', 'integer'].includes(schema.type) && ['date', 'time', 'datetime-local'].includes(schema.format) && 'datetime'
81
84
 
@@ -114,7 +117,7 @@ const markdown = schema => schema.type === 'string' && schema.format === 'markdo
114
117
  const xhtml = schema => schema.type === 'string' && ['xhtml', 'bbcode'].includes(schema.format) && 'sceditor'
115
118
 
116
119
  /* Use the ace editor for schemas with format equals any of ace editor modes */
117
- const aceModes = ['actionscript', 'batchfile', 'c', 'c++', 'cpp', 'coffee', 'csharp', 'css', 'dart', 'django', 'ejs', 'erlang', 'golang', 'groovy', 'handlebars', 'haskell', 'haxe', 'html', 'ini', 'jade', 'java', 'javascript', 'json', 'less', 'lisp', 'lua', 'makefile', 'matlab', 'mysql', 'objectivec', 'pascal', 'perl', 'pgsql', 'php', 'python', 'r', 'ruby', 'sass', 'scala', 'scss', 'smarty', 'sql', 'sqlserver', 'stylus', 'svg', 'twig', 'vbscript', 'xml', 'yaml']
120
+ const aceModes = ['actionscript', 'batchfile', 'c', 'c++', 'cpp', 'coffee', 'csharp', 'css', 'dart', 'django', 'ejs', 'erlang', 'golang', 'groovy', 'handlebars', 'haskell', 'haxe', 'html', 'ini', 'jade', 'java', 'javascript', 'json', 'less', 'lisp', 'lua', 'makefile', 'matlab', 'mysql', 'objectivec', 'pascal', 'perl', 'pgsql', 'php', 'python', 'r', 'ruby', 'sass', 'scala', 'scss', 'sh', 'smarty', 'sql', 'sqlserver', 'stylus', 'svg', 'twig', 'vbscript', 'xml', 'yaml']
118
121
  const ace = schema => schema.type === 'string' && aceModes.includes(schema.format) && 'ace'
119
122
 
120
123
  const ip = schema => schema.type === 'string' && ['ip', 'ipv4', 'ipv6', 'hostname'].includes(schema.format) && 'ip'
@@ -122,4 +125,4 @@ const ip = schema => schema.type === 'string' && ['ip', 'ipv4', 'ipv6', 'hostnam
122
125
  const colorPicker = schema => schema.type === 'string' && schema.format === 'color' && 'colorpicker'
123
126
 
124
127
  /* Export resolvers in order of discovery, first to last */
125
- export const resolvers = [colorPicker, ip, ace, xhtml, markdown, jodit, autoComplete, uuid, info, button, stepper, describeBy, starratings, date, oneOf, arraysOfStrings, enumeratedProperties, enumSource, table, upload, base64, any, boolean, signature, primitive, object, defaultResolver]
128
+ export const resolvers = [colorPicker, ip, ace, xhtml, markdown, jodit, autoComplete, uuid, info, button, stepper, describeBy, starratings, date, oneOf, ifThenElse, arraysOfStrings, enumeratedProperties, enumSource, table, upload, base64, any, boolean, signature, primitive, object, defaultResolver]
@@ -158,7 +158,9 @@ export class SchemaLoader {
158
158
  // If local ref
159
159
  if (refWithPointerSplit.length === 2 && !this.refs_with_info[_schema.$ref]) {
160
160
  const sub = this.expandRecursivePointer(this.schema, refWithPointerSplit[1])
161
- return this.extendSchemas(_schema, this.expandSchema(sub))
161
+ const expandedSchema = this.extendSchemas(_schema, this.expandSchema(sub))
162
+ delete expandedSchema.$ref
163
+ return expandedSchema
162
164
  }
163
165
  const refObj = (refWithPointerSplit.length > 2)
164
166
  ? this.refs_with_info['#' + refWithPointerSplit[1]]
@@ -1,5 +1,5 @@
1
1
  import { AbstractTheme } from '../theme.js'
2
- import rules from './bootstrap3.css'
2
+ import rules from './bootstrap3.css.js'
3
3
 
4
4
  export class bootstrap3Theme extends AbstractTheme {
5
5
  getSelectInput (options, multiple) {
package/src/utilities.js CHANGED
@@ -104,3 +104,25 @@ export function generateUUID () {
104
104
  return (c === 'x' ? r : (r & 0x3 | 0x8)).toString(16)
105
105
  })
106
106
  }
107
+
108
+ export function isObject (item) {
109
+ return (item && typeof item === 'object' && !Array.isArray(item))
110
+ }
111
+
112
+ export function mergeDeep (target, ...sources) {
113
+ if (!sources.length) return target
114
+ const source = sources.shift()
115
+
116
+ if (isObject(target) && isObject(source)) {
117
+ for (const key in source) {
118
+ if (isObject(source[key])) {
119
+ if (!target[key]) Object.assign(target, { [key]: {} })
120
+ mergeDeep(target[key], source[key])
121
+ } else {
122
+ Object.assign(target, { [key]: source[key] })
123
+ }
124
+ }
125
+ }
126
+
127
+ return mergeDeep(target, ...sources)
128
+ }
package/src/validator.js CHANGED
@@ -11,6 +11,99 @@ export class Validator {
11
11
  this.defaults = defaults
12
12
 
13
13
  this._validateSubSchema = {
14
+ dependentSchemas (schema, value, path) {
15
+ let errors = []
16
+
17
+ Object.keys(schema.dependentSchemas).forEach((key) => {
18
+ if (typeof value[key] !== 'undefined') {
19
+ const dependentSchema = schema.dependentSchemas[key]
20
+ const tmpErrors = this._validateSchema(dependentSchema, value, path)
21
+ errors = [...errors, ...tmpErrors]
22
+ }
23
+ })
24
+
25
+ return errors
26
+ },
27
+ contains (schema, value, path) {
28
+ const errors = []
29
+ let counter = 0
30
+
31
+ value.forEach((item) => {
32
+ const containsErrors = this._validateSchema(schema.contains, item, path)
33
+
34
+ if (containsErrors.length === 0) {
35
+ counter++
36
+ }
37
+ })
38
+
39
+ const containsInvalid = (counter === 0)
40
+
41
+ if (typeof schema.minContains !== 'undefined') {
42
+ const minContainsInvalid = (counter < schema.minContains)
43
+
44
+ if (minContainsInvalid) {
45
+ errors.push({
46
+ message: this.translate('error_minContains', [counter, schema.minContains], schema),
47
+ path: path
48
+ })
49
+ }
50
+ } else {
51
+ if (containsInvalid) {
52
+ errors.push({
53
+ message: this.translate('error_contains', null, schema),
54
+ path: path
55
+ })
56
+ }
57
+ }
58
+
59
+ if (typeof schema.maxContains !== 'undefined') {
60
+ const maxContainsInvalid = (counter > schema.maxContains)
61
+
62
+ if (maxContainsInvalid) {
63
+ errors.push({
64
+ message: this.translate('error_maxContains', [counter, schema.maxContains], schema),
65
+ path: path
66
+ })
67
+ }
68
+ }
69
+
70
+ return errors
71
+ },
72
+ if (schema, value, path) {
73
+ if (typeof schema.then === 'undefined' && typeof schema.else === 'undefined') {
74
+ return []
75
+ }
76
+
77
+ const ifErrors = this._validateSchema(schema.if, value, path)
78
+ let thenErrors = []
79
+ let elseErrors = []
80
+
81
+ if (typeof schema.then !== 'undefined') {
82
+ thenErrors = this._validateSchema(schema.then, value, path)
83
+ }
84
+
85
+ if (typeof schema.else !== 'undefined') {
86
+ elseErrors = this._validateSchema(schema.else, value, path)
87
+ }
88
+
89
+ if (schema.if === true) {
90
+ return thenErrors
91
+ }
92
+
93
+ if (schema.if === false) {
94
+ return elseErrors
95
+ }
96
+
97
+ if (ifErrors.length === 0) {
98
+ return thenErrors
99
+ }
100
+
101
+ if (ifErrors.length > 0) {
102
+ return elseErrors
103
+ }
104
+
105
+ return []
106
+ },
14
107
  const (schema, value, path) {
15
108
  const valid = JSON.stringify(schema.const) === JSON.stringify(value) && !(Array.isArray(value) || typeof value === 'object')
16
109
  if (!valid) {
@@ -0,0 +1,36 @@
1
+ /* global Feature Scenario */
2
+
3
+ Feature('contains')
4
+
5
+ Scenario('should display contains validation errors @contains', ({ I }) => {
6
+ I.amOnPage('contains.html')
7
+ I.waitForElement('.je-ready')
8
+ I.waitForText('No items match contains')
9
+ I.click('.json-editor-btn-add')
10
+ I.dontSee('No items match contains')
11
+ })
12
+
13
+ Scenario('should display @minContains validation errors @contains', ({ I }) => {
14
+ I.amOnPage('minContains.html')
15
+ I.waitForElement('.je-ready')
16
+ I.dontSee('No items match contains')
17
+ I.waitForText('Contains match count 0 is less than minimum contains count of 2')
18
+ I.click('.json-editor-btn-add')
19
+ I.dontSee('No items match contains')
20
+ I.waitForText('Contains match count 1 is less than minimum contains count of 2')
21
+ I.click('.json-editor-btn-add')
22
+ I.dontSee('No items match contains')
23
+ I.dontSee('minimum contains count of 2')
24
+ })
25
+
26
+ Scenario('should display @maxContains validation errors @contains', ({ I }) => {
27
+ I.amOnPage('maxContains.html')
28
+ I.waitForElement('.je-ready')
29
+ I.waitForText('No items match contains')
30
+ I.click('.json-editor-btn-add')
31
+ I.dontSee('No items match contains')
32
+ I.click('.json-editor-btn-add')
33
+ I.dontSee('No items match contains')
34
+ I.click('.json-editor-btn-add')
35
+ I.waitForText('Contains match count 3 exceeds maximum contains count of 2')
36
+ })
@@ -0,0 +1,15 @@
1
+ /* global Feature Scenario */
2
+
3
+ Feature('dependentSchemas')
4
+
5
+ Scenario('@dependentSchemas should display validation errors', ({ I }) => {
6
+ I.amOnPage('dependentSchemas.html')
7
+ I.waitForElement('.je-ready')
8
+ I.dontSee('Object is missing the required property \'billing_address\'')
9
+ I.click('[data-schemapath="root.credit_card"] .json-editor-opt-in')
10
+ I.waitForText('Object is missing the required property \'billing_address\'')
11
+ I.click('[data-schemapath="root.billing_address"] .json-editor-opt-in')
12
+ I.dontSee('Object is missing the required property \'billing_address\'')
13
+ I.click('[data-schemapath="root.credit_card"] .json-editor-opt-in')
14
+ I.dontSee('Object is missing the required property \'billing_address\'')
15
+ })
@@ -0,0 +1,143 @@
1
+ /* global Feature Scenario */
2
+
3
+ Feature('if-then-else')
4
+
5
+ Scenario('validate agaist allOf of if schemas @if-then-else', async ({ I }) => {
6
+ I.amOnPage('if-then-else-allOf.html')
7
+ I.waitForElement('.je-ready')
8
+
9
+ I.selectOption('[name="root[country]"]', 'United States of America')
10
+ I.waitForText('Value must match the pattern [0-9]{5}(-[0-9]{4})?.')
11
+
12
+ I.selectOption('[name="root[country]"]', 'Canada')
13
+ I.waitForText('Value must match the pattern [A-Z][0-9][A-Z] [0-9][A-Z][0-9].')
14
+
15
+ I.selectOption('[name="root[country]"]', 'Netherlands')
16
+ I.waitForText('Value must match the pattern [0-9]{4} [A-Z]{2}.')
17
+
18
+ I.fillField('#value', JSON.stringify({
19
+ street_address: '1600 Pennsylvania Avenue NW',
20
+ country: 'United States of America',
21
+ postal_code: '20500'
22
+ }))
23
+ I.click('#set-value')
24
+ I.dontSee('.invalid-feedback')
25
+
26
+ I.fillField('#value', JSON.stringify({
27
+ street_address: '1600 Pennsylvania Avenue NW',
28
+ postal_code: '20500'
29
+ }))
30
+ I.click('#set-value')
31
+ I.dontSee('.invalid-feedback')
32
+
33
+ I.fillField('#value', JSON.stringify({
34
+ street_address: '24 Sussex Drive',
35
+ country: 'Canada',
36
+ postal_code: 'K1M 1M4'
37
+ }))
38
+ I.click('#set-value')
39
+ I.dontSee('.invalid-feedback')
40
+
41
+ I.fillField('#value', JSON.stringify({
42
+ street_address: 'Adriaan Goekooplaan',
43
+ country: 'Netherlands',
44
+ postal_code: '2517 JX'
45
+ }))
46
+ I.click('#set-value')
47
+ I.dontSee('.invalid-feedback')
48
+
49
+ I.fillField('#value', JSON.stringify({
50
+ street_address: '24 Sussex Drive',
51
+ country: 'Canada',
52
+ postal_code: '10000'
53
+ }))
54
+ I.click('#set-value')
55
+ I.waitForText('Value must match the pattern [A-Z][0-9][A-Z] [0-9][A-Z][0-9].')
56
+ I.waitForElement('.invalid-feedback')
57
+
58
+ I.fillField('#value', JSON.stringify({
59
+ street_address: '1600 Pennsylvania Avenue NW',
60
+ postal_code: 'K1M 1M4'
61
+ }))
62
+ I.click('#set-value')
63
+ I.waitForElement('.invalid-feedback')
64
+ })
65
+
66
+ Scenario('validate agaist if-then-else @if-then-else', async ({ I }) => {
67
+ I.amOnPage('if-then-else.html')
68
+ I.waitForElement('.je-ready')
69
+
70
+ I.selectOption('[name="root[country]"]', 'America')
71
+ I.fillField('[name="root[postal_code]"]', 'K1M 1M4')
72
+ I.pressKey('Tab')
73
+ I.waitForElement('.invalid-feedback')
74
+ I.waitForText('Value must match the pattern [0-9]{5}(-[0-9]{4})?.')
75
+
76
+ I.selectOption('[name="root[country]"]', 'Canada')
77
+ I.fillField('[name="root[postal_code]"]', '10000')
78
+ I.pressKey('Tab')
79
+ I.waitForElement('.invalid-feedback')
80
+ I.waitForText('Value must match the pattern [A-Z][0-9][A-Z] [0-9][A-Z][0-9].')
81
+
82
+ I.selectOption('[name="root[country]"]', 'America')
83
+ I.fillField('[name="root[postal_code]"]', '10000')
84
+ I.pressKey('Tab')
85
+ I.dontSee('.invalid-feedback')
86
+
87
+ I.selectOption('[name="root[country]"]', 'Canada')
88
+ I.fillField('[name="root[postal_code]"]', 'K1M 1M4')
89
+ I.pressKey('Tab')
90
+ I.dontSee('.invalid-feedback')
91
+ })
92
+
93
+ Scenario('validate agaist if-then @if-then-else', async ({ I }) => {
94
+ I.amOnPage('if-then.html')
95
+ I.waitForElement('.je-ready')
96
+
97
+ I.selectOption('[name="root[country]"]', 'America')
98
+ I.fillField('[name="root[postal_code]"]', 'K1M 1M4')
99
+ I.pressKey('Tab')
100
+ I.waitForElement('.invalid-feedback')
101
+ I.waitForText('Value must match the pattern [0-9]{5}(-[0-9]{4})?.')
102
+
103
+ I.selectOption('[name="root[country]"]', 'Canada')
104
+ I.fillField('[name="root[postal_code]"]', '10000')
105
+ I.pressKey('Tab')
106
+ I.dontSee('.invalid-feedback')
107
+
108
+ I.selectOption('[name="root[country]"]', 'America')
109
+ I.fillField('[name="root[postal_code]"]', '10000')
110
+ I.pressKey('Tab')
111
+ I.dontSee('.invalid-feedback')
112
+
113
+ I.selectOption('[name="root[country]"]', 'Canada')
114
+ I.fillField('[name="root[postal_code]"]', 'K1M 1M4')
115
+ I.pressKey('Tab')
116
+ I.dontSee('.invalid-feedback')
117
+ })
118
+
119
+ Scenario('validate agaist if-else @if-then-else', async ({ I }) => {
120
+ I.amOnPage('if-else.html')
121
+ I.waitForElement('.je-ready')
122
+
123
+ I.selectOption('[name="root[country]"]', 'America')
124
+ I.fillField('[name="root[postal_code]"]', 'K1M 1M4')
125
+ I.pressKey('Tab')
126
+ I.dontSee('.invalid-feedback')
127
+
128
+ I.selectOption('[name="root[country]"]', 'Canada')
129
+ I.fillField('[name="root[postal_code]"]', '10000')
130
+ I.pressKey('Tab')
131
+ I.waitForElement('.invalid-feedback')
132
+ I.waitForText('Value must match the pattern [A-Z][0-9][A-Z] [0-9][A-Z][0-9].')
133
+
134
+ I.selectOption('[name="root[country]"]', 'America')
135
+ I.fillField('[name="root[postal_code]"]', '10000')
136
+ I.pressKey('Tab')
137
+ I.dontSee('.invalid-feedback')
138
+
139
+ I.selectOption('[name="root[country]"]', 'Canada')
140
+ I.fillField('[name="root[postal_code]"]', 'K1M 1M4')
141
+ I.pressKey('Tab')
142
+ I.dontSee('.invalid-feedback')
143
+ })