diff --git a/lib/chai/interface/assert.js b/lib/chai/interface/assert.js index d4a39cae1..86f4833d1 100644 --- a/lib/chai/interface/assert.js +++ b/lib/chai/interface/assert.js @@ -1012,6 +1012,197 @@ module.exports = function (chai, util) { new Assertion(exp, msg, assert.notDeepInclude, true).not.deep.include(inc); }; + /** + * ### .nestedInclude(haystack, needle, [message]) + * + * Asserts that 'haystack' includes 'needle'. + * Can be used to assert the inclusion of a subset of properties in an + * object. + * Enables the use of dot- and bracket-notation for referencing nested + * properties. + * '[]' and '.' in property names can be escaped using double backslashes. + * + * assert.nestedInclude({'.a': {'b': 'x'}}, {'\\.a.[b]': 'x'}); + * assert.nestedInclude({'a': {'[b]': 'x'}}, {'a.\\[b\\]': 'x'}); + * + * @name nestedInclude + * @param {Object} haystack + * @param {Object} needle + * @param {String} message + * @namespace Assert + * @api public + */ + + assert.nestedInclude = function (exp, inc, msg) { + new Assertion(exp, msg, assert.nestedInclude, true).nested.include(inc); + }; + + /** + * ### .notNestedInclude(haystack, needle, [message]) + * + * Asserts that 'haystack' does not include 'needle'. + * Can be used to assert the absence of a subset of properties in an + * object. + * Enables the use of dot- and bracket-notation for referencing nested + * properties. + * '[]' and '.' in property names can be escaped using double backslashes. + * + * assert.notNestedInclude({'.a': {'b': 'x'}}, {'\\.a.b': 'y'}); + * assert.notNestedInclude({'a': {'[b]': 'x'}}, {'a.\\[b\\]': 'y'}); + * + * @name notNestedInclude + * @param {Object} haystack + * @param {Object} needle + * @param {String} message + * @namespace Assert + * @api public + */ + + assert.notNestedInclude = function (exp, inc, msg) { + new Assertion(exp, msg, assert.notNestedInclude, true) + .not.nested.include(inc); + }; + + /** + * ### .deepNestedInclude(haystack, needle, [message]) + * + * Asserts that 'haystack' includes 'needle'. + * Can be used to assert the inclusion of a subset of properties in an + * object while checking for deep equality. + * Enables the use of dot- and bracket-notation for referencing nested + * properties. + * '[]' and '.' in property names can be escaped using double backslashes. + * + * assert.deepNestedInclude({a: {b: [{x: 1}]}}, {'a.b[0]': {x: 1}}); + * assert.deepNestedInclude({'.a': {'[b]': {x: 1}}}, {'\\.a.\\[b\\]': {x: 1}}); + * + * @name deepNestedInclude + * @param {Object} haystack + * @param {Object} needle + * @param {String} message + * @namespace Assert + * @api public + */ + + assert.deepNestedInclude = function(exp, inc, msg) { + new Assertion(exp, msg, assert.deepNestedInclude, true) + .deep.nested.include(inc); + }; + + /** + * ### .notDeepNestedInclude(haystack, needle, [message]) + * + * Asserts that 'haystack' does not include 'needle'. + * Can be used to assert the absence of a subset of properties in an + * object while checking for deep equality. + * Enables the use of dot- and bracket-notation for referencing nested + * properties. + * '[]' and '.' in property names can be escaped using double backslashes. + * + * assert.notDeepNestedInclude({a: {b: [{x: 1}]}}, {'a.b[0]': {y: 1}}) + * assert.notDeepNestedInclude({'.a': {'[b]': {x: 1}}}, {'\\.a.\\[b\\]': {y: 2}}); + * + * @name notDeepNestedInclude + * @param {Object} haystack + * @param {Object} needle + * @param {String} message + * @namespace Assert + * @api public + */ + + assert.notDeepNestedInclude = function(exp, inc, msg) { + new Assertion(exp, msg, assert.notDeepNestedInclude, true) + .not.deep.nested.include(inc); + }; + + /** + * ### .ownInclude(haystack, needle, [message]) + * + * Asserts that 'haystack' includes 'needle'. + * Can be used to assert the inclusion of a subset of properties in an + * object while ignoring inherited properties. + * + * assert.ownInclude({ a: 1 }, { a: 1 }); + * + * @name ownInclude + * @param {Object} haystack + * @param {Object} needle + * @param {String} message + * @namespace Assert + * @api public + */ + + assert.ownInclude = function(exp, inc, msg) { + new Assertion(exp, msg, assert.ownInclude, true).own.include(inc); + }; + + /** + * ### .notOwnInclude(haystack, needle, [message]) + * + * Asserts that 'haystack' includes 'needle'. + * Can be used to assert the absence of a subset of properties in an + * object while ignoring inherited properties. + * + * Object.prototype.b = 2; + * + * assert.notOwnInclude({ a: 1 }, { b: 2 }); + * + * @name notOwnInclude + * @param {Object} haystack + * @param {Object} needle + * @param {String} message + * @namespace Assert + * @api public + */ + + assert.notOwnInclude = function(exp, inc, msg) { + new Assertion(exp, msg, assert.notOwnInclude, true).not.own.include(inc); + }; + + /** + * ### .deepOwnInclude(haystack, needle, [message]) + * + * Asserts that 'haystack' includes 'needle'. + * Can be used to assert the inclusion of a subset of properties in an + * object while ignoring inherited properties and checking for deep equality. + * + * assert.deepOwnInclude({a: {b: 2}}, {a: {b: 2}}); + * + * @name deepOwnInclude + * @param {Object} haystack + * @param {Object} needle + * @param {String} message + * @namespace Assert + * @api public + */ + + assert.deepOwnInclude = function(exp, inc, msg) { + new Assertion(exp, msg, assert.deepOwnInclude, true) + .deep.own.include(inc); + }; + + /** + * ### .notDeepOwnInclude(haystack, needle, [message]) + * + * Asserts that 'haystack' includes 'needle'. + * Can be used to assert the absence of a subset of properties in an + * object while ignoring inherited properties and checking for deep equality. + * + * assert.notDeepOwnInclude({a: {b: 2}}, {a: {c: 3}}); + * + * @name notDeepOwnInclude + * @param {Object} haystack + * @param {Object} needle + * @param {String} message + * @namespace Assert + * @api public + */ + + assert.notDeepOwnInclude = function(exp, inc, msg) { + new Assertion(exp, msg, assert.notDeepOwnInclude, true) + .not.deep.own.include(inc); + }; + /** * ### .match(value, regexp, [message]) * diff --git a/test/assert.js b/test/assert.js index d3a0f023b..9cfff717c 100644 --- a/test/assert.js +++ b/test/assert.js @@ -744,8 +744,106 @@ describe('assert', function () { }, "blah: expected { foo: { a: 1 }, bar: { b: 2 } } to have deep property 'bar' of { b: 9 }, but got { b: 2 }"); err(function () { - assert.notDeepInclude({foo: obj1, bar: obj2}, {foo: {a: 1}, bar: {b: 2}}); - }, "expected { foo: { a: 1 }, bar: { b: 2 } } to not have deep property 'foo' of { a: 1 }"); + assert.notDeepInclude({foo: obj1, bar: obj2}, {foo: {a: 1}, bar: {b: 2}}, 'blah'); + }, "blah: expected { foo: { a: 1 }, bar: { b: 2 } } to not have deep property 'foo' of { a: 1 }"); + }); + + it('nestedInclude and notNestedInclude', function() { + assert.nestedInclude({a: {b: ['x', 'y']}}, {'a.b[1]': 'y'}); + assert.notNestedInclude({a: {b: ['x', 'y']}}, {'a.b[1]': 'x'}); + assert.notNestedInclude({a: {b: ['x', 'y']}}, {'a.c': 'y'}); + + assert.notNestedInclude({a: {b: [{x: 1}]}}, {'a.b[0]': {x: 1}}); + + assert.nestedInclude({'.a': {'[b]': 'x'}}, {'\\.a.\\[b\\]': 'x'}); + assert.notNestedInclude({'.a': {'[b]': 'x'}}, {'\\.a.\\[b\\]': 'y'}); + + err(function () { + assert.nestedInclude({a: {b: ['x', 'y']}}, {'a.b[1]': 'x'}, 'blah'); + }, "blah: expected { a: { b: [ 'x', 'y' ] } } to have nested property 'a.b[1]' of 'x', but got 'y'"); + + err(function () { + assert.nestedInclude({a: {b: ['x', 'y']}}, {'a.b[1]': 'x'}, 'blah'); + }, "blah: expected { a: { b: [ 'x', 'y' ] } } to have nested property 'a.b[1]' of 'x', but got 'y'"); + + err(function () { + assert.nestedInclude({a: {b: ['x', 'y']}}, {'a.c': 'y'}); + }, "expected { a: { b: [ 'x', 'y' ] } } to have nested property 'a.c'"); + + err(function () { + assert.notNestedInclude({a: {b: ['x', 'y']}}, {'a.b[1]': 'y'}, 'blah'); + }, "blah: expected { a: { b: [ 'x', 'y' ] } } to not have nested property 'a.b[1]' of 'y'"); + }); + + it('deepNestedInclude and notDeepNestedInclude', function() { + assert.deepNestedInclude({a: {b: [{x: 1}]}}, {'a.b[0]': {x: 1}}); + assert.notDeepNestedInclude({a: {b: [{x: 1}]}}, {'a.b[0]': {y: 2}}); + assert.notDeepNestedInclude({a: {b: [{x: 1}]}}, {'a.c': {x: 1}}); + + assert.deepNestedInclude({'.a': {'[b]': {x: 1}}}, {'\\.a.\\[b\\]': {x: 1}}); + assert.notDeepNestedInclude({'.a': {'[b]': {x: 1}}}, {'\\.a.\\[b\\]': {y: 2}}); + + err(function () { + assert.deepNestedInclude({a: {b: [{x: 1}]}}, {'a.b[0]': {y: 2}}, 'blah'); + }, "blah: expected { a: { b: [ [Object] ] } } to have deep nested property 'a.b[0]' of { y: 2 }, but got { x: 1 }"); + + err(function () { + assert.deepNestedInclude({a: {b: [{x: 1}]}}, {'a.b[0]': {y: 2}}, 'blah'); + }, "blah: expected { a: { b: [ [Object] ] } } to have deep nested property 'a.b[0]' of { y: 2 }, but got { x: 1 }"); + + err(function () { + assert.deepNestedInclude({a: {b: [{x: 1}]}}, {'a.c': {x: 1}}); + }, "expected { a: { b: [ [Object] ] } } to have deep nested property 'a.c'"); + + err(function () { + assert.notDeepNestedInclude({a: {b: [{x: 1}]}}, {'a.b[0]': {x: 1}}, 'blah'); + }, "blah: expected { a: { b: [ [Object] ] } } to not have deep nested property 'a.b[0]' of { x: 1 }"); + }); + + it('ownInclude and notOwnInclude', function() { + assert.ownInclude({a: 1}, {a: 1}); + assert.notOwnInclude({a: 1}, {a: 3}); + assert.notOwnInclude({a: 1}, {'toString': Object.prototype.toString}); + + assert.notOwnInclude({a: {b: 2}}, {a: {b: 2}}); + + err(function () { + assert.ownInclude({a: 1}, {a: 3}, 'blah'); + }, "blah: expected { a: 1 } to have own property 'a' of 3, but got 1"); + + err(function () { + assert.ownInclude({a: 1}, {a: 3}, 'blah'); + }, "blah: expected { a: 1 } to have own property 'a' of 3, but got 1"); + + err(function () { + assert.ownInclude({a: 1}, {'toString': Object.prototype.toString}); + }, "expected { a: 1 } to have own property 'toString'"); + + err(function () { + assert.notOwnInclude({a: 1}, {a: 1}, 'blah'); + }, "blah: expected { a: 1 } to not have own property 'a' of 1"); + }); + + it('deepOwnInclude and notDeepOwnInclude', function() { + assert.deepOwnInclude({a: {b: 2}}, {a: {b: 2}}); + assert.notDeepOwnInclude({a: {b: 2}}, {a: {c: 3}}); + assert.notDeepOwnInclude({a: {b: 2}}, {'toString': Object.prototype.toString}); + + err(function () { + assert.deepOwnInclude({a: {b: 2}}, {a: {c: 3}}, 'blah'); + }, "blah: expected { a: { b: 2 } } to have deep own property 'a' of { c: 3 }, but got { b: 2 }"); + + err(function () { + assert.deepOwnInclude({a: {b: 2}}, {a: {c: 3}}, 'blah'); + }, "blah: expected { a: { b: 2 } } to have deep own property 'a' of { c: 3 }, but got { b: 2 }"); + + err(function () { + assert.deepOwnInclude({a: {b: 2}}, {'toString': Object.prototype.toString}); + }, "expected { a: { b: 2 } } to have deep own property 'toString'"); + + err(function () { + assert.notDeepOwnInclude({a: {b: 2}}, {a: {b: 2}}, 'blah'); + }, "blah: expected { a: { b: 2 } } to not have deep own property 'a' of { b: 2 }"); }); it('keys(array|Object|arguments)', function(){ diff --git a/test/expect.js b/test/expect.js index 8ef37d3ee..219411050 100644 --- a/test/expect.js +++ b/test/expect.js @@ -1797,16 +1797,16 @@ describe('expect', function () { }, "blah: expected [ { a: 1 }, { b: 2 } ] to deep include { a: 9 }"); err(function () { - expect([obj1, obj2]).to.not.deep.include({a: 1}); - }, "expected [ { a: 1 }, { b: 2 } ] to not deep include { a: 1 }"); + expect([obj1, obj2], 'blah').to.not.deep.include({a: 1}); + }, "blah: expected [ { a: 1 }, { b: 2 } ] to not deep include { a: 1 }"); err(function () { expect({foo: obj1, bar: obj2}).to.deep.include({foo: {a: 1}, bar: {b: 9}}); }, "expected { foo: { a: 1 }, bar: { b: 2 } } to have deep property 'bar' of { b: 9 }, but got { b: 2 }"); err(function () { - expect({foo: obj1, bar: obj2}).to.not.deep.include({foo: {a: 1}, bar: {b: 2}}); - }, "expected { foo: { a: 1 }, bar: { b: 2 } } to not have deep property 'foo' of { a: 1 }"); + expect({foo: obj1, bar: obj2}).to.not.deep.include({foo: {a: 1}, bar: {b: 2}}, 'blah'); + }, "blah: expected { foo: { a: 1 }, bar: { b: 2 } } to not have deep property 'foo' of { a: 1 }"); }); it('nested.include()', function () { @@ -1832,8 +1832,12 @@ describe('expect', function () { }, "expected { a: { b: [ 'x', 'y' ] } } to have nested property 'a.c'"); err(function () { - expect({a: {b: ['x', 'y']}}).to.not.nested.include({'a.b[1]': 'y'}); - }, "expected { a: { b: [ 'x', 'y' ] } } to not have nested property 'a.b[1]' of 'y'"); + expect({a: {b: ['x', 'y']}}).to.not.nested.include({'a.b[1]': 'y'}, 'blah'); + }, "blah: expected { a: { b: [ 'x', 'y' ] } } to not have nested property 'a.b[1]' of 'y'"); + + err(function () { + expect({a: {b: ['x', 'y']}}, 'blah').to.not.nested.include({'a.b[1]': 'y'}); + }, "blah: expected { a: { b: [ 'x', 'y' ] } } to not have nested property 'a.b[1]' of 'y'"); }); it('deep.nested.include()', function () { @@ -1859,8 +1863,12 @@ describe('expect', function () { }, "expected { a: { b: [ [Object] ] } } to have deep nested property 'a.c'"); err(function () { - expect({a: {b: [{x: 1}]}}).to.not.deep.nested.include({'a.b[0]': {x: 1}}); - }, "expected { a: { b: [ [Object] ] } } to not have deep nested property 'a.b[0]' of { x: 1 }"); + expect({a: {b: [{x: 1}]}}).to.not.deep.nested.include({'a.b[0]': {x: 1}}, 'blah'); + }, "blah: expected { a: { b: [ [Object] ] } } to not have deep nested property 'a.b[0]' of { x: 1 }"); + + err(function () { + expect({a: {b: [{x: 1}]}}, 'blah').to.not.deep.nested.include({'a.b[0]': {x: 1}}); + }, "blah: expected { a: { b: [ [Object] ] } } to not have deep nested property 'a.b[0]' of { x: 1 }"); }); it('own.include()', function () { @@ -1883,8 +1891,12 @@ describe('expect', function () { }, "expected { a: 1 } to have own property 'toString'"); err(function () { - expect({a: 1}).to.not.own.include({a: 1}); - }, "expected { a: 1 } to not have own property 'a' of 1"); + expect({a: 1}).to.not.own.include({a: 1}, 'blah'); + }, "blah: expected { a: 1 } to not have own property 'a' of 1"); + + err(function () { + expect({a: 1}, 'blah').to.not.own.include({a: 1}); + }, "blah: expected { a: 1 } to not have own property 'a' of 1"); }); it('deep.own.include()', function () { @@ -1906,8 +1918,12 @@ describe('expect', function () { }, "expected { a: { b: 2 } } to have deep own property 'toString'"); err(function () { - expect({a: {b: 2}}).to.not.deep.own.include({a: {b: 2}}); - }, "expected { a: { b: 2 } } to not have deep own property 'a' of { b: 2 }"); + expect({a: {b: 2}}).to.not.deep.own.include({a: {b: 2}}, 'blah'); + }, "blah: expected { a: { b: 2 } } to not have deep own property 'a' of { b: 2 }"); + + err(function () { + expect({a: {b: 2}}, 'blah').to.not.deep.own.include({a: {b: 2}}); + }, "blah: expected { a: { b: 2 } } to not have deep own property 'a' of { b: 2 }"); }); it('keys(array|Object|arguments)', function(){ diff --git a/test/should.js b/test/should.js index fc09edd15..e10a39b24 100644 --- a/test/should.js +++ b/test/should.js @@ -1426,8 +1426,8 @@ describe('should', function() { }, "expected { foo: { a: 1 }, bar: { b: 2 } } to have deep property 'bar' of { b: 9 }, but got { b: 2 }"); err(function () { - ({foo: obj1, bar: obj2}).should.not.deep.include({foo: {a: 1}, bar: {b: 2}}); - }, "expected { foo: { a: 1 }, bar: { b: 2 } } to not have deep property 'foo' of { a: 1 }"); + ({foo: obj1, bar: obj2}).should.not.deep.include({foo: {a: 1}, bar: {b: 2}}, 'blah'); + }, "blah: expected { foo: { a: 1 }, bar: { b: 2 } } to not have deep property 'foo' of { a: 1 }"); }); it('nested.include()', function () { @@ -1449,8 +1449,8 @@ describe('should', function() { }, "expected { a: { b: [ 'x', 'y' ] } } to have nested property 'a.c'"); err(function () { - ({a: {b: ['x', 'y']}}).should.not.nested.include({'a.b[1]': 'y'}); - }, "expected { a: { b: [ 'x', 'y' ] } } to not have nested property 'a.b[1]' of 'y'"); + ({a: {b: ['x', 'y']}}).should.not.nested.include({'a.b[1]': 'y'}, 'blah'); + }, "blah: expected { a: { b: [ 'x', 'y' ] } } to not have nested property 'a.b[1]' of 'y'"); }); it('deep.nested.include()', function () { @@ -1472,8 +1472,8 @@ describe('should', function() { }, "expected { a: { b: [ [Object] ] } } to have deep nested property 'a.c'"); err(function () { - ({a: {b: [{x: 1}]}}).should.not.deep.nested.include({'a.b[0]': {x: 1}}); - }, "expected { a: { b: [ [Object] ] } } to not have deep nested property 'a.b[0]' of { x: 1 }"); + ({a: {b: [{x: 1}]}}).should.not.deep.nested.include({'a.b[0]': {x: 1}}, 'blah'); + }, "blah: expected { a: { b: [ [Object] ] } } to not have deep nested property 'a.b[0]' of { x: 1 }"); }); it('own.include()', function () { @@ -1492,8 +1492,8 @@ describe('should', function() { }, "expected { a: 1 } to have own property 'toString'"); err(function () { - ({a: 1}).should.not.own.include({a: 1}); - }, "expected { a: 1 } to not have own property 'a' of 1"); + ({a: 1}).should.not.own.include({a: 1}, 'blah'); + }, "blah: expected { a: 1 } to not have own property 'a' of 1"); }); it('deep.own.include()', function () { @@ -1511,8 +1511,8 @@ describe('should', function() { }, "expected { a: { b: 2 } } to have deep own property 'toString'"); err(function () { - ({a: {b: 2}}).should.not.deep.own.include({a: {b: 2}}); - }, "expected { a: { b: 2 } } to not have deep own property 'a' of { b: 2 }"); + ({a: {b: 2}}).should.not.deep.own.include({a: {b: 2}}, 'blah'); + }, "blah: expected { a: { b: 2 } } to not have deep own property 'a' of { b: 2 }"); }); it('keys(array|Object|arguments)', function(){