diff --git a/README.md b/README.md index 8559d2d..add6329 100644 --- a/README.md +++ b/README.md @@ -13,7 +13,7 @@ var compare = require('json-schema-compare') var isEqual = compare({ title: 'title 1', type: ['object'], - uniqueItems: false, + uniqueItems: false, // false is same as undefined dependencies: { name: ['age', 'lastName'] }, @@ -37,6 +37,16 @@ var isEqual = compare({ console.log(isEqual) // => true ``` +In the example above the name with `minLength: 0`, it is the same as all the all the following: +```json +{ properties: { name : { minLength: undefined } } } +{ properties: { name: {} } } +{ properties: { name: true } } +{ properties: { name: undefined } } +{ properties: {} } +{ } +``` + Compare json schemas correctly. - Ignores sort for arrays where sort does not matter, like required, enum, type, anyOf, oneOf, anyOf, dependencies (if array) diff --git a/src/index.js b/src/index.js index 5060e9d..3ff693a 100644 --- a/src/index.js +++ b/src/index.js @@ -14,12 +14,11 @@ var keys = obj => isPlainObject(obj) || Array.isArray(obj) ? Object.keys(obj) : var has = (obj, key) => obj.hasOwnProperty(key) var stringArray = arr => sortBy(uniq(arr)) var undefEmpty = val => undef(val) || (Array.isArray(val) && val.length === 0) -var keyValEqual = (a, b, key, compare) => b && has(b, key) && a && has(a, key) && compare(a[key], b[key]) var undefAndZero = (a, b) => (undef(a) && b === 0) || (undef(b) && a === 0) || isEqual(a, b) var falseUndefined = (a, b) => (undef(a) && b === false) || (undef(b) && a === false) || isEqual(a, b) var emptySchema = schema => undef(schema) || isEqual(schema, {}) || schema === true -var emptyObjUndef = schema => undef(schema) || isEqual(schema, {}) var isSchema = val => undef(val) || isPlainObject(val) || val === true || val === false +var propOrFake = (a, key) => undef(a) ? undefined : a[key] function undefArrayEqual(a, b) { if (undefEmpty(a) && undefEmpty(b)) { @@ -37,17 +36,15 @@ function unsortedNormalizedArray(a, b) { function schemaGroup(a, b, key, compare) { var allProps = uniq(keys(a).concat(keys(b))) - if (emptyObjUndef(a) && emptyObjUndef(b)) { + if (emptySchema(a) && emptySchema(b)) { return true - } else if (emptyObjUndef(a) && keys(b).length) { - return false - } else if (emptyObjUndef(b) && keys(a).length) { - return false } return allProps.every(function(key) { - var aVal = a[key] - var bVal = b[key] + var aVal = propOrFake(a, key) + var bVal = propOrFake(b, key) + + // TODO, remove when no longer support dependencies if (Array.isArray(aVal) && Array.isArray(bVal)) { return isEqual(stringArray(a), stringArray(b)) } else if (Array.isArray(aVal) && !Array.isArray(bVal)) { @@ -55,7 +52,8 @@ function schemaGroup(a, b, key, compare) { } else if (Array.isArray(bVal) && !Array.isArray(aVal)) { return false } - return keyValEqual(a, b, key, compare) + + return compare(aVal, bVal) }) } @@ -127,15 +125,14 @@ function compare(a, b, options) { return a === b } - if ((a === undefined && b === false) || (b === undefined && a === false)) { + if ((a !== false && b === false) || (b !== false && a === false)) { return false } - if ((undef(a) && !undef(b)) || (!undef(a) && undef(b))) { - return false - } + var aKeys = isPlainObject(a) ? Object.keys(a) : [] + var bKeys = isPlainObject(b) ? Object.keys(b) : [] - var allKeys = uniq(Object.keys(a).concat(Object.keys(b))) + var allKeys = uniq([...aKeys, ...bKeys]) if (options.ignore.length) { allKeys = allKeys.filter(k => options.ignore.indexOf(k) === -1) @@ -150,8 +147,8 @@ function compare(a, b, options) { } return allKeys.every(function(key) { - var aValue = a[key] - var bValue = b[key] + var aValue = propOrFake(a, key) + var bValue = propOrFake(b, key) if (schemaProps.indexOf(key) !== -1) { return compare(aValue, bValue, options) diff --git a/test/specs/index.spec.js b/test/specs/index.spec.js index 261a5d5..d2a28fd 100644 --- a/test/specs/index.spec.js +++ b/test/specs/index.spec.js @@ -30,7 +30,7 @@ describe('comparison', function() { minLength: 0 } } - }, false, { + }, true, { ignore: ['title'] }) }) @@ -38,6 +38,7 @@ describe('comparison', function() { it('compares false and undefined', function() { compare(undefined, false, false) }) + it('compares required unsorted', function() { compare({ required: ['test', 'rest'] @@ -45,6 +46,7 @@ describe('comparison', function() { required: ['rest', 'test', 'rest'] }, true) }) + it('compares equal required empty array and undefined', function() { compare({ required: [] @@ -54,11 +56,13 @@ describe('comparison', function() { required: ['fds'] }, {}, false) }) + it('compares equal properties empty object and undefined', function() { compare({ properties: {} }, {}, true) }) + it('compares properties', function() { compare({ properties: { @@ -74,16 +78,162 @@ describe('comparison', function() { } }, true) }) + + it('compares schema undefined and true', function() { + compare(true, undefined, true) + }) + + it('compares schema empty object and true', function() { + compare(true, {}, true) + }) + + it('compares sub schema with matching sub validators equal to undefined', function() { + compare({ + properties: { + name: undefined + } + }, { + properties: { + name: { + minLength: 0 + } + } + }, true) + }) + + it('compares sub schema with matching sub validators equal to missing', function() { + compare({ + properties: { + } + }, { + properties: { + name: { + minLength: 0 + } + } + }, true) + }) + + it('compares sub schema with matching sub validators equal to no properties', function() { + compare({}, { + properties: { + name: { + minLength: 0 + } + } + }, true) + + compare(true, { + properties: { + name: { + minLength: 0 + } + } + }, true) + + compare(undefined, { + properties: { + name: { + minLength: 0, + minItems: 0 + } + } + }, true) + }) + + it('compares sub schema with matching sub validators equal to no properties', function() { + compare({ + properties: { + name: false + } + }, { + properties: { + name: { + minLength: 0 + } + } + }, false) + }) + it('compares equal patternProperties empty object and undefined', function() { compare({ patternProperties: {} }, {}, true) }) + + it('compares patternProperties with regular property as false', function() { + compare({ + patternProperties: { + '.+': { + minLength: 9 + } + } + }, { + properties: { + name: { + minLength: 9 + } + } + }, false) + }) + + it('compares equal patternProperties', function() { + compare({ + patternProperties: { + '.+': { + minLength: 9 + } + } + }, { + patternProperties: { + '.+': { + minLength: 9 + } + } + }, true) + }) + + it('compares equal patternProperties', function() { + compare({ + patternProperties: { + '.+': { + minLength: 0 + }, + '..+': { + minLength: 0 + }, + '...+': { + minLength: 0 + } + } + }, { + patternProperties: { + '.+': {}, + '..+': true + } + }, true) + }) + + it('compares patternProperties, subschema false', function() { + compare({ + patternProperties: { + '.+': false + } + }, { + patternProperties: { + '.+': { + minLength: 0 + } + } + }, false) + }) + it('compares equal dependencies empty object and undefined', function() { compare({ dependencies: {} }, {}, true) }) + it('compares type unsorted', function() { compare({ type: ['string', 'array'] @@ -101,12 +251,14 @@ describe('comparison', function() { type: ['string'] }, true) }) + it('compares equal an empty schema, true and undefined', function() { compare({}, true, true) compare({}, undefined, true) compare(false, false, true) compare(true, true, true) }) + it('ignores any in ignore list', function() { compare({ title: 'title' @@ -123,6 +275,7 @@ describe('comparison', function() { type: ['string'] }, false) }) + it('sorts anyOf before comparing', function() { compare({ anyOf: [ @@ -203,6 +356,7 @@ describe('comparison', function() { ] }, true) }) + it('sorts allOf before comparing', function() { compare({ allOf: [ @@ -283,6 +437,7 @@ describe('comparison', function() { ] }, true) }) + it('sorts oneOf before comparing', function() { compare({ oneOf: [ @@ -363,6 +518,7 @@ describe('comparison', function() { ] }, true) }) + it('compares enum unsorted', function() { compare({ enum: ['abc', '123'] @@ -370,6 +526,7 @@ describe('comparison', function() { enum: ['123', 'abc', 'abc'] }, true) }) + it('compares dependencies value if array unsorted', function() { compare({ dependencies: { @@ -381,6 +538,7 @@ describe('comparison', function() { } }, true) }) + it('compares items SORTED', function() { compare({ items: [true, false] @@ -394,21 +552,25 @@ describe('comparison', function() { items: [true, false] }, true) }) + it('compares equal uniqueItems false and undefined', function() { compare({ uniqueItems: false }, {}, true) }) + it('compares equal minLength undefined and 0', function() { compare({ minLength: 0 }, {}, true) }) + it('compares equal minItems undefined and 0', function() { compare({ minItems: 0 }, {}, true) }) + it('compares equal minProperties undefined and 0', function() { compare({ minProperties: 0