diff --git a/lib/chai/core/assertions.js b/lib/chai/core/assertions.js index f3bc6d6f4..9c995a764 100644 --- a/lib/chai/core/assertions.js +++ b/lib/chai/core/assertions.js @@ -835,6 +835,9 @@ module.exports = function (chai, _) { * var obj = { foo: 'bar' }; * expect(obj).to.have.property('foo'); * expect(obj).to.have.property('foo', 'bar'); + * expect(obj).to.not.have.property('baz'); + * expect(obj).to.not.have.property('foo', 'baz'); + * expect(obj).to.not.have.property('baz', 'bar'); * * // deep referencing * var deepObj = { @@ -910,12 +913,12 @@ module.exports = function (chai, _) { ? pathInfo.value : obj[name]; - if (negate && arguments.length > 1) { - if (undefined === value) { - msg = (msg != null) ? msg + ': ' : ''; - throw new Error(msg + _.inspect(obj) + ' has no ' + descriptor + _.inspect(name)); - } - } else { + // When performing a negated assertion for both name and val, merely having + // a property with the given name isn't enough to cause the assertion to + // fail. It must both have a property with the given name, and the value of + // that property must equal the given val. Therefore, skip this assertion in + // favor of the next. + if (!negate || arguments.length === 1) { this.assert( hasProperty , 'expected #{this} to have a ' + descriptor + _.inspect(name) @@ -924,7 +927,7 @@ module.exports = function (chai, _) { if (arguments.length > 1) { this.assert( - val === value + hasProperty && 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 850ac9f18..aff29c80f 100644 --- a/lib/chai/interface/assert.js +++ b/lib/chai/interface/assert.js @@ -1003,14 +1003,15 @@ module.exports = function (chai, util) { }; /** - * ### .propertyNotVal(object, property, value, [message]) + * ### .notPropertyVal(object, property, value, [message]) * - * Asserts that `object` has a property named by `property`, but with a value - * different from that given by `value`. + * Asserts that `object` does _not_ have a property named by `property` with + * value given by `value`. * - * assert.propertyNotVal({ tea: 'is good' }, 'tea', 'is bad'); + * assert.notPropertyVal({ tea: 'is good' }, 'tea', 'is bad'); + * assert.notPropertyVal({ tea: 'is good' }, 'coffee', 'is good'); * - * @name propertyNotVal + * @name notPropertyVal * @param {Object} object * @param {String} property * @param {Mixed} value @@ -1019,7 +1020,7 @@ module.exports = function (chai, util) { * @api public */ - assert.propertyNotVal = function (obj, prop, val, msg) { + assert.notPropertyVal = function (obj, prop, val, msg) { new Assertion(obj, msg).to.not.have.property(prop, val); }; @@ -1046,15 +1047,16 @@ module.exports = function (chai, util) { }; /** - * ### .deepPropertyNotVal(object, property, value, [message]) + * ### .notDeepPropertyVal(object, property, value, [message]) * - * Asserts that `object` has a property named by `property`, but with a value - * different from that given by `value`. `property` can use dot- and - * bracket-notation for deep reference. + * Asserts that `object` does _not_ have a property named by `property` with + * value given by `value`. `property` can use dot- and bracket-notation for deep + * reference. * - * assert.deepPropertyNotVal({ tea: { green: 'matcha' }}, 'tea.green', 'konacha'); + * assert.notDeepPropertyVal({ tea: { green: 'matcha' }}, 'tea.green', 'konacha'); + * assert.notDeepPropertyVal({ tea: { green: 'matcha' }}, 'coffee.green', 'matcha'); * - * @name deepPropertyNotVal + * @name notDeepPropertyVal * @param {Object} object * @param {String} property * @param {Mixed} value @@ -1063,7 +1065,7 @@ module.exports = function (chai, util) { * @api public */ - assert.deepPropertyNotVal = function (obj, prop, val, msg) { + assert.notDeepPropertyVal = function (obj, prop, val, msg) { new Assertion(obj, msg).to.not.have.deep.property(prop, val); }; diff --git a/test/assert.js b/test/assert.js index 594bdc191..7a51de72b 100644 --- a/test/assert.js +++ b/test/assert.js @@ -945,9 +945,12 @@ describe('assert', function () { assert.deepProperty(obj, 'foo.bar'); assert.notProperty(obj, 'baz'); assert.notProperty(obj, 'foo.bar'); + assert.notPropertyVal(simpleObj, 'foo', 'flow'); + assert.notPropertyVal(simpleObj, 'flow', 'bar'); assert.notDeepProperty(obj, 'foo.baz'); assert.deepPropertyVal(obj, 'foo.bar', 'baz'); - assert.deepPropertyNotVal(obj, 'foo.bar', 'flow'); + assert.notDeepPropertyVal(obj, 'foo.bar', 'flow'); + assert.notDeepPropertyVal(obj, 'foo.flow', 'baz'); err(function () { assert.property(obj, 'baz'); @@ -978,11 +981,11 @@ describe('assert', function () { }, "expected { foo: { bar: 'baz' } } to have a deep property 'foo.bar' of 'ball', but got 'baz'"); err(function () { - assert.propertyNotVal(simpleObj, 'foo', 'bar'); + assert.notPropertyVal(simpleObj, 'foo', 'bar'); }, "expected { foo: 'bar' } to not have a property 'foo' of 'bar'"); err(function () { - assert.deepPropertyNotVal(obj, 'foo.bar', 'baz'); + assert.notDeepPropertyVal(obj, 'foo.bar', 'baz'); }, "expected { foo: { bar: 'baz' } } to not have a deep property 'foo.bar' of 'baz'"); }); diff --git a/test/expect.js b/test/expect.js index 05950761a..4c93c1492 100644 --- a/test/expect.js +++ b/test/expect.js @@ -532,6 +532,8 @@ describe('expect', function () { it('property(name, val)', function(){ expect('test').to.have.property('length', 4); expect('asd').to.have.property('constructor', String); + expect('test').to.not.have.property('length', 3); + expect('test').to.not.have.property('foo', 4); var deepObj = { green: { tea: 'matcha' } @@ -582,10 +584,6 @@ describe('expect', function () { expect('asd').to.not.have.property('length', 3, 'blah'); }, "blah: expected 'asd' to not have a property 'length' of 3"); - err(function(){ - expect('asd').to.not.have.property('foo', 3, 'blah'); - }, "blah: 'asd' has no property 'foo'"); - err(function(){ expect('asd').to.have.property('constructor', Number, 'blah'); }, "blah: expected 'asd' to have a property 'constructor' of [Function: Number], but got [Function: String]"); @@ -594,6 +592,10 @@ describe('expect', function () { it('deep.property(name, val)', function(){ expect({ foo: { bar: 'baz' } }) .to.have.deep.property('foo.bar', 'baz'); + expect({ foo: { bar: 'baz' } }) + .to.not.have.deep.property('foo.bar', 'quux'); + expect({ foo: { bar: 'baz' } }) + .to.not.have.deep.property('foo.quux', 'baz'); err(function(){ expect({ foo: { bar: 'baz' } }) @@ -603,10 +605,6 @@ describe('expect', function () { expect({ foo: { bar: 'baz' } }) .to.not.have.deep.property('foo.bar', 'baz', 'blah'); }, "blah: expected { foo: { bar: 'baz' } } to not have a deep property 'foo.bar' of 'baz'"); - err(function(){ - expect({ foo: 5 }) - .to.not.have.deep.property('foo.bar', 'baz', 'blah'); - }, "blah: { foo: 5 } has no deep property 'foo.bar'"); }); it('ownProperty(name)', function(){ diff --git a/test/should.js b/test/should.js index 41cf8b15a..22dc2b242 100644 --- a/test/should.js +++ b/test/should.js @@ -438,10 +438,25 @@ describe('should', function() { }, "expected 'asd' to have a property 'foo'"); }); + it('deep.property(name)', function(){ + ({ 'foo.bar': 'baz'}).should.not.have.deep.property('foo.bar'); + ({ foo: { bar: 'baz' } }).should.have.deep.property('foo.bar'); + + ({ 'foo': [1, 2, 3] }).should.have.deep.property('foo[1]'); + + ({ 'foo.bar[]': 'baz'}).should.have.deep.property('foo\\.bar\\[\\]'); + + err(function(){ + ({ 'foo.bar': 'baz' }).should.have.deep.property('foo.bar'); + }, "expected { 'foo.bar': 'baz' } to have a deep property 'foo.bar'"); + }); + it('property(name, val)', function(){ 'test'.should.have.property('length', 4); 'asd'.should.have.property('constructor', String); ({ 1: 1 }).should.have.property(1, 1); + 'test'.should.not.have.property('length', 3); + 'test'.should.not.have.property('foo', 4); err(function(){ 'asd'.should.have.property('length', 4, 'blah'); @@ -451,15 +466,24 @@ describe('should', function() { 'asd'.should.not.have.property('length', 3, 'blah'); }, "blah: expected 'asd' to not have a property 'length' of 3"); - err(function(){ - 'asd'.should.not.have.property('foo', 3, 'blah'); - }, "blah: 'asd' has no property 'foo'"); - err(function(){ 'asd'.should.have.property('constructor', Number, 'blah'); }, "blah: expected 'asd' to have a property 'constructor' of [Function: Number], but got [Function: String]"); }); + it('deep.property(name, val)', function(){ + ({ foo: { bar: 'baz' } }).should.have.deep.property('foo.bar', 'baz'); + ({ foo: { bar: 'baz' } }).should.not.have.deep.property('foo.bar', 'quux'); + ({ foo: { bar: 'baz' } }).should.not.have.deep.property('foo.quux', 'baz'); + + err(function(){ + ({ foo: { bar: 'baz' } }).should.have.deep.property('foo.bar', 'quux', 'blah'); + }, "blah: expected { foo: { bar: 'baz' } } to have a deep property 'foo.bar' of 'quux', but got 'baz'"); + err(function(){ + ({ foo: { bar: 'baz' } }).should.not.have.deep.property('foo.bar', 'baz', 'blah'); + }, "blah: expected { foo: { bar: 'baz' } } to not have a deep property 'foo.bar' of 'baz'"); + }); + it('ownProperty(name)', function(){ 'test'.should.have.ownProperty('length'); 'test'.should.haveOwnProperty('length');