diff --git a/lib/assertion.js b/lib/assertion.js index 7a6b04d..a342a42 100644 --- a/lib/assertion.js +++ b/lib/assertion.js @@ -170,6 +170,8 @@ Assertion.add = function(name, func) { value: function() { var context = new Assertion(this.obj, this, name); context.anyOne = this.anyOne; + context.onlyThis = this.onlyThis; + // hack context.light = true; try { @@ -190,6 +192,7 @@ Assertion.add = function(name, func) { // positive fail context.negate = false; + // hack context.light = false; context.fail(); } @@ -201,6 +204,7 @@ Assertion.add = function(name, func) { if (this.negate) { context.negate = true; // because .fail will set negate context.params.details = 'false negative fail'; + // hack context.light = false; context.fail(); } @@ -306,3 +310,16 @@ Assertion.addChain('not', function() { Assertion.addChain('any', function() { this.anyOne = true; }); + + +/** + * Only modifier - currently used with .keys to check if object contains only exactly this .keys + * + * @name only + * @property + * @memberOf Assertion + * @category assertion + */ +Assertion.addChain('only', function() { + this.onlyThis = true; +}); diff --git a/lib/ext/property.js b/lib/ext/property.js index 365f3d5..8aab2e7 100644 --- a/lib/ext/property.js +++ b/lib/ext/property.js @@ -55,6 +55,7 @@ export default function(should, Assertion) { /** * Asserts given object has enumerable property with optionally value. **On success it change given object to be value of property**. + * **Deprecated**. Use .keys * * @name enumerable * @memberOf Assertion @@ -80,7 +81,8 @@ export default function(should, Assertion) { }); /** - * Asserts given object has enumerable properties + * Asserts given object has enumerable properties. + * **Deprecated**. Use .keys * * @name enumerables * @memberOf Assertion @@ -289,6 +291,8 @@ export default function(should, Assertion) { * ({ a: 10 }).should.have.keys('a'); * ({ a: 10, b: 20 }).should.have.keys('a', 'b'); * (new Map([[1, 2]])).should.have.key(1); + * + * json.should.have.only.keys('type', 'version') */ Assertion.add('keys', function(keys) { keys = aSlice.call(arguments); @@ -300,7 +304,7 @@ export default function(should, Assertion) { return !hasKey(obj, key); }); - var verb = 'to have ' + (keys.length === 1 ? 'key ' : 'keys '); + var verb = 'to have ' + (this.onlyThis ? 'only ': '') + (keys.length === 1 ? 'key ' : 'keys '); this.params = {operator: verb + keys.join(', ')}; @@ -309,6 +313,10 @@ export default function(should, Assertion) { } this.assert(missingKeys.length === 0); + + if (this.onlyThis) { + obj.should.have.size(keys.length); + } }); @@ -363,17 +371,11 @@ export default function(should, Assertion) { * ({ a: {b: 10}}).should.have.propertyByPath('a', 'b').eql(10); */ Assertion.add('propertyByPath', function(properties) { - if (arguments.length > 1) { - properties = aSlice.call(arguments); - } else if (arguments.length === 1 && typeof properties == 'string') { - properties = [properties]; - } else if (arguments.length === 0) { - properties = []; - } + properties = aSlice.call(arguments); var allProps = properties.map(formatProp); - properties = properties.map(String); + properties = properties.map(convertPropertyName); var obj = should(Object(this.obj)); diff --git a/lib/util.js b/lib/util.js index 9c525ca..85822cd 100644 --- a/lib/util.js +++ b/lib/util.js @@ -12,6 +12,7 @@ export function isWrapperType(obj) { obj instanceof Boolean; } +// XXX make it more strict: numbers, strings, symbols - and nothing else export function convertPropertyName(name) { return (typeof name === 'symbol') ? name : String(name); } diff --git a/test/ext/property.test.js b/test/ext/property.test.js index 09aeb41..9e6a0f0 100644 --- a/test/ext/property.test.js +++ b/test/ext/property.test.js @@ -179,6 +179,10 @@ describe('property', function() { ({ '1': 'cancelled', '3': 'deleted' }).should.have.keys(1, 3); + ({ a: 10, b: 11 }).should.only.have.keys('a', 'b'); + + ({ a: 10, b: 11, c: 999 }).should.not.only.have.keys('a', 'b'); + err(function() { ({ foo: 1 }).should.have.keys('bar'); }, "expected Object { foo: 1 } to have key bar\n\tmissing keys: bar");