Skip to content

Commit

Permalink
Deep flag support for keys assertion
Browse files Browse the repository at this point in the history
  • Loading branch information
lucasfcosta committed Sep 22, 2016
1 parent 01963b7 commit 816b5a3
Show file tree
Hide file tree
Showing 5 changed files with 358 additions and 87 deletions.
29 changes: 24 additions & 5 deletions lib/chai/core/assertions.js
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ module.exports = function (chai, _) {
/**
* ### .deep
*
* Sets the `deep` flag, later used by the `equal`, `include`, `members`, and
* Sets the `deep` flag, later used by the `equal`, `include`, `members`, `keys` and
* `property` assertions.
*
* const obj = {a: 1};
Expand Down Expand Up @@ -1328,6 +1328,10 @@ module.exports = function (chai, _) {
* can have objects as keys, so, in this case, you can pass multiple objects as arguments if
* you want to.
*
* The default behavior when it comes to Maps and Sets is to use strict equality
* (===) to compare values. You can also use the `deep` flag if you want a deep comparison
* instead of a strict comparison.
*
* expect({ foo: 1, bar: 2 }).to.have.any.keys('foo', 'baz');
* expect({ foo: 1, bar: 2 }).to.have.any.keys('foo');
* expect({ foo: 1, bar: 2 }).to.contain.any.keys('bar', 'baz');
Expand All @@ -1338,9 +1342,13 @@ module.exports = function (chai, _) {
* expect({ foo: 1, bar: 2, baz: 3 }).to.contain.all.keys(['bar', 'foo']);
* expect({ foo: 1, bar: 2, baz: 3 }).to.contain.all.keys({'bar': 6});
* expect(new Map([[{objKey: 'value'}, 'value'], [1, 2]])).to.contain.key({objKey: 'value'});
* expect(new Map([[{objKey: 'value'}, 'value'], [1, 2]])).to.contain.deep.key({objKey: 'value'});
* expect(new Map([[{objKey: 'value'}, 'value'], [1, 2]])).to.contain.any.keys([{objKey: 'value'}, {anotherKey: 'anotherValue'}]);
* expect(new Map([[{objKey: 'value'}, 'value'], [1, 2]])).to.contain.any.deep.keys([{objKey: 'value'}, {anotherKey: 'anotherValue'}]);
* expect(new Map([['firstKey', 'firstValue'], [1, 2]])).to.contain.all.keys('firstKey', 1);
* expect(new Map([['firstKey', 'firstValue'], [1, 2]])).to.contain.all.deep.keys('firstKey', 1);
* expect(new Set([['foo', 'bar'], ['example', 1]])).to.have.any.keys('foo');
* expect(new Set([['foo', 'bar'], ['example', 1]])).to.have.any.deep.keys('foo');
*
* @name keys
* @alias key
Expand All @@ -1351,11 +1359,14 @@ module.exports = function (chai, _) {

function assertKeys (keys) {
var obj = flag(this, 'object')
, isDeep = flag(this, 'deep')
, str
, deepStr = ''
, ok = true
, mixedArgsMsg = 'when testing keys against an object or an array you must give a single Array|Object|String argument or multiple String arguments';

if (_.type(obj) === 'map' || _.type(obj) === 'set') {
deepStr = isDeep ? 'deeply ' : '';
actual = [];

// Map and Set '.keys' aren't supported in IE 11. Therefore, use .forEach.
Expand Down Expand Up @@ -1402,7 +1413,11 @@ module.exports = function (chai, _) {
if (any) {
ok = expected.some(function(expectedKey) {
return actual.some(function(actualKey) {
return expectedKey === actualKey;
if (isDeep) {
return _.eql(expectedKey, actualKey);
} else {
return expectedKey === actualKey;
}
});
});
}
Expand All @@ -1411,7 +1426,11 @@ module.exports = function (chai, _) {
if (all) {
ok = expected.every(function(expectedKey) {
return actual.some(function(actualKey) {
return expectedKey === actualKey;
if (isDeep) {
return _.eql(expectedKey, actualKey);
} else {
return expectedKey === actualKey;
}
});
});

Expand Down Expand Up @@ -1445,8 +1464,8 @@ module.exports = function (chai, _) {
// Assertion
this.assert(
ok
, 'expected #{this} to ' + str
, 'expected #{this} to not ' + str
, 'expected #{this} to ' + deepStr + str
, 'expected #{this} to not ' + deepStr + str
, expected.slice(0).sort(_.compareByInspect)
, actual.sort(_.compareByInspect)
, true
Expand Down
152 changes: 132 additions & 20 deletions lib/chai/interface/assert.js
Original file line number Diff line number Diff line change
Expand Up @@ -1420,10 +1420,6 @@ module.exports = function (chai, util) {
*/

assert.hasAnyKeys = function (obj, keys, msg) {
if (keys === undefined) {
new Assertion(obj, msg).to.not.have.all.keys();
}

new Assertion(obj, msg).to.have.any.keys(keys);
}

Expand All @@ -1448,10 +1444,6 @@ module.exports = function (chai, util) {
*/

assert.hasAllKeys = function (obj, keys, msg) {
if (keys === undefined) {
new Assertion(obj, msg).to.not.have.all.keys();
}

new Assertion(obj, msg).to.have.all.keys(keys);
}

Expand Down Expand Up @@ -1480,10 +1472,6 @@ module.exports = function (chai, util) {
*/

assert.containsAllKeys = function (obj, keys, msg) {
if (keys === undefined) {
new Assertion(obj, msg).to.not.have.all.keys();
}

new Assertion(obj, msg).to.contain.all.keys(keys);
}

Expand All @@ -1508,10 +1496,6 @@ module.exports = function (chai, util) {
*/

assert.doesNotHaveAnyKeys = function (obj, keys, msg) {
if (keys === undefined) {
new Assertion(obj, msg).to.not.have.all.keys();
}

new Assertion(obj, msg).to.not.have.any.keys(keys);
}

Expand All @@ -1536,14 +1520,142 @@ module.exports = function (chai, util) {
*/

assert.doesNotHaveAllKeys = function (obj, keys, msg) {
if (keys === undefined) {
new Assertion(obj, msg).to.not.have.all.keys();
}

new Assertion(obj, msg).to.not.have.all.keys(keys);
}

/**
* ### .hasAnyDeepKeys(object, [keys], [message])
*
* Asserts that `object` has at least one of the `keys` provided.
* Since Sets and Maps can have objects as keys you can use this assertion to perform
* a deep comparison.
* You can also provide a single object instead of a `keys` array and its keys
* will be used as the expected set of keys.
*
* assert.hasAnyDeepKeys(new Map([[{one: 'one'}, 'valueOne'], [1, 2]]), {one: 'one'});
* assert.hasAnyDeepKeys(new Map([[{one: 'one'}, 'valueOne'], [1, 2]]), [{one: 'one'}, {two: 'two'}]);
* assert.hasAnyDeepKeys(new Map([[{one: 'one'}, 'valueOne'], [{two: 'two'}, 'valueTwo']]), [{one: 'one'}, {two: 'two'}]);
* assert.hasAnyDeepKeys(new Set([{one: 'one'}, {two: 'two'}]), {one: 'one'});
* assert.hasAnyDeepKeys(new Set([{one: 'one'}, {two: 'two'}]), [{one: 'one'}, {three: 'three'}]);
* assert.hasAnyDeepKeys(new Set([{one: 'one'}, {two: 'two'}]), [{one: 'one'}, {two: 'two'}]);
*
* @name doesNotHaveAllKeys
* @param {Mixed} object
* @param {Array|Object} keys
* @param {String} message
* @namespace Assert
* @api public
*/

assert.hasAnyDeepKeys = function (obj, keys, msg) {
new Assertion(obj, msg).to.have.any.deep.keys(keys);
}

/**
* ### .hasAllDeepKeys(object, [keys], [message])
*
* Asserts that `object` has all and only all of the `keys` provided.
* Since Sets and Maps can have objects as keys you can use this assertion to perform
* a deep comparison.
* You can also provide a single object instead of a `keys` array and its keys
* will be used as the expected set of keys.
*
* assert.hasAllDeepKeys(new Map([[{one: 'one'}, 'valueOne']]), {one: 'one'});
* assert.hasAllDeepKeys(new Map([[{one: 'one'}, 'valueOne'], [{two: 'two'}, 'valueTwo']]), [{one: 'one'}, {two: 'two'}]);
* assert.hasAllDeepKeys(new Set([{one: 'one'}]), {one: 'one'});
* assert.hasAllDeepKeys(new Set([{one: 'one'}, {two: 'two'}]), [{one: 'one'}, {two: 'two'}]);
*
* @name hasAllDeepKeys
* @param {Mixed} object
* @param {Array|Object} keys
* @param {String} message
* @namespace Assert
* @api public
*/

assert.hasAllDeepKeys = function (obj, keys, msg) {
new Assertion(obj, msg).to.have.all.deep.keys(keys);
}

/**
* ### .containsAllDeepKeys(object, [keys], [message])
*
* Asserts that `object` contains all of the `keys` provided.
* Since Sets and Maps can have objects as keys you can use this assertion to perform
* a deep comparison.
* You can also provide a single object instead of a `keys` array and its keys
* will be used as the expected set of keys.
*
* assert.containsAllDeepKeys(new Map([[{one: 'one'}, 'valueOne'], [1, 2]]), {one: 'one'});
* assert.containsAllDeepKeys(new Map([[{one: 'one'}, 'valueOne'], [{two: 'two'}, 'valueTwo']]), [{one: 'one'}, {two: 'two'}]);
* assert.containsAllDeepKeys(new Set([{one: 'one'}, {two: 'two'}]), {one: 'one'});
* assert.containsAllDeepKeys(new Set([{one: 'one'}, {two: 'two'}]), [{one: 'one'}, {two: 'two'}]);
*
* @name containsAllDeepKeys
* @param {Mixed} object
* @param {Array|Object} keys
* @param {String} message
* @namespace Assert
* @api public
*/

assert.containsAllDeepKeys = function (obj, keys, msg) {
new Assertion(obj, msg).to.contain.all.deep.keys(keys);
}

/**
* ### .doesNotHaveAnyDeepKeys(object, [keys], [message])
*
* Asserts that `object` has none of the `keys` provided.
* Since Sets and Maps can have objects as keys you can use this assertion to perform
* a deep comparison.
* You can also provide a single object instead of a `keys` array and its keys
* will be used as the expected set of keys.
*
* assert.doesNotHaveAnyDeepKeys(new Map([[{one: 'one'}, 'valueOne'], [1, 2]]), {thisDoesNot: 'exist'});
* assert.doesNotHaveAnyDeepKeys(new Map([[{one: 'one'}, 'valueOne'], [{two: 'two'}, 'valueTwo']]), [{twenty: 'twenty'}, {fifty: 'fifty'}]);
* assert.doesNotHaveAnyDeepKeys(new Set([{one: 'one'}, {two: 'two'}]), {twenty: 'twenty'});
* assert.doesNotHaveAnyDeepKeys(new Set([{one: 'one'}, {two: 'two'}]), [{twenty: 'twenty'}, {fifty: 'fifty'}]);
*
* @name doesNotHaveAnyDeepKeys
* @param {Mixed} object
* @param {Array|Object} keys
* @param {String} message
* @namespace Assert
* @api public
*/

assert.doesNotHaveAnyDeepKeys = function (obj, keys, msg) {
new Assertion(obj, msg).to.not.have.any.deep.keys(keys);
}

/**
* ### .doesNotHaveAllDeepKeys(object, [keys], [message])
*
* Asserts that `object` does not have at least one of the `keys` provided.
* Since Sets and Maps can have objects as keys you can use this assertion to perform
* a deep comparison.
* You can also provide a single object instead of a `keys` array and its keys
* will be used as the expected set of keys.
*
* assert.doesNotHaveAllDeepKeys(new Map([[{one: 'one'}, 'valueOne'], [1, 2]]), {thisDoesNot: 'exist'});
* assert.doesNotHaveAllDeepKeys(new Map([[{one: 'one'}, 'valueOne'], [{two: 'two'}, 'valueTwo']]), [{twenty: 'twenty'}, {one: 'one'}]);
* assert.doesNotHaveAllDeepKeys(new Set([{one: 'one'}, {two: 'two'}]), {twenty: 'twenty'});
* assert.doesNotHaveAllDeepKeys(new Set([{one: 'one'}, {two: 'two'}]), [{one: 'one'}, {fifty: 'fifty'}]);
*
* @name doesNotHaveAllDeepKeys
* @param {Mixed} object
* @param {Array|Object} keys
* @param {String} message
* @namespace Assert
* @api public
*/

assert.doesNotHaveAllDeepKeys = function (obj, keys, msg) {
new Assertion(obj, msg).to.not.have.all.deep.keys(keys);
}

/**
* ### .throws(fn, [errorLike/string/regexp], [string/regexp], [message])
*
* If `errorLike` is an `Error` constructor, asserts that `fn` will throw an error that is an
Expand Down

0 comments on commit 816b5a3

Please sign in to comment.