From 6d6bf60dbab71ab556c7df137a6fb845d8519e47 Mon Sep 17 00:00:00 2001 From: Grant Snodgrass Date: Mon, 25 Jul 2016 20:44:58 -0400 Subject: [PATCH 1/2] Add `.deep.property` for deep equality comparisons --- lib/chai/core/assertions.js | 28 +++++++++-- lib/chai/interface/assert.js | 90 ++++++++++++++++++++++++++++++++++++ test/assert.js | 41 ++++++++++++++++ test/expect.js | 42 +++++++++++++++++ test/should.js | 42 +++++++++++++++++ 5 files changed, 239 insertions(+), 4 deletions(-) diff --git a/lib/chai/core/assertions.js b/lib/chai/core/assertions.js index f7b5dbaed..dfafa8bcb 100644 --- a/lib/chai/core/assertions.js +++ b/lib/chai/core/assertions.js @@ -70,9 +70,13 @@ module.exports = function (chai, _) { /** * ### .deep * - * Sets the `deep` flag, later used by the `equal` assertion. + * Sets the `deep` flag, later used by the `equal`, `members`, and `property` + * assertions. * - * expect(foo).to.deep.equal({ bar: 'baz' }); + * const obj = {a: 1}; + * expect(obj).to.deep.equal({a: 1}); + * expect([obj]).to.have.deep.members([{a: 1}]); + * expect({foo: obj}).to.have.deep.property('foo', {a: 1}); * * @name deep * @namespace BDD @@ -850,6 +854,13 @@ module.exports = function (chai, _) { * expect(obj).to.not.have.property('foo', 'baz'); * expect(obj).to.not.have.property('baz', 'bar'); * + * If the `deep` flag is set, asserts that the value of the property is deeply + * equal to `value`. + * + * var obj = { foo: { bar: 'baz' } }; + * expect(obj).to.have.deep.property('foo', { bar: 'baz' }); + * expect(obj).to.not.have.deep.property('foo', { bar: 'quux' }); + * * If the `nested` flag is set, you can use dot- and bracket-notation for * nested references into objects and arrays. * @@ -861,6 +872,11 @@ module.exports = function (chai, _) { * expect(deepObj).to.have.nested.property('teas[1]', 'matcha'); * expect(deepObj).to.have.nested.property('teas[2].tea', 'konacha'); * + * The `deep` and `nested` flags can be combined. + * + * expect({ foo: { bar: { baz: 'quux' } } }) + * .to.have.deep.nested.property('foo.bar', { baz: 'quux' }); + * * You can also use an array as the starting point of a `nested.property` * assertion, or traverse nested arrays. * @@ -900,6 +916,7 @@ module.exports = function (chai, _) { * expect(deepCss).to.have.nested.property('\\.link.\\[target\\]', 42); * * @name property + * @alias deep.property * @alias nested.property * @param {String} name * @param {Mixed} value (optional) @@ -913,7 +930,10 @@ module.exports = function (chai, _) { if (msg) flag(this, 'message', msg); var isNested = !!flag(this, 'nested') - , descriptor = isNested ? 'nested property ' : 'property ' + , isDeep = !!flag(this, 'deep') + , descriptor = (isDeep ? 'deep ' : '') + + (isNested ? 'nested ' : '') + + 'property ' , negate = flag(this, 'negate') , obj = flag(this, 'object') , pathInfo = isNested ? _.getPathInfo(name, obj) : null @@ -938,7 +958,7 @@ module.exports = function (chai, _) { if (arguments.length > 1) { this.assert( - hasProperty && val === value + hasProperty && (isDeep ? _.eql(val, value) : val === value) , 'expected #{this} to have a ' + descriptor + _.inspect(name) + ' of #{exp}, but got #{act}' , 'expected #{this} to not have a ' + descriptor + _.inspect(name) + ' of #{act}' , val diff --git a/lib/chai/interface/assert.js b/lib/chai/interface/assert.js index 1e2951448..a9d96e6c5 100644 --- a/lib/chai/interface/assert.js +++ b/lib/chai/interface/assert.js @@ -1024,6 +1024,50 @@ module.exports = function (chai, util) { new Assertion(obj, msg).to.not.have.property(prop, val); }; + /** + * ### .deepPropertyVal(object, property, value, [message]) + * + * Asserts that `object` has a property named by `property` with a value given + * by `value`. Uses a deep equality check. + * + * assert.deepPropertyVal({ tea: { green: 'matcha' } }, 'tea', { green: 'matcha' }); + * + * @name deepPropertyVal + * @param {Object} object + * @param {String} property + * @param {Mixed} value + * @param {String} message + * @namespace Assert + * @api public + */ + + assert.deepPropertyVal = function (obj, prop, val, msg) { + new Assertion(obj, msg).to.have.deep.property(prop, val); + }; + + /** + * ### .notDeepPropertyVal(object, property, value, [message]) + * + * Asserts that `object` does _not_ have a property named by `property` with + * value given by `value`. Uses a deep equality check. + * + * assert.notDeepPropertyVal({ tea: { green: 'matcha' } }, 'tea', { black: 'matcha' }); + * assert.notDeepPropertyVal({ tea: { green: 'matcha' } }, 'tea', { green: 'oolong' }); + * assert.notDeepPropertyVal({ tea: { green: 'matcha' } }, 'coffee', { green: 'matcha' }); + * + * @name notDeepPropertyVal + * @param {Object} object + * @param {String} property + * @param {Mixed} value + * @param {String} message + * @namespace Assert + * @api public + */ + + assert.notDeepPropertyVal = function (obj, prop, val, msg) { + new Assertion(obj, msg).to.not.have.deep.property(prop, val); + }; + /** * ### .nestedPropertyVal(object, property, value, [message]) * @@ -1069,6 +1113,52 @@ module.exports = function (chai, util) { new Assertion(obj, msg).to.not.have.nested.property(prop, val); }; + /** + * ### .deepNestedPropertyVal(object, property, value, [message]) + * + * Asserts that `object` has a property named by `property` with a value given + * by `value`. `property` can use dot- and bracket-notation for nested + * reference. Uses a deep equality check. + * + * assert.deepNestedPropertyVal({ tea: { green: { matcha: 'yum' } } }, 'tea.green', { matcha: 'yum' }); + * + * @name deepNestedPropertyVal + * @param {Object} object + * @param {String} property + * @param {Mixed} value + * @param {String} message + * @namespace Assert + * @api public + */ + + assert.deepNestedPropertyVal = function (obj, prop, val, msg) { + new Assertion(obj, msg).to.have.deep.nested.property(prop, val); + }; + + /** + * ### .notDeepNestedPropertyVal(object, property, value, [message]) + * + * Asserts that `object` does _not_ have a property named by `property` with + * value given by `value`. `property` can use dot- and bracket-notation for + * nested reference. Uses a deep equality check. + * + * assert.notDeepNestedPropertyVal({ tea: { green: { matcha: 'yum' } } }, 'tea.green', { oolong: 'yum' }); + * assert.notDeepNestedPropertyVal({ tea: { green: { matcha: 'yum' } } }, 'tea.green', { matcha: 'yuck' }); + * assert.notDeepNestedPropertyVal({ tea: { green: { matcha: 'yum' } } }, 'tea.black', { matcha: 'yum' }); + * + * @name notDeepNestedPropertyVal + * @param {Object} object + * @param {String} property + * @param {Mixed} value + * @param {String} message + * @namespace Assert + * @api public + */ + + assert.notDeepNestedPropertyVal = function (obj, prop, val, msg) { + new Assertion(obj, msg).to.not.have.deep.nested.property(prop, val); + } + /** * ### .lengthOf(object, length, [message]) * diff --git a/test/assert.js b/test/assert.js index a9c74645c..defd4b0b9 100644 --- a/test/assert.js +++ b/test/assert.js @@ -947,6 +947,7 @@ describe('assert', function () { assert.notProperty(obj, 'foo.bar'); assert.notPropertyVal(simpleObj, 'foo', 'flow'); assert.notPropertyVal(simpleObj, 'flow', 'bar'); + assert.notPropertyVal(obj, 'foo', {bar: 'baz'}); assert.notNestedProperty(obj, 'foo.baz'); assert.nestedPropertyVal(obj, 'foo.bar', 'baz'); assert.notNestedPropertyVal(obj, 'foo.bar', 'flow'); @@ -989,6 +990,46 @@ describe('assert', function () { }, "expected { foo: { bar: 'baz' } } to not have a nested property 'foo.bar' of 'baz'"); }); + it('deepPropertyVal', function () { + var obj = {a: {b: 1}}; + assert.deepPropertyVal(obj, 'a', {b: 1}); + assert.notDeepPropertyVal(obj, 'a', {b: 7}); + assert.notDeepPropertyVal(obj, 'a', {z: 1}); + assert.notDeepPropertyVal(obj, 'z', {b: 1}); + + err(function () { + assert.deepPropertyVal(obj, 'a', {b: 7}, 'blah'); + }, "blah: expected { a: { b: 1 } } to have a deep property 'a' of { b: 7 }, but got { b: 1 }"); + + err(function () { + assert.deepPropertyVal(obj, 'z', {b: 1}, 'blah'); + }, "blah: expected { a: { b: 1 } } to have a deep property 'z'"); + + err(function () { + assert.notDeepPropertyVal(obj, 'a', {b: 1}, 'blah'); + }, "blah: expected { a: { b: 1 } } to not have a deep property 'a' of { b: 1 }"); + }); + + it('deepNestedPropertyVal', function () { + var obj = {a: {b: {c: 1}}}; + assert.deepNestedPropertyVal(obj, 'a.b', {c: 1}); + assert.notDeepNestedPropertyVal(obj, 'a.b', {c: 7}); + assert.notDeepNestedPropertyVal(obj, 'a.b', {z: 1}); + assert.notDeepNestedPropertyVal(obj, 'a.z', {c: 1}); + + err(function () { + assert.deepNestedPropertyVal(obj, 'a.b', {c: 7}, 'blah'); + }, "blah: expected { a: { b: { c: 1 } } } to have a deep nested property 'a.b' of { c: 7 }, but got { c: 1 }"); + + err(function () { + assert.deepNestedPropertyVal(obj, 'a.z', {c: 1}, 'blah'); + }, "blah: expected { a: { b: { c: 1 } } } to have a deep nested property 'a.z'"); + + err(function () { + assert.notDeepNestedPropertyVal(obj, 'a.b', {c: 1}, 'blah'); + }, "blah: expected { a: { b: { c: 1 } } } to not have a deep nested property 'a.b' of { c: 1 }"); + }); + it('throws / throw / Throw', function() { ['throws', 'throw', 'Throw'].forEach(function (throws) { assert[throws](function() { throw new Error('foo'); }); diff --git a/test/expect.js b/test/expect.js index 4f0105334..4298879e1 100644 --- a/test/expect.js +++ b/test/expect.js @@ -534,6 +534,7 @@ describe('expect', function () { expect('asd').to.have.property('constructor', String); expect('test').to.not.have.property('length', 3); expect('test').to.not.have.property('foo', 4); + expect({a: {b: 1}}).to.not.have.property('a', {b: 1}); var deepObj = { green: { tea: 'matcha' } @@ -589,6 +590,26 @@ describe('expect', function () { }, "blah: expected 'asd' to have a property 'constructor' of [Function: Number], but got [Function: String]"); }); + it('deep.property(name, val)', function () { + var obj = {a: {b: 1}}; + expect(obj).to.have.deep.property('a', {b: 1}); + expect(obj).to.not.have.deep.property('a', {b: 7}); + expect(obj).to.not.have.deep.property('a', {z: 1}); + expect(obj).to.not.have.deep.property('z', {b: 1}); + + err(function () { + expect(obj).to.have.deep.property('a', {b: 7}, 'blah'); + }, "blah: expected { a: { b: 1 } } to have a deep property 'a' of { b: 7 }, but got { b: 1 }"); + + err(function () { + expect(obj).to.have.deep.property('z', {b: 1}, 'blah'); + }, "blah: expected { a: { b: 1 } } to have a deep property 'z'"); + + err(function () { + expect(obj).to.not.have.deep.property('a', {b: 1}, 'blah'); + }, "blah: expected { a: { b: 1 } } to not have a deep property 'a' of { b: 1 }"); + }); + it('nested.property(name, val)', function(){ expect({ foo: { bar: 'baz' } }) .to.have.nested.property('foo.bar', 'baz'); @@ -596,6 +617,7 @@ describe('expect', function () { .to.not.have.nested.property('foo.bar', 'quux'); expect({ foo: { bar: 'baz' } }) .to.not.have.nested.property('foo.quux', 'baz'); + expect({a: {b: {c: 1}}}).to.not.have.nested.property('a.b', {c: 1}); err(function(){ expect({ foo: { bar: 'baz' } }) @@ -607,6 +629,26 @@ describe('expect', function () { }, "blah: expected { foo: { bar: 'baz' } } to not have a nested property 'foo.bar' of 'baz'"); }); + it('deep.nested.property(name, val)', function () { + var obj = {a: {b: {c: 1}}}; + expect(obj).to.have.deep.nested.property('a.b', {c: 1}); + expect(obj).to.not.have.deep.nested.property('a.b', {c: 7}); + expect(obj).to.not.have.deep.nested.property('a.b', {z: 1}); + expect(obj).to.not.have.deep.nested.property('a.z', {c: 1}); + + err(function () { + expect(obj).to.have.deep.nested.property('a.b', {c: 7}, 'blah'); + }, "blah: expected { a: { b: { c: 1 } } } to have a deep nested property 'a.b' of { c: 7 }, but got { c: 1 }"); + + err(function () { + expect(obj).to.have.deep.nested.property('a.z', {c: 1}, 'blah'); + }, "blah: expected { a: { b: { c: 1 } } } to have a deep nested property 'a.z'"); + + err(function () { + expect(obj).to.not.have.deep.nested.property('a.b', {c: 1}, 'blah'); + }, "blah: expected { a: { b: { c: 1 } } } to not have a deep nested property 'a.b' of { c: 1 }"); + }); + it('ownProperty(name)', function(){ expect('test').to.have.ownProperty('length'); expect('test').to.haveOwnProperty('length'); diff --git a/test/should.js b/test/should.js index e369b29ae..9f7dc4985 100644 --- a/test/should.js +++ b/test/should.js @@ -457,6 +457,7 @@ describe('should', function() { ({ 1: 1 }).should.have.property(1, 1); 'test'.should.not.have.property('length', 3); 'test'.should.not.have.property('foo', 4); + ({a: {b: 1}}).should.not.have.property('a', {b: 1}); err(function(){ 'asd'.should.have.property('length', 4, 'blah'); @@ -471,10 +472,31 @@ describe('should', function() { }, "blah: expected 'asd' to have a property 'constructor' of [Function: Number], but got [Function: String]"); }); + it('deep.property(name, val)', function () { + var obj = {a: {b: 1}}; + obj.should.have.deep.property('a', {b: 1}); + obj.should.not.have.deep.property('a', {b: 7}); + obj.should.not.have.deep.property('a', {z: 1}); + obj.should.not.have.deep.property('z', {b: 1}); + + err(function () { + obj.should.have.deep.property('a', {b: 7}, 'blah'); + }, "blah: expected { a: { b: 1 } } to have a deep property 'a' of { b: 7 }, but got { b: 1 }"); + + err(function () { + obj.should.have.deep.property('z', {b: 1}, 'blah'); + }, "blah: expected { a: { b: 1 } } to have a deep property 'z'"); + + err(function () { + obj.should.not.have.deep.property('a', {b: 1}, 'blah'); + }, "blah: expected { a: { b: 1 } } to not have a deep property 'a' of { b: 1 }"); + }); + it('nested.property(name, val)', function(){ ({ foo: { bar: 'baz' } }).should.have.nested.property('foo.bar', 'baz'); ({ foo: { bar: 'baz' } }).should.not.have.nested.property('foo.bar', 'quux'); ({ foo: { bar: 'baz' } }).should.not.have.nested.property('foo.quux', 'baz'); + ({a: {b: {c: 1}}}).should.not.have.nested.property('a.b', {c: 1}); err(function(){ ({ foo: { bar: 'baz' } }).should.have.nested.property('foo.bar', 'quux', 'blah'); @@ -484,6 +506,26 @@ describe('should', function() { }, "blah: expected { foo: { bar: 'baz' } } to not have a nested property 'foo.bar' of 'baz'"); }); + it('deep.nested.property(name, val)', function () { + var obj = {a: {b: {c: 1}}}; + obj.should.have.deep.nested.property('a.b', {c: 1}); + obj.should.not.have.deep.nested.property('a.b', {c: 7}); + obj.should.not.have.deep.nested.property('a.b', {z: 1}); + obj.should.not.have.deep.nested.property('a.z', {c: 1}); + + err(function () { + obj.should.have.deep.nested.property('a.b', {c: 7}, 'blah'); + }, "blah: expected { a: { b: { c: 1 } } } to have a deep nested property 'a.b' of { c: 7 }, but got { c: 1 }"); + + err(function () { + obj.should.have.deep.nested.property('a.z', {c: 1}, 'blah'); + }, "blah: expected { a: { b: { c: 1 } } } to have a deep nested property 'a.z'"); + + err(function () { + obj.should.not.have.deep.nested.property('a.b', {c: 1}, 'blah'); + }, "blah: expected { a: { b: { c: 1 } } } to not have a deep nested property 'a.b' of { c: 1 }"); + }); + it('ownProperty(name)', function(){ 'test'.should.have.ownProperty('length'); 'test'.should.haveOwnProperty('length'); From 38739f45fd7c164abe4c7b4b87f979c700661d33 Mon Sep 17 00:00:00 2001 From: Grant Snodgrass Date: Tue, 26 Jul 2016 22:48:42 -0400 Subject: [PATCH 2/2] Breaking: Fix `.include` to always use strict equality - Previously, `.include` was using strict equality for non-negated property inclusion, but deep equality for negated property inclusion and array inclusion. This fix causes `.include` to always use strict equality. --- lib/chai/core/assertions.js | 69 ++++++++++++++++++++++++++---------- lib/chai/interface/assert.js | 49 +++++++++++++++++++------ test/assert.js | 33 ++++++++++++++++- test/expect.js | 39 ++++++++++++++------ test/should.js | 31 ++++++++++++++-- 5 files changed, 179 insertions(+), 42 deletions(-) diff --git a/lib/chai/core/assertions.js b/lib/chai/core/assertions.js index dfafa8bcb..531220c9e 100644 --- a/lib/chai/core/assertions.js +++ b/lib/chai/core/assertions.js @@ -7,6 +7,7 @@ module.exports = function (chai, _) { var Assertion = chai.Assertion + , AssertionError = chai.AssertionError , toString = Object.prototype.toString , flag = _.flag; @@ -221,6 +222,22 @@ module.exports = function (chai, _) { * expect([1,2,3]).to.include(2); * expect('foobar').to.contain('foo'); * expect({ foo: 'bar', hello: 'universe' }).to.include({ foo: 'bar' }); + * + * By default, strict equality (===) is used. When asserting the inclusion of + * a value in an array, the array is searched for an element that's strictly + * equal to the given value. When asserting a subset of properties in an + * object, the object is searched for the given property keys, checking that + * each one is present and stricty equal to the given property value. For + * instance: + * + * var obj1 = {a: 1} + * , obj2 = {b: 2}; + * expect([obj1, obj2]).to.include(obj1); + * expect([obj1, obj2]).to.not.include({a: 1}); + * expect({foo: obj1, bar: obj2}).to.include({foo: obj1}); + * expect({foo: obj1, bar: obj2}).to.include({foo: obj1, bar: obj2}); + * expect({foo: obj1, bar: obj2}).to.not.include({foo: {a: 1}}); + * expect({foo: obj1, bar: obj2}).to.not.include({foo: obj1, bar: {b: 2}}); * * These assertions can also be used as property based language chains, * enabling the `contains` flag for the `keys` assertion. For instance: @@ -246,28 +263,44 @@ module.exports = function (chai, _) { if (msg) flag(this, 'message', msg); var obj = flag(this, 'object'); - var expected = false; - if (_.type(obj) === 'array' && _.type(val) === 'object') { - for (var i in obj) { - if (_.eql(obj[i], val)) { - expected = true; - break; + // This block is for asserting a subset of properties in an object. + if (_.type(obj) === 'object') { + var props = Object.keys(val) + , negate = flag(this, 'negate') + , firstErr = null + , numErrs = 0; + + props.forEach(function (prop) { + var propAssertion = new Assertion(obj); + _.transferFlags(this, propAssertion, false); + + if (!negate || props.length === 1) { + propAssertion.property(prop, val[prop]); + return; } - } - } else if (_.type(val) === 'object') { - if (!flag(this, 'negate')) { - for (var k in val) new Assertion(obj).property(k, val[k]); - return; - } - var subset = {}; - for (var k in val) subset[k] = obj[k]; - expected = _.eql(subset, val); - } else { - expected = (obj != undefined) && ~obj.indexOf(val); + + try { + propAssertion.property(prop, val[prop]); + } catch (err) { + if (!_.checkError.compatibleConstructor(err, AssertionError)) throw err; + if (firstErr === null) firstErr = err; + numErrs++; + } + }, this); + + // When validating .not.include with multiple properties, we only want + // to throw an assertion error if all of the properties are included, + // in which case we throw the first property assertion error that we + // encountered. + if (negate && props.length > 1 && numErrs === props.length) throw firstErr; + + return; } + + // Assert inclusion in an array or substring in a string. this.assert( - expected + typeof obj !== "undefined" && typeof obj !== "null" && ~obj.indexOf(val) , 'expected #{this} to include ' + _.inspect(val) , 'expected #{this} to not include ' + _.inspect(val)); } diff --git a/lib/chai/interface/assert.js b/lib/chai/interface/assert.js index a9d96e6c5..3f780501a 100644 --- a/lib/chai/interface/assert.js +++ b/lib/chai/interface/assert.js @@ -826,11 +826,25 @@ module.exports = function (chai, util) { /** * ### .include(haystack, needle, [message]) * - * Asserts that `haystack` includes `needle`. Works - * for strings and arrays. - * - * assert.include('foobar', 'bar', 'foobar contains string "bar"'); - * assert.include([ 1, 2, 3 ], 3, 'array contains value'); + * Asserts that `haystack` includes `needle`. Can be used to assert the + * inclusion of a value in an array, a substring in a string, or a subset of + * properties in an object. + * + * assert.include([1,2,3], 2, 'array contains value'); + * assert.include('foobar', 'foo', 'string contains substring'); + * assert.include({ foo: 'bar', hello: 'universe' }, { foo: 'bar' }, 'object contains property'); + * + * Strict equality (===) is used. When asserting the inclusion of a value in + * an array, the array is searched for an element that's strictly equal to the + * given value. When asserting a subset of properties in an object, the object + * is searched for the given property keys, checking that each one is present + * and stricty equal to the given property value. For instance: + * + * var obj1 = {a: 1} + * , obj2 = {b: 2}; + * assert.include([obj1, obj2], obj1); + * assert.include({foo: obj1, bar: obj2}, {foo: obj1}); + * assert.include({foo: obj1, bar: obj2}, {foo: obj1, bar: obj2}); * * @name include * @param {Array|String} haystack @@ -847,11 +861,26 @@ module.exports = function (chai, util) { /** * ### .notInclude(haystack, needle, [message]) * - * Asserts that `haystack` does not include `needle`. Works - * for strings and arrays. - * - * assert.notInclude('foobar', 'baz', 'string not include substring'); - * assert.notInclude([ 1, 2, 3 ], 4, 'array not include contain value'); + * Asserts that `haystack` does not include `needle`. Can be used to assert + * the absence of a value in an array, a substring in a string, or a subset of + * properties in an object. + * + * assert.notInclude([1,2,3], 4, 'array doesn't contain value'); + * assert.notInclude('foobar', 'baz', 'string doesn't contain substring'); + * assert.notInclude({ foo: 'bar', hello: 'universe' }, { foo: 'baz' }, 'object doesn't contain property'); + * + * Strict equality (===) is used. When asserting the absence of a value in an + * array, the array is searched to confirm the absence of an element that's + * strictly equal to the given value. When asserting a subset of properties in + * an object, the object is searched to confirm that at least one of the given + * property keys is either not present or not strictly equal to the given + * property value. For instance: + * + * var obj1 = {a: 1} + * , obj2 = {b: 2}; + * assert.notInclude([obj1, obj2], {a: 1}); + * assert.notInclude({foo: obj1, bar: obj2}, {foo: {a: 1}}); + * assert.notInclude({foo: obj1, bar: obj2}, {foo: obj1, bar: {b: 2}}); * * @name notInclude * @param {Array|String} haystack diff --git a/test/assert.js b/test/assert.js index defd4b0b9..a9688931e 100644 --- a/test/assert.js +++ b/test/assert.js @@ -459,7 +459,12 @@ describe('assert', function () { assert.include('foobar', 'bar'); assert.include('', ''); assert.include([ 1, 2, 3], 3); - assert.include({a:1, b:2}, {b:2}); + + var obj1 = {a: 1} + , obj2 = {b: 2}; + assert.include([obj1, obj2], obj1); + assert.include({foo: obj1, bar: obj2}, {foo: obj1}); + assert.include({foo: obj1, bar: obj2}, {foo: obj1, bar: obj2}); if (typeof Symbol === 'function') { var sym1 = Symbol() @@ -471,6 +476,14 @@ describe('assert', function () { assert.include('foobar', 'baz'); }, "expected \'foobar\' to include \'baz\'"); + err(function () { + assert.include([{a: 1}, {b: 2}], {a: 1}); + }, "expected [ { a: 1 }, { b: 2 } ] to include { a: 1 }"); + + err(function () { + assert.include({foo: {a: 1}, bar: {b: 2}}, {foo: {a: 1}}); + }, "expected { foo: { a: 1 }, bar: { b: 2 } } to have a property 'foo' of { a: 1 }, but got { a: 1 }"); + err(function(){ assert.include(true, true); }, "object tested must be an array, an object, or a string, but boolean given"); @@ -492,6 +505,12 @@ describe('assert', function () { assert.notInclude('foobar', 'baz'); assert.notInclude([ 1, 2, 3 ], 4); + var obj1 = {a: 1} + , obj2 = {b: 2}; + assert.notInclude([obj1, obj2], {a: 1}); + assert.notInclude({foo: obj1, bar: obj2}, {foo: {a: 1}}); + assert.notInclude({foo: obj1, bar: obj2}, {foo: obj1, bar: {b: 2}}); + if (typeof Symbol === 'function') { var sym1 = Symbol() , sym2 = Symbol() @@ -499,6 +518,18 @@ describe('assert', function () { assert.notInclude([sym1, sym2], sym3); } + err(function () { + var obj1 = {a: 1} + , obj2 = {b: 2}; + assert.notInclude([obj1, obj2], obj1); + }, "expected [ { a: 1 }, { b: 2 } ] to not include { a: 1 }"); + + err(function () { + var obj1 = {a: 1} + , obj2 = {b: 2}; + assert.notInclude({foo: obj1, bar: obj2}, {foo: obj1, bar: obj2}); + }, "expected { foo: { a: 1 }, bar: { b: 2 } } to not have a property 'foo' of { a: 1 }"); + err(function(){ assert.notInclude(true, true); }, "object tested must be an array, an object, or a string, but boolean given"); diff --git a/test/expect.js b/test/expect.js index 4298879e1..29d91f2e7 100644 --- a/test/expect.js +++ b/test/expect.js @@ -722,14 +722,15 @@ describe('expect', function () { expect([1,2]).to.include(1); expect(['foo', 'bar']).to.not.include('baz'); expect(['foo', 'bar']).to.not.include(1); - expect({a:1,b:2}).to.include({b:2}); - expect({a:1,b:2}).to.not.include({b:3}); - expect({a:1,b:2}).to.include({a:1,b:2}); - expect({a:1,b:2}).to.not.include({a:1,c:2}); - expect([{a:1},{b:2}]).to.include({a:1}); - expect([{a:1}]).to.include({a:1}); - expect([{a:1}]).to.not.include({b:1}); + var obj1 = {a: 1} + , obj2 = {b: 2}; + expect([obj1, obj2]).to.include(obj1); + expect([obj1, obj2]).to.not.include({a: 1}); + expect({foo: obj1, bar: obj2}).to.include({foo: obj1}); + expect({foo: obj1, bar: obj2}).to.include({foo: obj1, bar: obj2}); + expect({foo: obj1, bar: obj2}).to.not.include({foo: {a: 1}}); + expect({foo: obj1, bar: obj2}).to.not.include({foo: obj1, bar: {b: 2}}); if (typeof Symbol === 'function') { var sym1 = Symbol() @@ -753,11 +754,27 @@ describe('expect', function () { err(function(){ expect({a:1,b:2}).to.not.include({b:2}); - }, "expected { a: 1, b: 2 } to not include { b: 2 }"); + }, "expected { a: 1, b: 2 } to not have a property 'b' of 2"); - err(function(){ - expect([{a:1},{b:2}]).to.not.include({b:2}); - }, "expected [ { a: 1 }, { b: 2 } ] to not include { b: 2 }"); + err(function () { + expect([{a: 1}, {b: 2}]).to.include({a: 1}); + }, "expected [ { a: 1 }, { b: 2 } ] to include { a: 1 }"); + + err(function () { + var obj1 = {a: 1} + , obj2 = {b: 2}; + expect([obj1, obj2]).to.not.include(obj1); + }, "expected [ { a: 1 }, { b: 2 } ] to not include { a: 1 }"); + + err(function () { + expect({foo: {a: 1}, bar: {b: 2}}).to.include({foo: {a: 1}}); + }, "expected { foo: { a: 1 }, bar: { b: 2 } } to have a property 'foo' of { a: 1 }, but got { a: 1 }"); + + err(function () { + var obj1 = {a: 1} + , obj2 = {b: 2}; + expect({foo: obj1, bar: obj2}).to.not.include({foo: obj1, bar: obj2}); + }, "expected { foo: { a: 1 }, bar: { b: 2 } } to not have a property 'foo' of { a: 1 }"); err(function(){ expect(true).to.include(true); diff --git a/test/should.js b/test/should.js index 9f7dc4985..345af8417 100644 --- a/test/should.js +++ b/test/should.js @@ -614,8 +614,15 @@ describe('should', function() { [1,2].should.include(1); ['foo', 'bar'].should.not.include('baz'); ['foo', 'bar'].should.not.include(1); - ({a:1,b:2}).should.include({b:2}); - ({a:1,b:2}).should.not.include({b:3}); + + var obj1 = {a: 1} + , obj2 = {b: 2}; + [obj1, obj2].should.include(obj1); + [obj1, obj2].should.not.include({a: 1}); + ({foo: obj1, bar: obj2}).should.include({foo: obj1}); + ({foo: obj1, bar: obj2}).should.include({foo: obj1, bar: obj2}); + ({foo: obj1, bar: obj2}).should.not.include({foo: {a: 1}}); + ({foo: obj1, bar: obj2}).should.not.include({foo: obj1, bar: {b: 2}}); if (typeof Symbol === 'function') { var sym1 = Symbol() @@ -637,6 +644,26 @@ describe('should', function() { ({a:1}).should.include({b:2}); }, "expected { a: 1 } to have a property 'b'"); + err(function () { + [{a: 1}, {b: 2}].should.include({a: 1}); + }, "expected [ { a: 1 }, { b: 2 } ] to include { a: 1 }"); + + err(function () { + var obj1 = {a: 1} + , obj2 = {b: 2}; + [obj1, obj2].should.not.include(obj1); + }, "expected [ { a: 1 }, { b: 2 } ] to not include { a: 1 }"); + + err(function () { + ({foo: {a: 1}, bar: {b: 2}}).should.include({foo: {a: 1}}); + }, "expected { foo: { a: 1 }, bar: { b: 2 } } to have a property 'foo' of { a: 1 }, but got { a: 1 }"); + + err(function () { + var obj1 = {a: 1} + , obj2 = {b: 2}; + ({foo: obj1, bar: obj2}).should.not.include({foo: obj1, bar: obj2}); + }, "expected { foo: { a: 1 }, bar: { b: 2 } } to not have a property 'foo' of { a: 1 }"); + err(function(){ (true).should.include(true); }, "object tested must be an array, an object, or a string, but boolean given");