diff --git a/src/asserts/equal-keys-assert.js b/src/asserts/equal-keys-assert.js index 0df1fa5..9f69171 100644 --- a/src/asserts/equal-keys-assert.js +++ b/src/asserts/equal-keys-assert.js @@ -4,19 +4,23 @@ */ import { Violation } from 'validator.js'; -import { difference, isPlainObject } from 'lodash'; +import { difference, intersection, isArray, isPlainObject } from 'lodash'; /** * Export `EqualKeysAssert`. */ -export default function equalKeysAssert(keys) { +export default function equalKeysAssert(...keys) { /** * Class name. */ this.__class__ = 'EqualKeys'; + if (keys.length === 1 && isArray(keys[0])) { + keys = keys[0]; + } + /** * Keys. */ @@ -32,10 +36,16 @@ export default function equalKeysAssert(keys) { throw new Violation(this, value, { value: 'must_be_a_plain_object' }); } - const diff = difference(Object.keys(value), this.keys); + const keys = Object.keys(value); + + if (keys.length === 0 || this.keys.length > keys.length) { + throw new Violation(this, value, { difference: difference(this.keys, keys) }); + } + + const intersects = intersection(this.keys, keys); - if (diff.length > 0) { - throw new Violation(this, value, { difference: diff }); + if (keys.length > this.keys.length || intersects.length !== keys.length) { + throw new Violation(this, value, { difference: difference(keys, this.keys) }); } return true; diff --git a/test/asserts/equal-keys-assert_test.js b/test/asserts/equal-keys-assert_test.js index 2f4b8e1..f8c93ee 100644 --- a/test/asserts/equal-keys-assert_test.js +++ b/test/asserts/equal-keys-assert_test.js @@ -35,7 +35,7 @@ describe('EqualKeysAssert', () => { }); }); - it('should throw an error if an object does not have the expected keys', () => { + it('should throw an error if the object not have the expected keys', () => { try { new Assert().EqualKeys(['foo']).validate({ bar: 'biz', foo: 'qux' }); @@ -45,9 +45,49 @@ describe('EqualKeysAssert', () => { } }); + it('should throw an error if the object is empty', () => { + try { + new Assert().EqualKeys(['foo']).validate({}); + + should.fail(); + } catch (e) { + e.show().violation.difference.should.eql(['foo']); + } + }); + + it('should allow setting no keys', () => { + try { + new Assert().EqualKeys().validate({ foo: 'oof' }); + + should.fail(); + } catch (e) { + e.show().violation.difference.should.eql(['foo']); + } + }); + + it('should allow setting keys as multiple arguments', () => { + try { + new Assert().EqualKeys('foo', 'bar').validate({ foo: 'oof' }); + + should.fail(); + } catch (e) { + e.show().violation.difference.should.eql(['bar']); + } + }); + + it('should allow setting a single key as a string argument', () => { + try { + new Assert().EqualKeys('bar').validate({ foo: 'oof' }); + + should.fail(); + } catch (e) { + e.show().violation.difference.should.eql(['foo']); + } + }); + it('should expose `assert` equal to `EqualKeys`', () => { try { - new Assert().EqualKeys().validate(123); + new Assert().EqualKeys(['foo']).validate(123); should.fail(); } catch (e) { @@ -55,7 +95,7 @@ describe('EqualKeysAssert', () => { } }); - it('should expose `difference` on the violation', () => { + it('should expose `difference` on the violation if object has extra keys', () => { try { new Assert().EqualKeys(['foo']).validate({ bar: 'biz', foo: 'qux' }); @@ -65,6 +105,16 @@ describe('EqualKeysAssert', () => { } }); + it('should expose `difference` on the violation if object has missing keys', () => { + try { + new Assert().EqualKeys(['foo', 'biz']).validate({ foo: 'qux' }); + + should.fail(); + } catch (e) { + e.show().violation.difference.should.eql(['biz']); + } + }); + it('should accept an object with expected keys', () => { new Assert().EqualKeys(['foo', 'bar']).validate({ bar: 'biz', foo: 'qux' }); });