Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We鈥檒l occasionally send you account related emails.

Already on GitHub? Sign in to your account

Deep flag support for keys assertion #668

Merged
merged 1 commit into from
Sep 28, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
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.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Even though it's shown in the example, probably worth mentioning explicitly that the object can have more keys than those provided, just to contrast with hasAll

* 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