Fix Node._findSchema() (#651)

* Add test setup function for simulating browser globals

* Add tests for Node._findSchema

Some of the tests currently fail, which will be helpful in fixing the
incorrect behavior of the `_findSchema` function. The current failures
are:
- the last schema in the pattern properties object is always returned
- when pattern properties are present, wrong object schemas are returned

* Add schema-based tooltip to field names

Using the `title` and `description` properties from the schema, create
and set a tooltip on each field name. When the user hovers over a field
name, it will show the applicable information: title, description, both,
or neither, depending on what data is present in the schema.

* Remove redundant setting of field name title

* Remove accidental .only() from Node tests

* Fix Node._findSchema for pattern properties

The method now checks the key against the RegExp specified by the
pattern properties instead of always returning the last pattern property
in the object.

* Fix path used for recursive calls in Node._findSchema

* Add failing Node._findSchema tests for multi-level pattern properties

* Fix Node._findSchema for schemas with properties and patternProperties
This commit is contained in:
Adam Vigneaux 2019-02-16 08:01:30 -05:00 committed by Jos de Jong
parent 53a4f57dda
commit f8279537d0
5 changed files with 1005 additions and 36 deletions

816
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -38,6 +38,7 @@
"gulp-concat-css": "2.3.0", "gulp-concat-css": "2.3.0",
"gulp-shell": "0.6.3", "gulp-shell": "0.6.3",
"gulp-util": "3.0.8", "gulp-util": "3.0.8",
"jsdom": "^13.2.0",
"json-loader": "0.5.7", "json-loader": "0.5.7",
"mkdirp": "0.5.1", "mkdirp": "0.5.1",
"mocha": "5.2.0", "mocha": "5.2.0",

View File

@ -2610,36 +2610,39 @@ Node._findSchema = function (schema, schemaRefs, path) {
childSchema = allSchemas[j]; childSchema = allSchemas[j];
for (var i = 0; i < path.length && childSchema; i++) { for (var i = 0; i < path.length && childSchema; i++) {
var nextPath = path.slice(i + 1, path.length);
var key = path[i]; var key = path[i];
// fix childSchema with $ref, and not display the select element on the child schema because of not found enum // fix childSchema with $ref, and not display the select element on the child schema because of not found enum
if (typeof key === 'string' && childSchema['$ref']) { if (typeof key === 'string' && childSchema['$ref']) {
childSchema = schemaRefs[childSchema['$ref']]; childSchema = schemaRefs[childSchema['$ref']];
if (childSchema) { if (childSchema) {
foundSchema = Node._findSchema(childSchema, schemaRefs, path.slice(i, path.length)); foundSchema = Node._findSchema(childSchema, schemaRefs, nextPath);
} }
} }
else if (typeof key === 'string' && childSchema.patternProperties && i == path.length - 1) { else if (typeof key === 'string' && childSchema.patternProperties && !(childSchema.properties && key in childSchema.properties)) {
for (var prop in childSchema.patternProperties) { for (var prop in childSchema.patternProperties) {
foundSchema = Node._findSchema(childSchema.patternProperties[prop], schemaRefs, path.slice(i, path.length)); if (key.match(prop)) {
foundSchema = Node._findSchema(childSchema.patternProperties[prop], schemaRefs, nextPath);
}
} }
} }
else if (childSchema.items && childSchema.items.properties) { else if (childSchema.items && childSchema.items.properties) {
childSchema = childSchema.items.properties[key]; childSchema = childSchema.items.properties[key];
if (childSchema) { if (childSchema) {
foundSchema = Node._findSchema(childSchema, schemaRefs, path.slice(i, path.length)); foundSchema = Node._findSchema(childSchema, schemaRefs, nextPath);
} }
} }
else if (typeof key === 'string' && childSchema.properties) { else if (typeof key === 'string' && childSchema.properties) {
childSchema = childSchema.properties[key] || null; childSchema = childSchema.properties[key] || null;
if (childSchema) { if (childSchema) {
foundSchema = Node._findSchema(childSchema, schemaRefs, path.slice(i, path.length)); foundSchema = Node._findSchema(childSchema, schemaRefs, nextPath);
} }
} }
else if (typeof key === 'number' && childSchema.items) { else if (typeof key === 'number' && childSchema.items) {
childSchema = childSchema.items; childSchema = childSchema.items;
if (childSchema) { if (childSchema) {
foundSchema = Node._findSchema(childSchema, schemaRefs, path.slice(i, path.length)); foundSchema = Node._findSchema(childSchema, schemaRefs, nextPath);
} }
} }
} }

198
test/Node.test.js Normal file
View File

@ -0,0 +1,198 @@
var assert = require('assert');
var setUpTestEnvironment = require('./setup');
setUpTestEnvironment();
var JSONEditor = require('../src/js/JSONEditor');
var Node = require('../src/js/Node');
describe('Node', function () {
var node;
beforeEach(function () {
var editor = new JSONEditor(document.createElement('foo'));
node = new Node(editor);
});
describe('_findSchema', function () {
it('should find schema', function () {
var schema = {
type: 'object',
properties: {
child: {
type: 'string'
}
}
};
var path = ['child'];
assert.strictEqual(Node._findSchema(schema, {}, path), schema.properties.child);
});
it('should find schema within multi-level object properties', function () {
var schema = {
type: 'object',
properties: {
levelTwo: {
type: 'object',
properties: {
levelThree: {
type: 'object',
properties: {
bool: {
type: 'boolean'
}
}
}
}
}
}
};
var path = [];
assert.strictEqual(Node._findSchema(schema, {}, path), schema);
path = ['levelTwo'];
assert.strictEqual(Node._findSchema(schema, {}, path), schema.properties.levelTwo);
path = ['levelTwo', 'levelThree'];
assert.strictEqual(Node._findSchema(schema, {}, path), schema.properties.levelTwo.properties.levelThree);
path = ['levelTwo', 'levelThree', 'bool'];
assert.strictEqual(
Node._findSchema(schema, {}, path),
schema.properties.levelTwo.properties.levelThree.properties.bool
);
})
describe('with pattern properties', function () {
it('should find schema', function () {
var schema = {
type: 'object',
properties: {
str: {
title: 'str',
type: 'boolean'
}
},
patternProperties: {
'^foo[0-9]': {
title: 'foo[0-] pattern property',
type: 'string'
}
}
};
var path = [];
assert.strictEqual(Node._findSchema(schema, {}, path), schema, 'top level');
path = ['str'];
assert.strictEqual(Node._findSchema(schema, {}, path), schema.properties.str, 'normal property');
});
it('should find schema within multi-level object properties', function () {
var schema = {
type: 'object',
properties: {
levelTwo: {
type: 'object',
properties: {
levelThree: {
type: 'object',
properties: {
bool: {
title: 'bool',
type: 'boolean'
}
}
}
}
}
},
patternProperties: {
'^foo[0-9]': {
title: 'foo[0-9] pattern property',
type: 'string'
}
}
};
var path = [];
assert.strictEqual(Node._findSchema(schema, {}, path), schema, 'top level');
path = ['levelTwo'];
assert.strictEqual(Node._findSchema(schema, {}, path), schema.properties.levelTwo, 'level two');
path = ['levelTwo', 'levelThree'];
assert.strictEqual(Node._findSchema(schema, {}, path), schema.properties.levelTwo.properties.levelThree, 'level three');
path = ['levelTwo', 'levelThree', 'bool'];
assert.strictEqual(
Node._findSchema(schema, {}, path),
schema.properties.levelTwo.properties.levelThree.properties.bool,
'normal property'
);
});
it('should find schema for pattern properties', function () {
var schema = {
type: 'object',
patternProperties: {
'^foo[0-9]': {
title: 'foo[0-9] pattern property',
type: 'string'
},
'^bar[0-9]': {
title: 'bar[0-9] pattern property',
type: 'string'
}
}
};
var path = ['foo1'];
assert.strictEqual(
Node._findSchema(schema, {}, path),
schema.patternProperties['^foo[0-9]'],
'first pattern property'
);
path = ['bar5'];
assert.strictEqual(
Node._findSchema(schema, {}, path),
schema.patternProperties['^bar[0-9]'],
'second pattern property'
);
});
it('should find schema for multi-level pattern properties', function () {
var schema = {
type: 'object',
patternProperties: {
'^foo[0-9]': {
title: 'foo[0-9] pattern property',
type: 'object',
properties: {
fooChild: {
type: 'object',
properties: {
fooChild2: {
type: 'string'
}
}
}
}
},
'^bar[0-9]': {
title: 'bar[0-9] pattern property',
type: 'object',
properties: {
barChild: {
type: 'string'
}
}
}
}
};
var path = ['foo1', 'fooChild', 'fooChild2'];
assert.strictEqual(
Node._findSchema(schema, {}, path),
schema.patternProperties['^foo[0-9]'].properties.fooChild.properties.fooChild2,
'first pattern property child of child'
);
path = ['bar5', 'barChild'];
assert.strictEqual(
Node._findSchema(schema, {}, path),
schema.patternProperties['^bar[0-9]'].properties.barChild,
'second pattern property child'
);
});
});
});
});

11
test/setup.js Normal file
View File

@ -0,0 +1,11 @@
var JSDOM = require('jsdom').JSDOM;
/**
* Set up the test environment by simulating browser globals.
* @return {void}
*/
module.exports = function setUpTestEnvironment() {
var dom = new JSDOM('...');
global.window = dom.window;
global.document = dom.window.document;
};