From edfe0647746b19c136501be7740424f786f09c3f Mon Sep 17 00:00:00 2001 From: Grant Snodgrass Date: Tue, 27 Sep 2016 20:00:03 -0400 Subject: [PATCH] Refactor `ownProperty` - Add `own` flag - Make `ownProperty` and `haveOwnProperty` aliases of `own.property` - Add `deep` support to `own.property` - Add `assert.deepOwnPropertyVal` - Add `assert.notDeepOwnPropertyVal` --- lib/chai/core/assertions.js | 170 ++++++++++----------- lib/chai/interface/assert.js | 288 +++++++++++++++++++++-------------- test/assert.js | 65 +++++--- test/expect.js | 227 +++++++++++++++++++-------- test/should.js | 202 +++++++++++++++++------- 5 files changed, 608 insertions(+), 344 deletions(-) diff --git a/lib/chai/core/assertions.js b/lib/chai/core/assertions.js index b5a4afd48..99f9daabf 100644 --- a/lib/chai/core/assertions.js +++ b/lib/chai/core/assertions.js @@ -104,6 +104,8 @@ module.exports = function (chai, _) { * var deepCss = { '.link': { '[target]': 42 }}; * expect(deepCss).to.have.nested.property('\\.link.\\[target\\]', 42); * + * The `nested` flag cannot be combined with the `own` flag. + * * @name nested * @namespace BDD * @api public @@ -113,6 +115,25 @@ module.exports = function (chai, _) { flag(this, 'nested', true); }); + /** + * ### .own + * + * Sets the `own` flag, later used by the `property` assertion. + * + * expect({a: 1}).to.have.own.property('a'); + * expect({a: 1}).to.have.own.property('a', 1); + * + * The `own` flag cannot be combined with the `nested` flag. + * + * @name own + * @namespace BDD + * @api public + */ + + Assertion.addProperty('own', function () { + flag(this, 'own', true); + }); + /** * ### .ordered * @@ -954,12 +975,14 @@ module.exports = function (chai, _) { /** * ### .property(name, [value]) * - * Asserts that the target has a property `name`, optionally asserting that - * the value of that property is strictly equal to `value`. + * Asserts that the target has a direct or inherited property `name`, + * optionally asserting that the value of that property is strictly equal to + * `value`. * * var obj = { foo: 'bar' }; * expect(obj).to.have.property('foo'); * expect(obj).to.have.property('foo', 'bar'); + * expect(obj).to.have.property('toString'); * expect(obj).to.not.have.property('baz'); * expect(obj).to.not.have.property('foo', 'baz'); * expect(obj).to.not.have.property('baz', 'bar'); @@ -971,6 +994,14 @@ module.exports = function (chai, _) { * expect(obj).to.have.deep.property('foo', { bar: 'baz' }); * expect(obj).to.not.have.deep.property('foo', { bar: 'quux' }); * + * If the `own` flag is set, the property must exist directly on the object. + * Inherited properties aren't checked. + * + * var obj = { foo: 'bar' }; + * expect(obj).to.have.own.property('foo'); + * expect(obj).to.have.own.property('foo', 'bar'); + * expect(obj).to.not.have.own.property('toString'); + * * If the `nested` flag is set, you can use dot- and bracket-notation for * nested references into objects and arrays. * @@ -982,11 +1013,6 @@ 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. * @@ -999,20 +1025,6 @@ module.exports = function (chai, _) { * expect(arr).to.have.nested.property('[0][1]', 'matcha'); * expect(arr).to.have.nested.property('[1][2].tea', 'konacha'); * - * Furthermore, `property` changes the subject of the assertion - * to be the value of that property from the original object. This - * permits for further chainable assertions on that property. - * - * expect(obj).to.have.property('foo') - * .that.is.a('string'); - * expect(deepObj).to.have.property('green') - * .that.is.an('object') - * .that.deep.equals({ tea: 'matcha' }); - * expect(deepObj).to.have.property('teas') - * .that.is.an('array') - * .with.nested.property('[2]') - * .that.deep.equals({ tea: 'konacha' }); - * * Note that dots and brackets in `name` must be backslash-escaped when * the `nested` flag is set, while they must NOT be escaped when the `nested` * flag is not set. @@ -1025,7 +1037,32 @@ module.exports = function (chai, _) { * var deepCss = { '.link': { '[target]': 42 }}; * expect(deepCss).to.have.nested.property('\\.link.\\[target\\]', 42); * + * The `deep` and `own` flags can be combined, and the `deep` and `nested` + * flags can be combined, but the `own` and `nested` flags cannot be combined. + * + * expect({ foo: { bar: 'baz' } }) + * .to.have.deep.own.property('foo', { bar: 'baz' }); + * expect({ foo: { bar: { baz: 'quux' } } }) + * .to.have.deep.nested.property('foo.bar', { baz: 'quux' }); + * + * Note that `property` changes the subject of the assertion + * to be the value of that property from the original object. This + * permits for further chainable assertions on that property. + * + * expect(obj).to.have.property('foo') + * .that.is.a('string'); + * expect(deepObj).to.have.own.property('green') + * .that.is.an('object') + * .that.deep.equals({ tea: 'matcha' }); + * expect(deepObj).to.have.property('teas') + * .that.is.an('array') + * .with.nested.property('[2]') + * .that.deep.equals({ tea: 'konacha' }); + * * @name property + * @alias own.property + * @alias ownProperty + * @alias haveOwnProperty * @alias deep.property * @alias nested.property * @param {String} name @@ -1036,23 +1073,32 @@ module.exports = function (chai, _) { * @api public */ - Assertion.addMethod('property', function (name, val, msg) { + function assertProperty (name, val, msg) { if (msg) flag(this, 'message', msg); - var isNested = !!flag(this, 'nested') - , isDeep = !!flag(this, 'deep') - , descriptor = (isDeep ? 'deep ' : '') - + (isNested ? 'nested ' : '') - + 'property ' + var isNested = flag(this, 'nested') + , isOwn = flag(this, 'own'); + + if (isNested && isOwn) { + throw new Error('The "nested" and "own" flags cannot be combined.'); + } + + var isDeep = flag(this, 'deep') , negate = flag(this, 'negate') , obj = flag(this, 'object') , pathInfo = isNested ? _.getPathInfo(name, obj) : null - , hasProperty = isNested - ? pathInfo.exists - : _.hasProperty(name, obj) - , value = isNested - ? pathInfo.value - : obj[name]; + , value = isNested ? pathInfo.value : obj[name]; + + var descriptor = ''; + if (isDeep) descriptor += 'deep '; + if (isOwn) descriptor += 'own '; + if (isNested) descriptor += 'nested '; + descriptor += 'property '; + + var hasProperty; + if (isOwn) hasProperty = Object.prototype.hasOwnProperty.call(obj, name); + else if (isNested) hasProperty = pathInfo.exists; + else hasProperty = _.hasProperty(name, obj); // 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 @@ -1077,63 +1123,13 @@ module.exports = function (chai, _) { } flag(this, 'object', value); - }); - + } - /** - * ### .ownProperty(name, [value]) - * - * Asserts that the target has an own property `name` and, optionally, if it has - * (or not, if using `.not`) the desired `value`. - * - * expect('test').to.have.ownProperty('length'); - * expect('test').to.haveOwnProperty('length'); - * expect('test').to.not.have.ownProperty('foo'); - * expect('test').to.not.haveOwnProperty('foo'); - * expect({ length: 12 }).to.have.ownProperty('length', 12); - * expect({ length: 1337 }).to.not.have.ownProperty('length', 20); - * expect({ length: 12 }).to.haveOwnProperty('length', 12); - * expect({ length: 1337 }).to.not.haveOwnProperty('length', 20); - * - * @name ownProperty - * @alias haveOwnProperty - * @param {String} name - * @param {Mixed} value (optional) - * @param {String} message _optional_ - * @namespace BDD - * @api public - */ + Assertion.addMethod('property', assertProperty); function assertOwnProperty (name, value, msg) { - if (msg) flag(this, 'message', msg); - var obj = flag(this, 'object'); - var negate = flag(this, 'negate'); - var objHasProperty = Object.prototype.hasOwnProperty.call(obj, name) - var actualValue = obj[name]; - - if (negate && value !== undefined) { - if (actualValue === undefined) { - throw new Error(_.inspect(obj) + ' does not have own property ' + _.inspect(name)); - } - } else { - this.assert( - objHasProperty - , 'expected #{this} to have own property ' + _.inspect(name) - , 'expected #{this} to not have own property ' + _.inspect(name) - ); - } - - if (value !== undefined) { - this.assert( - actualValue === value - , 'expected #{this} to have own property ' + _.inspect(name) + ' of #{exp}, but got #{act}' - , 'expected #{this} to not have own property ' + _.inspect(name) + ' of #{act}' - , value - , actualValue - ); - } - - flag(this, 'object', actualValue); + flag(this, 'own', true); + assertProperty.apply(this, arguments); } Assertion.addMethod('ownProperty', assertOwnProperty); diff --git a/lib/chai/interface/assert.js b/lib/chai/interface/assert.js index 0e8d97a80..257446206 100644 --- a/lib/chai/interface/assert.js +++ b/lib/chai/interface/assert.js @@ -1050,9 +1050,11 @@ module.exports = function (chai, util) { /** * ### .property(object, property, [message]) * - * Asserts that `object` has a property named by `property`. + * Asserts that `object` has a direct or inherited property named by + * `property`. * * assert.property({ tea: { green: 'matcha' }}, 'tea'); + * assert.property({ tea: { green: 'matcha' }}, 'toString'); * * @name property * @param {Object} object @@ -1069,7 +1071,8 @@ module.exports = function (chai, util) { /** * ### .notProperty(object, property, [message]) * - * Asserts that `object` does _not_ have a property named by `property`. + * Asserts that `object` does _not_ have a direct or inherited property named + * by `property`. * * assert.notProperty({ tea: { green: 'matcha' }}, 'coffee'); * @@ -1085,51 +1088,12 @@ module.exports = function (chai, util) { new Assertion(obj, msg).to.not.have.property(prop); }; - /** - * ### .nestedProperty(object, property, [message]) - * - * Asserts that `object` has a property named by `property`, which can be a - * string using dot- and bracket-notation for nested reference. - * - * assert.nestedProperty({ tea: { green: 'matcha' }}, 'tea.green'); - * - * @name nestedProperty - * @param {Object} object - * @param {String} property - * @param {String} message - * @namespace Assert - * @api public - */ - - assert.nestedProperty = function (obj, prop, msg) { - new Assertion(obj, msg).to.have.nested.property(prop); - }; - - /** - * ### .notNestedProperty(object, property, [message]) - * - * Asserts that `object` does _not_ have a property named by `property`, which - * can be a string using dot- and bracket-notation for nested reference. - * - * assert.notNestedProperty({ tea: { green: 'matcha' }}, 'tea.oolong'); - * - * @name notNestedProperty - * @param {Object} object - * @param {String} property - * @param {String} message - * @namespace Assert - * @api public - */ - - assert.notNestedProperty = function (obj, prop, msg) { - new Assertion(obj, msg).to.not.have.nested.property(prop); - }; - /** * ### .propertyVal(object, property, value, [message]) * - * Asserts that `object` has a property named by `property` with a value given - * by `value`. Uses a strict equality check (===). + * Asserts that `object` has a direct or inherited property named by + * `property` with a value given by `value`. Uses a strict equality check + * (===). * * assert.propertyVal({ tea: 'is good' }, 'tea', 'is good'); * @@ -1149,8 +1113,9 @@ module.exports = function (chai, util) { /** * ### .notPropertyVal(object, property, value, [message]) * - * Asserts that `object` does _not_ have a property named by `property` with - * value given by `value`. Uses a strict equality check (===). + * Asserts that `object` does _not_ have a direct or inherited property named + * by `property` with value given by `value`. Uses a strict equality check + * (===). * * assert.notPropertyVal({ tea: 'is good' }, 'tea', 'is bad'); * assert.notPropertyVal({ tea: 'is good' }, 'coffee', 'is good'); @@ -1171,8 +1136,8 @@ module.exports = function (chai, util) { /** * ### .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. + * Asserts that `object` has a direct or inherited property named by + * `property` with a value given by `value`. Uses a deep equality check. * * assert.deepPropertyVal({ tea: { green: 'matcha' } }, 'tea', { green: 'matcha' }); * @@ -1192,8 +1157,8 @@ module.exports = function (chai, util) { /** * ### .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. + * Asserts that `object` does _not_ have a direct or inherited 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' }); @@ -1213,172 +1178,265 @@ module.exports = function (chai, util) { }; /** - * ### .nestedPropertyVal(object, property, value, [message]) + * ### .ownProperty(object, property, [message]) * - * Asserts that `object` has a property named by `property` with value given - * by `value`. `property` can use dot- and bracket-notation for nested - * reference. Uses a strict equality check (===). + * Asserts that `object` has a direct property named by `property`. Inherited + * properties aren't checked. * - * assert.nestedPropertyVal({ tea: { green: 'matcha' }}, 'tea.green', 'matcha'); + * assert.ownProperty({ tea: { green: 'matcha' }}, 'tea'); * - * @name nestedPropertyVal + * @name ownProperty + * @param {Object} object + * @param {String} property + * @param {String} message + * @api public + */ + + assert.ownProperty = function (obj, prop, msg) { + new Assertion(obj, msg).to.have.own.property(prop); + }; + + /** + * ### .notOwnProperty(object, property, [message]) + * + * Asserts that `object` does _not_ have a direct property named by + * `property`. Inherited properties aren't checked. + * + * assert.notOwnProperty({ tea: { green: 'matcha' }}, 'coffee'); + * assert.notOwnProperty({}, 'toString'); + * + * @name notOwnProperty + * @param {Object} object + * @param {String} property + * @param {String} message + * @api public + */ + + assert.notOwnProperty = function (obj, prop, msg) { + new Assertion(obj, msg).to.not.have.own.property(prop); + }; + + /** + * ### .ownPropertyVal(object, property, value, [message]) + * + * Asserts that `object` has a direct property named by `property` and a value + * equal to the provided `value`. Uses a strict equality check (===). + * Inherited properties aren't checked. + * + * assert.ownPropertyVal({ coffee: 'is good'}, 'coffee', 'is good'); + * + * @name ownPropertyVal * @param {Object} object * @param {String} property * @param {Mixed} value * @param {String} message - * @namespace Assert * @api public */ - assert.nestedPropertyVal = function (obj, prop, val, msg) { - new Assertion(obj, msg).to.have.nested.property(prop, val); + assert.ownPropertyVal = function (obj, prop, value, msg) { + new Assertion(obj, msg).to.have.own.property(prop, value); }; /** - * ### .notNestedPropertyVal(object, property, value, [message]) + * ### .notOwnPropertyVal(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 strict equality check (===). + * Asserts that `object` does _not_ have a direct property named by `property` + * with a value equal to the provided `value`. Uses a strict equality check + * (===). Inherited properties aren't checked. * - * assert.notNestedPropertyVal({ tea: { green: 'matcha' }}, 'tea.green', 'konacha'); - * assert.notNestedPropertyVal({ tea: { green: 'matcha' }}, 'coffee.green', 'matcha'); + * assert.notOwnPropertyVal({ tea: 'is better'}, 'tea', 'is worse'); + * assert.notOwnPropertyVal({}, 'toString', Object.prototype.toString); * - * @name notNestedPropertyVal + * @name notOwnPropertyVal * @param {Object} object * @param {String} property * @param {Mixed} value * @param {String} message - * @namespace Assert * @api public */ - assert.notNestedPropertyVal = function (obj, prop, val, msg) { - new Assertion(obj, msg).to.not.have.nested.property(prop, val); + assert.notOwnPropertyVal = function (obj, prop, value, msg) { + new Assertion(obj, msg).to.not.have.own.property(prop, value); }; /** - * ### .deepNestedPropertyVal(object, property, value, [message]) + * ### .deepOwnPropertyVal(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. + * Asserts that `object` has a direct property named by `property` and a value + * equal to the provided `value`. Uses a deep equality check. Inherited + * properties aren't checked. * - * assert.deepNestedPropertyVal({ tea: { green: { matcha: 'yum' } } }, 'tea.green', { matcha: 'yum' }); + * assert.deepOwnPropertyVal({ tea: { green: 'matcha' } }, 'tea', { green: 'matcha' }); * - * @name deepNestedPropertyVal + * @name deepOwnPropertyVal * @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); + assert.deepOwnPropertyVal = function (obj, prop, value, msg) { + new Assertion(obj, msg).to.have.deep.own.property(prop, value); }; /** - * ### .notDeepNestedPropertyVal(object, property, value, [message]) + * ### .notDeepOwnPropertyVal(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. + * Asserts that `object` does _not_ have a direct property named by `property` + * with a value equal to the provided `value`. Uses a deep equality check. + * Inherited properties aren't checked. * - * 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' }); + * assert.notDeepOwnPropertyVal({ tea: { green: 'matcha' } }, 'tea', { black: 'matcha' }); + * assert.notDeepOwnPropertyVal({ tea: { green: 'matcha' } }, 'tea', { green: 'oolong' }); + * assert.notDeepOwnPropertyVal({ tea: { green: 'matcha' } }, 'coffee', { green: 'matcha' }); + * assert.notDeepOwnPropertyVal({}, 'toString', Object.prototype.toString); * - * @name notDeepNestedPropertyVal + * @name notDeepOwnPropertyVal * @param {Object} object * @param {String} property * @param {Mixed} value * @param {String} message + * @api public + */ + + assert.notDeepOwnPropertyVal = function (obj, prop, value, msg) { + new Assertion(obj, msg).to.not.have.deep.own.property(prop, value); + }; + + /** + * ### .nestedProperty(object, property, [message]) + * + * Asserts that `object` has a direct or inherited property named by + * `property`, which can be a string using dot- and bracket-notation for + * nested reference. + * + * assert.nestedProperty({ tea: { green: 'matcha' }}, 'tea.green'); + * + * @name nestedProperty + * @param {Object} object + * @param {String} property + * @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); - } + assert.nestedProperty = function (obj, prop, msg) { + new Assertion(obj, msg).to.have.nested.property(prop); + }; /** - * ### .ownProperty(object, property, [message]) + * ### .notNestedProperty(object, property, [message]) * - * Asserts that `object` has an own property named by `property`. + * Asserts that `object` does _not_ have a property named by `property`, which + * can be a string using dot- and bracket-notation for nested reference. The + * property cannot exist on the object nor anywhere in its prototype chain. * - * assert.ownProperty({ tea: { green: 'matcha' }}, 'tea'); + * assert.notNestedProperty({ tea: { green: 'matcha' }}, 'tea.oolong'); * - * @name ownProperty + * @name notNestedProperty * @param {Object} object * @param {String} property * @param {String} message + * @namespace Assert * @api public */ - assert.ownProperty = function (obj, prop, msg) { - new Assertion(obj, msg).to.have.ownProperty(prop); + assert.notNestedProperty = function (obj, prop, msg) { + new Assertion(obj, msg).to.not.have.nested.property(prop); }; /** - * ### .notOwnProperty(object, property, [message]) + * ### .nestedPropertyVal(object, property, value, [message]) * - * Asserts that `object` does not have an own property named by `property`. + * Asserts that `object` has a property named by `property` with value given + * by `value`. `property` can use dot- and bracket-notation for nested + * reference. Uses a strict equality check (===). * - * assert.notOwnProperty({ tea: { green: 'matcha' }}, 'coffee'); + * assert.nestedPropertyVal({ tea: { green: 'matcha' }}, 'tea.green', 'matcha'); * - * @name notOwnProperty + * @name nestedPropertyVal * @param {Object} object * @param {String} property + * @param {Mixed} value * @param {String} message + * @namespace Assert * @api public */ - assert.notOwnProperty = function (obj, prop, msg) { - new Assertion(obj, msg).to.not.have.ownProperty(prop); + assert.nestedPropertyVal = function (obj, prop, val, msg) { + new Assertion(obj, msg).to.have.nested.property(prop, val); }; /** - * ### .ownPropertyVal(object, property, value, [message]) + * ### .notNestedPropertyVal(object, property, value, [message]) * - * Asserts that `object` has an own property named by `property` and a value - * equal to the provided `value`. + * 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 strict equality check (===). * - * assert.ownPropertyVal({ coffee: 'is good'}, 'coffee', 'is good'); + * assert.notNestedPropertyVal({ tea: { green: 'matcha' }}, 'tea.green', 'konacha'); + * assert.notNestedPropertyVal({ tea: { green: 'matcha' }}, 'coffee.green', 'matcha'); * - * @name ownPropertyVal + * @name notNestedPropertyVal * @param {Object} object * @param {String} property * @param {Mixed} value * @param {String} message + * @namespace Assert * @api public */ - assert.ownPropertyVal = function (obj, prop, value, msg) { - new Assertion(obj, msg).to.have.ownProperty(prop, value); + assert.notNestedPropertyVal = function (obj, prop, val, msg) { + new Assertion(obj, msg).to.not.have.nested.property(prop, val); }; /** - * ### .notOwnPropertyVal(object, property, value, [message]) + * ### .deepNestedPropertyVal(object, property, value, [message]) * - * Asserts that `object` has an own property named by `property`, but with a value - * different from that given by `value`. + * 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.notOwnPropertyVal({ tea: 'is better'}, 'tea', 'is worse'); + * assert.deepNestedPropertyVal({ tea: { green: { matcha: 'yum' } } }, 'tea.green', { matcha: 'yum' }); * - * @name notOwnPropertyVal + * @name deepNestedPropertyVal * @param {Object} object * @param {String} property * @param {Mixed} value * @param {String} message + * @namespace Assert * @api public */ - assert.notOwnPropertyVal = function (obj, prop, value, msg) { - new Assertion(obj, msg).to.not.have.ownProperty(prop, value); + 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 a3fc8258b..c21b03bc9 100644 --- a/test/assert.js +++ b/test/assert.js @@ -1069,6 +1069,8 @@ describe('assert', function () { var simpleObj = { foo: 'bar' }; var undefinedKeyObj = { foo: undefined }; assert.property(obj, 'foo'); + assert.property(obj, 'toString'); + assert.propertyVal(obj, 'toString', Object.prototype.toString); assert.property(undefinedKeyObj, 'foo'); assert.propertyVal(undefinedKeyObj, 'foo', undefined); assert.nestedProperty(obj, 'foo.bar'); @@ -1139,26 +1141,6 @@ describe('assert', function () { }, "blah: expected { a: { b: 1 } } to not have 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 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 deep nested property 'a.z'"); - - err(function () { - assert.notDeepNestedPropertyVal(obj, 'a.b', {c: 1}, 'blah'); - }, "blah: expected { a: { b: { c: 1 } } } to not have deep nested property 'a.b' of { c: 1 }"); - }); - it('ownProperty', function() { var coffeeObj = { coffee: 'is good' }; @@ -1172,10 +1154,13 @@ describe('assert', function () { assert.ownPropertyVal(teaObj, 'length', 17); assert.notOwnProperty(coffeeObj, 'length'); + assert.notOwnProperty(coffeeObj, 'toString'); assert.notOwnProperty(teaObj, 'calories'); assert.notOwnPropertyVal(coffeeObj, 'coffee', 'is bad'); assert.notOwnPropertyVal(teaObj, 'length', 1); + assert.notOwnPropertyVal(coffeeObj, 'toString', Object.prototype.toString); + assert.notOwnPropertyVal({a: {b: 1}}, 'a', {b: 1}); err(function () { assert.ownProperty(coffeeObj, 'calories'); @@ -1200,10 +1185,46 @@ describe('assert', function () { err(function () { assert.ownPropertyVal(teaObj, 'calories', 17); }, "expected 'but tea is better' to have own property 'calories'"); + }); + + it('deepOwnPropertyVal', function () { + var obj = {a: {b: 1}}; + assert.deepOwnPropertyVal(obj, 'a', {b: 1}); + assert.notDeepOwnPropertyVal(obj, 'a', {z: 1}); + assert.notDeepOwnPropertyVal(obj, 'a', {b: 7}); + assert.notDeepOwnPropertyVal(obj, 'toString', Object.prototype.toString); err(function () { - assert.notOwnPropertyVal(coffeeObj, 'sugar', 1337); - }, "{ coffee: 'is good' } does not have own property 'sugar'"); + assert.deepOwnPropertyVal(obj, 'a', {z: 7}, 'blah'); + }, "blah: expected { a: { b: 1 } } to have deep own property 'a' of { z: 7 }, but got { b: 1 }"); + + err(function () { + assert.deepOwnPropertyVal(obj, 'z', {b: 1}, 'blah'); + }, "blah: expected { a: { b: 1 } } to have deep own property 'z'"); + + err(function () { + assert.notDeepOwnPropertyVal(obj, 'a', {b: 1}, 'blah'); + }, "blah: expected { a: { b: 1 } } to not have deep own 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 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 deep nested property 'a.z'"); + + err(function () { + assert.notDeepNestedPropertyVal(obj, 'a.b', {c: 1}, 'blah'); + }, "blah: expected { a: { b: { c: 1 } } } to not have deep nested property 'a.b' of { c: 1 }"); }); it('throws / throw / Throw', function() { diff --git a/test/expect.js b/test/expect.js index f0058fa75..fc604ff88 100644 --- a/test/expect.js +++ b/test/expect.js @@ -770,6 +770,7 @@ describe('expect', function () { it('property(name)', function(){ expect('test').to.have.property('length'); + expect({a: 1}).to.have.property('toString'); expect(4).to.not.have.property('length'); expect({ 'foo.bar': 'baz' }) @@ -795,29 +796,16 @@ describe('expect', function () { expect({ foo: { bar: 'baz' } }) .to.have.property('foo.bar'); }, "expected { foo: { bar: 'baz' } } to have property 'foo.bar'"); - }); - - it('nested.property(name)', function(){ - expect({ 'foo.bar': 'baz'}) - .to.not.have.nested.property('foo.bar'); - expect({ foo: { bar: 'baz' } }) - .to.have.nested.property('foo.bar'); - - expect({ 'foo': [1, 2, 3] }) - .to.have.nested.property('foo[1]'); - expect({ 'foo.bar[]': 'baz'}) - .to.have.nested.property('foo\\.bar\\[\\]'); - - err(function(){ - expect({ 'foo.bar': 'baz' }) - .to.have.nested.property('foo.bar'); - }, "expected { 'foo.bar': 'baz' } to have nested property 'foo.bar'"); + err(function() { + expect({a: {b: 1}}).to.have.own.nested.property("a.b"); + }, "The \"nested\" and \"own\" flags cannot be combined."); }); it('property(name, val)', function(){ expect('test').to.have.property('length', 4); expect('asd').to.have.property('constructor', String); + expect({a: 1}).to.have.property('toString', Object.prototype.toString); 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}); @@ -874,6 +862,10 @@ describe('expect', function () { err(function(){ expect('asd').to.have.property('constructor', Number, 'blah'); }, "blah: expected 'asd' to have property 'constructor' of [Function: Number], but got [Function: String]"); + + err(function() { + expect({a: {b: 1}}).to.have.own.nested.property("a.b", 1); + }, "The \"nested\" and \"own\" flags cannot be combined."); }); it('deep.property(name, val)', function () { @@ -896,60 +888,37 @@ describe('expect', function () { }, "blah: expected { a: { b: 1 } } to not have deep property 'a' of { b: 1 }"); }); - it('nested.property(name, val)', function(){ - expect({ foo: { bar: 'baz' } }) - .to.have.nested.property('foo.bar', 'baz'); - expect({ foo: { bar: 'baz' } }) - .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' } }) - .to.have.nested.property('foo.bar', 'quux', 'blah'); - }, "blah: expected { foo: { bar: 'baz' } } to have nested property 'foo.bar' of 'quux', but got 'baz'"); - err(function(){ - expect({ foo: { bar: 'baz' } }) - .to.not.have.nested.property('foo.bar', 'baz', 'blah'); - }, "blah: expected { foo: { bar: 'baz' } } to not have 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 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 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 deep nested property 'a.b' of { c: 1 }"); - }); - - it('ownProperty(name)', function(){ + it('own.property(name)', function(){ + expect('test').to.have.own.property('length'); expect('test').to.have.ownProperty('length'); expect('test').to.haveOwnProperty('length'); + expect('test').to.not.have.own.property('iDontExist'); expect('test').to.not.have.ownProperty('iDontExist'); expect('test').to.not.haveOwnProperty('iDontExist'); + expect({a: 1}).to.not.have.own.property('toString'); + expect({a: 1}).to.not.have.ownProperty('toString'); + expect({a: 1}).to.not.haveOwnProperty('toString'); + expect({ length: 12 }).to.have.own.property('length'); expect({ length: 12 }).to.have.ownProperty('length'); expect({ length: 12 }).to.haveOwnProperty('length'); + expect({ length: 12 }).to.not.have.own.property('iDontExist'); expect({ length: 12 }).to.not.have.ownProperty('iDontExist'); expect({ length: 12 }).to.not.haveOwnProperty('iDontExist'); // Chaining property's value + expect('test').to.have.own.property('length').that.is.a('number'); expect('test').to.have.ownProperty('length').that.is.a('number'); expect('test').to.haveOwnProperty('length').that.is.a('number'); + err(function(){ + expect({ length: 12 }).to.have.own.property('iDontExist'); + }, "expected { length: 12 } to have own property 'iDontExist'"); + + err(function(){ + expect({ length: 12 }).to.not.have.own.property('length'); + }, "expected { length: 12 } to not have own property 'length'"); + err(function(){ expect({ length: 12 }).to.have.ownProperty('iDontExist'); }, "expected { length: 12 } to have own property 'iDontExist'"); @@ -967,24 +936,49 @@ describe('expect', function () { }, "expected { length: 12 } to not have own property 'length'"); }); - it('ownProperty(name, value)', function(){ + it('own.property(name, value)', function(){ + expect('test').to.have.own.property('length', 4); expect('test').to.have.ownProperty('length', 4); expect('test').to.haveOwnProperty('length', 4); + expect('test').to.not.have.own.property('length', 1337); expect('test').to.not.have.ownProperty('length', 1337); expect('test').to.not.haveOwnProperty('length', 1337); - + expect({a: 1}).to.not.have.own.property('toString', Object.prototype.toString); + expect({a: 1}).to.not.have.ownProperty('toString', Object.prototype.toString); + expect({a: 1}).to.not.haveOwnProperty('toString', Object.prototype.toString); + expect({a: {b: 1}}).to.not.have.own.property('a', {b: 1}); + expect({a: {b: 1}}).to.not.have.ownProperty('a', {b: 1}); + expect({a: {b: 1}}).to.not.haveOwnProperty('a', {b: 1}); + + expect({ length: 12 }).to.have.own.property('length', 12); expect({ length: 12 }).to.have.ownProperty('length', 12); expect({ length: 12 }).to.haveOwnProperty('length', 12); + expect({ length: 12 }).to.not.have.own.property('length', 15); expect({ length: 12 }).to.not.have.ownProperty('length', 15); expect({ length: 12 }).to.not.haveOwnProperty('length', 15); // Chaining property's value + expect('test').to.have.own.property('length', 4).that.is.a('number'); expect('test').to.have.ownProperty('length', 4).that.is.a('number'); expect('test').to.haveOwnProperty('length', 4).that.is.a('number'); var objNoProto = Object.create(null); objNoProto.a = 'a'; + expect(objNoProto).to.have.own.property('a'); expect(objNoProto).to.have.ownProperty('a'); + expect(objNoProto).to.haveOwnProperty('a'); + + err(function(){ + expect({ length: 12 }).to.have.own.property('iDontExist', 12); + }, "expected { length: 12 } to have own property 'iDontExist'"); + + err(function() { + expect({ length: 12 }).to.not.have.own.property('length', 12); + }, "expected { length: 12 } to not have own property 'length' of 12"); + + err(function() { + expect({ length: 12 }).to.have.own.property('length', 15); + }, "expected { length: 12 } to have own property 'length' of 15, but got 12"); err(function(){ expect({ length: 12 }).to.have.ownProperty('iDontExist', 12); @@ -998,10 +992,6 @@ describe('expect', function () { expect({ length: 12 }).to.have.ownProperty('length', 15); }, "expected { length: 12 } to have own property 'length' of 15, but got 12"); - err(function() { - expect({ length: 12 }).to.not.have.ownProperty('iDontExist', 15); - }, "{ length: 12 } does not have own property 'iDontExist'"); - err(function(){ expect({ length: 12 }).to.haveOwnProperty('iDontExist', 12); }, "expected { length: 12 } to have own property 'iDontExist'"); @@ -1013,10 +1003,115 @@ describe('expect', function () { err(function() { expect({ length: 12 }).to.haveOwnProperty('length', 15); }, "expected { length: 12 } to have own property 'length' of 15, but got 12"); + }); - err(function() { - expect({ length: 12 }).to.not.haveOwnProperty('iDontExist', 15); - }, "{ length: 12 } does not have own property 'iDontExist'"); + it('deep.own.property(name, val)', function () { + var obj = {a: {b: 1}}; + expect(obj).to.have.deep.own.property('a', {b: 1}); + expect(obj).to.have.deep.ownProperty('a', {b: 1}); + expect(obj).to.deep.haveOwnProperty('a', {b: 1}); + expect(obj).to.not.have.deep.own.property('a', {z: 1}); + expect(obj).to.not.have.deep.ownProperty('a', {z: 1}); + expect(obj).to.not.deep.haveOwnProperty('a', {z: 1}); + expect(obj).to.not.have.deep.own.property('a', {b: 7}); + expect(obj).to.not.have.deep.ownProperty('a', {b: 7}); + expect(obj).to.not.deep.haveOwnProperty('a', {b: 7}); + expect(obj).to.not.have.deep.own.property('toString', Object.prototype.toString); + expect(obj).to.not.have.deep.ownProperty('toString', Object.prototype.toString); + expect(obj).to.not.deep.haveOwnProperty('toString', Object.prototype.toString); + + err(function () { + expect(obj).to.have.deep.own.property('a', {z: 7}, 'blah'); + }, "blah: expected { a: { b: 1 } } to have deep own property 'a' of { z: 7 }, but got { b: 1 }"); + + err(function () { + expect(obj).to.have.deep.own.property('z', {b: 1}, 'blah'); + }, "blah: expected { a: { b: 1 } } to have deep own property 'z'"); + + err(function () { + expect(obj).to.not.have.deep.own.property('a', {b: 1}, 'blah'); + }, "blah: expected { a: { b: 1 } } to not have deep own property 'a' of { b: 1 }"); + + err(function () { + expect(obj).to.have.deep.ownProperty('a', {z: 7}, 'blah'); + }, "blah: expected { a: { b: 1 } } to have deep own property 'a' of { z: 7 }, but got { b: 1 }"); + + err(function () { + expect(obj).to.have.deep.ownProperty('z', {b: 1}, 'blah'); + }, "blah: expected { a: { b: 1 } } to have deep own property 'z'"); + + err(function () { + expect(obj).to.not.have.deep.ownProperty('a', {b: 1}, 'blah'); + }, "blah: expected { a: { b: 1 } } to not have deep own property 'a' of { b: 1 }"); + + err(function () { + expect(obj).to.deep.haveOwnProperty('a', {z: 7}, 'blah'); + }, "blah: expected { a: { b: 1 } } to have deep own property 'a' of { z: 7 }, but got { b: 1 }"); + + err(function () { + expect(obj).to.deep.haveOwnProperty('z', {b: 1}, 'blah'); + }, "blah: expected { a: { b: 1 } } to have deep own property 'z'"); + + err(function () { + expect(obj).to.not.deep.haveOwnProperty('a', {b: 1}, 'blah'); + }, "blah: expected { a: { b: 1 } } to not have deep own property 'a' of { b: 1 }"); + }); + + it('nested.property(name)', function(){ + expect({ 'foo.bar': 'baz'}) + .to.not.have.nested.property('foo.bar'); + expect({ foo: { bar: 'baz' } }) + .to.have.nested.property('foo.bar'); + + expect({ 'foo': [1, 2, 3] }) + .to.have.nested.property('foo[1]'); + + expect({ 'foo.bar[]': 'baz'}) + .to.have.nested.property('foo\\.bar\\[\\]'); + + err(function(){ + expect({ 'foo.bar': 'baz' }) + .to.have.nested.property('foo.bar'); + }, "expected { 'foo.bar': 'baz' } to have nested property 'foo.bar'"); + }); + + it('nested.property(name, val)', function(){ + expect({ foo: { bar: 'baz' } }) + .to.have.nested.property('foo.bar', 'baz'); + expect({ foo: { bar: 'baz' } }) + .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' } }) + .to.have.nested.property('foo.bar', 'quux', 'blah'); + }, "blah: expected { foo: { bar: 'baz' } } to have nested property 'foo.bar' of 'quux', but got 'baz'"); + err(function(){ + expect({ foo: { bar: 'baz' } }) + .to.not.have.nested.property('foo.bar', 'baz', 'blah'); + }, "blah: expected { foo: { bar: 'baz' } } to not have 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 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 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 deep nested property 'a.b' of { c: 1 }"); }); it('ownPropertyDescriptor(name)', function(){ diff --git a/test/should.js b/test/should.js index ebe781621..1f46ba565 100644 --- a/test/should.js +++ b/test/should.js @@ -688,29 +688,22 @@ describe('should', function() { 'test'.should.have.property('length'); (4).should.not.have.property('length'); ({ 1: 1 }).should.have.property(1); + ({ a: 1 }).should.have.property('toString'); err(function(){ 'asd'.should.have.property('foo'); }, "expected 'asd' to have property 'foo'"); - }); - - it('nested.property(name)', function(){ - ({ 'foo.bar': 'baz'}).should.not.have.nested.property('foo.bar'); - ({ foo: { bar: 'baz' } }).should.have.nested.property('foo.bar'); - - ({ 'foo': [1, 2, 3] }).should.have.nested.property('foo[1]'); - - ({ 'foo.bar[]': 'baz'}).should.have.nested.property('foo\\.bar\\[\\]'); - err(function(){ - ({ 'foo.bar': 'baz' }).should.have.nested.property('foo.bar'); - }, "expected { 'foo.bar': 'baz' } to have nested property 'foo.bar'"); + err(function() { + ({a: {b: 1}}).should.have.own.nested.property("a.b"); + }, "The \"nested\" and \"own\" flags cannot be combined."); }); it('property(name, val)', function(){ 'test'.should.have.property('length', 4); 'asd'.should.have.property('constructor', String); ({ 1: 1 }).should.have.property(1, 1); + ({ a: 1 }).should.have.property('toString', Object.prototype.toString); 'test'.should.not.have.property('length', 3); 'test'.should.not.have.property('foo', 4); ({a: {b: 1}}).should.not.have.property('a', {b: 1}); @@ -726,6 +719,10 @@ describe('should', function() { err(function(){ 'asd'.should.have.property('constructor', Number, 'blah'); }, "blah: expected 'asd' to have property 'constructor' of [Function: Number], but got [Function: String]"); + + err(function() { + ({a: {b: 1}}).should.have.own.nested.property("a.b", 1); + }, "The \"nested\" and \"own\" flags cannot be combined."); }); it('deep.property(name, val)', function () { @@ -748,60 +745,44 @@ describe('should', function() { }, "blah: expected { a: { b: 1 } } to not have 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'); - }, "blah: expected { foo: { bar: 'baz' } } to have nested property 'foo.bar' of 'quux', but got 'baz'"); - err(function(){ - ({ foo: { bar: 'baz' } }).should.not.have.nested.property('foo.bar', 'baz', 'blah'); - }, "blah: expected { foo: { bar: 'baz' } } to not have 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 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 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 deep nested property 'a.b' of { c: 1 }"); - }); - it('ownProperty(name)', function(){ + 'test'.should.have.own.property('length'); 'test'.should.have.ownProperty('length'); 'test'.should.haveOwnProperty('length'); + 'test'.should.not.have.own.property('iDontExist'); 'test'.should.not.have.ownProperty('iDontExist'); 'test'.should.not.haveOwnProperty('iDontExist'); + ({ a: 1 }).should.not.have.own.property('toString'); + ({ a: 1 }).should.not.have.ownProperty('toString'); + ({ a: 1 }).should.not.haveOwnProperty('toString'); + ({ length: 12 }).should.have.own.property('length'); ({ length: 12 }).should.have.ownProperty('length'); ({ length: 12 }).should.haveOwnProperty('length'); + ({ length: 12 }).should.not.have.own.property('iDontExist'); ({ length: 12 }).should.not.have.ownProperty('iDontExist'); ({ length: 12 }).should.not.haveOwnProperty('iDontExist'); ({ 1: 1 }).should.have.ownProperty(1); var objNoHasOwnProperty = {hasOwnProperty: null}; objNoHasOwnProperty.a = 'a'; + objNoHasOwnProperty.should.have.own.property('a'); objNoHasOwnProperty.should.have.ownProperty('a'); + objNoHasOwnProperty.should.haveOwnProperty('a'); // Chaining property's value + 'test'.should.have.own.property('length').that.is.a('number'); 'test'.should.have.ownProperty('length').that.is.a('number'); 'test'.should.haveOwnProperty('length').that.is.a('number'); + err(function(){ + ({ length: 12 }).should.have.own.property('iDontExist'); + }, "expected { length: 12 } to have own property 'iDontExist'"); + + err(function(){ + ({ length: 12 }).should.not.have.own.property('length'); + }, "expected { length: 12 } to not have own property 'length'"); + err(function(){ ({ length: 12 }).should.have.ownProperty('iDontExist'); }, "expected { length: 12 } to have own property 'iDontExist'"); @@ -820,20 +801,43 @@ describe('should', function() { }); it('ownProperty(name, value)', function(){ + 'test'.should.have.own.property('length', 4); 'test'.should.have.ownProperty('length', 4); 'test'.should.haveOwnProperty('length', 4); + 'test'.should.not.have.own.property('length', 1337); 'test'.should.not.have.ownProperty('length', 1337); 'test'.should.not.haveOwnProperty('length', 1337); - + 'test'.should.not.have.own.property('toString', Object.prototype.toString); + 'test'.should.not.have.ownProperty('toString', Object.prototype.toString); + 'test'.should.not.haveOwnProperty('toString', Object.prototype.toString); + ({a: {b: 1}}).should.not.have.own.property('a', {b: 1}); + ({a: {b: 1}}).should.not.have.ownProperty('a', {b: 1}); + ({a: {b: 1}}).should.not.haveOwnProperty('a', {b: 1}); + + ({ length: 12 }).should.have.own.property('length', 12); ({ length: 12 }).should.have.ownProperty('length', 12); ({ length: 12 }).should.haveOwnProperty('length', 12); + ({ length: 12 }).should.not.have.own.property('length', 15); ({ length: 12 }).should.not.have.ownProperty('length', 15); ({ length: 12 }).should.not.haveOwnProperty('length', 15); // Chaining property's value + 'test'.should.have.own.property('length', 4).that.is.a('number'); 'test'.should.have.ownProperty('length', 4).that.is.a('number'); 'test'.should.haveOwnProperty('length', 4).that.is.a('number'); + err(function(){ + ({ length: 12 }).should.have.own.property('iDontExist', 12); + }, "expected { length: 12 } to have own property 'iDontExist'"); + + err(function() { + ({ length: 12 }).should.not.have.own.property('length', 12); + }, "expected { length: 12 } to not have own property 'length' of 12"); + + err(function() { + ({ length: 12 }).should.have.own.property('length', 15); + }, "expected { length: 12 } to have own property 'length' of 15, but got 12"); + err(function(){ ({ length: 12 }).should.have.ownProperty('iDontExist', 12); }, "expected { length: 12 } to have own property 'iDontExist'"); @@ -846,10 +850,6 @@ describe('should', function() { ({ length: 12 }).should.have.ownProperty('length', 15); }, "expected { length: 12 } to have own property 'length' of 15, but got 12"); - err(function() { - ({ length: 12 }).should.not.have.ownProperty('iDontExist', 15); - }, "{ length: 12 } does not have own property 'iDontExist'"); - err(function(){ ({ length: 12 }).should.haveOwnProperty('iDontExist', 12); }, "expected { length: 12 } to have own property 'iDontExist'"); @@ -861,10 +861,104 @@ describe('should', function() { err(function() { ({ length: 12 }).should.haveOwnProperty('length', 15); }, "expected { length: 12 } to have own property 'length' of 15, but got 12"); + }); - err(function() { - ({ length: 12 }).should.not.haveOwnProperty('iDontExist', 15); - }, "{ length: 12 } does not have own property 'iDontExist'"); + it('deep.own.property(name, val)', function () { + var obj = {a: {b: 1}}; + obj.should.have.deep.own.property('a', {b: 1}); + obj.should.have.deep.ownProperty('a', {b: 1}); + obj.should.deep.haveOwnProperty('a', {b: 1}); + obj.should.not.have.deep.own.property('a', {z: 1}); + obj.should.not.have.deep.ownProperty('a', {z: 1}); + obj.should.not.deep.haveOwnProperty('a', {z: 1}); + obj.should.not.have.deep.own.property('a', {b: 7}); + obj.should.not.have.deep.ownProperty('a', {b: 7}); + obj.should.not.deep.haveOwnProperty('a', {b: 7}); + obj.should.not.have.deep.own.property('toString', Object.prototype.toString); + obj.should.not.have.deep.ownProperty('toString', Object.prototype.toString); + obj.should.not.deep.haveOwnProperty('toString', Object.prototype.toString); + + err(function () { + obj.should.have.deep.own.property('a', {z: 7}, 'blah'); + }, "blah: expected { a: { b: 1 } } to have deep own property 'a' of { z: 7 }, but got { b: 1 }"); + + err(function () { + obj.should.have.deep.own.property('z', {b: 1}, 'blah'); + }, "blah: expected { a: { b: 1 } } to have deep own property 'z'"); + + err(function () { + obj.should.not.have.deep.own.property('a', {b: 1}, 'blah'); + }, "blah: expected { a: { b: 1 } } to not have deep own property 'a' of { b: 1 }"); + err(function () { + obj.should.have.deep.ownProperty('a', {z: 7}, 'blah'); + }, "blah: expected { a: { b: 1 } } to have deep own property 'a' of { z: 7 }, but got { b: 1 }"); + + err(function () { + obj.should.have.deep.ownProperty('z', {b: 1}, 'blah'); + }, "blah: expected { a: { b: 1 } } to have deep own property 'z'"); + + err(function () { + obj.should.not.have.deep.ownProperty('a', {b: 1}, 'blah'); + }, "blah: expected { a: { b: 1 } } to not have deep own property 'a' of { b: 1 }"); + + err(function () { + obj.should.deep.haveOwnProperty('a', {z: 7}, 'blah'); + }, "blah: expected { a: { b: 1 } } to have deep own property 'a' of { z: 7 }, but got { b: 1 }"); + + err(function () { + obj.should.deep.haveOwnProperty('z', {b: 1}, 'blah'); + }, "blah: expected { a: { b: 1 } } to have deep own property 'z'"); + + err(function () { + obj.should.not.deep.haveOwnProperty('a', {b: 1}, 'blah'); + }, "blah: expected { a: { b: 1 } } to not have deep own property 'a' of { b: 1 }"); + }); + + it('nested.property(name)', function(){ + ({ 'foo.bar': 'baz'}).should.not.have.nested.property('foo.bar'); + ({ foo: { bar: 'baz' } }).should.have.nested.property('foo.bar'); + + ({ 'foo': [1, 2, 3] }).should.have.nested.property('foo[1]'); + + ({ 'foo.bar[]': 'baz'}).should.have.nested.property('foo\\.bar\\[\\]'); + + err(function(){ + ({ 'foo.bar': 'baz' }).should.have.nested.property('foo.bar'); + }, "expected { 'foo.bar': 'baz' } to have nested property 'foo.bar'"); + }); + + 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'); + }, "blah: expected { foo: { bar: 'baz' } } to have nested property 'foo.bar' of 'quux', but got 'baz'"); + err(function(){ + ({ foo: { bar: 'baz' } }).should.not.have.nested.property('foo.bar', 'baz', 'blah'); + }, "blah: expected { foo: { bar: 'baz' } } to not have 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 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 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 deep nested property 'a.b' of { c: 1 }"); }); it('ownPropertyDescriptor(name)', function(){