From 4c2c3d893529313f553ba14eccb2620aa75e1289 Mon Sep 17 00:00:00 2001 From: Keith Cirkel Date: Tue, 11 Apr 2017 21:22:21 +0100 Subject: [PATCH 1/2] chore: release 4.0.0-canary.2 --- chai.js | 8172 +++++++++++++++++++++++++++++++++++++++----------- package.json | 2 +- 2 files changed, 6426 insertions(+), 1748 deletions(-) diff --git a/chai.js b/chai.js index 935a071..f6e0071 100644 --- a/chai.js +++ b/chai.js @@ -8,14 +8,13 @@ module.exports = require('./lib/chai'); * MIT Licensed */ -var used = [] - , exports = module.exports = {}; +var used = []; /*! * Chai version */ -exports.version = '3.5.0'; +exports.version = require('../package').version; /*! * Assertion Error @@ -32,7 +31,7 @@ var util = require('./chai/utils'); /** * # .use(function) * - * Provides a way to extend the internals of Chai + * Provides a way to extend the internals of Chai. * * @param {Function} * @returns {this} for chaining @@ -41,11 +40,11 @@ var util = require('./chai/utils'); exports.use = function (fn) { if (!~used.indexOf(fn)) { - fn(this, util); + fn(exports, util); used.push(fn); } - return this; + return exports; }; /*! @@ -96,7 +95,7 @@ exports.use(should); var assert = require('./chai/interface/assert'); exports.use(assert); -},{"./chai/assertion":3,"./chai/config":4,"./chai/core/assertions":5,"./chai/interface/assert":6,"./chai/interface/expect":7,"./chai/interface/should":8,"./chai/utils":22,"assertion-error":30}],3:[function(require,module,exports){ +},{"../package":40,"./chai/assertion":3,"./chai/config":4,"./chai/core/assertions":5,"./chai/interface/assert":6,"./chai/interface/expect":7,"./chai/interface/should":8,"./chai/utils":22,"assertion-error":33}],3:[function(require,module,exports){ /*! * chai * http://chaijs.com @@ -125,13 +124,46 @@ module.exports = function (_chai, util) { * * Creates object for chaining. * + * `Assertion` objects contain metadata in the form of flags. Three flags can + * be assigned during instantiation by passing arguments to this constructor: + * + * - `object`: This flag contains the target of the assertion. For example, in + * the assertion `expect(numKittens).to.equal(7);`, the `object` flag will + * contain `numKittens` so that the `equal` assertion can reference it when + * needed. + * + * - `message`: This flag contains an optional custom error message to be + * prepended to the error message that's generated by the assertion when it + * fails. + * + * - `ssfi`: This flag stands for "start stack function indicator". It + * contains a function reference that serves as the starting point for + * removing frames from the stack trace of the error that's created by the + * assertion when it fails. The goal is to provide a cleaner stack trace to + * end users by removing Chai's internal functions. Note that it only works + * in environments that support `Error.captureStackTrace`, and only when + * `Chai.config.includeStack` hasn't been set to `false`. + * + * - `lockSsfi`: This flag controls whether or not the given `ssfi` flag + * should retain its current value, even as assertions are chained off of + * this object. This is usually set to `true` when creating a new assertion + * from within another assertion. It's also temporarily set to `true` before + * an overwritten assertion gets called by the overwriting assertion. + * + * @param {Mixed} obj target of the assertion + * @param {String} msg (optional) custom error message + * @param {Function} ssfi (optional) starting point for removing stack frames + * @param {Boolean} lockSsfi (optional) whether or not the ssfi flag is locked * @api private */ - function Assertion (obj, msg, stack) { - flag(this, 'ssfi', stack || arguments.callee); + function Assertion (obj, msg, ssfi, lockSsfi) { + flag(this, 'ssfi', ssfi || Assertion); + flag(this, 'lockSsfi', lockSsfi); flag(this, 'object', obj); flag(this, 'message', msg); + + return util.proxify(this); } Object.defineProperty(Assertion, 'includeStack', { @@ -197,12 +229,13 @@ module.exports = function (_chai, util) { Assertion.prototype.assert = function (expr, msg, negateMsg, expected, _actual, showDiff) { var ok = util.test(this, arguments); - if (true !== showDiff) showDiff = false; + if (false !== showDiff) showDiff = true; + if (undefined === expected && undefined === _actual) showDiff = false; if (true !== config.showDiff) showDiff = false; if (!ok) { - var msg = util.getMessage(this, arguments) - , actual = util.getActual(this, arguments); + msg = util.getMessage(this, arguments); + var actual = util.getActual(this, arguments); throw new AssertionError(msg, { actual: actual , expected: expected @@ -245,7 +278,7 @@ module.exports = { * @api public */ - includeStack: false, + includeStack: false, /** * ### config.showDiff @@ -282,8 +315,47 @@ module.exports = { * @api public */ - truncateThreshold: 40 + truncateThreshold: 40, + /** + * ### config.useProxy + * + * User configurable property, defines if chai will use a Proxy to throw + * an error when a non-existent property is read, which protects users + * from typos when using property-based assertions. + * + * Set it to false if you want to disable this feature. + * + * chai.config.useProxy = false; // disable use of Proxy + * + * This feature is automatically disabled regardless of this config value + * in environments that don't support proxies. + * + * @param {Boolean} + * @api public + */ + + useProxy: true, + + /** + * ### config.proxyExcludedKeys + * + * User configurable property, defines which properties should be ignored + * instead of throwing an error if they do not exist on the assertion. + * This is only applied if the environment Chai is running in supports proxies and + * if the `useProxy` configuration setting is enabled. + * By default, `then` and `inspect` will not throw an error if they do not exist on the + * assertion object because the `.inspect` property is read by `util.inspect` (for example, when + * using `console.log` on the assertion object) and `.then` is necessary for promise type-checking. + * + * // By default these keys will not throw an error if they do not exist on the assertion object + * chai.config.proxyExcludedKeys = ['then', 'inspect']; + * + * @param {Array} + * @api public + */ + + proxyExcludedKeys: ['then', 'inspect', 'toJSON'] }; },{}],5:[function(require,module,exports){ @@ -296,16 +368,14 @@ module.exports = { module.exports = function (chai, _) { var Assertion = chai.Assertion - , toString = Object.prototype.toString + , AssertionError = chai.AssertionError , flag = _.flag; /** * ### Language Chains * - * The following are provided as chainable getters to - * improve the readability of your assertions. They - * do not provide testing capabilities unless they - * have been overwritten by a plugin. + * The following are provided as chainable getters to improve the readability + * of your assertions. * * **Chains** * @@ -322,6 +392,8 @@ module.exports = function (chai, _) { * - at * - of * - same + * - but + * - does * * @name language chains * @namespace BDD @@ -331,21 +403,27 @@ module.exports = function (chai, _) { [ 'to', 'be', 'been' , 'is', 'and', 'has', 'have' , 'with', 'that', 'which', 'at' - , 'of', 'same' ].forEach(function (chain) { - Assertion.addProperty(chain, function () { - return this; - }); + , 'of', 'same', 'but', 'does' ].forEach(function (chain) { + Assertion.addProperty(chain); }); /** * ### .not * - * Negates any of assertions following in the chain. + * Negates all assertions that follow in the chain. * - * expect(foo).to.not.equal('bar'); - * expect(goodFn).to.not.throw(Error); - * expect({ foo: 'baz' }).to.have.property('foo') - * .and.not.equal('bar'); + * expect(function () {}).to.not.throw(); + * expect({a: 1}).to.not.have.property('b'); + * expect([1, 2]).to.be.an('array').that.does.not.include(3); + * + * Just because you can negate any assertion with `.not` doesn't mean you + * should. With great power comes great responsibility. It's often best to + * assert that the one expected output was produced, rather than asserting + * that one of countless unexpected outputs wasn't produced. See individual + * assertions for specific guidance. + * + * expect(2).to.equal(2); // Recommended + * expect(2).to.not.equal(1); // Not recommended * * @name not * @namespace BDD @@ -359,18 +437,34 @@ module.exports = function (chai, _) { /** * ### .deep * - * Sets the `deep` flag, later used by the `equal` and - * `property` assertions. + * Causes all `.equal`, `.include`, `.members`, `.keys`, and `.property` + * assertions that follow in the chain to use deep equality instead of strict + * (`===`) equality. See the `deep-eql` project page for info on the deep + * equality algorithm: https://github.com/chaijs/deep-eql. * - * expect(foo).to.deep.equal({ bar: 'baz' }); - * expect({ foo: { bar: { baz: 'quux' } } }) - * .to.have.deep.property('foo.bar.baz', 'quux'); + * // Target object deeply (but not strictly) equals `{a: 1}` + * expect({a: 1}).to.deep.equal({a: 1}); + * expect({a: 1}).to.not.equal({a: 1}); * - * `.deep.property` special characters can be escaped - * by adding two slashes before the `.` or `[]`. + * // Target array deeply (but not strictly) includes `{a: 1}` + * expect([{a: 1}]).to.deep.include({a: 1}); + * expect([{a: 1}]).to.not.include({a: 1}); * - * var deepCss = { '.link': { '[target]': 42 }}; - * expect(deepCss).to.have.deep.property('\\.link.\\[target\\]', 42); + * // Target object deeply (but not strictly) includes `x: {a: 1}` + * expect({x: {a: 1}}).to.deep.include({x: {a: 1}}); + * expect({x: {a: 1}}).to.not.include({x: {a: 1}}); + * + * // Target array deeply (but not strictly) has member `{a: 1}` + * expect([{a: 1}]).to.have.deep.members([{a: 1}]); + * expect([{a: 1}]).to.not.have.members([{a: 1}]); + * + * // Target set deeply (but not strictly) has key `{a: 1}` + * expect(new Set([{a: 1}])).to.have.deep.keys([{a: 1}]); + * expect(new Set([{a: 1}])).to.not.have.keys([{a: 1}]); + * + * // Target object deeply (but not strictly) has property `x: {a: 1}` + * expect({x: {a: 1}}).to.have.deep.property('x', {a: 1}); + * expect({x: {a: 1}}).to.not.have.property('x', {a: 1}); * * @name deep * @namespace BDD @@ -381,13 +475,91 @@ module.exports = function (chai, _) { flag(this, 'deep', true); }); + /** + * ### .nested + * + * Enables dot- and bracket-notation in all `.property` and `.include` + * assertions that follow in the chain. + * + * expect({a: {b: ['x', 'y']}}).to.have.nested.property('a.b[1]'); + * expect({a: {b: ['x', 'y']}}).to.nested.include({'a.b[1]': 'y'}); + * + * If `.` or `[]` are part of an actual property name, they can be escaped by + * adding two backslashes before them. + * + * expect({'.a': {'[b]': 'x'}}).to.have.nested.property('\\.a.\\[b\\]'); + * expect({'.a': {'[b]': 'x'}}).to.nested.include({'\\.a.\\[b\\]': 'x'}); + * + * `.nested` cannot be combined with `.own`. + * + * @name nested + * @namespace BDD + * @api public + */ + + Assertion.addProperty('nested', function () { + flag(this, 'nested', true); + }); + + /** + * ### .own + * + * Causes all `.property` and `.include` assertions that follow in the chain + * to ignore inherited properties. + * + * Object.prototype.b = 2; + * + * expect({a: 1}).to.have.own.property('a'); + * expect({a: 1}).to.have.property('b').but.not.own.property('b'); + * + * expect({a: 1}).to.own.include({a: 1}); + * expect({a: 1}).to.include({b: 2}).but.not.own.include({b: 2}); + * + * `.own` cannot be combined with `.nested`. + * + * @name own + * @namespace BDD + * @api public + */ + + Assertion.addProperty('own', function () { + flag(this, 'own', true); + }); + + /** + * ### .ordered + * + * Causes all `.members` assertions that follow in the chain to require that + * members be in the same order. + * + * expect([1, 2]).to.have.ordered.members([1, 2]) + * .but.not.have.ordered.members([2, 1]); + * + * When `.include` and `.ordered` are combined, the ordering begins at the + * start of both arrays. + * + * expect([1, 2, 3]).to.include.ordered.members([1, 2]) + * .but.not.include.ordered.members([2, 3]); + * + * @name ordered + * @namespace BDD + * @api public + */ + + Assertion.addProperty('ordered', function () { + flag(this, 'ordered', true); + }); + /** * ### .any * - * Sets the `any` flag, (opposite of the `all` flag) - * later used in the `keys` assertion. + * Causes all `.keys` assertions that follow in the chain to only require that + * the target have at least one of the given keys. This is the opposite of + * `.all`, which requires that the target have all of the given keys. * - * expect(foo).to.have.any.keys('bar', 'baz'); + * expect({a: 1, b: 2}).to.not.have.any.keys('c', 'd'); + * + * See the `.keys` doc for guidance on when to use `.any` or `.all`. * * @name any * @namespace BDD @@ -396,17 +568,24 @@ module.exports = function (chai, _) { Assertion.addProperty('any', function () { flag(this, 'any', true); - flag(this, 'all', false) + flag(this, 'all', false); }); /** * ### .all * - * Sets the `all` flag (opposite of the `any` flag) - * later used by the `keys` assertion. + * Causes all `.keys` assertions that follow in the chain to require that the + * target have all of the given keys. This is the opposite of `.any`, which + * only requires that the target have at least one of the given keys. * - * expect(foo).to.have.all.keys('bar', 'baz'); + * expect({a: 1, b: 2}).to.have.all.keys('a', 'b'); + * + * Note that `.all` is used by default when neither `.all` nor `.any` are + * added earlier in the chain. However, it's often best to add `.all` anyway + * because it improves readability. + * + * See the `.keys` doc for guidance on when to use `.any` or `.all`. * * @name all * @namespace BDD @@ -419,32 +598,61 @@ module.exports = function (chai, _) { }); /** - * ### .a(type) + * ### .a(type[, msg]) * - * The `a` and `an` assertions are aliases that can be - * used either as language chains or to assert a value's - * type. + * Asserts that the target's type is equal to the given string `type`. Types + * are case insensitive. See the `type-detect` project page for info on the + * type detection algorithm: https://github.com/chaijs/type-detect. * - * // typeof - * expect('test').to.be.a('string'); - * expect({ foo: 'bar' }).to.be.an('object'); + * expect('foo').to.be.a('string'); + * expect({a: 1}).to.be.an('object'); * expect(null).to.be.a('null'); * expect(undefined).to.be.an('undefined'); * expect(new Error).to.be.an('error'); - * expect(new Promise).to.be.a('promise'); - * expect(new Float32Array()).to.be.a('float32array'); + * expect(Promise.resolve()).to.be.a('promise'); + * expect(new Float32Array).to.be.a('float32array'); * expect(Symbol()).to.be.a('symbol'); * - * // es6 overrides - * expect({[Symbol.toStringTag]:()=>'foo'}).to.be.a('foo'); + * `.a` supports objects that have a custom type set via `Symbol.toStringTag`. * - * // language chain - * expect(foo).to.be.an.instanceof(Foo); + * var myObj = { + * [Symbol.toStringTag]: 'myCustomType' + * }; + * + * expect(myObj).to.be.a('myCustomType').but.not.an('object'); + * + * It's often best to use `.a` to check a target's type before making more + * assertions on the same target. That way, you avoid unexpected behavior from + * any assertion that does different things based on the target's type. + * + * expect([1, 2, 3]).to.be.an('array').that.includes(2); + * expect([]).to.be.an('array').that.is.empty; + * + * Add `.not` earlier in the chain to negate `.a`. However, it's often best to + * assert that the target is the expected type, rather than asserting that it + * isn't one of many unexpected types. + * + * expect('foo').to.be.a('string'); // Recommended + * expect('foo').to.not.be.an('array'); // Not recommended + * + * `.a` accepts an optional `msg` argument which is a custom error message to + * show when the assertion fails. The message can also be given as the second + * argument to `expect`. + * + * expect(1).to.be.a('string', 'nooo why fail??'); + * expect(1, 'nooo why fail??').to.be.a('string'); + * + * `.a` can also be used as a language chain to improve the readability of + * your assertions. + * + * expect({b: 2}).to.have.a.property('b'); + * + * The alias `.an` can be used interchangeably with `.a`. * * @name a * @alias an * @param {String} type - * @param {String} message _optional_ + * @param {String} msg _optional_ * @namespace BDD * @api public */ @@ -456,7 +664,7 @@ module.exports = function (chai, _) { , article = ~[ 'a', 'e', 'i', 'o', 'u' ].indexOf(type.charAt(0)) ? 'an ' : 'a '; this.assert( - type === _.type(obj) + type === _.type(obj).toLowerCase() , 'expected #{this} to be ' + article + type , 'expected #{this} not to be ' + article + type ); @@ -466,23 +674,138 @@ module.exports = function (chai, _) { Assertion.addChainableMethod('a', an); /** - * ### .include(value) + * ### .include(val[, msg]) * - * The `include` and `contain` assertions can be used as either property - * based language chains or as methods to assert the inclusion of an object - * in an array or a substring in a string. When used as language chains, - * they toggle the `contains` flag for the `keys` assertion. + * When the target is a string, `.include` asserts that the given string `val` + * is a substring of the target. * - * expect([1,2,3]).to.include(2); - * expect('foobar').to.contain('foo'); - * expect({ foo: 'bar', hello: 'universe' }).to.include.keys('foo'); + * expect('foobar').to.include('foo'); + * + * When the target is an array, `.include` asserts that the given `val` is a + * member of the target. + * + * expect([1, 2, 3]).to.include(2); + * + * When the target is an object, `.include` asserts that the given object + * `val`'s properties are a subset of the target's properties. + * + * expect({a: 1, b: 2, c: 3}).to.include({a: 1, b: 2}); + * + * Because `.include` does different things based on the target's type, it's + * important to check the target's type before using `.include`. See the `.a` + * doc for info on testing a target's type. + * + * expect([1, 2, 3]).to.be.an('array').that.includes(2); + * + * By default, strict (`===`) equality is used to compare array members and + * object properties. Add `.deep` earlier in the chain to use deep equality + * instead. See the `deep-eql` project page for info on the deep equality + * algorithm: https://github.com/chaijs/deep-eql. + * + * // Target array deeply (but not strictly) includes `{a: 1}` + * expect([{a: 1}]).to.deep.include({a: 1}); + * expect([{a: 1}]).to.not.include({a: 1}); + * + * // Target object deeply (but not strictly) includes `x: {a: 1}` + * expect({x: {a: 1}}).to.deep.include({x: {a: 1}}); + * expect({x: {a: 1}}).to.not.include({x: {a: 1}}); + * + * By default, all of the target's properties are searched when working with + * objects. This includes properties that are inherited and/or non-enumerable. + * Add `.own` earlier in the chain to exclude the target's inherited + * properties from the search. + * + * Object.prototype.b = 2; + * + * expect({a: 1}).to.own.include({a: 1}); + * expect({a: 1}).to.include({b: 2}).but.not.own.include({b: 2}); + * + * Note that a target object is always only searched for `val`'s own + * enumerable properties. + * + * `.deep` and `.own` can be combined. + * + * expect({a: {b: 2}}).to.deep.own.include({a: {b: 2}}); + * + * Add `.nested` earlier in the chain to enable dot- and bracket-notation when + * referencing nested properties. + * + * expect({a: {b: ['x', 'y']}}).to.nested.include({'a.b[1]': 'y'}); + * + * If `.` or `[]` are part of an actual property name, they can be escaped by + * adding two backslashes before them. + * + * expect({'.a': {'[b]': 2}}).to.nested.include({'\\.a.\\[b\\]': 2}); + * + * `.deep` and `.nested` can be combined. + * + * expect({a: {b: [{c: 3}]}}).to.deep.nested.include({'a.b[0]': {c: 3}}); + * + * `.own` and `.nested` cannot be combined. + * + * Add `.not` earlier in the chain to negate `.include`. + * + * expect('foobar').to.not.include('taco'); + * expect([1, 2, 3]).to.not.include(4); + * + * However, it's dangerous to negate `.include` when the target is an object. + * The problem is that it creates uncertain expectations by asserting that the + * target object doesn't have all of `val`'s key/value pairs but may or may + * not have some of them. It's often best to identify the exact output that's + * expected, and then write an assertion that only accepts that exact output. + * + * When the target object isn't even expected to have `val`'s keys, it's + * often best to assert exactly that. + * + * expect({c: 3}).to.not.have.any.keys('a', 'b'); // Recommended + * expect({c: 3}).to.not.include({a: 1, b: 2}); // Not recommended + * + * When the target object is expected to have `val`'s keys, it's often best to + * assert that each of the properties has its expected value, rather than + * asserting that each property doesn't have one of many unexpected values. + * + * expect({a: 3, b: 4}).to.include({a: 3, b: 4}); // Recommended + * expect({a: 3, b: 4}).to.not.include({a: 1, b: 2}); // Not recommended + * + * `.include` accepts an optional `msg` argument which is a custom error + * message to show when the assertion fails. The message can also be given as + * the second argument to `expect`. + * + * expect([1, 2, 3]).to.include(4, 'nooo why fail??'); + * expect([1, 2, 3], 'nooo why fail??').to.include(4); + * + * `.include` can also be used as a language chain, causing all `.members` and + * `.keys` assertions that follow in the chain to require the target to be a + * superset of the expected set, rather than an identical set. Note that + * `.members` ignores duplicates in the subset when `.include` is added. + * + * // Target object's keys are a superset of ['a', 'b'] but not identical + * expect({a: 1, b: 2, c: 3}).to.include.all.keys('a', 'b'); + * expect({a: 1, b: 2, c: 3}).to.not.have.all.keys('a', 'b'); + * + * // Target array is a superset of [1, 2] but not identical + * expect([1, 2, 3]).to.include.members([1, 2]); + * expect([1, 2, 3]).to.not.have.members([1, 2]); + * + * // Duplicates in the subset are ignored + * expect([1, 2, 3]).to.include.members([1, 2, 2, 2]); + * + * Note that adding `.any` earlier in the chain causes the `.keys` assertion + * to ignore `.include`. + * + * // Both assertions are identical + * expect({a: 1}).to.include.any.keys('a', 'b'); + * expect({a: 1}).to.have.any.keys('a', 'b'); + * + * The aliases `.includes`, `.contain`, and `.contains` can be used + * interchangeably with `.include`. * * @name include * @alias contain * @alias includes * @alias contains - * @param {Object|String|Number} obj - * @param {String} message _optional_ + * @param {Mixed} val + * @param {String} msg _optional_ * @namespace BDD * @api public */ @@ -491,35 +814,63 @@ module.exports = function (chai, _) { flag(this, 'contains', true); } + function isDeepIncluded (arr, val) { + return arr.some(function (arrVal) { + return _.eql(arrVal, val); + }); + } + function include (val, msg) { - _.expectTypes(this, ['array', 'object', 'string']); - 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; + _.expectTypes(this, ['array', 'object', 'string'], flag(this, 'ssfi')); + + var obj = flag(this, 'object') + , objType = _.type(obj).toLowerCase() + , isDeep = flag(this, 'deep') + , descriptor = isDeep ? 'deep ' : ''; + + // This block is for asserting a subset of properties in an object. + if (objType === '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, true); + flag(propAssertion, 'lockSsfi', true); + + 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 - , 'expected #{this} to include ' + _.inspect(val) - , 'expected #{this} to not include ' + _.inspect(val)); + objType === 'string' || !isDeep ? ~obj.indexOf(val) + : isDeepIncluded(obj, val) + , 'expected #{this} to ' + descriptor + 'include ' + _.inspect(val) + , 'expected #{this} to not ' + descriptor + 'include ' + _.inspect(val)); } Assertion.addChainableMethod('include', include, includeChainingBehavior); @@ -530,13 +881,33 @@ module.exports = function (chai, _) { /** * ### .ok * - * Asserts that the target is truthy. + * Asserts that the target is loosely (`==`) equal to `true`. However, it's + * often best to assert that the target is strictly (`===`) or deeply equal to + * its expected value. * - * expect('everything').to.be.ok; - * expect(1).to.be.ok; - * expect(false).to.not.be.ok; - * expect(undefined).to.not.be.ok; - * expect(null).to.not.be.ok; + * expect(1).to.equal(1); // Recommended + * expect(1).to.be.ok; // Not recommended + * + * expect(true).to.be.true; // Recommended + * expect(true).to.be.ok; // Not recommended + * + * Add `.not` earlier in the chain to negate `.ok`. + * + * expect(0).to.equal(0); // Recommended + * expect(0).to.not.be.ok; // Not recommended + * + * expect(false).to.be.false; // Recommended + * expect(false).to.not.be.ok; // Not recommended + * + * expect(null).to.be.null; // Recommended + * expect(null).to.not.be.ok; // Not recommended + * + * expect(undefined).to.be.undefined; // Recommended + * expect(undefined).to.not.be.ok; // Not recommended + * + * A custom error message can be given as the second argument to `expect`. + * + * expect(false, 'nooo why fail??').to.be.ok; * * @name ok * @namespace BDD @@ -553,10 +924,23 @@ module.exports = function (chai, _) { /** * ### .true * - * Asserts that the target is `true`. + * Asserts that the target is strictly (`===`) equal to `true`. * * expect(true).to.be.true; - * expect(1).to.not.be.true; + * + * Add `.not` earlier in the chain to negate `.true`. However, it's often best + * to assert that the target is equal to its expected value, rather than not + * equal to `true`. + * + * expect(false).to.be.false; // Recommended + * expect(false).to.not.be.true; // Not recommended + * + * expect(1).to.equal(1); // Recommended + * expect(1).to.not.be.true; // Not recommended + * + * A custom error message can be given as the second argument to `expect`. + * + * expect(false, 'nooo why fail??').to.be.true; * * @name true * @namespace BDD @@ -568,17 +952,30 @@ module.exports = function (chai, _) { true === flag(this, 'object') , 'expected #{this} to be true' , 'expected #{this} to be false' - , this.negate ? false : true + , flag(this, 'negate') ? false : true ); }); /** * ### .false * - * Asserts that the target is `false`. + * Asserts that the target is strictly (`===`) equal to `false`. * * expect(false).to.be.false; - * expect(0).to.not.be.false; + * + * Add `.not` earlier in the chain to negate `.false`. However, it's often + * best to assert that the target is equal to its expected value, rather than + * not equal to `false`. + * + * expect(true).to.be.true; // Recommended + * expect(true).to.not.be.false; // Not recommended + * + * expect(1).to.equal(1); // Recommended + * expect(1).to.not.be.false; // Not recommended + * + * A custom error message can be given as the second argument to `expect`. + * + * expect(true, 'nooo why fail??').to.be.false; * * @name false * @namespace BDD @@ -590,17 +987,27 @@ module.exports = function (chai, _) { false === flag(this, 'object') , 'expected #{this} to be false' , 'expected #{this} to be true' - , this.negate ? true : false + , flag(this, 'negate') ? true : false ); }); /** * ### .null * - * Asserts that the target is `null`. + * Asserts that the target is strictly (`===`) equal to `null`. * * expect(null).to.be.null; - * expect(undefined).to.not.be.null; + * + * Add `.not` earlier in the chain to negate `.null`. However, it's often best + * to assert that the target is equal to its expected value, rather than not + * equal to `null`. + * + * expect(1).to.equal(1); // Recommended + * expect(1).to.not.be.null; // Not recommended + * + * A custom error message can be given as the second argument to `expect`. + * + * expect(42, 'nooo why fail??').to.be.null; * * @name null * @namespace BDD @@ -618,10 +1025,20 @@ module.exports = function (chai, _) { /** * ### .undefined * - * Asserts that the target is `undefined`. + * Asserts that the target is strictly (`===`) equal to `undefined`. * * expect(undefined).to.be.undefined; - * expect(null).to.not.be.undefined; + * + * Add `.not` earlier in the chain to negate `.undefined`. However, it's often + * best to assert that the target is equal to its expected value, rather than + * not equal to `undefined`. + * + * expect(1).to.equal(1); // Recommended + * expect(1).to.not.be.undefined; // Not recommended + * + * A custom error message can be given as the second argument to `expect`. + * + * expect(42, 'nooo why fail??').to.be.undefined; * * @name undefined * @namespace BDD @@ -638,10 +1055,21 @@ module.exports = function (chai, _) { /** * ### .NaN - * Asserts that the target is `NaN`. * - * expect('foo').to.be.NaN; - * expect(4).not.to.be.NaN; + * Asserts that the target is exactly `NaN`. + * + * expect(NaN).to.be.NaN; + * + * Add `.not` earlier in the chain to negate `.NaN`. However, it's often best + * to assert that the target is equal to its expected value, rather than not + * equal to `NaN`. + * + * expect('foo').to.equal('foo'); // Recommended + * expect('foo').to.not.be.NaN; // Not recommended + * + * A custom error message can be given as the second argument to `expect`. + * + * expect(42, 'nooo why fail??').to.be.NaN; * * @name NaN * @namespace BDD @@ -650,7 +1078,7 @@ module.exports = function (chai, _) { Assertion.addProperty('NaN', function () { this.assert( - isNaN(flag(this, 'object')) + _.isNaN(flag(this, 'object')) , 'expected #{this} to be NaN' , 'expected #{this} not to be NaN' ); @@ -659,15 +1087,27 @@ module.exports = function (chai, _) { /** * ### .exist * - * Asserts that the target is neither `null` nor `undefined`. + * Asserts that the target is not strictly (`===`) equal to either `null` or + * `undefined`. However, it's often best to assert that the target is equal to + * its expected value. * - * var foo = 'hi' - * , bar = null - * , baz; + * expect(1).to.equal(1); // Recommended + * expect(1).to.exist; // Not recommended * - * expect(foo).to.exist; - * expect(bar).to.not.exist; - * expect(baz).to.not.exist; + * expect(0).to.equal(0); // Recommended + * expect(0).to.exist; // Not recommended + * + * Add `.not` earlier in the chain to negate `.exist`. + * + * expect(null).to.be.null; // Recommended + * expect(null).to.not.exist; // Not recommended + * + * expect(undefined).to.be.undefined; // Recommended + * expect(undefined).to.not.exist; // Not recommended + * + * A custom error message can be given as the second argument to `expect`. + * + * expect(null, 'nooo why fail??').to.exist; * * @name exist * @namespace BDD @@ -675,42 +1115,103 @@ module.exports = function (chai, _) { */ Assertion.addProperty('exist', function () { + var val = flag(this, 'object'); this.assert( - null != flag(this, 'object') + val !== null && val !== undefined , 'expected #{this} to exist' , 'expected #{this} to not exist' ); }); - /** * ### .empty * - * Asserts that the target's length is `0`. For arrays and strings, it checks - * the `length` property. For objects, it gets the count of - * enumerable keys. + * When the target is a string or array, `.empty` asserts that the target's + * `length` property is strictly (`===`) equal to `0`. * * expect([]).to.be.empty; * expect('').to.be.empty; + * + * When the target is a map or set, `.empty` asserts that the target's `size` + * property is strictly equal to `0`. + * + * expect(new Set()).to.be.empty; + * expect(new Map()).to.be.empty; + * + * When the target is a non-function object, `.empty` asserts that the target + * doesn't have any own enumerable properties. Properties with Symbol-based + * keys are excluded from the count. + * * expect({}).to.be.empty; * + * Because `.empty` does different things based on the target's type, it's + * important to check the target's type before using `.empty`. See the `.a` + * doc for info on testing a target's type. + * + * expect([]).to.be.an('array').that.is.empty; + * + * Add `.not` earlier in the chain to negate `.empty`. However, it's often + * best to assert that the target contains its expected number of values, + * rather than asserting that it's not empty. + * + * expect([1, 2, 3]).to.have.lengthOf(3); // Recommended + * expect([1, 2, 3]).to.not.be.empty; // Not recommended + * + * expect(new Set([1, 2, 3])).to.have.property('size', 3); // Recommended + * expect(new Set([1, 2, 3])).to.not.be.empty; // Not recommended + * + * expect(Object.keys({a: 1})).to.have.lengthOf(1); // Recommended + * expect({a: 1}).to.not.be.empty; // Not recommended + * + * A custom error message can be given as the second argument to `expect`. + * + * expect([1, 2, 3], 'nooo why fail??').to.be.empty; + * * @name empty * @namespace BDD * @api public */ Assertion.addProperty('empty', function () { - var obj = flag(this, 'object') - , expected = obj; + var val = flag(this, 'object') + , ssfi = flag(this, 'ssfi') + , flagMsg = flag(this, 'message') + , itemsCount; - if (Array.isArray(obj) || 'string' === typeof object) { - expected = obj.length; - } else if (typeof obj === 'object') { - expected = Object.keys(obj).length; + flagMsg = flagMsg ? flagMsg + ': ' : ''; + + switch (_.type(val).toLowerCase()) { + case 'array': + case 'string': + itemsCount = val.length; + break; + case 'map': + case 'set': + itemsCount = val.size; + break; + case 'weakmap': + case 'weakset': + throw new AssertionError( + flagMsg + '.empty was passed a weak collection', + undefined, + ssfi + ); + case 'function': + var msg = flagMsg + '.empty was passed a function ' + _.getName(val); + throw new AssertionError(msg.trim(), undefined, ssfi); + default: + if (val !== Object(val)) { + throw new AssertionError( + flagMsg + '.empty was passed non-string primitive ' + _.inspect(val), + undefined, + ssfi + ); + } + itemsCount = Object.keys(val).length; } this.assert( - !expected + 0 === itemsCount , 'expected #{this} to be empty' , 'expected #{this} not to be empty' ); @@ -719,12 +1220,27 @@ module.exports = function (chai, _) { /** * ### .arguments * - * Asserts that the target is an arguments object. + * Asserts that the target is an `arguments` object. * * function test () { * expect(arguments).to.be.arguments; * } * + * test(); + * + * Add `.not` earlier in the chain to negate `.arguments`. However, it's often + * best to assert which type the target is expected to be, rather than + * asserting that its not an `arguments` object. + * + * expect('foo').to.be.a('string'); // Recommended + * expect('foo').to.not.be.arguments; // Not recommended + * + * A custom error message can be given as the second argument to `expect`. + * + * expect({}, 'nooo why fail??').to.be.arguments; + * + * The alias `.Arguments` can be used interchangeably with `.arguments`. + * * @name arguments * @alias Arguments * @namespace BDD @@ -733,9 +1249,9 @@ module.exports = function (chai, _) { function checkArguments () { var obj = flag(this, 'object') - , type = Object.prototype.toString.call(obj); + , type = _.type(obj); this.assert( - '[object Arguments]' === type + 'Arguments' === type , 'expected #{this} to be arguments but got ' + type , 'expected #{this} to not be arguments' ); @@ -745,24 +1261,46 @@ module.exports = function (chai, _) { Assertion.addProperty('Arguments', checkArguments); /** - * ### .equal(value) + * ### .equal(val[, msg]) * - * Asserts that the target is strictly equal (`===`) to `value`. - * Alternately, if the `deep` flag is set, asserts that - * the target is deeply equal to `value`. + * Asserts that the target is strictly (`===`) equal to the given `val`. * - * expect('hello').to.equal('hello'); - * expect(42).to.equal(42); - * expect(1).to.not.equal(true); - * expect({ foo: 'bar' }).to.not.equal({ foo: 'bar' }); - * expect({ foo: 'bar' }).to.deep.equal({ foo: 'bar' }); + * expect(1).to.equal(1); + * expect('foo').to.equal('foo'); + * + * Add `.deep` earlier in the chain to use deep equality instead. See the + * `deep-eql` project page for info on the deep equality algorithm: + * https://github.com/chaijs/deep-eql. + * + * // Target object deeply (but not strictly) equals `{a: 1}` + * expect({a: 1}).to.deep.equal({a: 1}); + * expect({a: 1}).to.not.equal({a: 1}); + * + * // Target array deeply (but not strictly) equals `[1, 2]` + * expect([1, 2]).to.deep.equal([1, 2]); + * expect([1, 2]).to.not.equal([1, 2]); + * + * Add `.not` earlier in the chain to negate `.equal`. However, it's often + * best to assert that the target is equal to its expected value, rather than + * not equal to one of countless unexpected values. + * + * expect(1).to.equal(1); // Recommended + * expect(1).to.not.equal(2); // Not recommended + * + * `.equal` accepts an optional `msg` argument which is a custom error message + * to show when the assertion fails. The message can also be given as the + * second argument to `expect`. + * + * expect(1).to.equal(2, 'nooo why fail??'); + * expect(1, 'nooo why fail??').to.equal(2); + * + * The aliases `.equals` and `eq` can be used interchangeably with `.equal`. * * @name equal * @alias equals * @alias eq - * @alias deep.equal - * @param {Mixed} value - * @param {String} message _optional_ + * @param {Mixed} val + * @param {String} msg _optional_ * @namespace BDD * @api public */ @@ -789,17 +1327,42 @@ module.exports = function (chai, _) { Assertion.addMethod('eq', assertEqual); /** - * ### .eql(value) + * ### .eql(obj[, msg]) * - * Asserts that the target is deeply equal to `value`. + * Asserts that the target is deeply equal to the given `obj`. See the + * `deep-eql` project page for info on the deep equality algorithm: + * https://github.com/chaijs/deep-eql. * - * expect({ foo: 'bar' }).to.eql({ foo: 'bar' }); - * expect([ 1, 2, 3 ]).to.eql([ 1, 2, 3 ]); + * // Target object is deeply (but not strictly) equal to {a: 1} + * expect({a: 1}).to.eql({a: 1}).but.not.equal({a: 1}); + * + * // Target array is deeply (but not strictly) equal to [1, 2] + * expect([1, 2]).to.eql([1, 2]).but.not.equal([1, 2]); + * + * Add `.not` earlier in the chain to negate `.eql`. However, it's often best + * to assert that the target is deeply equal to its expected value, rather + * than not deeply equal to one of countless unexpected values. + * + * expect({a: 1}).to.eql({a: 1}); // Recommended + * expect({a: 1}).to.not.eql({b: 2}); // Not recommended + * + * `.eql` accepts an optional `msg` argument which is a custom error message + * to show when the assertion fails. The message can also be given as the + * second argument to `expect`. + * + * expect({a: 1}).to.eql({b: 2}, 'nooo why fail??'); + * expect({a: 1}, 'nooo why fail??').to.eql({b: 2}); + * + * The alias `.eqls` can be used interchangeably with `.eql`. + * + * The `.deep.equal` assertion is almost identical to `.eql` but with one + * difference: `.deep.equal` causes deep equality comparisons to also be used + * for any other assertions that follow in the chain. * * @name eql * @alias eqls - * @param {Mixed} value - * @param {String} message _optional_ + * @param {Mixed} obj + * @param {String} msg _optional_ * @namespace BDD * @api public */ @@ -820,34 +1383,71 @@ module.exports = function (chai, _) { Assertion.addMethod('eqls', assertEql); /** - * ### .above(value) + * ### .above(n[, msg]) * - * Asserts that the target is greater than `value`. + * Asserts that the target is a number greater than the given number `n`. + * However, it's often best to assert that the target is equal to its expected + * value. * - * expect(10).to.be.above(5); + * expect(2).to.equal(2); // Recommended + * expect(2).to.be.above(1); // Not recommended * - * Can also be used in conjunction with `length` to - * assert a minimum length. The benefit being a - * more informative error message than if the length - * was supplied directly. + * Add `.lengthOf` earlier in the chain to assert that the value of the + * target's `length` property is greater than the given number `n`. * - * expect('foo').to.have.length.above(2); - * expect([ 1, 2, 3 ]).to.have.length.above(2); + * expect('foo').to.have.lengthOf(3); // Recommended + * expect('foo').to.have.lengthOf.above(2); // Not recommended + * + * expect([1, 2, 3]).to.have.lengthOf(3); // Recommended + * expect([1, 2, 3]).to.have.lengthOf.above(2); // Not recommended + * + * Add `.not` earlier in the chain to negate `.above`. + * + * expect(2).to.equal(2); // Recommended + * expect(1).to.not.be.above(2); // Not recommended + * + * `.above` accepts an optional `msg` argument which is a custom error message + * to show when the assertion fails. The message can also be given as the + * second argument to `expect`. + * + * expect(1).to.be.above(2, 'nooo why fail??'); + * expect(1, 'nooo why fail??').to.be.above(2); + * + * The aliases `.gt` and `.greaterThan` can be used interchangeably with + * `.above`. * * @name above * @alias gt * @alias greaterThan - * @param {Number} value - * @param {String} message _optional_ + * @param {Number} n + * @param {String} msg _optional_ * @namespace BDD * @api public */ function assertAbove (n, msg) { if (msg) flag(this, 'message', msg); - var obj = flag(this, 'object'); - if (flag(this, 'doLength')) { - new Assertion(obj, msg).to.have.property('length'); + var obj = flag(this, 'object') + , doLength = flag(this, 'doLength') + , flagMsg = flag(this, 'message') + , ssfi = flag(this, 'ssfi'); + + if (doLength) { + new Assertion(obj, flagMsg, ssfi, true).to.have.property('length'); + } else { + new Assertion(obj, flagMsg, ssfi, true).is.a('number'); + } + + if (typeof n !== 'number') { + flagMsg = flagMsg ? flagMsg + ': ' : ''; + throw new AssertionError( + flagMsg + 'the argument to above must be a number', + undefined, + ssfi + ); + } + + if (doLength) { var len = obj.length; this.assert( len > n @@ -870,33 +1470,71 @@ module.exports = function (chai, _) { Assertion.addMethod('greaterThan', assertAbove); /** - * ### .least(value) + * ### .least(n[, msg]) * - * Asserts that the target is greater than or equal to `value`. + * Asserts that the target is a number greater than or equal to the given + * number `n`. However, it's often best to assert that the target is equal to + * its expected value. * - * expect(10).to.be.at.least(10); + * expect(2).to.equal(2); // Recommended + * expect(2).to.be.at.least(1); // Not recommended + * expect(2).to.be.at.least(2); // Not recommended * - * Can also be used in conjunction with `length` to - * assert a minimum length. The benefit being a - * more informative error message than if the length - * was supplied directly. + * Add `.lengthOf` earlier in the chain to assert that the value of the + * target's `length` property is greater than or equal to the given number + * `n`. * - * expect('foo').to.have.length.of.at.least(2); - * expect([ 1, 2, 3 ]).to.have.length.of.at.least(3); + * expect('foo').to.have.lengthOf(3); // Recommended + * expect('foo').to.have.lengthOf.at.least(2); // Not recommended + * + * expect([1, 2, 3]).to.have.lengthOf(3); // Recommended + * expect([1, 2, 3]).to.have.lengthOf.at.least(2); // Not recommended + * + * Add `.not` earlier in the chain to negate `.least`. + * + * expect(1).to.equal(1); // Recommended + * expect(1).to.not.be.at.least(2); // Not recommended + * + * `.least` accepts an optional `msg` argument which is a custom error message + * to show when the assertion fails. The message can also be given as the + * second argument to `expect`. + * + * expect(1).to.be.at.least(2, 'nooo why fail??'); + * expect(1, 'nooo why fail??').to.be.at.least(2); + * + * The alias `.gte` can be used interchangeably with `.least`. * * @name least * @alias gte - * @param {Number} value - * @param {String} message _optional_ + * @param {Number} n + * @param {String} msg _optional_ * @namespace BDD * @api public */ function assertLeast (n, msg) { if (msg) flag(this, 'message', msg); - var obj = flag(this, 'object'); - if (flag(this, 'doLength')) { - new Assertion(obj, msg).to.have.property('length'); + var obj = flag(this, 'object') + , doLength = flag(this, 'doLength') + , flagMsg = flag(this, 'message') + , ssfi = flag(this, 'ssfi'); + + if (doLength) { + new Assertion(obj, flagMsg, ssfi, true).to.have.property('length'); + } else { + new Assertion(obj, flagMsg, ssfi, true).is.a('number'); + } + + if (typeof n !== 'number') { + flagMsg = flagMsg ? flagMsg + ': ' : ''; + throw new AssertionError( + flagMsg + 'the argument to least must be a number', + undefined, + ssfi + ); + } + + if (doLength) { var len = obj.length; this.assert( len >= n @@ -918,34 +1556,71 @@ module.exports = function (chai, _) { Assertion.addMethod('gte', assertLeast); /** - * ### .below(value) + * ### .below(n[, msg]) * - * Asserts that the target is less than `value`. + * Asserts that the target is a number less than the given number `n`. + * However, it's often best to assert that the target is equal to its expected + * value. * - * expect(5).to.be.below(10); + * expect(1).to.equal(1); // Recommended + * expect(1).to.be.below(2); // Not recommended * - * Can also be used in conjunction with `length` to - * assert a maximum length. The benefit being a - * more informative error message than if the length - * was supplied directly. + * Add `.lengthOf` earlier in the chain to assert that the value of the + * target's `length` property is less than the given number `n`. * - * expect('foo').to.have.length.below(4); - * expect([ 1, 2, 3 ]).to.have.length.below(4); + * expect('foo').to.have.lengthOf(3); // Recommended + * expect('foo').to.have.lengthOf.below(4); // Not recommended + * + * expect([1, 2, 3]).to.have.length(3); // Recommended + * expect([1, 2, 3]).to.have.lengthOf.below(4); // Not recommended + * + * Add `.not` earlier in the chain to negate `.below`. + * + * expect(2).to.equal(2); // Recommended + * expect(2).to.not.be.below(1); // Not recommended + * + * `.below` accepts an optional `msg` argument which is a custom error message + * to show when the assertion fails. The message can also be given as the + * second argument to `expect`. + * + * expect(2).to.be.below(1, 'nooo why fail??'); + * expect(2, 'nooo why fail??').to.be.below(1); + * + * The aliases `.lt` and `.lessThan` can be used interchangeably with + * `.below`. * * @name below * @alias lt * @alias lessThan - * @param {Number} value - * @param {String} message _optional_ + * @param {Number} n + * @param {String} msg _optional_ * @namespace BDD * @api public */ function assertBelow (n, msg) { if (msg) flag(this, 'message', msg); - var obj = flag(this, 'object'); - if (flag(this, 'doLength')) { - new Assertion(obj, msg).to.have.property('length'); + var obj = flag(this, 'object') + , doLength = flag(this, 'doLength') + , flagMsg = flag(this, 'message') + , ssfi = flag(this, 'ssfi'); + + if (doLength) { + new Assertion(obj, flagMsg, ssfi, true).to.have.property('length'); + } else { + new Assertion(obj, flagMsg, ssfi, true).is.a('number'); + } + + if (typeof n !== 'number') { + flagMsg = flagMsg ? flagMsg + ': ' : ''; + throw new AssertionError( + flagMsg + 'the argument to below must be a number', + undefined, + ssfi + ); + } + + if (doLength) { var len = obj.length; this.assert( len < n @@ -968,33 +1643,70 @@ module.exports = function (chai, _) { Assertion.addMethod('lessThan', assertBelow); /** - * ### .most(value) + * ### .most(n[, msg]) * - * Asserts that the target is less than or equal to `value`. + * Asserts that the target is a number less than or equal to the given number + * `n`. However, it's often best to assert that the target is equal to its + * expected value. * - * expect(5).to.be.at.most(5); + * expect(1).to.equal(1); // Recommended + * expect(1).to.be.at.most(2); // Not recommended + * expect(1).to.be.at.most(1); // Not recommended * - * Can also be used in conjunction with `length` to - * assert a maximum length. The benefit being a - * more informative error message than if the length - * was supplied directly. + * Add `.lengthOf` earlier in the chain to assert that the value of the + * target's `length` property is less than or equal to the given number `n`. * - * expect('foo').to.have.length.of.at.most(4); - * expect([ 1, 2, 3 ]).to.have.length.of.at.most(3); + * expect('foo').to.have.lengthOf(3); // Recommended + * expect('foo').to.have.lengthOf.at.most(4); // Not recommended + * + * expect([1, 2, 3]).to.have.lengthOf(3); // Recommended + * expect([1, 2, 3]).to.have.lengthOf.at.most(4); // Not recommended + * + * Add `.not` earlier in the chain to negate `.most`. + * + * expect(2).to.equal(2); // Recommended + * expect(2).to.not.be.at.most(1); // Not recommended + * + * `.most` accepts an optional `msg` argument which is a custom error message + * to show when the assertion fails. The message can also be given as the + * second argument to `expect`. + * + * expect(2).to.be.at.most(1, 'nooo why fail??'); + * expect(2, 'nooo why fail??').to.be.at.most(1); + * + * The alias `.lte` can be used interchangeably with `.most`. * * @name most * @alias lte - * @param {Number} value - * @param {String} message _optional_ + * @param {Number} n + * @param {String} msg _optional_ * @namespace BDD * @api public */ function assertMost (n, msg) { if (msg) flag(this, 'message', msg); - var obj = flag(this, 'object'); - if (flag(this, 'doLength')) { - new Assertion(obj, msg).to.have.property('length'); + var obj = flag(this, 'object') + , doLength = flag(this, 'doLength') + , flagMsg = flag(this, 'message') + , ssfi = flag(this, 'ssfi'); + + if (doLength) { + new Assertion(obj, flagMsg, ssfi, true).to.have.property('length'); + } else { + new Assertion(obj, flagMsg, ssfi, true).is.a('number'); + } + + if (typeof n !== 'number') { + flagMsg = flagMsg ? flagMsg + ': ' : ''; + throw new AssertionError( + flagMsg + 'the argument to most must be a number', + undefined, + ssfi + ); + } + + if (doLength) { var len = obj.length; this.assert( len <= n @@ -1016,24 +1728,44 @@ module.exports = function (chai, _) { Assertion.addMethod('lte', assertMost); /** - * ### .within(start, finish) + * ### .within(start, finish[, msg]) * - * Asserts that the target is within a range. + * Asserts that the target is a number greater than or equal to the given + * number `start`, and less than or equal to the given number `finish`. + * However, it's often best to assert that the target is equal to its expected + * value. * - * expect(7).to.be.within(5,10); + * expect(2).to.equal(2); // Recommended + * expect(2).to.be.within(1, 3); // Not recommended + * expect(2).to.be.within(2, 3); // Not recommended + * expect(2).to.be.within(1, 2); // Not recommended * - * Can also be used in conjunction with `length` to - * assert a length range. The benefit being a - * more informative error message than if the length - * was supplied directly. + * Add `.lengthOf` earlier in the chain to assert that the value of the + * target's `length` property is greater than or equal to the given number + * `start`, and less than or equal to the given number `finish`. * - * expect('foo').to.have.length.within(2,4); - * expect([ 1, 2, 3 ]).to.have.length.within(2,4); + * expect('foo').to.have.lengthOf(3); // Recommended + * expect('foo').to.have.lengthOf.within(2, 4); // Not recommended + * + * expect([1, 2, 3]).to.have.lengthOf(3); // Recommended + * expect([1, 2, 3]).to.have.lengthOf.within(2, 4); // Not recommended + * + * Add `.not` earlier in the chain to negate `.within`. + * + * expect(1).to.equal(1); // Recommended + * expect(1).to.not.be.within(2, 4); // Not recommended + * + * `.within` accepts an optional `msg` argument which is a custom error + * message to show when the assertion fails. The message can also be given as + * the second argument to `expect`. + * + * expect(4).to.be.within(1, 3, 'nooo why fail??'); + * expect(4, 'nooo why fail??').to.be.within(1, 3); * * @name within - * @param {Number} start lowerbound inclusive - * @param {Number} finish upperbound inclusive - * @param {String} message _optional_ + * @param {Number} start lower bound inclusive + * @param {Number} finish upper bound inclusive + * @param {String} msg _optional_ * @namespace BDD * @api public */ @@ -1041,9 +1773,27 @@ module.exports = function (chai, _) { Assertion.addMethod('within', function (start, finish, msg) { if (msg) flag(this, 'message', msg); var obj = flag(this, 'object') - , range = start + '..' + finish; - if (flag(this, 'doLength')) { - new Assertion(obj, msg).to.have.property('length'); + , range = start + '..' + finish + , doLength = flag(this, 'doLength') + , flagMsg = flag(this, 'message') + , ssfi = flag(this, 'ssfi'); + + if (doLength) { + new Assertion(obj, flagMsg, ssfi, true).to.have.property('length'); + } else { + new Assertion(obj, flagMsg, ssfi, true).is.a('number'); + } + + if (typeof start !== 'number' || typeof finish !== 'number') { + flagMsg = flagMsg ? flagMsg + ': ' : ''; + throw new AssertionError( + flagMsg + 'the arguments to within must be numbers', + undefined, + ssfi + ); + } + + if (doLength) { var len = obj.length; this.assert( len >= start && len <= finish @@ -1060,19 +1810,39 @@ module.exports = function (chai, _) { }); /** - * ### .instanceof(constructor) + * ### .instanceof(constructor[, msg]) * - * Asserts that the target is an instance of `constructor`. + * Asserts that the target is an instance of the given `constructor`. * - * var Tea = function (name) { this.name = name; } - * , Chai = new Tea('chai'); + * function Cat () { } * - * expect(Chai).to.be.an.instanceof(Tea); - * expect([ 1, 2, 3 ]).to.be.instanceof(Array); + * expect(new Cat()).to.be.an.instanceof(Cat); + * expect([1, 2]).to.be.an.instanceof(Array); + * + * Add `.not` earlier in the chain to negate `.instanceof`. + * + * expect({a: 1}).to.not.be.an.instanceof(Array); + * + * `.instanceof` accepts an optional `msg` argument which is a custom error + * message to show when the assertion fails. The message can also be given as + * the second argument to `expect`. + * + * expect(1).to.be.an.instanceof(Array, 'nooo why fail??'); + * expect(1, 'nooo why fail??').to.be.an.instanceof(Array); + * + * Due to limitations in ES5, `.instanceof` may not always work as expected + * when using a transpiler such as Babel or TypeScript. In particular, it may + * produce unexpected results when subclassing built-in object such as + * `Array`, `Error`, and `Map`. See your transpiler's docs for details: + * + * - ([Babel](https://babeljs.io/docs/usage/caveats/#classes)) + * - ([TypeScript](https://github.com/Microsoft/TypeScript/wiki/Breaking-Changes#extending-built-ins-like-error-array-and-map-may-no-longer-work)) + * + * The alias `.instanceOf` can be used interchangeably with `.instanceof`. * * @name instanceof * @param {Constructor} constructor - * @param {String} message _optional_ + * @param {String} msg _optional_ * @alias instanceOf * @namespace BDD * @api public @@ -1080,9 +1850,36 @@ module.exports = function (chai, _) { function assertInstanceOf (constructor, msg) { if (msg) flag(this, 'message', msg); + + var target = flag(this, 'object') + var ssfi = flag(this, 'ssfi'); + var flagMsg = flag(this, 'message'); + var validInstanceOfTarget = constructor === Object(constructor) && ( + typeof constructor === 'function' || + (typeof Symbol !== 'undefined' && + typeof Symbol.hasInstance !== 'undefined' && + Symbol.hasInstance in constructor) + ); + + if (!validInstanceOfTarget) { + flagMsg = flagMsg ? flagMsg + ': ' : ''; + var constructorType = constructor === null ? 'null' : typeof constructor; + throw new AssertionError( + flagMsg + 'The instanceof assertion needs a constructor but ' + constructorType + ' was given.', + undefined, + ssfi + ); + } + + var isInstanceOf = target instanceof constructor + var name = _.getName(constructor); + if (name === null) { + name = 'an unnamed constructor'; + } + this.assert( - flag(this, 'object') instanceof constructor + isInstanceOf , 'expected #{this} to be an instance of ' + name , 'expected #{this} to not be an instance of ' + name ); @@ -1092,162 +1889,300 @@ module.exports = function (chai, _) { Assertion.addMethod('instanceOf', assertInstanceOf); /** - * ### .property(name, [value]) + * ### .property(name[, val[, msg]]) * - * Asserts that the target has a property `name`, optionally asserting that - * the value of that property is strictly equal to `value`. - * If the `deep` flag is set, you can use dot- and bracket-notation for deep - * references into objects and arrays. + * Asserts that the target has a property with the given key `name`. * - * // simple referencing - * var obj = { foo: 'bar' }; - * expect(obj).to.have.property('foo'); - * expect(obj).to.have.property('foo', 'bar'); + * expect({a: 1}).to.have.property('a'); * - * // deep referencing - * var deepObj = { - * green: { tea: 'matcha' } - * , teas: [ 'chai', 'matcha', { tea: 'konacha' } ] - * }; + * When `val` is provided, `.property` also asserts that the property's value + * is equal to the given `val`. * - * expect(deepObj).to.have.deep.property('green.tea', 'matcha'); - * expect(deepObj).to.have.deep.property('teas[1]', 'matcha'); - * expect(deepObj).to.have.deep.property('teas[2].tea', 'konacha'); + * expect({a: 1}).to.have.property('a', 1); * - * You can also use an array as the starting point of a `deep.property` - * assertion, or traverse nested arrays. + * By default, strict (`===`) equality is used. Add `.deep` earlier in the + * chain to use deep equality instead. See the `deep-eql` project page for + * info on the deep equality algorithm: https://github.com/chaijs/deep-eql. * - * var arr = [ - * [ 'chai', 'matcha', 'konacha' ] - * , [ { tea: 'chai' } - * , { tea: 'matcha' } - * , { tea: 'konacha' } ] - * ]; + * // Target object deeply (but not strictly) has property `x: {a: 1}` + * expect({x: {a: 1}}).to.have.deep.property('x', {a: 1}); + * expect({x: {a: 1}}).to.not.have.property('x', {a: 1}); * - * expect(arr).to.have.deep.property('[0][1]', 'matcha'); - * expect(arr).to.have.deep.property('[1][2].tea', 'konacha'); + * The target's enumerable and non-enumerable properties are always included + * in the search. By default, both own and inherited properties are included. + * Add `.own` earlier in the chain to exclude inherited properties from the + * search. * - * 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. + * Object.prototype.b = 2; * - * 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.deep.property('[2]') - * .that.deep.equals({ tea: 'konacha' }); + * expect({a: 1}).to.have.own.property('a'); + * expect({a: 1}).to.have.own.property('a', 1); + * expect({a: 1}).to.have.property('b').but.not.own.property('b'); * - * Note that dots and bracket in `name` must be backslash-escaped when - * the `deep` flag is set, while they must NOT be escaped when the `deep` - * flag is not set. + * `.deep` and `.own` can be combined. * - * // simple referencing - * var css = { '.link[target]': 42 }; - * expect(css).to.have.property('.link[target]', 42); + * expect({x: {a: 1}}).to.have.deep.own.property('x', {a: 1}); * - * // deep referencing - * var deepCss = { '.link': { '[target]': 42 }}; - * expect(deepCss).to.have.deep.property('\\.link.\\[target\\]', 42); + * Add `.nested` earlier in the chain to enable dot- and bracket-notation when + * referencing nested properties. + * + * expect({a: {b: ['x', 'y']}}).to.have.nested.property('a.b[1]'); + * expect({a: {b: ['x', 'y']}}).to.have.nested.property('a.b[1]', 'y'); + * + * If `.` or `[]` are part of an actual property name, they can be escaped by + * adding two backslashes before them. + * + * expect({'.a': {'[b]': 'x'}}).to.have.nested.property('\\.a.\\[b\\]'); + * + * `.deep` and `.nested` can be combined. + * + * expect({a: {b: [{c: 3}]}}) + * .to.have.deep.nested.property('a.b[0]', {c: 3}); + * + * `.own` and `.nested` cannot be combined. + * + * Add `.not` earlier in the chain to negate `.property`. + * + * expect({a: 1}).to.not.have.property('b'); + * + * However, it's dangerous to negate `.property` when providing `val`. The + * problem is that it creates uncertain expectations by asserting that the + * target either doesn't have a property with the given key `name`, or that it + * does have a property with the given key `name` but its value isn't equal to + * the given `val`. It's often best to identify the exact output that's + * expected, and then write an assertion that only accepts that exact output. + * + * When the target isn't expected to have a property with the given key + * `name`, it's often best to assert exactly that. + * + * expect({b: 2}).to.not.have.property('a'); // Recommended + * expect({b: 2}).to.not.have.property('a', 1); // Not recommended + * + * When the target is expected to have a property with the given key `name`, + * it's often best to assert that the property has its expected value, rather + * than asserting that it doesn't have one of many unexpected values. + * + * expect({a: 3}).to.have.property('a', 3); // Recommended + * expect({a: 3}).to.not.have.property('a', 1); // Not recommended + * + * `.property` changes the target of any assertions that follow in the chain + * to be the value of the property from the original target object. + * + * expect({a: 1}).to.have.property('a').that.is.a('number'); + * + * `.property` accepts an optional `msg` argument which is a custom error + * message to show when the assertion fails. The message can also be given as + * the second argument to `expect`. When not providing `val`, only use the + * second form. + * + * // Recommended + * expect({a: 1}).to.have.property('a', 2, 'nooo why fail??'); + * expect({a: 1}, 'nooo why fail??').to.have.property('a', 2); + * expect({a: 1}, 'nooo why fail??').to.have.property('b'); + * + * // Not recommended + * expect({a: 1}).to.have.property('b', undefined, 'nooo why fail??'); + * + * The above assertion isn't the same thing as not providing `val`. Instead, + * it's asserting that the target object has a `b` property that's equal to + * `undefined`. + * + * The assertions `.ownProperty` and `.haveOwnProperty` can be used + * interchangeably with `.own.property`. * * @name property - * @alias deep.property * @param {String} name - * @param {Mixed} value (optional) - * @param {String} message _optional_ + * @param {Mixed} val (optional) + * @param {String} msg _optional_ * @returns value of property for chaining * @namespace BDD * @api public */ - Assertion.addMethod('property', function (name, val, msg) { + function assertProperty (name, val, msg) { if (msg) flag(this, 'message', msg); - var isDeep = !!flag(this, 'deep') - , descriptor = isDeep ? 'deep property ' : 'property ' + var isNested = flag(this, 'nested') + , isOwn = flag(this, 'own') + , flagMsg = flag(this, 'message') + , ssfi = flag(this, 'ssfi'); + + if (isNested && isOwn) { + flagMsg = flagMsg ? flagMsg + ': ' : ''; + throw new AssertionError( + flagMsg + 'The "nested" and "own" flags cannot be combined.', + undefined, + ssfi + ); + } + + var isDeep = flag(this, 'deep') , negate = flag(this, 'negate') , obj = flag(this, 'object') - , pathInfo = isDeep ? _.getPathInfo(name, obj) : null - , hasProperty = isDeep - ? pathInfo.exists - : _.hasProperty(name, obj) - , value = isDeep - ? pathInfo.value - : obj[name]; + , pathInfo = isNested ? _.getPathInfo(obj, name) : null + , value = isNested ? 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 { + 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(obj, name); + + // 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) + , 'expected #{this} to have ' + descriptor + _.inspect(name) , 'expected #{this} to not have ' + descriptor + _.inspect(name)); } if (arguments.length > 1) { this.assert( - 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}' + hasProperty && (isDeep ? _.eql(val, value) : val === value) + , 'expected #{this} to have ' + descriptor + _.inspect(name) + ' of #{exp}, but got #{act}' + , 'expected #{this} to not have ' + descriptor + _.inspect(name) + ' of #{act}' , val , value ); } flag(this, 'object', value); - }); + } + Assertion.addMethod('property', assertProperty); - /** - * ### .ownProperty(name) - * - * Asserts that the target has an own property `name`. - * - * expect('test').to.have.ownProperty('length'); - * - * @name ownProperty - * @alias haveOwnProperty - * @param {String} name - * @param {String} message _optional_ - * @namespace BDD - * @api public - */ - - function assertOwnProperty (name, msg) { - if (msg) flag(this, 'message', msg); - var obj = flag(this, 'object'); - this.assert( - obj.hasOwnProperty(name) - , 'expected #{this} to have own property ' + _.inspect(name) - , 'expected #{this} to not have own property ' + _.inspect(name) - ); + function assertOwnProperty (name, value, msg) { + flag(this, 'own', true); + assertProperty.apply(this, arguments); } Assertion.addMethod('ownProperty', assertOwnProperty); Assertion.addMethod('haveOwnProperty', assertOwnProperty); /** - * ### .ownPropertyDescriptor(name[, descriptor[, message]]) + * ### .ownPropertyDescriptor(name[, descriptor[, msg]]) * - * Asserts that the target has an own property descriptor `name`, that optionally matches `descriptor`. + * Asserts that the target has its own property descriptor with the given key + * `name`. Enumerable and non-enumerable properties are included in the + * search. * - * expect('test').to.have.ownPropertyDescriptor('length'); - * expect('test').to.have.ownPropertyDescriptor('length', { enumerable: false, configurable: false, writable: false, value: 4 }); - * expect('test').not.to.have.ownPropertyDescriptor('length', { enumerable: false, configurable: false, writable: false, value: 3 }); - * expect('test').ownPropertyDescriptor('length').to.have.property('enumerable', false); - * expect('test').ownPropertyDescriptor('length').to.have.keys('value'); + * expect({a: 1}).to.have.ownPropertyDescriptor('a'); + * + * When `descriptor` is provided, `.ownPropertyDescriptor` also asserts that + * the property's descriptor is deeply equal to the given `descriptor`. See + * the `deep-eql` project page for info on the deep equality algorithm: + * https://github.com/chaijs/deep-eql. + * + * expect({a: 1}).to.have.ownPropertyDescriptor('a', { + * configurable: true, + * enumerable: true, + * writable: true, + * value: 1, + * }); + * + * Add `.not` earlier in the chain to negate `.ownPropertyDescriptor`. + * + * expect({a: 1}).to.not.have.ownPropertyDescriptor('b'); + * + * However, it's dangerous to negate `.ownPropertyDescriptor` when providing + * a `descriptor`. The problem is that it creates uncertain expectations by + * asserting that the target either doesn't have a property descriptor with + * the given key `name`, or that it does have a property descriptor with the + * given key `name` but its not deeply equal to the given `descriptor`. It's + * often best to identify the exact output that's expected, and then write an + * assertion that only accepts that exact output. + * + * When the target isn't expected to have a property descriptor with the given + * key `name`, it's often best to assert exactly that. + * + * // Recommended + * expect({b: 2}).to.not.have.ownPropertyDescriptor('a'); + * + * // Not recommended + * expect({b: 2}).to.not.have.ownPropertyDescriptor('a', { + * configurable: true, + * enumerable: true, + * writable: true, + * value: 1, + * }); + * + * When the target is expected to have a property descriptor with the given + * key `name`, it's often best to assert that the property has its expected + * descriptor, rather than asserting that it doesn't have one of many + * unexpected descriptors. + * + * // Recommended + * expect({a: 3}).to.have.ownPropertyDescriptor('a', { + * configurable: true, + * enumerable: true, + * writable: true, + * value: 3, + * }); + * + * // Not recommended + * expect({a: 3}).to.not.have.ownPropertyDescriptor('a', { + * configurable: true, + * enumerable: true, + * writable: true, + * value: 1, + * }); + * + * `.ownPropertyDescriptor` changes the target of any assertions that follow + * in the chain to be the value of the property descriptor from the original + * target object. + * + * expect({a: 1}).to.have.ownPropertyDescriptor('a') + * .that.has.property('enumerable', true); + * + * `.ownPropertyDescriptor` accepts an optional `msg` argument which is a + * custom error message to show when the assertion fails. The message can also + * be given as the second argument to `expect`. When not providing + * `descriptor`, only use the second form. + * + * // Recommended + * expect({a: 1}).to.have.ownPropertyDescriptor('a', { + * configurable: true, + * enumerable: true, + * writable: true, + * value: 2, + * }, 'nooo why fail??'); + * + * // Recommended + * expect({a: 1}, 'nooo why fail??').to.have.ownPropertyDescriptor('a', { + * configurable: true, + * enumerable: true, + * writable: true, + * value: 2, + * }); + * + * // Recommended + * expect({a: 1}, 'nooo why fail??').to.have.ownPropertyDescriptor('b'); + * + * // Not recommended + * expect({a: 1}) + * .to.have.ownPropertyDescriptor('b', undefined, 'nooo why fail??'); + * + * The above assertion isn't the same thing as not providing `descriptor`. + * Instead, it's asserting that the target object has a `b` property + * descriptor that's deeply equal to `undefined`. + * + * The alias `.haveOwnPropertyDescriptor` can be used interchangeably with + * `.ownPropertyDescriptor`. * * @name ownPropertyDescriptor * @alias haveOwnPropertyDescriptor * @param {String} name * @param {Object} descriptor _optional_ - * @param {String} message _optional_ + * @param {String} msg _optional_ * @namespace BDD * @api public */ @@ -1283,40 +2218,57 @@ module.exports = function (chai, _) { Assertion.addMethod('haveOwnPropertyDescriptor', assertOwnPropertyDescriptor); /** - * ### .length + * ### .lengthOf(n[, msg]) * - * Sets the `doLength` flag later used as a chain precursor to a value - * comparison for the `length` property. + * Asserts that the target's `length` property is equal to the given number + * `n`. * - * expect('foo').to.have.length.above(2); - * expect([ 1, 2, 3 ]).to.have.length.above(2); - * expect('foo').to.have.length.below(4); - * expect([ 1, 2, 3 ]).to.have.length.below(4); - * expect('foo').to.have.length.within(2,4); - * expect([ 1, 2, 3 ]).to.have.length.within(2,4); + * expect([1, 2, 3]).to.have.lengthOf(3); + * expect('foo').to.have.lengthOf(3); * - * *Deprecation notice:* Using `length` as an assertion will be deprecated - * in version 2.4.0 and removed in 3.0.0. Code using the old style of - * asserting for `length` property value using `length(value)` should be - * switched to use `lengthOf(value)` instead. + * Add `.not` earlier in the chain to negate `.lengthOf`. However, it's often + * best to assert that the target's `length` property is equal to its expected + * value, rather than not equal to one of many unexpected values. * - * @name length - * @namespace BDD - * @api public - */ - - /** - * ### .lengthOf(value[, message]) + * expect('foo').to.have.lengthOf(3); // Recommended + * expect('foo').to.not.have.lengthOf(4); // Not recommended * - * Asserts that the target's `length` property has - * the expected value. + * `.lengthOf` accepts an optional `msg` argument which is a custom error + * message to show when the assertion fails. The message can also be given as + * the second argument to `expect`. * - * expect([ 1, 2, 3]).to.have.lengthOf(3); - * expect('foobar').to.have.lengthOf(6); + * expect([1, 2, 3]).to.have.lengthOf(2, 'nooo why fail??'); + * expect([1, 2, 3], 'nooo why fail??').to.have.lengthOf(2); + * + * `.lengthOf` can also be used as a language chain, causing all `.above`, + * `.below`, `.least`, `.most`, and `.within` assertions that follow in the + * chain to use the target's `length` property as the target. However, it's + * often best to assert that the target's `length` property is equal to its + * expected length, rather than asserting that its `length` property falls + * within some range of values. + * + * // Recommended + * expect([1, 2, 3]).to.have.lengthOf(3); + * + * // Not recommended + * expect([1, 2, 3]).to.have.lengthOf.above(2); + * expect([1, 2, 3]).to.have.lengthOf.below(4); + * expect([1, 2, 3]).to.have.lengthOf.at.least(3); + * expect([1, 2, 3]).to.have.lengthOf.at.most(3); + * expect([1, 2, 3]).to.have.lengthOf.within(2,4); + * + * Due to a compatibility issue, the alias `.length` can't be chained directly + * off of an uninvoked method such as `.a`. Therefore, `.length` can't be used + * interchangeably with `.lengthOf` in every situation. It's recommended to + * always use `.lengthOf` instead of `.length`. + * + * expect([1, 2, 3]).to.have.a.length(3); // incompatible; throws error + * expect([1, 2, 3]).to.have.a.lengthOf(3); // passes as expected * * @name lengthOf - * @param {Number} length - * @param {String} message _optional_ + * @alias length + * @param {Number} n + * @param {String} msg _optional_ * @namespace BDD * @api public */ @@ -1327,8 +2279,10 @@ module.exports = function (chai, _) { function assertLength (n, msg) { if (msg) flag(this, 'message', msg); - var obj = flag(this, 'object'); - new Assertion(obj, msg).to.have.property('length'); + var obj = flag(this, 'object') + , flagMsg = flag(this, 'message') + , ssfi = flag(this, 'ssfi'); + new Assertion(obj, flagMsg, ssfi, true).to.have.property('length'); var len = obj.length; this.assert( @@ -1341,19 +2295,32 @@ module.exports = function (chai, _) { } Assertion.addChainableMethod('length', assertLength, assertLengthChain); - Assertion.addMethod('lengthOf', assertLength); + Assertion.addChainableMethod('lengthOf', assertLength, assertLengthChain); /** - * ### .match(regexp) + * ### .match(re[, msg]) * - * Asserts that the target matches a regular expression. + * Asserts that the target matches the given regular expression `re`. * * expect('foobar').to.match(/^foo/); * + * Add `.not` earlier in the chain to negate `.match`. + * + * expect('foobar').to.not.match(/taco/); + * + * `.match` accepts an optional `msg` argument which is a custom error message + * to show when the assertion fails. The message can also be given as the + * second argument to `expect`. + * + * expect('foobar').to.match(/taco/, 'nooo why fail??'); + * expect('foobar', 'nooo why fail??').to.match(/taco/); + * + * The alias `.matches` can be used interchangeably with `.match`. + * * @name match * @alias matches - * @param {RegExp} RegularExpression - * @param {String} message _optional_ + * @param {RegExp} re + * @param {String} msg _optional_ * @namespace BDD * @api public */ @@ -1371,23 +2338,36 @@ module.exports = function (chai, _) { Assertion.addMethod('matches', assertMatch); /** - * ### .string(string) + * ### .string(str[, msg]) * - * Asserts that the string target contains another string. + * Asserts that the target string contains the given substring `str`. * * expect('foobar').to.have.string('bar'); * + * Add `.not` earlier in the chain to negate `.string`. + * + * expect('foobar').to.not.have.string('taco'); + * + * `.string` accepts an optional `msg` argument which is a custom error + * message to show when the assertion fails. The message can also be given as + * the second argument to `expect`. + * + * expect('foobar').to.have.string(/taco/, 'nooo why fail??'); + * expect('foobar', 'nooo why fail??').to.have.string(/taco/); + * * @name string - * @param {String} string - * @param {String} message _optional_ + * @param {String} str + * @param {String} msg _optional_ * @namespace BDD * @api public */ Assertion.addMethod('string', function (str, msg) { if (msg) flag(this, 'message', msg); - var obj = flag(this, 'object'); - new Assertion(obj, msg).is.a('string'); + var obj = flag(this, 'object') + , flagMsg = flag(this, 'message') + , ssfi = flag(this, 'ssfi'); + new Assertion(obj, flagMsg, ssfi, true).is.a('string'); this.assert( ~obj.indexOf(str) @@ -1396,38 +2376,102 @@ module.exports = function (chai, _) { ); }); - /** - * ### .keys(key1, [key2], [...]) + * ### .keys(key1[, key2[, ...]]) * - * Asserts that the target contains any or all of the passed-in keys. - * Use in combination with `any`, `all`, `contains`, or `have` will affect - * what will pass. + * Asserts that the target object, array, map, or set has the given keys. Only + * the target's own inherited properties are included in the search. * - * When used in conjunction with `any`, at least one key that is passed - * in must exist in the target object. This is regardless whether or not - * the `have` or `contain` qualifiers are used. Note, either `any` or `all` - * should be used in the assertion. If neither are used, the assertion is - * defaulted to `all`. + * When the target is an object or array, keys can be provided as one or more + * string arguments, a single array argument, or a single object argument. In + * the latter case, only the keys in the given object matter; the values are + * ignored. * - * When both `all` and `contain` are used, the target object must have at - * least all of the passed-in keys but may have more keys not listed. + * expect({a: 1, b: 2}).to.have.all.keys('a', 'b'); + * expect(['x', 'y']).to.have.all.keys(0, 1); * - * When both `all` and `have` are used, the target object must both contain - * all of the passed-in keys AND the number of keys in the target object must - * match the number of keys passed in (in other words, a target object must - * have all and only all of the passed-in keys). + * expect({a: 1, b: 2}).to.have.all.keys(['a', 'b']); + * expect(['x', 'y']).to.have.all.keys([0, 1]); * - * expect({ foo: 1, bar: 2 }).to.have.any.keys('foo', 'baz'); - * expect({ foo: 1, bar: 2 }).to.have.any.keys('foo'); - * expect({ foo: 1, bar: 2 }).to.contain.any.keys('bar', 'baz'); - * expect({ foo: 1, bar: 2 }).to.contain.any.keys(['foo']); - * expect({ foo: 1, bar: 2 }).to.contain.any.keys({'foo': 6}); - * expect({ foo: 1, bar: 2 }).to.have.all.keys(['bar', 'foo']); - * expect({ foo: 1, bar: 2 }).to.have.all.keys({'bar': 6, 'foo': 7}); - * expect({ foo: 1, bar: 2, baz: 3 }).to.contain.all.keys(['bar', 'foo']); - * expect({ foo: 1, bar: 2, baz: 3 }).to.contain.all.keys({'bar': 6}); + * expect({a: 1, b: 2}).to.have.all.keys({a: 4, b: 5}); // ignore 4 and 5 + * expect(['x', 'y']).to.have.all.keys({0: 4, 1: 5}); // ignore 4 and 5 * + * When the target is a map or set, each key must be provided as a separate + * argument. + * + * expect(new Map([['a', 1], ['b', 2]])).to.have.all.keys('a', 'b'); + * expect(new Set(['a', 'b'])).to.have.all.keys('a', 'b'); + * + * Because `.keys` does different things based on the target's type, it's + * important to check the target's type before using `.keys`. See the `.a` doc + * for info on testing a target's type. + * + * expect({a: 1, b: 2}).to.be.an('object').that.has.all.keys('a', 'b'); + * + * By default, strict (`===`) equality is used to compare keys of maps and + * sets. Add `.deep` earlier in the chain to use deep equality instead. See + * the `deep-eql` project page for info on the deep equality algorithm: + * https://github.com/chaijs/deep-eql. + * + * // Target set deeply (but not strictly) has key `{a: 1}` + * expect(new Set([{a: 1}])).to.have.all.deep.keys([{a: 1}]); + * expect(new Set([{a: 1}])).to.not.have.all.keys([{a: 1}]); + * + * By default, the target must have all of the given keys and no more. Add + * `.any` earlier in the chain to only require that the target have at least + * one of the given keys. Also, add `.not` earlier in the chain to negate + * `.keys`. It's often best to add `.any` when negating `.keys`, and to use + * `.all` when asserting `.keys` without negation. + * + * When negating `.keys`, `.any` is preferred because `.not.any.keys` asserts + * exactly what's expected of the output, whereas `.not.all.keys` creates + * uncertain expectations. + * + * // Recommended; asserts that target doesn't have any of the given keys + * expect({a: 1, b: 2}).to.not.have.any.keys('c', 'd'); + * + * // Not recommended; asserts that target doesn't have all of the given + * // keys but may or may not have some of them + * expect({a: 1, b: 2}).to.not.have.all.keys('c', 'd'); + * + * When asserting `.keys` without negation, `.all` is preferred because + * `.all.keys` asserts exactly what's expected of the output, whereas + * `.any.keys` creates uncertain expectations. + * + * // Recommended; asserts that target has all the given keys + * expect({a: 1, b: 2}).to.have.all.keys('a', 'b'); + * + * // Not recommended; asserts that target has at least one of the given + * // keys but may or may not have more of them + * expect({a: 1, b: 2}).to.have.any.keys('a', 'b'); + * + * Note that `.all` is used by default when neither `.all` nor `.any` appear + * earlier in the chain. However, it's often best to add `.all` anyway because + * it improves readability. + * + * // Both assertions are identical + * expect({a: 1, b: 2}).to.have.all.keys('a', 'b'); // Recommended + * expect({a: 1, b: 2}).to.have.keys('a', 'b'); // Not recommended + * + * Add `.include` earlier in the chain to require that the target's keys be a + * superset of the expected keys, rather than identical sets. + * + * // Target object's keys are a superset of ['a', 'b'] but not identical + * expect({a: 1, b: 2, c: 3}).to.include.all.keys('a', 'b'); + * expect({a: 1, b: 2, c: 3}).to.not.have.all.keys('a', 'b'); + * + * However, if `.any` and `.include` are combined, only the `.any` takes + * effect. The `.include` is ignored in this case. + * + * // Both assertions are identical + * expect({a: 1}).to.have.any.keys('a', 'b'); + * expect({a: 1}).to.include.any.keys('a', 'b'); + * + * A custom error message can be given as the second argument to `expect`. + * + * expect({a: 1}, 'nooo why fail??').to.have.key('b'); + * + * The alias `.key` can be used interchangeably with `.keys`. * * @name keys * @alias key @@ -1438,29 +2482,63 @@ module.exports = function (chai, _) { function assertKeys (keys) { var obj = flag(this, 'object') + , objType = _.type(obj) + , keysType = _.type(keys) + , ssfi = flag(this, 'ssfi') + , isDeep = flag(this, 'deep') , str + , deepStr = '' , ok = true - , mixedArgsMsg = 'keys must be given single argument of Array|Object|String, or multiple String arguments'; + , flagMsg = flag(this, 'message'); - switch (_.type(keys)) { - case "array": - if (arguments.length > 1) throw (new Error(mixedArgsMsg)); - break; - case "object": - if (arguments.length > 1) throw (new Error(mixedArgsMsg)); - keys = Object.keys(keys); - break; - default: + flagMsg = flagMsg ? flagMsg + ': ' : ''; + var mixedArgsMsg = flagMsg + 'when testing keys against an object or an array you must give a single Array|Object|String argument or multiple String arguments'; + + if (objType === 'Map' || objType === 'Set') { + deepStr = isDeep ? 'deeply ' : ''; + actual = []; + + // Map and Set '.keys' aren't supported in IE 11. Therefore, use .forEach. + obj.forEach(function (val, key) { actual.push(key) }); + + if (keysType !== 'Array') { keys = Array.prototype.slice.call(arguments); + } + + } else { + actual = _.getOwnEnumerableProperties(obj); + + switch (keysType) { + case 'Array': + if (arguments.length > 1) { + throw new AssertionError(mixedArgsMsg, undefined, ssfi); + } + break; + case 'Object': + if (arguments.length > 1) { + throw new AssertionError(mixedArgsMsg, undefined, ssfi); + } + keys = Object.keys(keys); + break; + default: + keys = Array.prototype.slice.call(arguments); + } + + // Only stringify non-Symbols because Symbols would become "Symbol()" + keys = keys.map(function (val) { + return typeof val === 'symbol' ? val : String(val); + }); } - if (!keys.length) throw new Error('keys required'); + if (!keys.length) { + throw new AssertionError(flagMsg + 'keys required', undefined, ssfi); + } - var actual = Object.keys(obj) - , expected = keys - , len = keys.length + var len = keys.length , any = flag(this, 'any') - , all = flag(this, 'all'); + , all = flag(this, 'all') + , expected = keys + , actual; if (!any && !all) { all = true; @@ -1468,25 +2546,37 @@ module.exports = function (chai, _) { // Has any if (any) { - var intersection = expected.filter(function(key) { - return ~actual.indexOf(key); + ok = expected.some(function(expectedKey) { + return actual.some(function(actualKey) { + if (isDeep) { + return _.eql(expectedKey, actualKey); + } else { + return expectedKey === actualKey; + } + }); }); - ok = intersection.length > 0; } // Has all if (all) { - ok = keys.every(function(key){ - return ~actual.indexOf(key); + ok = expected.every(function(expectedKey) { + return actual.some(function(actualKey) { + if (isDeep) { + return _.eql(expectedKey, actualKey); + } else { + return expectedKey === actualKey; + } + }); }); - if (!flag(this, 'negate') && !flag(this, 'contains')) { + + if (!flag(this, 'contains')) { ok = ok && keys.length == actual.length; } } // Key string if (len > 1) { - keys = keys.map(function(key){ + keys = keys.map(function(key) { return _.inspect(key); }); var last = keys.pop(); @@ -1509,10 +2599,10 @@ module.exports = function (chai, _) { // Assertion this.assert( ok - , 'expected #{this} to ' + str - , 'expected #{this} to not ' + str - , expected.slice(0).sort() - , actual.sort() + , 'expected #{this} to ' + deepStr + str + , 'expected #{this} to not ' + deepStr + str + , expected.slice(0).sort(_.compareByInspect) + , actual.sort(_.compareByInspect) , true ); } @@ -1521,172 +2611,294 @@ module.exports = function (chai, _) { Assertion.addMethod('key', assertKeys); /** - * ### .throw(constructor) + * ### .throw([errorLike], [errMsgMatcher], [msg]) * - * Asserts that the function target will throw a specific error, or specific type of error - * (as determined using `instanceof`), optionally with a RegExp or string inclusion test - * for the error's message. + * When no arguments are provided, `.throw` invokes the target function and + * asserts that an error is thrown. + * + * var badFn = function () { throw new TypeError('Illegal salmon!'); }; * - * var err = new ReferenceError('This is a bad function.'); - * var fn = function () { throw err; } - * expect(fn).to.throw(ReferenceError); - * expect(fn).to.throw(Error); - * expect(fn).to.throw(/bad function/); - * expect(fn).to.not.throw('good function'); - * expect(fn).to.throw(ReferenceError, /bad function/); - * expect(fn).to.throw(err); + * expect(badFn).to.throw(); * - * Please note that when a throw expectation is negated, it will check each - * parameter independently, starting with error constructor type. The appropriate way - * to check for the existence of a type of error but for a message that does not match - * is to use `and`. + * When one argument is provided, and it's an error constructor, `.throw` + * invokes the target function and asserts that an error is thrown that's an + * instance of that error constructor. * - * expect(fn).to.throw(ReferenceError) - * .and.not.throw(/good function/); + * var badFn = function () { throw new TypeError('Illegal salmon!'); }; * - * To check a function with arguments you can wrap the call in an anonymous function: + * expect(badFn).to.throw(TypeError); * - * expect(function() { fn('arg1', 2) }).to.throw(ReferenceError); + * When one argument is provided, and it's an error instance, `.throw` invokes + * the target function and asserts that an error is thrown that's strictly + * (`===`) equal to that error instance. * - * If you need some more complicated check you can chain `throws` with `satisfy` + * var err = new TypeError('Illegal salmon!'); + * var badFn = function () { throw err; }; * - * expect(fn).to.throw().and.to.satisfy((error) => { - * return error.code === 404; - * }); + * expect(badFn).to.throw(err); * - * or even more appropriate method which gives better error message in case of fail + * When one argument is provided, and it's a string, `.throw` invokes the + * target function and asserts that an error is thrown with a message that + * contains that string. * - * expect(fn).to.throw(Error).which.has.property('code', 404); + * var badFn = function () { throw new TypeError('Illegal salmon!'); }; * - * expect(fn).to.throw(Error).which.has.property('code').that.is.within(399, 500); + * expect(badFn).to.throw('salmon'); + * + * When one argument is provided, and it's a regular expression, `.throw` + * invokes the target function and asserts that an error is thrown with a + * message that matches that regular expression. + * + * var badFn = function () { throw new TypeError('Illegal salmon!'); }; + * + * expect(badFn).to.throw(/salmon/); + * + * When two arguments are provided, and the first is an error instance or + * constructor, and the second is a string or regular expression, `.throw` + * invokes the function and asserts that an error is thrown that fulfills both + * conditions as described above. + * + * var err = new TypeError('Illegal salmon!'); + * var badFn = function () { throw err; }; + * + * expect(badFn).to.throw(TypeError, 'salmon'); + * expect(badFn).to.throw(TypeError, /salmon/); + * expect(badFn).to.throw(err, 'salmon'); + * expect(badFn).to.throw(err, /salmon/); + * + * Add `.not` earlier in the chain to negate `.throw`. + * + * var goodFn = function () {}; + * + * expect(goodFn).to.not.throw(); + * + * However, it's dangerous to negate `.throw` when providing any arguments. + * The problem is that it creates uncertain expectations by asserting that the + * target either doesn't throw an error, or that it throws an error but of a + * different type than the given type, or that it throws an error of the given + * type but with a message that doesn't include the given string. It's often + * best to identify the exact output that's expected, and then write an + * assertion that only accepts that exact output. + * + * When the target isn't expected to throw an error, it's often best to assert + * exactly that. + * + * var goodFn = function () {}; + * + * expect(goodFn).to.not.throw(); // Recommended + * expect(goodFn).to.not.throw(ReferenceError, 'x'); // Not recommended + * + * When the target is expected to throw an error, it's often best to assert + * that the error is of its expected type, and has a message that includes an + * expected string, rather than asserting that it doesn't have one of many + * unexpected types, and doesn't have a message that includes some string. + * + * var badFn = function () { throw new TypeError('Illegal salmon!'); }; + * + * expect(badFn).to.throw(TypeError, 'salmon'); // Recommended + * expect(badFn).to.not.throw(ReferenceError, 'x'); // Not recommended + * + * `.throw` changes the target of any assertions that follow in the chain to + * be the error object that's thrown. + * + * var err = new TypeError('Illegal salmon!'); + * err.code = 42; + * var badFn = function () { throw err; }; + * + * expect(badFn).to.throw(TypeError).with.property('code', 42); + * + * `.throw` accepts an optional `msg` argument which is a custom error message + * to show when the assertion fails. The message can also be given as the + * second argument to `expect`. When not providing two arguments, always use + * the second form. + * + * var goodFn = function () {}; + * + * expect(goodFn).to.throw(TypeError, 'x', 'nooo why fail??'); + * expect(goodFn, 'nooo why fail??').to.throw(); + * + * Due to limitations in ES5, `.throw` may not always work as expected when + * using a transpiler such as Babel or TypeScript. In particular, it may + * produce unexpected results when subclassing the built-in `Error` object and + * then passing the subclassed constructor to `.throw`. See your transpiler's + * docs for details: + * + * - ([Babel](https://babeljs.io/docs/usage/caveats/#classes)) + * - ([TypeScript](https://github.com/Microsoft/TypeScript/wiki/Breaking-Changes#extending-built-ins-like-error-array-and-map-may-no-longer-work)) + * + * Beware of some common mistakes when using the `throw` assertion. One common + * mistake is to accidentally invoke the function yourself instead of letting + * the `throw` assertion invoke the function for you. For example, when + * testing if a function named `fn` throws, provide `fn` instead of `fn()` as + * the target for the assertion. + * + * expect(fn).to.throw(); // Good! Tests `fn` as desired + * expect(fn()).to.throw(); // Bad! Tests result of `fn()`, not `fn` + * + * If you need to assert that your function `fn` throws when passed certain + * arguments, then wrap a call to `fn` inside of another function. + * + * expect(function () { fn(42); }).to.throw(); // Function expression + * expect(() => fn(42)).to.throw(); // ES6 arrow function + * + * Another common mistake is to provide an object method (or any stand-alone + * function that relies on `this`) as the target of the assertion. Doing so is + * problematic because the `this` context will be lost when the function is + * invoked by `.throw`; there's no way for it to know what `this` is supposed + * to be. There are two ways around this problem. One solution is to wrap the + * method or function call inside of another function. Another solution is to + * use `bind`. + * + * expect(function () { cat.meow(); }).to.throw(); // Function expression + * expect(() => cat.meow()).to.throw(); // ES6 arrow function + * expect(cat.meow.bind(cat)).to.throw(); // Bind + * + * Finally, it's worth mentioning that it's a best practice in JavaScript to + * only throw `Error` and derivatives of `Error` such as `ReferenceError`, + * `TypeError`, and user-defined objects that extend `Error`. No other type of + * value will generate a stack trace when initialized. With that said, the + * `throw` assertion does technically support any type of value being thrown, + * not just `Error` and its derivatives. + * + * The aliases `.throws` and `.Throw` can be used interchangeably with + * `.throw`. * * @name throw * @alias throws * @alias Throw - * @param {ErrorConstructor} constructor - * @param {String|RegExp} expected error message - * @param {String} message _optional_ + * @param {Error|ErrorConstructor} errorLike + * @param {String|RegExp} errMsgMatcher error message + * @param {String} msg _optional_ * @see https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Error#Error_types * @returns error for chaining (null if no error) * @namespace BDD * @api public */ - function assertThrows (constructor, errMsg, msg) { + function assertThrows (errorLike, errMsgMatcher, msg) { if (msg) flag(this, 'message', msg); - var obj = flag(this, 'object'); - new Assertion(obj, msg).is.a('function'); + var obj = flag(this, 'object') + , ssfi = flag(this, 'ssfi') + , flagMsg = flag(this, 'message') + , negate = flag(this, 'negate') || false; + new Assertion(obj, flagMsg, ssfi, true).is.a('function'); - var thrown = false - , desiredError = null - , name = null - , thrownError = null; - - if (arguments.length === 0) { - errMsg = null; - constructor = null; - } else if (constructor && (constructor instanceof RegExp || 'string' === typeof constructor)) { - errMsg = constructor; - constructor = null; - } else if (constructor && constructor instanceof Error) { - desiredError = constructor; - constructor = null; - errMsg = null; - } else if (typeof constructor === 'function') { - name = constructor.prototype.name; - if (!name || (name === 'Error' && constructor !== Error)) { - name = constructor.name || (new constructor()).name; - } - } else { - constructor = null; + if (errorLike instanceof RegExp || typeof errorLike === 'string') { + errMsgMatcher = errorLike; + errorLike = null; } + var caughtErr; try { obj(); } catch (err) { - // first, check desired error - if (desiredError) { - this.assert( - err === desiredError - , 'expected #{this} to throw #{exp} but #{act} was thrown' - , 'expected #{this} to not throw #{exp}' - , (desiredError instanceof Error ? desiredError.toString() : desiredError) - , (err instanceof Error ? err.toString() : err) - ); + caughtErr = err; + } - flag(this, 'object', err); - return this; + // If we have the negate flag enabled and at least one valid argument it means we do expect an error + // but we want it to match a given set of criteria + var everyArgIsUndefined = errorLike === undefined && errMsgMatcher === undefined; + + // If we've got the negate flag enabled and both args, we should only fail if both aren't compatible + // See Issue #551 and PR #683@GitHub + var everyArgIsDefined = Boolean(errorLike && errMsgMatcher); + var errorLikeFail = false; + var errMsgMatcherFail = false; + + // Checking if error was thrown + if (everyArgIsUndefined || !everyArgIsUndefined && !negate) { + // We need this to display results correctly according to their types + var errorLikeString = 'an error'; + if (errorLike instanceof Error) { + errorLikeString = '#{exp}'; + } else if (errorLike) { + errorLikeString = _.checkError.getConstructorName(errorLike); } - // next, check constructor - if (constructor) { - this.assert( - err instanceof constructor - , 'expected #{this} to throw #{exp} but #{act} was thrown' - , 'expected #{this} to not throw #{exp} but #{act} was thrown' - , name - , (err instanceof Error ? err.toString() : err) - ); + this.assert( + caughtErr + , 'expected #{this} to throw ' + errorLikeString + , 'expected #{this} to not throw an error but #{act} was thrown' + , errorLike && errorLike.toString() + , (caughtErr instanceof Error ? + caughtErr.toString() : (typeof caughtErr === 'string' ? caughtErr : caughtErr && + _.checkError.getConstructorName(caughtErr))) + ); + } - if (!errMsg) { - flag(this, 'object', err); - return this; + if (errorLike && caughtErr) { + // We should compare instances only if `errorLike` is an instance of `Error` + if (errorLike instanceof Error) { + var isCompatibleInstance = _.checkError.compatibleInstance(caughtErr, errorLike); + + if (isCompatibleInstance === negate) { + // These checks were created to ensure we won't fail too soon when we've got both args and a negate + // See Issue #551 and PR #683@GitHub + if (everyArgIsDefined && negate) { + errorLikeFail = true; + } else { + this.assert( + negate + , 'expected #{this} to throw #{exp} but #{act} was thrown' + , 'expected #{this} to not throw #{exp}' + (caughtErr && !negate ? ' but #{act} was thrown' : '') + , errorLike.toString() + , caughtErr.toString() + ); + } } } - // next, check message - var message = 'error' === _.type(err) && "message" in err - ? err.message - : '' + err; - - if ((message != null) && errMsg && errMsg instanceof RegExp) { - this.assert( - errMsg.exec(message) - , 'expected #{this} to throw error matching #{exp} but got #{act}' - , 'expected #{this} to throw error not matching #{exp}' - , errMsg - , message - ); - - flag(this, 'object', err); - return this; - } else if ((message != null) && errMsg && 'string' === typeof errMsg) { - this.assert( - ~message.indexOf(errMsg) - , 'expected #{this} to throw error including #{exp} but got #{act}' - , 'expected #{this} to throw error not including #{act}' - , errMsg - , message - ); - - flag(this, 'object', err); - return this; - } else { - thrown = true; - thrownError = err; + var isCompatibleConstructor = _.checkError.compatibleConstructor(caughtErr, errorLike); + if (isCompatibleConstructor === negate) { + if (everyArgIsDefined && negate) { + errorLikeFail = true; + } else { + this.assert( + negate + , 'expected #{this} to throw #{exp} but #{act} was thrown' + , 'expected #{this} to not throw #{exp}' + (caughtErr ? ' but #{act} was thrown' : '') + , (errorLike instanceof Error ? errorLike.toString() : errorLike && _.checkError.getConstructorName(errorLike)) + , (caughtErr instanceof Error ? caughtErr.toString() : caughtErr && _.checkError.getConstructorName(caughtErr)) + ); + } } } - var actuallyGot = '' - , expectedThrown = name !== null - ? name - : desiredError - ? '#{exp}' //_.inspect(desiredError) - : 'an error'; + if (caughtErr && errMsgMatcher !== undefined && errMsgMatcher !== null) { + // Here we check compatible messages + var placeholder = 'including'; + if (errMsgMatcher instanceof RegExp) { + placeholder = 'matching' + } - if (thrown) { - actuallyGot = ' but #{act} was thrown' + var isCompatibleMessage = _.checkError.compatibleMessage(caughtErr, errMsgMatcher); + if (isCompatibleMessage === negate) { + if (everyArgIsDefined && negate) { + errMsgMatcherFail = true; + } else { + this.assert( + negate + , 'expected #{this} to throw error ' + placeholder + ' #{exp} but got #{act}' + , 'expected #{this} to throw error not ' + placeholder + ' #{exp}' + , errMsgMatcher + , _.checkError.getMessage(caughtErr) + ); + } + } } - this.assert( - thrown === true - , 'expected #{this} to throw ' + expectedThrown + actuallyGot - , 'expected #{this} to not throw ' + expectedThrown + actuallyGot - , (desiredError instanceof Error ? desiredError.toString() : desiredError) - , (thrownError instanceof Error ? thrownError.toString() : thrownError) - ); + // If both assertions failed and both should've matched we throw an error + if (errorLikeFail && errMsgMatcherFail) { + this.assert( + negate + , 'expected #{this} to throw #{exp} but #{act} was thrown' + , 'expected #{this} to not throw #{exp}' + (caughtErr ? ' but #{act} was thrown' : '') + , (errorLike instanceof Error ? errorLike.toString() : errorLike && _.checkError.getConstructorName(errorLike)) + , (caughtErr instanceof Error ? caughtErr.toString() : caughtErr && _.checkError.getConstructorName(caughtErr)) + ); + } - flag(this, 'object', thrownError); + flag(this, 'object', caughtErr); }; Assertion.addMethod('throw', assertThrows); @@ -1694,24 +2906,66 @@ module.exports = function (chai, _) { Assertion.addMethod('Throw', assertThrows); /** - * ### .respondTo(method) + * ### .respondTo(method[, msg]) * - * Asserts that the object or class target will respond to a method. + * When the target is a non-function object, `.respondTo` asserts that the + * target has a method with the given name `method`. The method can be own or + * inherited, and it can be enumerable or non-enumerable. * - * Klass.prototype.bar = function(){}; - * expect(Klass).to.respondTo('bar'); - * expect(obj).to.respondTo('bar'); + * function Cat () {} + * Cat.prototype.meow = function () {}; * - * To check if a constructor will respond to a static function, - * set the `itself` flag. + * expect(new Cat()).to.respondTo('meow'); * - * Klass.baz = function(){}; - * expect(Klass).itself.to.respondTo('baz'); + * When the target is a function, `.respondTo` asserts that the target's + * `prototype` property has a method with the given name `method`. Again, the + * method can be own or inherited, and it can be enumerable or non-enumerable. + * + * function Cat () {} + * Cat.prototype.meow = function () {}; + * + * expect(Cat).to.respondTo('meow'); + * + * Add `.itself` earlier in the chain to force `.respondTo` to treat the + * target as a non-function object, even if it's a function. Thus, it asserts + * that the target has a method with the given name `method`, rather than + * asserting that the target's `prototype` property has a method with the + * given name `method`. + * + * function Cat () {} + * Cat.prototype.meow = function () {}; + * Cat.hiss = function () {}; + * + * expect(Cat).itself.to.respondTo('hiss').but.not.respondTo('meow'); + * + * When not adding `.itself`, it's important to check the target's type before + * using `.respondTo`. See the `.a` doc for info on checking a target's type. + * + * function Cat () {} + * Cat.prototype.meow = function () {}; + * + * expect(new Cat()).to.be.an('object').that.respondsTo('meow'); + * + * Add `.not` earlier in the chain to negate `.respondTo`. + * + * function Dog () {} + * Dog.prototype.bark = function () {}; + * + * expect(new Dog()).to.not.respondTo('meow'); + * + * `.respondTo` accepts an optional `msg` argument which is a custom error + * message to show when the assertion fails. The message can also be given as + * the second argument to `expect`. + * + * expect({}).to.respondTo('meow', 'nooo why fail??'); + * expect({}, 'nooo why fail??').to.respondTo('meow'); + * + * The alias `.respondsTo` can be used interchangeably with `.respondTo`. * * @name respondTo * @alias respondsTo * @param {String} method - * @param {String} message _optional_ + * @param {String} msg _optional_ * @namespace BDD * @api public */ @@ -1720,7 +2974,7 @@ module.exports = function (chai, _) { if (msg) flag(this, 'message', msg); var obj = flag(this, 'object') , itself = flag(this, 'itself') - , context = ('function' === _.type(obj) && !itself) + , context = ('function' === typeof obj && !itself) ? obj.prototype[method] : obj[method]; @@ -1737,14 +2991,17 @@ module.exports = function (chai, _) { /** * ### .itself * - * Sets the `itself` flag, later used by the `respondTo` assertion. + * Forces all `.respondTo` assertions that follow in the chain to behave as if + * the target is a non-function object, even if it's a function. Thus, it + * causes `.respondTo` to assert that the target has a method with the given + * name, rather than asserting that the target's `prototype` property has a + * method with the given name. * - * function Foo() {} - * Foo.bar = function() {} - * Foo.prototype.baz = function() {} + * function Cat () {} + * Cat.prototype.meow = function () {}; + * Cat.hiss = function () {}; * - * expect(Foo).itself.to.respondTo('bar'); - * expect(Foo).itself.not.to.respondTo('baz'); + * expect(Cat).itself.to.respondTo('hiss').but.not.respondTo('meow'); * * @name itself * @namespace BDD @@ -1756,16 +3013,39 @@ module.exports = function (chai, _) { }); /** - * ### .satisfy(method) + * ### .satisfy(matcher[, msg]) * - * Asserts that the target passes a given truth test. + * Invokes the given `matcher` function with the target being passed as the + * first argument, and asserts that the value returned is truthy. * - * expect(1).to.satisfy(function(num) { return num > 0; }); + * expect(1).to.satisfy(function(num) { + * return num > 0; + * }); + * + * Add `.not` earlier in the chain to negate `.satisfy`. + * + * expect(1).to.not.satisfy(function(num) { + * return num > 2; + * }); + * + * `.satisfy` accepts an optional `msg` argument which is a custom error + * message to show when the assertion fails. The message can also be given as + * the second argument to `expect`. + * + * expect(1).to.satisfy(function(num) { + * return num > 2; + * }, 'nooo why fail??'); + * + * expect(1, 'nooo why fail??').to.satisfy(function(num) { + * return num > 2; + * }); + * + * The alias `.satisfies` can be used interchangeably with `.satisfy`. * * @name satisfy * @alias satisfies * @param {Function} matcher - * @param {String} message _optional_ + * @param {String} msg _optional_ * @namespace BDD * @api public */ @@ -1778,7 +3058,7 @@ module.exports = function (chai, _) { result , 'expected #{this} to satisfy ' + _.objDisplay(matcher) , 'expected #{this} to not satisfy' + _.objDisplay(matcher) - , this.negate ? false : true + , flag(this, 'negate') ? false : true , result ); } @@ -1787,28 +3067,57 @@ module.exports = function (chai, _) { Assertion.addMethod('satisfies', satisfy); /** - * ### .closeTo(expected, delta) + * ### .closeTo(expected, delta[, msg]) * - * Asserts that the target is equal `expected`, to within a +/- `delta` range. + * Asserts that the target is a number that's within a given +/- `delta` range + * of the given number `expected`. However, it's often best to assert that the + * target is equal to its expected value. * + * // Recommended + * expect(1.5).to.equal(1.5); + * + * // Not recommended * expect(1.5).to.be.closeTo(1, 0.5); + * expect(1.5).to.be.closeTo(2, 0.5); + * expect(1.5).to.be.closeTo(1, 1); + * + * Add `.not` earlier in the chain to negate `.closeTo`. + * + * expect(1.5).to.equal(1.5); // Recommended + * expect(1.5).to.not.be.closeTo(3, 1); // Not recommended + * + * `.closeTo` accepts an optional `msg` argument which is a custom error + * message to show when the assertion fails. The message can also be given as + * the second argument to `expect`. + * + * expect(1.5).to.be.closeTo(3, 1, 'nooo why fail??'); + * expect(1.5, 'nooo why fail??').to.be.closeTo(3, 1); + * + * The alias `.approximately` can be used interchangeably with `.closeTo`. * * @name closeTo * @alias approximately * @param {Number} expected * @param {Number} delta - * @param {String} message _optional_ + * @param {String} msg _optional_ * @namespace BDD * @api public */ function closeTo(expected, delta, msg) { if (msg) flag(this, 'message', msg); - var obj = flag(this, 'object'); + var obj = flag(this, 'object') + , flagMsg = flag(this, 'message') + , ssfi = flag(this, 'ssfi'); - new Assertion(obj, msg).is.a('number'); - if (_.type(expected) !== 'number' || _.type(delta) !== 'number') { - throw new Error('the arguments to closeTo or approximately must be numbers'); + new Assertion(obj, flagMsg, ssfi, true).is.a('number'); + if (typeof expected !== 'number' || typeof delta !== 'number') { + flagMsg = flagMsg ? flagMsg + ': ' : ''; + throw new AssertionError( + flagMsg + 'the arguments to closeTo or approximately must be numbers', + undefined, + ssfi + ); } this.assert( @@ -1821,93 +3130,176 @@ module.exports = function (chai, _) { Assertion.addMethod('closeTo', closeTo); Assertion.addMethod('approximately', closeTo); - function isSubsetOf(subset, superset, cmp) { - return subset.every(function(elem) { - if (!cmp) return superset.indexOf(elem) !== -1; + // Note: Duplicates are ignored if testing for inclusion instead of sameness. + function isSubsetOf(subset, superset, cmp, contains, ordered) { + if (!contains) { + if (subset.length !== superset.length) return false; + superset = superset.slice(); + } - return superset.some(function(elem2) { - return cmp(elem, elem2); + return subset.every(function(elem, idx) { + if (ordered) return cmp ? cmp(elem, superset[idx]) : elem === superset[idx]; + + if (!cmp) { + var matchIdx = superset.indexOf(elem); + if (matchIdx === -1) return false; + + // Remove match from superset so not counted twice if duplicate in subset. + if (!contains) superset.splice(matchIdx, 1); + return true; + } + + return superset.some(function(elem2, matchIdx) { + if (!cmp(elem, elem2)) return false; + + // Remove match from superset so not counted twice if duplicate in subset. + if (!contains) superset.splice(matchIdx, 1); + return true; }); - }) + }); } /** - * ### .members(set) + * ### .members(set[, msg]) * - * Asserts that the target is a superset of `set`, - * or that the target and `set` have the same strictly-equal (===) members. - * Alternately, if the `deep` flag is set, set members are compared for deep - * equality. + * Asserts that the target array has the same members as the given array + * `set`. * - * expect([1, 2, 3]).to.include.members([3, 2]); - * expect([1, 2, 3]).to.not.include.members([3, 2, 8]); + * expect([1, 2, 3]).to.have.members([2, 1, 3]); + * expect([1, 2, 2]).to.have.members([2, 1, 2]); * - * expect([4, 2]).to.have.members([2, 4]); - * expect([5, 2]).to.not.have.members([5, 2, 1]); + * By default, members are compared using strict (`===`) equality. Add `.deep` + * earlier in the chain to use deep equality instead. See the `deep-eql` + * project page for info on the deep equality algorithm: + * https://github.com/chaijs/deep-eql. * - * expect([{ id: 1 }]).to.deep.include.members([{ id: 1 }]); + * // Target array deeply (but not strictly) has member `{a: 1}` + * expect([{a: 1}]).to.have.deep.members([{a: 1}]); + * expect([{a: 1}]).to.not.have.members([{a: 1}]); + * + * By default, order doesn't matter. Add `.ordered` earlier in the chain to + * require that members appear in the same order. + * + * expect([1, 2, 3]).to.have.ordered.members([1, 2, 3]); + * expect([1, 2, 3]).to.have.members([2, 1, 3]) + * .but.not.ordered.members([2, 1, 3]); + * + * By default, both arrays must be the same size. Add `.include` earlier in + * the chain to require that the target's members be a superset of the + * expected members. Note that duplicates are ignored in the subset when + * `.include` is added. + * + * // Target array is a superset of [1, 2] but not identical + * expect([1, 2, 3]).to.include.members([1, 2]); + * expect([1, 2, 3]).to.not.have.members([1, 2]); + * + * // Duplicates in the subset are ignored + * expect([1, 2, 3]).to.include.members([1, 2, 2, 2]); + * + * `.deep`, `.ordered`, and `.include` can all be combined. However, if + * `.include` and `.ordered` are combined, the ordering begins at the start of + * both arrays. + * + * expect([{a: 1}, {b: 2}, {c: 3}]) + * .to.include.deep.ordered.members([{a: 1}, {b: 2}]) + * .but.not.include.deep.ordered.members([{b: 2}, {c: 3}]); + * + * Add `.not` earlier in the chain to negate `.members`. However, it's + * dangerous to do so. The problem is that it creates uncertain expectations + * by asserting that the target array doesn't have all of the same members as + * the given array `set` but may or may not have some of them. It's often best + * to identify the exact output that's expected, and then write an assertion + * that only accepts that exact output. + * + * expect([1, 2]).to.not.include(3).and.not.include(4); // Recommended + * expect([1, 2]).to.not.have.members([3, 4]); // Not recommended + * + * `.members` accepts an optional `msg` argument which is a custom error + * message to show when the assertion fails. The message can also be given as + * the second argument to `expect`. + * + * expect([1, 2]).to.have.members([1, 2, 3], 'nooo why fail??'); + * expect([1, 2], 'nooo why fail??').to.have.members([1, 2, 3]); * * @name members * @param {Array} set - * @param {String} message _optional_ + * @param {String} msg _optional_ * @namespace BDD * @api public */ Assertion.addMethod('members', function (subset, msg) { if (msg) flag(this, 'message', msg); - var obj = flag(this, 'object'); + var obj = flag(this, 'object') + , flagMsg = flag(this, 'message') + , ssfi = flag(this, 'ssfi'); - new Assertion(obj).to.be.an('array'); - new Assertion(subset).to.be.an('array'); + new Assertion(obj, flagMsg, ssfi, true).to.be.an('array'); + new Assertion(subset, flagMsg, ssfi, true).to.be.an('array'); + + var contains = flag(this, 'contains'); + var ordered = flag(this, 'ordered'); + + var subject, failMsg, failNegateMsg, lengthCheck; + + if (contains) { + subject = ordered ? 'an ordered superset' : 'a superset'; + failMsg = 'expected #{this} to be ' + subject + ' of #{exp}'; + failNegateMsg = 'expected #{this} to not be ' + subject + ' of #{exp}'; + } else { + subject = ordered ? 'ordered members' : 'members'; + failMsg = 'expected #{this} to have the same ' + subject + ' as #{exp}'; + failNegateMsg = 'expected #{this} to not have the same ' + subject + ' as #{exp}'; + } var cmp = flag(this, 'deep') ? _.eql : undefined; - if (flag(this, 'contains')) { - return this.assert( - isSubsetOf(subset, obj, cmp) - , 'expected #{this} to be a superset of #{act}' - , 'expected #{this} to not be a superset of #{act}' - , obj - , subset - ); - } - this.assert( - isSubsetOf(obj, subset, cmp) && isSubsetOf(subset, obj, cmp) - , 'expected #{this} to have the same members as #{act}' - , 'expected #{this} to not have the same members as #{act}' - , obj - , subset + isSubsetOf(subset, obj, cmp, contains, ordered) + , failMsg + , failNegateMsg + , subset + , obj + , true ); }); /** - * ### .oneOf(list) + * ### .oneOf(list[, msg]) * - * Assert that a value appears somewhere in the top level of array `list`. + * Asserts that the target is a member of the given array `list`. However, + * it's often best to assert that the target is equal to its expected value. * - * expect('a').to.be.oneOf(['a', 'b', 'c']); - * expect(9).to.not.be.oneOf(['z']); - * expect([3]).to.not.be.oneOf([1, 2, [3]]); + * expect(1).to.equal(1); // Recommended + * expect(1).to.be.oneOf([1, 2, 3]); // Not recommended * - * var three = [3]; - * // for object-types, contents are not compared - * expect(three).to.not.be.oneOf([1, 2, [3]]); - * // comparing references works - * expect(three).to.be.oneOf([1, 2, three]); + * Comparisons are performed using strict (`===`) equality. + * + * Add `.not` earlier in the chain to negate `.oneOf`. + * + * expect(1).to.equal(1); // Recommended + * expect(1).to.not.be.oneOf([2, 3, 4]); // Not recommended + * + * `.oneOf` accepts an optional `msg` argument which is a custom error message + * to show when the assertion fails. The message can also be given as the + * second argument to `expect`. + * + * expect(1).to.be.oneOf([2, 3, 4], 'nooo why fail??'); + * expect(1, 'nooo why fail??').to.be.oneOf([2, 3, 4]); * * @name oneOf * @param {Array<*>} list - * @param {String} message _optional_ + * @param {String} msg _optional_ * @namespace BDD * @api public */ function oneOf (list, msg) { if (msg) flag(this, 'message', msg); - var expected = flag(this, 'object'); - new Assertion(list).to.be.an('array'); + var expected = flag(this, 'object') + , flagMsg = flag(this, 'message') + , ssfi = flag(this, 'ssfi'); + new Assertion(list, flagMsg, ssfi, true).to.be.an('array'); this.assert( list.indexOf(expected) > -1 @@ -1922,135 +3314,489 @@ module.exports = function (chai, _) { /** - * ### .change(function) + * ### .change(subject[, prop[, msg]]) * - * Asserts that a function changes an object property + * When one argument is provided, `.change` asserts that the given function + * `subject` returns a different value when it's invoked before the target + * function compared to when it's invoked afterward. However, it's often best + * to assert that `subject` is equal to its expected value. * - * var obj = { val: 10 }; - * var fn = function() { obj.val += 3 }; - * var noChangeFn = function() { return 'foo' + 'bar'; } - * expect(fn).to.change(obj, 'val'); - * expect(noChangeFn).to.not.change(obj, 'val') + * var dots = '' + * , addDot = function () { dots += '.'; } + * , getDots = function () { return dots; }; + * + * // Recommended + * expect(getDots()).to.equal(''); + * addDot(); + * expect(getDots()).to.equal('.'); + * + * // Not recommended + * expect(addDot).to.change(getDots); + * + * When two arguments are provided, `.change` asserts that the value of the + * given object `subject`'s `prop` property is different before invoking the + * target function compared to afterward. + * + * var myObj = {dots: ''} + * , addDot = function () { myObj.dots += '.'; }; + * + * // Recommended + * expect(myObj).to.have.property('dots', ''); + * addDot(); + * expect(myObj).to.have.property('dots', '.'); + * + * // Not recommended + * expect(addDot).to.change(myObj, 'dots'); + * + * Strict (`===`) equality is used to compare before and after values. + * + * Add `.not` earlier in the chain to negate `.change`. + * + * var dots = '' + * , noop = function () {} + * , getDots = function () { return dots; }; + * + * expect(noop).to.not.change(getDots); + * + * var myObj = {dots: ''} + * , noop = function () {}; + * + * expect(noop).to.not.change(myObj, 'dots'); + * + * `.change` accepts an optional `msg` argument which is a custom error + * message to show when the assertion fails. The message can also be given as + * the second argument to `expect`. When not providing two arguments, always + * use the second form. + * + * var myObj = {dots: ''} + * , addDot = function () { myObj.dots += '.'; }; + * + * expect(addDot).to.not.change(myObj, 'dots', 'nooo why fail??'); + * + * var dots = '' + * , addDot = function () { dots += '.'; } + * , getDots = function () { return dots; }; + * + * expect(addDot, 'nooo why fail??').to.not.change(getDots); + * + * `.change` also causes all `.by` assertions that follow in the chain to + * assert how much a numeric subject was increased or decreased by. However, + * it's dangerous to use `.change.by`. The problem is that it creates + * uncertain expectations by asserting that the subject either increases by + * the given delta, or that it decreases by the given delta. It's often best + * to identify the exact output that's expected, and then write an assertion + * that only accepts that exact output. + * + * var myObj = {val: 1} + * , addTwo = function () { myObj.val += 2; } + * , subtractTwo = function () { myObj.val -= 2; }; + * + * expect(addTwo).to.increase(myObj, 'val').by(2); // Recommended + * expect(addTwo).to.change(myObj, 'val').by(2); // Not recommended + * + * expect(subtractTwo).to.decrease(myObj, 'val').by(2); // Recommended + * expect(subtractTwo).to.change(myObj, 'val').by(2); // Not recommended + * + * The alias `.changes` can be used interchangeably with `.change`. * * @name change * @alias changes - * @alias Change - * @param {String} object - * @param {String} property name - * @param {String} message _optional_ + * @param {String} subject + * @param {String} prop name _optional_ + * @param {String} msg _optional_ * @namespace BDD * @api public */ - function assertChanges (object, prop, msg) { + function assertChanges (subject, prop, msg) { if (msg) flag(this, 'message', msg); - var fn = flag(this, 'object'); - new Assertion(object, msg).to.have.property(prop); - new Assertion(fn).is.a('function'); + var fn = flag(this, 'object') + , flagMsg = flag(this, 'message') + , ssfi = flag(this, 'ssfi'); + new Assertion(fn, flagMsg, ssfi, true).is.a('function'); + + var initial; + if (!prop) { + new Assertion(subject, flagMsg, ssfi, true).is.a('function'); + initial = subject(); + } else { + new Assertion(subject, flagMsg, ssfi, true).to.have.property(prop); + initial = subject[prop]; + } - var initial = object[prop]; fn(); + var final = prop === undefined || prop === null ? subject() : subject[prop]; + var msgObj = prop === undefined || prop === null ? initial : '.' + prop; + + // This gets flagged because of the .by(delta) assertion + flag(this, 'deltaMsgObj', msgObj); + flag(this, 'initialDeltaValue', initial); + flag(this, 'finalDeltaValue', final); + flag(this, 'deltaBehavior', 'change'); + flag(this, 'realDelta', final !== initial); + this.assert( - initial !== object[prop] - , 'expected .' + prop + ' to change' - , 'expected .' + prop + ' to not change' + initial !== final + , 'expected ' + msgObj + ' to change' + , 'expected ' + msgObj + ' to not change' ); } - Assertion.addChainableMethod('change', assertChanges); - Assertion.addChainableMethod('changes', assertChanges); + Assertion.addMethod('change', assertChanges); + Assertion.addMethod('changes', assertChanges); /** - * ### .increase(function) + * ### .increase(subject[, prop[, msg]]) * - * Asserts that a function increases an object property + * When one argument is provided, `.increase` asserts that the given function + * `subject` returns a greater number when it's invoked after invoking the + * target function compared to when it's invoked beforehand. `.increase` also + * causes all `.by` assertions that follow in the chain to assert how much + * greater of a number is returned. It's often best to assert that the return + * value increased by the expected amount, rather than asserting it increased + * by any amount. * - * var obj = { val: 10 }; - * var fn = function() { obj.val = 15 }; - * expect(fn).to.increase(obj, 'val'); + * var val = 1 + * , addTwo = function () { val += 2; } + * , getVal = function () { return val; }; + * + * expect(addTwo).to.increase(getVal).by(2); // Recommended + * expect(addTwo).to.increase(getVal); // Not recommended + * + * When two arguments are provided, `.increase` asserts that the value of the + * given object `subject`'s `prop` property is greater after invoking the + * target function compared to beforehand. + * + * var myObj = {val: 1} + * , addTwo = function () { myObj.val += 2; }; + * + * expect(addTwo).to.increase(myObj, 'val').by(2); // Recommended + * expect(addTwo).to.increase(myObj, 'val'); // Not recommended + * + * Add `.not` earlier in the chain to negate `.increase`. However, it's + * dangerous to do so. The problem is that it creates uncertain expectations + * by asserting that the subject either decreases, or that it stays the same. + * It's often best to identify the exact output that's expected, and then + * write an assertion that only accepts that exact output. + * + * When the subject is expected to decrease, it's often best to assert that it + * decreased by the expected amount. + * + * var myObj = {val: 1} + * , subtractTwo = function () { myObj.val -= 2; }; + * + * expect(subtractTwo).to.decrease(myObj, 'val').by(2); // Recommended + * expect(subtractTwo).to.not.increase(myObj, 'val'); // Not recommended + * + * When the subject is expected to stay the same, it's often best to assert + * exactly that. + * + * var myObj = {val: 1} + * , noop = function () {}; + * + * expect(noop).to.not.change(myObj, 'val'); // Recommended + * expect(noop).to.not.increase(myObj, 'val'); // Not recommended + * + * `.increase` accepts an optional `msg` argument which is a custom error + * message to show when the assertion fails. The message can also be given as + * the second argument to `expect`. When not providing two arguments, always + * use the second form. + * + * var myObj = {val: 1} + * , noop = function () {}; + * + * expect(noop).to.increase(myObj, 'val', 'nooo why fail??'); + * + * var val = 1 + * , noop = function () {} + * , getVal = function () { return val; }; + * + * expect(noop, 'nooo why fail??').to.increase(getVal); + * + * The alias `.increases` can be used interchangeably with `.increase`. * * @name increase * @alias increases - * @alias Increase - * @param {String} object - * @param {String} property name - * @param {String} message _optional_ + * @param {String|Function} subject + * @param {String} prop name _optional_ + * @param {String} msg _optional_ * @namespace BDD * @api public */ - function assertIncreases (object, prop, msg) { + function assertIncreases (subject, prop, msg) { if (msg) flag(this, 'message', msg); - var fn = flag(this, 'object'); - new Assertion(object, msg).to.have.property(prop); - new Assertion(fn).is.a('function'); + var fn = flag(this, 'object') + , flagMsg = flag(this, 'message') + , ssfi = flag(this, 'ssfi'); + new Assertion(fn, flagMsg, ssfi, true).is.a('function'); + + var initial; + if (!prop) { + new Assertion(subject, flagMsg, ssfi, true).is.a('function'); + initial = subject(); + } else { + new Assertion(subject, flagMsg, ssfi, true).to.have.property(prop); + initial = subject[prop]; + } + + // Make sure that the target is a number + new Assertion(initial, flagMsg, ssfi, true).is.a('number'); - var initial = object[prop]; fn(); + var final = prop === undefined || prop === null ? subject() : subject[prop]; + var msgObj = prop === undefined || prop === null ? initial : '.' + prop; + + flag(this, 'deltaMsgObj', msgObj); + flag(this, 'initialDeltaValue', initial); + flag(this, 'finalDeltaValue', final); + flag(this, 'deltaBehavior', 'increase'); + flag(this, 'realDelta', final - initial); + this.assert( - object[prop] - initial > 0 - , 'expected .' + prop + ' to increase' - , 'expected .' + prop + ' to not increase' + final - initial > 0 + , 'expected ' + msgObj + ' to increase' + , 'expected ' + msgObj + ' to not increase' ); } - Assertion.addChainableMethod('increase', assertIncreases); - Assertion.addChainableMethod('increases', assertIncreases); + Assertion.addMethod('increase', assertIncreases); + Assertion.addMethod('increases', assertIncreases); /** - * ### .decrease(function) + * ### .decrease(subject[, prop[, msg]]) * - * Asserts that a function decreases an object property + * When one argument is provided, `.decrease` asserts that the given function + * `subject` returns a lesser number when it's invoked after invoking the + * target function compared to when it's invoked beforehand. `.decrease` also + * causes all `.by` assertions that follow in the chain to assert how much + * lesser of a number is returned. It's often best to assert that the return + * value decreased by the expected amount, rather than asserting it decreased + * by any amount. * - * var obj = { val: 10 }; - * var fn = function() { obj.val = 5 }; - * expect(fn).to.decrease(obj, 'val'); + * var val = 1 + * , subtractTwo = function () { val -= 2; } + * , getVal = function () { return val; }; + * + * expect(subtractTwo).to.decrease(getVal).by(2); // Recommended + * expect(subtractTwo).to.decrease(getVal); // Not recommended + * + * When two arguments are provided, `.decrease` asserts that the value of the + * given object `subject`'s `prop` property is lesser after invoking the + * target function compared to beforehand. + * + * var myObj = {val: 1} + * , subtractTwo = function () { myObj.val -= 2; }; + * + * expect(subtractTwo).to.decrease(myObj, 'val').by(2); // Recommended + * expect(subtractTwo).to.decrease(myObj, 'val'); // Not recommended + * + * Add `.not` earlier in the chain to negate `.decrease`. However, it's + * dangerous to do so. The problem is that it creates uncertain expectations + * by asserting that the subject either increases, or that it stays the same. + * It's often best to identify the exact output that's expected, and then + * write an assertion that only accepts that exact output. + * + * When the subject is expected to increase, it's often best to assert that it + * increased by the expected amount. + * + * var myObj = {val: 1} + * , addTwo = function () { myObj.val += 2; }; + * + * expect(addTwo).to.increase(myObj, 'val').by(2); // Recommended + * expect(addTwo).to.not.decrease(myObj, 'val'); // Not recommended + * + * When the subject is expected to stay the same, it's often best to assert + * exactly that. + * + * var myObj = {val: 1} + * , noop = function () {}; + * + * expect(noop).to.not.change(myObj, 'val'); // Recommended + * expect(noop).to.not.decrease(myObj, 'val'); // Not recommended + * + * `.decrease` accepts an optional `msg` argument which is a custom error + * message to show when the assertion fails. The message can also be given as + * the second argument to `expect`. When not providing two arguments, always + * use the second form. + * + * var myObj = {val: 1} + * , noop = function () {}; + * + * expect(noop).to.decrease(myObj, 'val', 'nooo why fail??'); + * + * var val = 1 + * , noop = function () {} + * , getVal = function () { return val; }; + * + * expect(noop, 'nooo why fail??').to.decrease(getVal); + * + * The alias `.decreases` can be used interchangeably with `.decrease`. * * @name decrease * @alias decreases - * @alias Decrease - * @param {String} object - * @param {String} property name - * @param {String} message _optional_ + * @param {String|Function} subject + * @param {String} prop name _optional_ + * @param {String} msg _optional_ * @namespace BDD * @api public */ - function assertDecreases (object, prop, msg) { + function assertDecreases (subject, prop, msg) { if (msg) flag(this, 'message', msg); - var fn = flag(this, 'object'); - new Assertion(object, msg).to.have.property(prop); - new Assertion(fn).is.a('function'); + var fn = flag(this, 'object') + , flagMsg = flag(this, 'message') + , ssfi = flag(this, 'ssfi'); + new Assertion(fn, flagMsg, ssfi, true).is.a('function'); + + var initial; + if (!prop) { + new Assertion(subject, flagMsg, ssfi, true).is.a('function'); + initial = subject(); + } else { + new Assertion(subject, flagMsg, ssfi, true).to.have.property(prop); + initial = subject[prop]; + } + + // Make sure that the target is a number + new Assertion(initial, flagMsg, ssfi, true).is.a('number'); - var initial = object[prop]; fn(); + var final = prop === undefined || prop === null ? subject() : subject[prop]; + var msgObj = prop === undefined || prop === null ? initial : '.' + prop; + + flag(this, 'deltaMsgObj', msgObj); + flag(this, 'initialDeltaValue', initial); + flag(this, 'finalDeltaValue', final); + flag(this, 'deltaBehavior', 'decrease'); + flag(this, 'realDelta', initial - final); + this.assert( - object[prop] - initial < 0 - , 'expected .' + prop + ' to decrease' - , 'expected .' + prop + ' to not decrease' + final - initial < 0 + , 'expected ' + msgObj + ' to decrease' + , 'expected ' + msgObj + ' to not decrease' ); } - Assertion.addChainableMethod('decrease', assertDecreases); - Assertion.addChainableMethod('decreases', assertDecreases); + Assertion.addMethod('decrease', assertDecreases); + Assertion.addMethod('decreases', assertDecreases); + + /** + * ### .by(delta[, msg]) + * + * When following an `.increase` assertion in the chain, `.by` asserts that + * the subject of the `.increase` assertion increased by the given `delta`. + * + * var myObj = {val: 1} + * , addTwo = function () { myObj.val += 2; }; + * + * expect(addTwo).to.increase(myObj, 'val').by(2); + * + * When following a `.decrease` assertion in the chain, `.by` asserts that the + * subject of the `.decrease` assertion decreased by the given `delta`. + * + * var myObj = {val: 1} + * , subtractTwo = function () { myObj.val -= 2; }; + * + * expect(subtractTwo).to.decrease(myObj, 'val').by(2); + * + * When following a `.change` assertion in the chain, `.by` asserts that the + * subject of the `.change` assertion either increased or decreased by the + * given `delta`. However, it's dangerous to use `.change.by`. The problem is + * that it creates uncertain expectations. It's often best to identify the + * exact output that's expected, and then write an assertion that only accepts + * that exact output. + * + * var myObj = {val: 1} + * , addTwo = function () { myObj.val += 2; } + * , subtractTwo = function () { myObj.val -= 2; }; + * + * expect(addTwo).to.increase(myObj, 'val').by(2); // Recommended + * expect(addTwo).to.change(myObj, 'val').by(2); // Not recommended + * + * expect(subtractTwo).to.decrease(myObj, 'val').by(2); // Recommended + * expect(subtractTwo).to.change(myObj, 'val').by(2); // Not recommended + * + * Add `.not` earlier in the chain to negate `.by`. However, it's often best + * to assert that the subject changed by its expected delta, rather than + * asserting that it didn't change by one of countless unexpected deltas. + * + * var myObj = {val: 1} + * , addTwo = function () { myObj.val += 2; }; + * + * // Recommended + * expect(addTwo).to.increase(myObj, 'val').by(2); + * + * // Not recommended + * expect(addTwo).to.increase(myObj, 'val').but.not.by(3); + * + * `.by` accepts an optional `msg` argument which is a custom error message to + * show when the assertion fails. The message can also be given as the second + * argument to `expect`. + * + * var myObj = {val: 1} + * , addTwo = function () { myObj.val += 2; }; + * + * expect(addTwo).to.increase(myObj, 'val').by(3, 'nooo why fail??'); + * expect(addTwo, 'nooo why fail??').to.increase(myObj, 'val').by(3); + * + * @name by + * @param {Number} delta + * @param {String} msg _optional_ + * @namespace BDD + * @api public + */ + + function assertDelta(delta, msg) { + if (msg) flag(this, 'message', msg); + + var msgObj = flag(this, 'deltaMsgObj'); + var initial = flag(this, 'initialDeltaValue'); + var final = flag(this, 'finalDeltaValue'); + var behavior = flag(this, 'deltaBehavior'); + var realDelta = flag(this, 'realDelta'); + + var expression; + if (behavior === 'change') { + expression = Math.abs(final - initial) === Math.abs(delta); + } else { + expression = realDelta === Math.abs(delta); + } + + this.assert( + expression + , 'expected ' + msgObj + ' to ' + behavior + ' by ' + delta + , 'expected ' + msgObj + ' to not ' + behavior + ' by ' + delta + ); + } + + Assertion.addMethod('by', assertDelta); /** * ### .extensible * - * Asserts that the target is extensible (can have new properties added to - * it). + * Asserts that the target is extensible, which means that new properties can + * be added to it. Primitives are never extensible. * - * var nonExtensibleObject = Object.preventExtensions({}); - * var sealedObject = Object.seal({}); - * var frozenObject = Object.freeze({}); + * expect({a: 1}).to.be.extensible; + * + * Add `.not` earlier in the chain to negate `.extensible`. + * + * var nonExtensibleObject = Object.preventExtensions({}) + * , sealedObject = Object.seal({}) + * , frozenObject = Object.freeze({}); * - * expect({}).to.be.extensible; * expect(nonExtensibleObject).to.not.be.extensible; * expect(sealedObject).to.not.be.extensible; * expect(frozenObject).to.not.be.extensible; + * expect(1).to.not.be.extensible; + * + * A custom error message can be given as the second argument to `expect`. + * + * expect(1, 'nooo why fail??').to.be.extensible; * * @name extensible * @namespace BDD @@ -2060,19 +3806,12 @@ module.exports = function (chai, _) { Assertion.addProperty('extensible', function() { var obj = flag(this, 'object'); - // In ES5, if the argument to this method is not an object (a primitive), then it will cause a TypeError. + // In ES5, if the argument to this method is a primitive, then it will cause a TypeError. // In ES6, a non-object argument will be treated as if it was a non-extensible ordinary object, simply return false. // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/isExtensible - // The following provides ES6 behavior when a TypeError is thrown under ES5. + // The following provides ES6 behavior for ES5 environments. - var isExtensible; - - try { - isExtensible = Object.isExtensible(obj); - } catch (err) { - if (err instanceof TypeError) isExtensible = false; - else throw err; - } + var isExtensible = obj === Object(obj) && Object.isExtensible(obj); this.assert( isExtensible @@ -2084,15 +3823,25 @@ module.exports = function (chai, _) { /** * ### .sealed * - * Asserts that the target is sealed (cannot have new properties added to it - * and its existing properties cannot be removed). + * Asserts that the target is sealed, which means that new properties can't be + * added to it, and its existing properties can't be reconfigured or deleted. + * However, it's possible that its existing properties can still be reassigned + * to different values. Primitives are always sealed. * * var sealedObject = Object.seal({}); * var frozenObject = Object.freeze({}); * * expect(sealedObject).to.be.sealed; * expect(frozenObject).to.be.sealed; - * expect({}).to.not.be.sealed; + * expect(1).to.be.sealed; + * + * Add `.not` earlier in the chain to negate `.sealed`. + * + * expect({a: 1}).to.not.be.sealed; + * + * A custom error message can be given as the second argument to `expect`. + * + * expect({a: 1}, 'nooo why fail??').to.be.sealed; * * @name sealed * @namespace BDD @@ -2102,19 +3851,12 @@ module.exports = function (chai, _) { Assertion.addProperty('sealed', function() { var obj = flag(this, 'object'); - // In ES5, if the argument to this method is not an object (a primitive), then it will cause a TypeError. + // In ES5, if the argument to this method is a primitive, then it will cause a TypeError. // In ES6, a non-object argument will be treated as if it was a sealed ordinary object, simply return true. // See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/isSealed - // The following provides ES6 behavior when a TypeError is thrown under ES5. + // The following provides ES6 behavior for ES5 environments. - var isSealed; - - try { - isSealed = Object.isSealed(obj); - } catch (err) { - if (err instanceof TypeError) isSealed = true; - else throw err; - } + var isSealed = obj === Object(obj) ? Object.isSealed(obj) : true; this.assert( isSealed @@ -2126,13 +3868,22 @@ module.exports = function (chai, _) { /** * ### .frozen * - * Asserts that the target is frozen (cannot have new properties added to it - * and its existing properties cannot be modified). + * Asserts that the target is frozen, which means that new properties can't be + * added to it, and its existing properties can't be reassigned to different + * values, reconfigured, or deleted. Primitives are always frozen. * * var frozenObject = Object.freeze({}); * * expect(frozenObject).to.be.frozen; - * expect({}).to.not.be.frozen; + * expect(1).to.be.frozen; + * + * Add `.not` earlier in the chain to negate `.frozen`. + * + * expect({a: 1}).to.not.be.frozen; + * + * A custom error message can be given as the second argument to `expect`. + * + * expect({a: 1}, 'nooo why fail??').to.be.frozen; * * @name frozen * @namespace BDD @@ -2142,19 +3893,12 @@ module.exports = function (chai, _) { Assertion.addProperty('frozen', function() { var obj = flag(this, 'object'); - // In ES5, if the argument to this method is not an object (a primitive), then it will cause a TypeError. + // In ES5, if the argument to this method is a primitive, then it will cause a TypeError. // In ES6, a non-object argument will be treated as if it was a frozen ordinary object, simply return true. // See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/isFrozen - // The following provides ES6 behavior when a TypeError is thrown under ES5. + // The following provides ES6 behavior for ES5 environments. - var isFrozen; - - try { - isFrozen = Object.isFrozen(obj); - } catch (err) { - if (err instanceof TypeError) isFrozen = true; - else throw err; - } + var isFrozen = obj === Object(obj) ? Object.isFrozen(obj) : true; this.assert( isFrozen @@ -2162,6 +3906,65 @@ module.exports = function (chai, _) { , 'expected #{this} to not be frozen' ); }); + + /** + * ### .finite + * + * Asserts that the target is a number, and isn't `NaN` or positive/negative + * `Infinity`. + * + * expect(1).to.be.finite; + * + * Add `.not` earlier in the chain to negate `.finite`. However, it's + * dangerous to do so. The problem is that it creates uncertain expectations + * by asserting that the subject either isn't a number, or that it's `NaN`, or + * that it's positive `Infinity`, or that it's negative `Infinity`. It's often + * best to identify the exact output that's expected, and then write an + * assertion that only accepts that exact output. + * + * When the target isn't expected to be a number, it's often best to assert + * that it's the expected type, rather than asserting that it isn't one of + * many unexpected types. + * + * expect('foo').to.be.a('string'); // Recommended + * expect('foo').to.not.be.finite; // Not recommended + * + * When the target is expected to be `NaN`, it's often best to assert exactly + * that. + * + * expect(NaN).to.be.NaN; // Recommended + * expect(NaN).to.not.be.finite; // Not recommended + * + * When the target is expected to be positive infinity, it's often best to + * assert exactly that. + * + * expect(Infinity).to.equal(Infinity); // Recommended + * expect(Infinity).to.not.be.finite; // Not recommended + * + * When the target is expected to be negative infinity, it's often best to + * assert exactly that. + * + * expect(-Infinity).to.equal(-Infinity); // Recommended + * expect(-Infinity).to.not.be.finite; // Not recommended + * + * A custom error message can be given as the second argument to `expect`. + * + * expect('foo', 'nooo why fail??').to.be.finite; + * + * @name finite + * @namespace BDD + * @api public + */ + + Assertion.addProperty('finite', function(msg) { + var obj = flag(this, 'object'); + + this.assert( + typeof obj === "number" && isFinite(obj) + , 'expected #{this} to be a finite number' + , 'expected #{this} to not be a finite number' + ); + }); }; },{}],6:[function(require,module,exports){ @@ -2201,7 +4004,7 @@ module.exports = function (chai, util) { */ var assert = chai.assert = function (express, errmsg) { - var test = new Assertion(null, null, chai.assert); + var test = new Assertion(null, null, chai.assert, true); test.assert( express , errmsg @@ -2249,7 +4052,7 @@ module.exports = function (chai, util) { */ assert.isOk = function (val, msg) { - new Assertion(val, msg).is.ok; + new Assertion(val, msg, assert.isOk, true).is.ok; }; /** @@ -2269,7 +4072,7 @@ module.exports = function (chai, util) { */ assert.isNotOk = function (val, msg) { - new Assertion(val, msg).is.not.ok; + new Assertion(val, msg, assert.isNotOk, true).is.not.ok; }; /** @@ -2288,7 +4091,7 @@ module.exports = function (chai, util) { */ assert.equal = function (act, exp, msg) { - var test = new Assertion(act, msg, assert.equal); + var test = new Assertion(act, msg, assert.equal, true); test.assert( exp == flag(test, 'object') @@ -2296,6 +4099,7 @@ module.exports = function (chai, util) { , 'expected #{this} to not equal #{act}' , exp , act + , true ); }; @@ -2315,7 +4119,7 @@ module.exports = function (chai, util) { */ assert.notEqual = function (act, exp, msg) { - var test = new Assertion(act, msg, assert.notEqual); + var test = new Assertion(act, msg, assert.notEqual, true); test.assert( exp != flag(test, 'object') @@ -2323,6 +4127,7 @@ module.exports = function (chai, util) { , 'expected #{this} to equal #{act}' , exp , act + , true ); }; @@ -2342,7 +4147,7 @@ module.exports = function (chai, util) { */ assert.strictEqual = function (act, exp, msg) { - new Assertion(act, msg).to.equal(exp); + new Assertion(act, msg, assert.strictEqual, true).to.equal(exp); }; /** @@ -2361,7 +4166,7 @@ module.exports = function (chai, util) { */ assert.notStrictEqual = function (act, exp, msg) { - new Assertion(act, msg).to.not.equal(exp); + new Assertion(act, msg, assert.notStrictEqual, true).to.not.equal(exp); }; /** @@ -2375,12 +4180,13 @@ module.exports = function (chai, util) { * @param {Mixed} actual * @param {Mixed} expected * @param {String} message + * @alias deepStrictEqual * @namespace Assert * @api public */ - assert.deepEqual = function (act, exp, msg) { - new Assertion(act, msg).to.eql(exp); + assert.deepEqual = assert.deepStrictEqual = function (act, exp, msg) { + new Assertion(act, msg, assert.deepEqual, true).to.eql(exp); }; /** @@ -2399,13 +4205,13 @@ module.exports = function (chai, util) { */ assert.notDeepEqual = function (act, exp, msg) { - new Assertion(act, msg).to.not.eql(exp); + new Assertion(act, msg, assert.notDeepEqual, true).to.not.eql(exp); }; /** * ### .isAbove(valueToCheck, valueToBeAbove, [message]) * - * Asserts `valueToCheck` is strictly greater than (>) `valueToBeAbove` + * Asserts `valueToCheck` is strictly greater than (>) `valueToBeAbove`. * * assert.isAbove(5, 2, '5 is strictly greater than 2'); * @@ -2418,13 +4224,13 @@ module.exports = function (chai, util) { */ assert.isAbove = function (val, abv, msg) { - new Assertion(val, msg).to.be.above(abv); + new Assertion(val, msg, assert.isAbove, true).to.be.above(abv); }; /** * ### .isAtLeast(valueToCheck, valueToBeAtLeast, [message]) * - * Asserts `valueToCheck` is greater than or equal to (>=) `valueToBeAtLeast` + * Asserts `valueToCheck` is greater than or equal to (>=) `valueToBeAtLeast`. * * assert.isAtLeast(5, 2, '5 is greater or equal to 2'); * assert.isAtLeast(3, 3, '3 is greater or equal to 3'); @@ -2438,13 +4244,13 @@ module.exports = function (chai, util) { */ assert.isAtLeast = function (val, atlst, msg) { - new Assertion(val, msg).to.be.least(atlst); + new Assertion(val, msg, assert.isAtLeast, true).to.be.least(atlst); }; /** * ### .isBelow(valueToCheck, valueToBeBelow, [message]) * - * Asserts `valueToCheck` is strictly less than (<) `valueToBeBelow` + * Asserts `valueToCheck` is strictly less than (<) `valueToBeBelow`. * * assert.isBelow(3, 6, '3 is strictly less than 6'); * @@ -2457,13 +4263,13 @@ module.exports = function (chai, util) { */ assert.isBelow = function (val, blw, msg) { - new Assertion(val, msg).to.be.below(blw); + new Assertion(val, msg, assert.isBelow, true).to.be.below(blw); }; /** * ### .isAtMost(valueToCheck, valueToBeAtMost, [message]) * - * Asserts `valueToCheck` is less than or equal to (<=) `valueToBeAtMost` + * Asserts `valueToCheck` is less than or equal to (<=) `valueToBeAtMost`. * * assert.isAtMost(3, 6, '3 is less than or equal to 6'); * assert.isAtMost(4, 4, '4 is less than or equal to 4'); @@ -2477,7 +4283,7 @@ module.exports = function (chai, util) { */ assert.isAtMost = function (val, atmst, msg) { - new Assertion(val, msg).to.be.most(atmst); + new Assertion(val, msg, assert.isAtMost, true).to.be.most(atmst); }; /** @@ -2496,7 +4302,7 @@ module.exports = function (chai, util) { */ assert.isTrue = function (val, msg) { - new Assertion(val, msg).is['true']; + new Assertion(val, msg, assert.isTrue, true).is['true']; }; /** @@ -2515,7 +4321,7 @@ module.exports = function (chai, util) { */ assert.isNotTrue = function (val, msg) { - new Assertion(val, msg).to.not.equal(true); + new Assertion(val, msg, assert.isNotTrue, true).to.not.equal(true); }; /** @@ -2534,7 +4340,7 @@ module.exports = function (chai, util) { */ assert.isFalse = function (val, msg) { - new Assertion(val, msg).is['false']; + new Assertion(val, msg, assert.isFalse, true).is['false']; }; /** @@ -2553,7 +4359,7 @@ module.exports = function (chai, util) { */ assert.isNotFalse = function (val, msg) { - new Assertion(val, msg).to.not.equal(false); + new Assertion(val, msg, assert.isNotFalse, true).to.not.equal(false); }; /** @@ -2571,7 +4377,7 @@ module.exports = function (chai, util) { */ assert.isNull = function (val, msg) { - new Assertion(val, msg).to.equal(null); + new Assertion(val, msg, assert.isNull, true).to.equal(null); }; /** @@ -2590,14 +4396,15 @@ module.exports = function (chai, util) { */ assert.isNotNull = function (val, msg) { - new Assertion(val, msg).to.not.equal(null); + new Assertion(val, msg, assert.isNotNull, true).to.not.equal(null); }; /** * ### .isNaN - * Asserts that value is NaN * - * assert.isNaN('foo', 'foo is NaN'); + * Asserts that value is NaN. + * + * assert.isNaN(NaN, 'NaN is NaN'); * * @name isNaN * @param {Mixed} value @@ -2607,14 +4414,15 @@ module.exports = function (chai, util) { */ assert.isNaN = function (val, msg) { - new Assertion(val, msg).to.be.NaN; + new Assertion(val, msg, assert.isNaN, true).to.be.NaN; }; /** * ### .isNotNaN - * Asserts that value is not NaN * - * assert.isNotNaN(4, '4 is not NaN'); + * Asserts that value is not NaN. + * + * assert.isNotNaN(4, '4 is not NaN'); * * @name isNotNaN * @param {Mixed} value @@ -2623,7 +4431,49 @@ module.exports = function (chai, util) { * @api public */ assert.isNotNaN = function (val, msg) { - new Assertion(val, msg).not.to.be.NaN; + new Assertion(val, msg, assert.isNotNaN, true).not.to.be.NaN; + }; + + /** + * ### .exists + * + * Asserts that the target is neither `null` nor `undefined`. + * + * var foo = 'hi'; + * + * assert.exists(foo, 'foo is neither `null` nor `undefined`'); + * + * @name exists + * @param {Mixed} value + * @param {String} message + * @namespace Assert + * @api public + */ + + assert.exists = function (val, msg) { + new Assertion(val, msg, assert.exists, true).to.exist; + }; + + /** + * ### .notExists + * + * Asserts that the target is either `null` or `undefined`. + * + * var bar = null + * , baz; + * + * assert.notExists(bar); + * assert.notExists(baz, 'baz is either null or undefined'); + * + * @name notExists + * @param {Mixed} value + * @param {String} message + * @namespace Assert + * @api public + */ + + assert.notExists = function (val, msg) { + new Assertion(val, msg, assert.notExists, true).to.not.exist; }; /** @@ -2642,7 +4492,7 @@ module.exports = function (chai, util) { */ assert.isUndefined = function (val, msg) { - new Assertion(val, msg).to.equal(undefined); + new Assertion(val, msg, assert.isUndefined, true).to.equal(undefined); }; /** @@ -2661,7 +4511,7 @@ module.exports = function (chai, util) { */ assert.isDefined = function (val, msg) { - new Assertion(val, msg).to.not.equal(undefined); + new Assertion(val, msg, assert.isDefined, true).to.not.equal(undefined); }; /** @@ -2680,7 +4530,7 @@ module.exports = function (chai, util) { */ assert.isFunction = function (val, msg) { - new Assertion(val, msg).to.be.a('function'); + new Assertion(val, msg, assert.isFunction, true).to.be.a('function'); }; /** @@ -2699,7 +4549,7 @@ module.exports = function (chai, util) { */ assert.isNotFunction = function (val, msg) { - new Assertion(val, msg).to.not.be.a('function'); + new Assertion(val, msg, assert.isNotFunction, true).to.not.be.a('function'); }; /** @@ -2719,7 +4569,7 @@ module.exports = function (chai, util) { */ assert.isObject = function (val, msg) { - new Assertion(val, msg).to.be.a('object'); + new Assertion(val, msg, assert.isObject, true).to.be.a('object'); }; /** @@ -2739,7 +4589,7 @@ module.exports = function (chai, util) { */ assert.isNotObject = function (val, msg) { - new Assertion(val, msg).to.not.be.a('object'); + new Assertion(val, msg, assert.isNotObject, true).to.not.be.a('object'); }; /** @@ -2758,7 +4608,7 @@ module.exports = function (chai, util) { */ assert.isArray = function (val, msg) { - new Assertion(val, msg).to.be.an('array'); + new Assertion(val, msg, assert.isArray, true).to.be.an('array'); }; /** @@ -2777,7 +4627,7 @@ module.exports = function (chai, util) { */ assert.isNotArray = function (val, msg) { - new Assertion(val, msg).to.not.be.an('array'); + new Assertion(val, msg, assert.isNotArray, true).to.not.be.an('array'); }; /** @@ -2796,7 +4646,7 @@ module.exports = function (chai, util) { */ assert.isString = function (val, msg) { - new Assertion(val, msg).to.be.a('string'); + new Assertion(val, msg, assert.isString, true).to.be.a('string'); }; /** @@ -2815,7 +4665,7 @@ module.exports = function (chai, util) { */ assert.isNotString = function (val, msg) { - new Assertion(val, msg).to.not.be.a('string'); + new Assertion(val, msg, assert.isNotString, true).to.not.be.a('string'); }; /** @@ -2834,7 +4684,7 @@ module.exports = function (chai, util) { */ assert.isNumber = function (val, msg) { - new Assertion(val, msg).to.be.a('number'); + new Assertion(val, msg, assert.isNumber, true).to.be.a('number'); }; /** @@ -2853,7 +4703,28 @@ module.exports = function (chai, util) { */ assert.isNotNumber = function (val, msg) { - new Assertion(val, msg).to.not.be.a('number'); + new Assertion(val, msg, assert.isNotNumber, true).to.not.be.a('number'); + }; + + /** + * ### .isFinite(value, [message]) + * + * Asserts that `value` is a finite number. Unlike `.isNumber`, this will fail for `NaN` and `Infinity`. + * + * var cups = 2; + * assert.isFinite(cups, 'how many cups'); + * + * assert.isFinite(NaN); // throws + * + * @name isFinite + * @param {Number} value + * @param {String} message + * @namespace Assert + * @api public + */ + + assert.isFinite = function (val, msg) { + new Assertion(val, msg, assert.isFinite, true).to.be.finite; }; /** @@ -2875,7 +4746,7 @@ module.exports = function (chai, util) { */ assert.isBoolean = function (val, msg) { - new Assertion(val, msg).to.be.a('boolean'); + new Assertion(val, msg, assert.isBoolean, true).to.be.a('boolean'); }; /** @@ -2897,7 +4768,7 @@ module.exports = function (chai, util) { */ assert.isNotBoolean = function (val, msg) { - new Assertion(val, msg).to.not.be.a('boolean'); + new Assertion(val, msg, assert.isNotBoolean, true).to.not.be.a('boolean'); }; /** @@ -2922,7 +4793,7 @@ module.exports = function (chai, util) { */ assert.typeOf = function (val, type, msg) { - new Assertion(val, msg).to.be.a(type); + new Assertion(val, msg, assert.typeOf, true).to.be.a(type); }; /** @@ -2942,7 +4813,7 @@ module.exports = function (chai, util) { */ assert.notTypeOf = function (val, type, msg) { - new Assertion(val, msg).to.not.be.a(type); + new Assertion(val, msg, assert.notTypeOf, true).to.not.be.a(type); }; /** @@ -2964,7 +4835,7 @@ module.exports = function (chai, util) { */ assert.instanceOf = function (val, type, msg) { - new Assertion(val, msg).to.be.instanceOf(type); + new Assertion(val, msg, assert.instanceOf, true).to.be.instanceOf(type); }; /** @@ -2986,17 +4857,32 @@ module.exports = function (chai, util) { */ assert.notInstanceOf = function (val, type, msg) { - new Assertion(val, msg).to.not.be.instanceOf(type); + new Assertion(val, msg, assert.notInstanceOf, true) + .to.not.be.instanceOf(type); }; /** * ### .include(haystack, needle, [message]) * - * Asserts that `haystack` includes `needle`. Works - * for strings and arrays. + * 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('foobar', 'bar', 'foobar contains string "bar"'); - * assert.include([ 1, 2, 3 ], 3, 'array contains value'); + * 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 @@ -3007,17 +4893,32 @@ module.exports = function (chai, util) { */ assert.include = function (exp, inc, msg) { - new Assertion(exp, msg, assert.include).include(inc); + new Assertion(exp, msg, assert.include, true).include(inc); }; /** * ### .notInclude(haystack, needle, [message]) * - * Asserts that `haystack` does not include `needle`. Works - * for strings and arrays. + * 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('foobar', 'baz', 'string not include substring'); - * assert.notInclude([ 1, 2, 3 ], 4, 'array not include contain value'); + * 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 @@ -3028,7 +4929,57 @@ module.exports = function (chai, util) { */ assert.notInclude = function (exp, inc, msg) { - new Assertion(exp, msg, assert.notInclude).not.include(inc); + new Assertion(exp, msg, assert.notInclude, true).not.include(inc); + }; + + /** + * ### .deepInclude(haystack, needle, [message]) + * + * Asserts that `haystack` includes `needle`. Can be used to assert the + * inclusion of a value in an array or a subset of properties in an object. + * Deep equality is used. + * + * var obj1 = {a: 1} + * , obj2 = {b: 2}; + * assert.deepInclude([obj1, obj2], {a: 1}); + * assert.deepInclude({foo: obj1, bar: obj2}, {foo: {a: 1}}); + * assert.deepInclude({foo: obj1, bar: obj2}, {foo: {a: 1}, bar: {b: 2}}); + * + * @name deepInclude + * @param {Array|String} haystack + * @param {Mixed} needle + * @param {String} message + * @namespace Assert + * @api public + */ + + assert.deepInclude = function (exp, inc, msg) { + new Assertion(exp, msg, assert.deepInclude, true).deep.include(inc); + }; + + /** + * ### .notDeepInclude(haystack, needle, [message]) + * + * Asserts that `haystack` does not include `needle`. Can be used to assert + * the absence of a value in an array or a subset of properties in an object. + * Deep equality is used. + * + * var obj1 = {a: 1} + * , obj2 = {b: 2}; + * assert.notDeepInclude([obj1, obj2], {a: 9}); + * assert.notDeepInclude({foo: obj1, bar: obj2}, {foo: {a: 9}}); + * assert.notDeepInclude({foo: obj1, bar: obj2}, {foo: {a: 1}, bar: {b: 9}}); + * + * @name notDeepInclude + * @param {Array|String} haystack + * @param {Mixed} needle + * @param {String} message + * @namespace Assert + * @api public + */ + + assert.notDeepInclude = function (exp, inc, msg) { + new Assertion(exp, msg, assert.notDeepInclude, true).not.deep.include(inc); }; /** @@ -3047,7 +4998,7 @@ module.exports = function (chai, util) { */ assert.match = function (exp, re, msg) { - new Assertion(exp, msg).to.match(re); + new Assertion(exp, msg, assert.match, true).to.match(re); }; /** @@ -3066,15 +5017,17 @@ module.exports = function (chai, util) { */ assert.notMatch = function (exp, re, msg) { - new Assertion(exp, msg).to.not.match(re); + new Assertion(exp, msg, assert.notMatch, true).to.not.match(re); }; /** * ### .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 @@ -3085,13 +5038,14 @@ module.exports = function (chai, util) { */ assert.property = function (obj, prop, msg) { - new Assertion(obj, msg).to.have.property(prop); + new Assertion(obj, msg, assert.property, true).to.have.property(prop); }; /** * ### .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'); * @@ -3104,54 +5058,16 @@ module.exports = function (chai, util) { */ assert.notProperty = function (obj, prop, msg) { - new Assertion(obj, msg).to.not.have.property(prop); - }; - - /** - * ### .deepProperty(object, property, [message]) - * - * Asserts that `object` has a property named by `property`, which can be a - * string using dot- and bracket-notation for deep reference. - * - * assert.deepProperty({ tea: { green: 'matcha' }}, 'tea.green'); - * - * @name deepProperty - * @param {Object} object - * @param {String} property - * @param {String} message - * @namespace Assert - * @api public - */ - - assert.deepProperty = function (obj, prop, msg) { - new Assertion(obj, msg).to.have.deep.property(prop); - }; - - /** - * ### .notDeepProperty(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 deep reference. - * - * assert.notDeepProperty({ tea: { green: 'matcha' }}, 'tea.oolong'); - * - * @name notDeepProperty - * @param {Object} object - * @param {String} property - * @param {String} message - * @namespace Assert - * @api public - */ - - assert.notDeepProperty = function (obj, prop, msg) { - new Assertion(obj, msg).to.not.have.deep.property(prop); + new Assertion(obj, msg, assert.notProperty, true) + .to.not.have.property(prop); }; /** * ### .propertyVal(object, property, value, [message]) * - * Asserts that `object` has a property named by `property` with value given - * by `value`. + * 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'); * @@ -3165,18 +5081,21 @@ module.exports = function (chai, util) { */ assert.propertyVal = function (obj, prop, val, msg) { - new Assertion(obj, msg).to.have.property(prop, val); + new Assertion(obj, msg, assert.propertyVal, true) + .to.have.property(prop, val); }; /** - * ### .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 direct or inherited property named + * by `property` with value given by `value`. Uses a strict equality check + * (===). * - * 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 @@ -3185,18 +5104,18 @@ module.exports = function (chai, util) { * @api public */ - assert.propertyNotVal = function (obj, prop, val, msg) { - new Assertion(obj, msg).to.not.have.property(prop, val); + assert.notPropertyVal = function (obj, prop, val, msg) { + new Assertion(obj, msg, assert.notPropertyVal, true) + .to.not.have.property(prop, val); }; /** * ### .deepPropertyVal(object, property, value, [message]) * - * Asserts that `object` has a property named by `property` with value given - * by `value`. `property` can use dot- and bracket-notation for deep - * reference. + * 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'); + * assert.deepPropertyVal({ tea: { green: 'matcha' } }, 'tea', { green: 'matcha' }); * * @name deepPropertyVal * @param {Object} object @@ -3208,19 +5127,21 @@ module.exports = function (chai, util) { */ assert.deepPropertyVal = function (obj, prop, val, msg) { - new Assertion(obj, msg).to.have.deep.property(prop, val); + new Assertion(obj, msg, assert.deepPropertyVal, true) + .to.have.deep.property(prop, val); }; /** - * ### .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 direct or inherited property named + * by `property` with value given by `value`. Uses a deep equality check. * - * assert.deepPropertyNotVal({ tea: { green: 'matcha' }}, 'tea.green', 'konacha'); + * 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 deepPropertyNotVal + * @name notDeepPropertyVal * @param {Object} object * @param {String} property * @param {Mixed} value @@ -3229,10 +5150,283 @@ module.exports = function (chai, util) { * @api public */ - assert.deepPropertyNotVal = function (obj, prop, val, msg) { - new Assertion(obj, msg).to.not.have.deep.property(prop, val); + assert.notDeepPropertyVal = function (obj, prop, val, msg) { + new Assertion(obj, msg, assert.notDeepPropertyVal, true) + .to.not.have.deep.property(prop, val); }; + /** + * ### .ownProperty(object, property, [message]) + * + * Asserts that `object` has a direct property named by `property`. Inherited + * properties aren't checked. + * + * assert.ownProperty({ tea: { green: 'matcha' }}, 'tea'); + * + * @name ownProperty + * @param {Object} object + * @param {String} property + * @param {String} message + * @api public + */ + + assert.ownProperty = function (obj, prop, msg) { + new Assertion(obj, msg, assert.ownProperty, true) + .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, assert.notOwnProperty, true) + .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 + * @api public + */ + + assert.ownPropertyVal = function (obj, prop, value, msg) { + new Assertion(obj, msg, assert.ownPropertyVal, true) + .to.have.own.property(prop, value); + }; + + /** + * ### .notOwnPropertyVal(object, property, value, [message]) + * + * 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.notOwnPropertyVal({ tea: 'is better'}, 'tea', 'is worse'); + * assert.notOwnPropertyVal({}, 'toString', Object.prototype.toString); + * + * @name notOwnPropertyVal + * @param {Object} object + * @param {String} property + * @param {Mixed} value + * @param {String} message + * @api public + */ + + assert.notOwnPropertyVal = function (obj, prop, value, msg) { + new Assertion(obj, msg, assert.notOwnPropertyVal, true) + .to.not.have.own.property(prop, value); + }; + + /** + * ### .deepOwnPropertyVal(object, property, value, [message]) + * + * 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.deepOwnPropertyVal({ tea: { green: 'matcha' } }, 'tea', { green: 'matcha' }); + * + * @name deepOwnPropertyVal + * @param {Object} object + * @param {String} property + * @param {Mixed} value + * @param {String} message + * @api public + */ + + assert.deepOwnPropertyVal = function (obj, prop, value, msg) { + new Assertion(obj, msg, assert.deepOwnPropertyVal, true) + .to.have.deep.own.property(prop, value); + }; + + /** + * ### .notDeepOwnPropertyVal(object, property, value, [message]) + * + * 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.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 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, assert.notDeepOwnPropertyVal, true) + .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.nestedProperty = function (obj, prop, msg) { + new Assertion(obj, msg, assert.nestedProperty, true) + .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. The + * property cannot exist on the object nor anywhere in its prototype chain. + * + * 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, assert.notNestedProperty, true) + .to.not.have.nested.property(prop); + }; + + /** + * ### .nestedPropertyVal(object, property, value, [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 (===). + * + * assert.nestedPropertyVal({ tea: { green: 'matcha' }}, 'tea.green', 'matcha'); + * + * @name nestedPropertyVal + * @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, assert.nestedPropertyVal, true) + .to.have.nested.property(prop, val); + }; + + /** + * ### .notNestedPropertyVal(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 (===). + * + * assert.notNestedPropertyVal({ tea: { green: 'matcha' }}, 'tea.green', 'konacha'); + * assert.notNestedPropertyVal({ tea: { green: 'matcha' }}, 'coffee.green', 'matcha'); + * + * @name notNestedPropertyVal + * @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, assert.notNestedPropertyVal, true) + .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, assert.deepNestedPropertyVal, true) + .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, assert.notDeepNestedPropertyVal, true) + .to.not.have.deep.nested.property(prop, val); + } + /** * ### .lengthOf(object, length, [message]) * @@ -3250,70 +5444,352 @@ module.exports = function (chai, util) { */ assert.lengthOf = function (exp, len, msg) { - new Assertion(exp, msg).to.have.length(len); + new Assertion(exp, msg, assert.lengthOf, true).to.have.lengthOf(len); }; /** - * ### .throws(function, [constructor/string/regexp], [string/regexp], [message]) + * ### .hasAnyKeys(object, [keys], [message]) * - * Asserts that `function` will throw an error that is an instance of - * `constructor`, or alternately that it will throw an error with message - * matching `regexp`. + * Asserts that `object` has at least one of the `keys` provided. + * You can also provide a single object instead of a `keys` array and its keys + * will be used as the expected set of keys. + * + * assert.hasAnyKey({foo: 1, bar: 2, baz: 3}, ['foo', 'iDontExist', 'baz']); + * assert.hasAnyKey({foo: 1, bar: 2, baz: 3}, {foo: 30, iDontExist: 99, baz: 1337]); + * assert.hasAnyKey(new Map([[{foo: 1}, 'bar'], ['key', 'value']]), [{foo: 1}, 'thisKeyDoesNotExist']); + * assert.hasAnyKey(new Set([{foo: 'bar'}, 'anotherKey'], [{foo: 'bar'}, 'thisKeyDoesNotExist']); + * + * @name hasAnyKeys + * @param {Mixed} object + * @param {Array|Object} keys + * @param {String} message + * @namespace Assert + * @api public + */ + + assert.hasAnyKeys = function (obj, keys, msg) { + new Assertion(obj, msg, assert.hasAnyKeys, true).to.have.any.keys(keys); + } + + /** + * ### .hasAllKeys(object, [keys], [message]) + * + * Asserts that `object` has all and only all of the `keys` provided. + * You can also provide a single object instead of a `keys` array and its keys + * will be used as the expected set of keys. + * + * assert.hasAllKeys({foo: 1, bar: 2, baz: 3}, ['foo', 'bar', 'baz']); + * assert.hasAllKeys({foo: 1, bar: 2, baz: 3}, {foo: 30, bar: 99, baz: 1337]); + * assert.hasAllKeys(new Map([[{foo: 1}, 'bar'], ['key', 'value']]), [{foo: 1}, 'key']); + * assert.hasAllKeys(new Set([{foo: 'bar'}, 'anotherKey'], [{foo: 'bar'}, 'anotherKey']); + * + * @name hasAllKeys + * @param {Mixed} object + * @param {String[]} keys + * @param {String} message + * @namespace Assert + * @api public + */ + + assert.hasAllKeys = function (obj, keys, msg) { + new Assertion(obj, msg, assert.hasAllKeys, true).to.have.all.keys(keys); + } + + /** + * ### .containsAllKeys(object, [keys], [message]) + * + * Asserts that `object` has all of the `keys` provided but may have more keys not listed. + * You can also provide a single object instead of a `keys` array and its keys + * will be used as the expected set of keys. + * + * assert.containsAllKeys({foo: 1, bar: 2, baz: 3}, ['foo', 'baz']); + * assert.containsAllKeys({foo: 1, bar: 2, baz: 3}, ['foo', 'bar', 'baz']); + * assert.containsAllKeys({foo: 1, bar: 2, baz: 3}, {foo: 30, baz: 1337}); + * assert.containsAllKeys({foo: 1, bar: 2, baz: 3}, {foo: 30, bar: 99, baz: 1337}); + * assert.containsAllKeys(new Map([[{foo: 1}, 'bar'], ['key', 'value']]), [{foo: 1}]); + * assert.containsAllKeys(new Map([[{foo: 1}, 'bar'], ['key', 'value']]), [{foo: 1}, 'key']); + * assert.containsAllKeys(new Set([{foo: 'bar'}, 'anotherKey'], [{foo: 'bar'}]); + * assert.containsAllKeys(new Set([{foo: 'bar'}, 'anotherKey'], [{foo: 'bar'}, 'anotherKey']); + * + * @name containsAllKeys + * @param {Mixed} object + * @param {String[]} keys + * @param {String} message + * @namespace Assert + * @api public + */ + + assert.containsAllKeys = function (obj, keys, msg) { + new Assertion(obj, msg, assert.containsAllKeys, true) + .to.contain.all.keys(keys); + } + + /** + * ### .doesNotHaveAnyKeys(object, [keys], [message]) + * + * Asserts that `object` has none of the `keys` provided. + * You can also provide a single object instead of a `keys` array and its keys + * will be used as the expected set of keys. + * + * assert.doesNotHaveAnyKeys({foo: 1, bar: 2, baz: 3}, ['one', 'two', 'example']); + * assert.doesNotHaveAnyKeys({foo: 1, bar: 2, baz: 3}, {one: 1, two: 2, example: 'foo'}); + * assert.doesNotHaveAnyKeys(new Map([[{foo: 1}, 'bar'], ['key', 'value']]), [{one: 'two'}, 'example']); + * assert.doesNotHaveAnyKeys(new Set([{foo: 'bar'}, 'anotherKey'], [{one: 'two'}, 'example']); + * + * @name doesNotHaveAnyKeys + * @param {Mixed} object + * @param {String[]} keys + * @param {String} message + * @namespace Assert + * @api public + */ + + assert.doesNotHaveAnyKeys = function (obj, keys, msg) { + new Assertion(obj, msg, assert.doesNotHaveAnyKeys, true) + .to.not.have.any.keys(keys); + } + + /** + * ### .doesNotHaveAllKeys(object, [keys], [message]) + * + * Asserts that `object` does not have at least one of the `keys` provided. + * You can also provide a single object instead of a `keys` array and its keys + * will be used as the expected set of keys. + * + * assert.doesNotHaveAllKeys({foo: 1, bar: 2, baz: 3}, ['one', 'two', 'example']); + * assert.doesNotHaveAllKeys({foo: 1, bar: 2, baz: 3}, {one: 1, two: 2, example: 'foo'}); + * assert.doesNotHaveAllKeys(new Map([[{foo: 1}, 'bar'], ['key', 'value']]), [{one: 'two'}, 'example']); + * assert.doesNotHaveAllKeys(new Set([{foo: 'bar'}, 'anotherKey'], [{one: 'two'}, 'example']); + * + * @name doesNotHaveAllKeys + * @param {Mixed} object + * @param {String[]} keys + * @param {String} message + * @namespace Assert + * @api public + */ + + assert.doesNotHaveAllKeys = function (obj, keys, msg) { + new Assertion(obj, msg, assert.doesNotHaveAllKeys, true) + .to.not.have.all.keys(keys); + } + + /** + * ### .hasAnyDeepKeys(object, [keys], [message]) + * + * Asserts that `object` has at least one of the `keys` provided. + * Since Sets and Maps can have objects as keys you can use this assertion to perform + * a deep comparison. + * You can also provide a single object instead of a `keys` array and its keys + * will be used as the expected set of keys. + * + * assert.hasAnyDeepKeys(new Map([[{one: 'one'}, 'valueOne'], [1, 2]]), {one: 'one'}); + * assert.hasAnyDeepKeys(new Map([[{one: 'one'}, 'valueOne'], [1, 2]]), [{one: 'one'}, {two: 'two'}]); + * assert.hasAnyDeepKeys(new Map([[{one: 'one'}, 'valueOne'], [{two: 'two'}, 'valueTwo']]), [{one: 'one'}, {two: 'two'}]); + * assert.hasAnyDeepKeys(new Set([{one: 'one'}, {two: 'two'}]), {one: 'one'}); + * assert.hasAnyDeepKeys(new Set([{one: 'one'}, {two: 'two'}]), [{one: 'one'}, {three: 'three'}]); + * assert.hasAnyDeepKeys(new Set([{one: 'one'}, {two: 'two'}]), [{one: 'one'}, {two: 'two'}]); + * + * @name doesNotHaveAllKeys + * @param {Mixed} object + * @param {Array|Object} keys + * @param {String} message + * @namespace Assert + * @api public + */ + + assert.hasAnyDeepKeys = function (obj, keys, msg) { + new Assertion(obj, msg, assert.hasAnyDeepKeys, true) + .to.have.any.deep.keys(keys); + } + + /** + * ### .hasAllDeepKeys(object, [keys], [message]) + * + * Asserts that `object` has all and only all of the `keys` provided. + * Since Sets and Maps can have objects as keys you can use this assertion to perform + * a deep comparison. + * You can also provide a single object instead of a `keys` array and its keys + * will be used as the expected set of keys. + * + * assert.hasAllDeepKeys(new Map([[{one: 'one'}, 'valueOne']]), {one: 'one'}); + * assert.hasAllDeepKeys(new Map([[{one: 'one'}, 'valueOne'], [{two: 'two'}, 'valueTwo']]), [{one: 'one'}, {two: 'two'}]); + * assert.hasAllDeepKeys(new Set([{one: 'one'}]), {one: 'one'}); + * assert.hasAllDeepKeys(new Set([{one: 'one'}, {two: 'two'}]), [{one: 'one'}, {two: 'two'}]); + * + * @name hasAllDeepKeys + * @param {Mixed} object + * @param {Array|Object} keys + * @param {String} message + * @namespace Assert + * @api public + */ + + assert.hasAllDeepKeys = function (obj, keys, msg) { + new Assertion(obj, msg, assert.hasAllDeepKeys, true) + .to.have.all.deep.keys(keys); + } + + /** + * ### .containsAllDeepKeys(object, [keys], [message]) + * + * Asserts that `object` contains all of the `keys` provided. + * Since Sets and Maps can have objects as keys you can use this assertion to perform + * a deep comparison. + * You can also provide a single object instead of a `keys` array and its keys + * will be used as the expected set of keys. + * + * assert.containsAllDeepKeys(new Map([[{one: 'one'}, 'valueOne'], [1, 2]]), {one: 'one'}); + * assert.containsAllDeepKeys(new Map([[{one: 'one'}, 'valueOne'], [{two: 'two'}, 'valueTwo']]), [{one: 'one'}, {two: 'two'}]); + * assert.containsAllDeepKeys(new Set([{one: 'one'}, {two: 'two'}]), {one: 'one'}); + * assert.containsAllDeepKeys(new Set([{one: 'one'}, {two: 'two'}]), [{one: 'one'}, {two: 'two'}]); + * + * @name containsAllDeepKeys + * @param {Mixed} object + * @param {Array|Object} keys + * @param {String} message + * @namespace Assert + * @api public + */ + + assert.containsAllDeepKeys = function (obj, keys, msg) { + new Assertion(obj, msg, assert.containsAllDeepKeys, true) + .to.contain.all.deep.keys(keys); + } + + /** + * ### .doesNotHaveAnyDeepKeys(object, [keys], [message]) + * + * Asserts that `object` has none of the `keys` provided. + * Since Sets and Maps can have objects as keys you can use this assertion to perform + * a deep comparison. + * You can also provide a single object instead of a `keys` array and its keys + * will be used as the expected set of keys. + * + * assert.doesNotHaveAnyDeepKeys(new Map([[{one: 'one'}, 'valueOne'], [1, 2]]), {thisDoesNot: 'exist'}); + * assert.doesNotHaveAnyDeepKeys(new Map([[{one: 'one'}, 'valueOne'], [{two: 'two'}, 'valueTwo']]), [{twenty: 'twenty'}, {fifty: 'fifty'}]); + * assert.doesNotHaveAnyDeepKeys(new Set([{one: 'one'}, {two: 'two'}]), {twenty: 'twenty'}); + * assert.doesNotHaveAnyDeepKeys(new Set([{one: 'one'}, {two: 'two'}]), [{twenty: 'twenty'}, {fifty: 'fifty'}]); + * + * @name doesNotHaveAnyDeepKeys + * @param {Mixed} object + * @param {Array|Object} keys + * @param {String} message + * @namespace Assert + * @api public + */ + + assert.doesNotHaveAnyDeepKeys = function (obj, keys, msg) { + new Assertion(obj, msg, assert.doesNotHaveAnyDeepKeys, true) + .to.not.have.any.deep.keys(keys); + } + + /** + * ### .doesNotHaveAllDeepKeys(object, [keys], [message]) + * + * Asserts that `object` does not have at least one of the `keys` provided. + * Since Sets and Maps can have objects as keys you can use this assertion to perform + * a deep comparison. + * You can also provide a single object instead of a `keys` array and its keys + * will be used as the expected set of keys. + * + * assert.doesNotHaveAllDeepKeys(new Map([[{one: 'one'}, 'valueOne'], [1, 2]]), {thisDoesNot: 'exist'}); + * assert.doesNotHaveAllDeepKeys(new Map([[{one: 'one'}, 'valueOne'], [{two: 'two'}, 'valueTwo']]), [{twenty: 'twenty'}, {one: 'one'}]); + * assert.doesNotHaveAllDeepKeys(new Set([{one: 'one'}, {two: 'two'}]), {twenty: 'twenty'}); + * assert.doesNotHaveAllDeepKeys(new Set([{one: 'one'}, {two: 'two'}]), [{one: 'one'}, {fifty: 'fifty'}]); + * + * @name doesNotHaveAllDeepKeys + * @param {Mixed} object + * @param {Array|Object} keys + * @param {String} message + * @namespace Assert + * @api public + */ + + assert.doesNotHaveAllDeepKeys = function (obj, keys, msg) { + new Assertion(obj, msg, assert.doesNotHaveAllDeepKeys, true) + .to.not.have.all.deep.keys(keys); + } + + /** + * ### .throws(fn, [errorLike/string/regexp], [string/regexp], [message]) + * + * If `errorLike` is an `Error` constructor, asserts that `fn` will throw an error that is an + * instance of `errorLike`. + * If `errorLike` is an `Error` instance, asserts that the error thrown is the same + * instance as `errorLike`. + * If `errMsgMatcher` is provided, it also asserts that the error thrown will have a + * message matching `errMsgMatcher`. * * assert.throws(fn, 'function throws a reference error'); * assert.throws(fn, /function throws a reference error/); * assert.throws(fn, ReferenceError); - * assert.throws(fn, ReferenceError, 'function throws a reference error'); - * assert.throws(fn, ReferenceError, /function throws a reference error/); + * assert.throws(fn, errorInstance); + * assert.throws(fn, ReferenceError, 'Error thrown must be a ReferenceError and have this msg'); + * assert.throws(fn, errorInstance, 'Error thrown must be the same errorInstance and have this msg'); + * assert.throws(fn, ReferenceError, /Error thrown must be a ReferenceError and match this/); + * assert.throws(fn, errorInstance, /Error thrown must be the same errorInstance and match this/); * * @name throws * @alias throw * @alias Throw - * @param {Function} function - * @param {ErrorConstructor} constructor - * @param {RegExp} regexp + * @param {Function} fn + * @param {ErrorConstructor|Error} errorLike + * @param {RegExp|String} errMsgMatcher * @param {String} message * @see https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Error#Error_types * @namespace Assert * @api public */ - assert.throws = function (fn, errt, errs, msg) { - if ('string' === typeof errt || errt instanceof RegExp) { - errs = errt; - errt = null; + assert.throws = function (fn, errorLike, errMsgMatcher, msg) { + if ('string' === typeof errorLike || errorLike instanceof RegExp) { + errMsgMatcher = errorLike; + errorLike = null; } - var assertErr = new Assertion(fn, msg).to.throw(errt, errs); + var assertErr = new Assertion(fn, msg, assert.throws, true) + .to.throw(errorLike, errMsgMatcher); return flag(assertErr, 'object'); }; /** - * ### .doesNotThrow(function, [constructor/regexp], [message]) + * ### .doesNotThrow(fn, [errorLike/string/regexp], [string/regexp], [message]) * - * Asserts that `function` will _not_ throw an error that is an instance of - * `constructor`, or alternately that it will not throw an error with message - * matching `regexp`. + * If `errorLike` is an `Error` constructor, asserts that `fn` will _not_ throw an error that is an + * instance of `errorLike`. + * If `errorLike` is an `Error` instance, asserts that the error thrown is _not_ the same + * instance as `errorLike`. + * If `errMsgMatcher` is provided, it also asserts that the error thrown will _not_ have a + * message matching `errMsgMatcher`. * - * assert.doesNotThrow(fn, Error, 'function does not throw'); + * assert.doesNotThrow(fn, 'Any Error thrown must not have this message'); + * assert.doesNotThrow(fn, /Any Error thrown must not match this/); + * assert.doesNotThrow(fn, Error); + * assert.doesNotThrow(fn, errorInstance); + * assert.doesNotThrow(fn, Error, 'Error must not have this message'); + * assert.doesNotThrow(fn, errorInstance, 'Error must not have this message'); + * assert.doesNotThrow(fn, Error, /Error must not match this/); + * assert.doesNotThrow(fn, errorInstance, /Error must not match this/); * * @name doesNotThrow - * @param {Function} function - * @param {ErrorConstructor} constructor - * @param {RegExp} regexp + * @param {Function} fn + * @param {ErrorConstructor} errorLike + * @param {RegExp|String} errMsgMatcher * @param {String} message * @see https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Error#Error_types * @namespace Assert * @api public */ - assert.doesNotThrow = function (fn, type, msg) { - if ('string' === typeof type) { - msg = type; - type = null; + assert.doesNotThrow = function (fn, errorLike, errMsgMatcher, msg) { + if ('string' === typeof errorLike || errorLike instanceof RegExp) { + errMsgMatcher = errorLike; + errorLike = null; } - new Assertion(fn, msg).to.not.Throw(type); + new Assertion(fn, msg, assert.doesNotThrow, true) + .to.not.throw(errorLike, errMsgMatcher); }; /** @@ -3361,9 +5837,14 @@ module.exports = function (chai, util) { ok = val !== val2; break; default: - throw new Error('Invalid operator "' + operator + '"'); + msg = msg ? msg + ': ' : msg; + throw new chai.AssertionError( + msg + 'Invalid operator "' + operator + '"', + undefined, + assert.operator + ); } - var test = new Assertion(ok, msg); + var test = new Assertion(ok, msg, assert.operator, true); test.assert( true === flag(test, 'object') , 'expected ' + util.inspect(val) + ' to be ' + operator + ' ' + util.inspect(val2) @@ -3387,7 +5868,7 @@ module.exports = function (chai, util) { */ assert.closeTo = function (act, exp, delta, msg) { - new Assertion(act, msg).to.be.closeTo(exp, delta); + new Assertion(act, msg, assert.closeTo, true).to.be.closeTo(exp, delta); }; /** @@ -3407,14 +5888,15 @@ module.exports = function (chai, util) { */ assert.approximately = function (act, exp, delta, msg) { - new Assertion(act, msg).to.be.approximately(exp, delta); + new Assertion(act, msg, assert.approximately, true) + .to.be.approximately(exp, delta); }; /** * ### .sameMembers(set1, set2, [message]) * - * Asserts that `set1` and `set2` have the same members. - * Order is not taken into account. + * Asserts that `set1` and `set2` have the same members in any order. Uses a + * strict equality check (===). * * assert.sameMembers([ 1, 2, 3 ], [ 2, 1, 3 ], 'same members'); * @@ -3427,16 +5909,38 @@ module.exports = function (chai, util) { */ assert.sameMembers = function (set1, set2, msg) { - new Assertion(set1, msg).to.have.same.members(set2); + new Assertion(set1, msg, assert.sameMembers, true) + .to.have.same.members(set2); + } + + /** + * ### .notSameMembers(set1, set2, [message]) + * + * Asserts that `set1` and `set2` don't have the same members in any order. + * Uses a strict equality check (===). + * + * assert.notSameMembers([ 1, 2, 3 ], [ 5, 1, 3 ], 'not same members'); + * + * @name notSameMembers + * @param {Array} set1 + * @param {Array} set2 + * @param {String} message + * @namespace Assert + * @api public + */ + + assert.notSameMembers = function (set1, set2, msg) { + new Assertion(set1, msg, assert.notSameMembers, true) + .to.not.have.same.members(set2); } /** * ### .sameDeepMembers(set1, set2, [message]) * - * Asserts that `set1` and `set2` have the same members - using a deep equality checking. - * Order is not taken into account. + * Asserts that `set1` and `set2` have the same members in any order. Uses a + * deep equality check. * - * assert.sameDeepMembers([ {b: 3}, {a: 2}, {c: 5} ], [ {c: 5}, {b: 3}, {a: 2} ], 'same deep members'); + * assert.sameDeepMembers([ { a: 1 }, { b: 2 }, { c: 3 } ], [{ b: 2 }, { a: 1 }, { c: 3 }], 'same deep members'); * * @name sameDeepMembers * @param {Array} set1 @@ -3447,16 +5951,123 @@ module.exports = function (chai, util) { */ assert.sameDeepMembers = function (set1, set2, msg) { - new Assertion(set1, msg).to.have.same.deep.members(set2); + new Assertion(set1, msg, assert.sameDeepMembers, true) + .to.have.same.deep.members(set2); + } + + /** + * ### .notSameDeepMembers(set1, set2, [message]) + * + * Asserts that `set1` and `set2` don't have the same members in any order. + * Uses a deep equality check. + * + * assert.notSameDeepMembers([ { a: 1 }, { b: 2 }, { c: 3 } ], [{ b: 2 }, { a: 1 }, { f: 5 }], 'not same deep members'); + * + * @name notSameDeepMembers + * @param {Array} set1 + * @param {Array} set2 + * @param {String} message + * @namespace Assert + * @api public + */ + + assert.notSameDeepMembers = function (set1, set2, msg) { + new Assertion(set1, msg, assert.notSameDeepMembers, true) + .to.not.have.same.deep.members(set2); + } + + /** + * ### .sameOrderedMembers(set1, set2, [message]) + * + * Asserts that `set1` and `set2` have the same members in the same order. + * Uses a strict equality check (===). + * + * assert.sameOrderedMembers([ 1, 2, 3 ], [ 1, 2, 3 ], 'same ordered members'); + * + * @name sameOrderedMembers + * @param {Array} set1 + * @param {Array} set2 + * @param {String} message + * @namespace Assert + * @api public + */ + + assert.sameOrderedMembers = function (set1, set2, msg) { + new Assertion(set1, msg, assert.sameOrderedMembers, true) + .to.have.same.ordered.members(set2); + } + + /** + * ### .notSameOrderedMembers(set1, set2, [message]) + * + * Asserts that `set1` and `set2` don't have the same members in the same + * order. Uses a strict equality check (===). + * + * assert.notSameOrderedMembers([ 1, 2, 3 ], [ 2, 1, 3 ], 'not same ordered members'); + * + * @name notSameOrderedMembers + * @param {Array} set1 + * @param {Array} set2 + * @param {String} message + * @namespace Assert + * @api public + */ + + assert.notSameOrderedMembers = function (set1, set2, msg) { + new Assertion(set1, msg, assert.notSameOrderedMembers, true) + .to.not.have.same.ordered.members(set2); + } + + /** + * ### .sameDeepOrderedMembers(set1, set2, [message]) + * + * Asserts that `set1` and `set2` have the same members in the same order. + * Uses a deep equality check. + * + * assert.sameDeepOrderedMembers([ { a: 1 }, { b: 2 }, { c: 3 } ], [ { a: 1 }, { b: 2 }, { c: 3 } ], 'same deep ordered members'); + * + * @name sameDeepOrderedMembers + * @param {Array} set1 + * @param {Array} set2 + * @param {String} message + * @namespace Assert + * @api public + */ + + assert.sameDeepOrderedMembers = function (set1, set2, msg) { + new Assertion(set1, msg, assert.sameDeepOrderedMembers, true) + .to.have.same.deep.ordered.members(set2); + } + + /** + * ### .notSameDeepOrderedMembers(set1, set2, [message]) + * + * Asserts that `set1` and `set2` don't have the same members in the same + * order. Uses a deep equality check. + * + * assert.notSameDeepOrderedMembers([ { a: 1 }, { b: 2 }, { c: 3 } ], [ { a: 1 }, { b: 2 }, { z: 5 } ], 'not same deep ordered members'); + * assert.notSameDeepOrderedMembers([ { a: 1 }, { b: 2 }, { c: 3 } ], [ { b: 2 }, { a: 1 }, { c: 3 } ], 'not same deep ordered members'); + * + * @name notSameDeepOrderedMembers + * @param {Array} set1 + * @param {Array} set2 + * @param {String} message + * @namespace Assert + * @api public + */ + + assert.notSameDeepOrderedMembers = function (set1, set2, msg) { + new Assertion(set1, msg, assert.notSameDeepOrderedMembers, true) + .to.not.have.same.deep.ordered.members(set2); } /** * ### .includeMembers(superset, subset, [message]) * - * Asserts that `subset` is included in `superset`. - * Order is not taken into account. + * Asserts that `subset` is included in `superset` in any order. Uses a + * strict equality check (===). Duplicates are ignored. * - * assert.includeMembers([ 1, 2, 3 ], [ 2, 1 ], 'include members'); + * assert.includeMembers([ 1, 2, 3 ], [ 2, 1, 2 ], 'include members'); * * @name includeMembers * @param {Array} superset @@ -3467,17 +6078,38 @@ module.exports = function (chai, util) { */ assert.includeMembers = function (superset, subset, msg) { - new Assertion(superset, msg).to.include.members(subset); + new Assertion(superset, msg, assert.includeMembers, true) + .to.include.members(subset); + } + + /** + * ### .notIncludeMembers(superset, subset, [message]) + * + * Asserts that `subset` isn't included in `superset` in any order. Uses a + * strict equality check (===). Duplicates are ignored. + * + * assert.notIncludeMembers([ 1, 2, 3 ], [ 5, 1 ], 'not include members'); + * + * @name notIncludeMembers + * @param {Array} superset + * @param {Array} subset + * @param {String} message + * @namespace Assert + * @api public + */ + + assert.notIncludeMembers = function (superset, subset, msg) { + new Assertion(superset, msg, assert.notIncludeMembers, true) + .to.not.include.members(subset); } /** * ### .includeDeepMembers(superset, subset, [message]) * - * Asserts that `subset` is included in `superset` - using deep equality checking. - * Order is not taken into account. - * Duplicates are ignored. + * Asserts that `subset` is included in `superset` in any order. Uses a deep + * equality check. Duplicates are ignored. * - * assert.includeDeepMembers([ {a: 1}, {b: 2}, {c: 3} ], [ {b: 2}, {a: 1}, {b: 2} ], 'include deep members'); + * assert.includeDeepMembers([ { a: 1 }, { b: 2 }, { c: 3 } ], [ { b: 2 }, { a: 1 }, { b: 2 } ], 'include deep members'); * * @name includeDeepMembers * @param {Array} superset @@ -3488,7 +6120,120 @@ module.exports = function (chai, util) { */ assert.includeDeepMembers = function (superset, subset, msg) { - new Assertion(superset, msg).to.include.deep.members(subset); + new Assertion(superset, msg, assert.includeDeepMembers, true) + .to.include.deep.members(subset); + } + + /** + * ### .notIncludeDeepMembers(superset, subset, [message]) + * + * Asserts that `subset` isn't included in `superset` in any order. Uses a + * deep equality check. Duplicates are ignored. + * + * assert.notIncludeDeepMembers([ { a: 1 }, { b: 2 }, { c: 3 } ], [ { b: 2 }, { f: 5 } ], 'not include deep members'); + * + * @name notIncludeDeepMembers + * @param {Array} superset + * @param {Array} subset + * @param {String} message + * @namespace Assert + * @api public + */ + + assert.notIncludeDeepMembers = function (superset, subset, msg) { + new Assertion(superset, msg, assert.notIncludeDeepMembers, true) + .to.not.include.deep.members(subset); + } + + /** + * ### .includeOrderedMembers(superset, subset, [message]) + * + * Asserts that `subset` is included in `superset` in the same order + * beginning with the first element in `superset`. Uses a strict equality + * check (===). + * + * assert.includeOrderedMembers([ 1, 2, 3 ], [ 1, 2 ], 'include ordered members'); + * + * @name includeOrderedMembers + * @param {Array} superset + * @param {Array} subset + * @param {String} message + * @namespace Assert + * @api public + */ + + assert.includeOrderedMembers = function (superset, subset, msg) { + new Assertion(superset, msg, assert.includeOrderedMembers, true) + .to.include.ordered.members(subset); + } + + /** + * ### .notIncludeOrderedMembers(superset, subset, [message]) + * + * Asserts that `subset` isn't included in `superset` in the same order + * beginning with the first element in `superset`. Uses a strict equality + * check (===). + * + * assert.notIncludeOrderedMembers([ 1, 2, 3 ], [ 2, 1 ], 'not include ordered members'); + * assert.notIncludeOrderedMembers([ 1, 2, 3 ], [ 2, 3 ], 'not include ordered members'); + * + * @name notIncludeOrderedMembers + * @param {Array} superset + * @param {Array} subset + * @param {String} message + * @namespace Assert + * @api public + */ + + assert.notIncludeOrderedMembers = function (superset, subset, msg) { + new Assertion(superset, msg, assert.notIncludeOrderedMembers, true) + .to.not.include.ordered.members(subset); + } + + /** + * ### .includeDeepOrderedMembers(superset, subset, [message]) + * + * Asserts that `subset` is included in `superset` in the same order + * beginning with the first element in `superset`. Uses a deep equality + * check. + * + * assert.includeDeepOrderedMembers([ { a: 1 }, { b: 2 }, { c: 3 } ], [ { a: 1 }, { b: 2 } ], 'include deep ordered members'); + * + * @name includeDeepOrderedMembers + * @param {Array} superset + * @param {Array} subset + * @param {String} message + * @namespace Assert + * @api public + */ + + assert.includeDeepOrderedMembers = function (superset, subset, msg) { + new Assertion(superset, msg, assert.includeDeepOrderedMembers, true) + .to.include.deep.ordered.members(subset); + } + + /** + * ### .notIncludeDeepOrderedMembers(superset, subset, [message]) + * + * Asserts that `subset` isn't included in `superset` in the same order + * beginning with the first element in `superset`. Uses a deep equality + * check. + * + * assert.notIncludeDeepOrderedMembers([ { a: 1 }, { b: 2 }, { c: 3 } ], [ { a: 1 }, { f: 5 } ], 'not include deep ordered members'); + * assert.notIncludeDeepOrderedMembers([ { a: 1 }, { b: 2 }, { c: 3 } ], [ { b: 2 }, { a: 1 } ], 'not include deep ordered members'); + * assert.notIncludeDeepOrderedMembers([ { a: 1 }, { b: 2 }, { c: 3 } ], [ { b: 2 }, { c: 3 } ], 'not include deep ordered members'); + * + * @name notIncludeDeepOrderedMembers + * @param {Array} superset + * @param {Array} subset + * @param {String} message + * @namespace Assert + * @api public + */ + + assert.notIncludeDeepOrderedMembers = function (superset, subset, msg) { + new Assertion(superset, msg, assert.notIncludeDeepOrderedMembers, true) + .to.not.include.deep.ordered.members(subset); } /** @@ -3507,13 +6252,13 @@ module.exports = function (chai, util) { */ assert.oneOf = function (inList, list, msg) { - new Assertion(inList, msg).to.be.oneOf(list); + new Assertion(inList, msg, assert.oneOf, true).to.be.oneOf(list); } - /** - * ### .changes(function, object, property) + /** + * ### .changes(function, object, property, [message]) * - * Asserts that a function changes the value of a property + * Asserts that a function changes the value of a property. * * var obj = { val: 10 }; * var fn = function() { obj.val = 22 }; @@ -3521,21 +6266,59 @@ module.exports = function (chai, util) { * * @name changes * @param {Function} modifier function - * @param {Object} object - * @param {String} property name + * @param {Object} object or getter function + * @param {String} property name _optional_ * @param {String} message _optional_ * @namespace Assert * @api public */ - assert.changes = function (fn, obj, prop) { - new Assertion(fn).to.change(obj, prop); + assert.changes = function (fn, obj, prop, msg) { + if (arguments.length === 3 && typeof obj === 'function') { + msg = prop; + prop = null; + } + + new Assertion(fn, msg, assert.changes, true).to.change(obj, prop); } /** - * ### .doesNotChange(function, object, property) + * ### .changesBy(function, object, property, delta, [message]) * - * Asserts that a function does not changes the value of a property + * Asserts that a function changes the value of a property by an amount (delta). + * + * var obj = { val: 10 }; + * var fn = function() { obj.val += 2 }; + * assert.changesBy(fn, obj, 'val', 2); + * + * @name changesBy + * @param {Function} modifier function + * @param {Object} object or getter function + * @param {String} property name _optional_ + * @param {Number} change amount (delta) + * @param {String} message _optional_ + * @namespace Assert + * @api public + */ + + assert.changesBy = function (fn, obj, prop, delta, msg) { + if (arguments.length === 4 && typeof obj === 'function') { + var tmpMsg = delta; + delta = prop; + msg = tmpMsg; + } else if (arguments.length === 3) { + delta = prop; + prop = null; + } + + new Assertion(fn, msg, assert.changesBy, true) + .to.change(obj, prop).by(delta); + } + + /** + * ### .doesNotChange(function, object, property, [message]) + * + * Asserts that a function does not change the value of a property. * * var obj = { val: 10 }; * var fn = function() { console.log('foo'); }; @@ -3543,21 +6326,60 @@ module.exports = function (chai, util) { * * @name doesNotChange * @param {Function} modifier function - * @param {Object} object - * @param {String} property name + * @param {Object} object or getter function + * @param {String} property name _optional_ * @param {String} message _optional_ * @namespace Assert * @api public */ - assert.doesNotChange = function (fn, obj, prop) { - new Assertion(fn).to.not.change(obj, prop); + assert.doesNotChange = function (fn, obj, prop, msg) { + if (arguments.length === 3 && typeof obj === 'function') { + msg = prop; + prop = null; + } + + return new Assertion(fn, msg, assert.doesNotChange, true) + .to.not.change(obj, prop); } - /** - * ### .increases(function, object, property) + /** + * ### .changesButNotBy(function, object, property, delta, [message]) * - * Asserts that a function increases an object property + * Asserts that a function does not change the value of a property or of a function's return value by an amount (delta) + * + * var obj = { val: 10 }; + * var fn = function() { obj.val += 10 }; + * assert.changesButNotBy(fn, obj, 'val', 5); + * + * @name changesButNotBy + * @param {Function} modifier function + * @param {Object} object or getter function + * @param {String} property name _optional_ + * @param {Number} change amount (delta) + * @param {String} message _optional_ + * @namespace Assert + * @api public + */ + + assert.changesButNotBy = function (fn, obj, prop, delta, msg) { + if (arguments.length === 4 && typeof obj === 'function') { + var tmpMsg = delta; + delta = prop; + msg = tmpMsg; + } else if (arguments.length === 3) { + delta = prop; + prop = null; + } + + new Assertion(fn, msg, assert.changesButNotBy, true) + .to.change(obj, prop).but.not.by(delta); + } + + /** + * ### .increases(function, object, property, [message]) + * + * Asserts that a function increases a numeric object property. * * var obj = { val: 10 }; * var fn = function() { obj.val = 13 }; @@ -3565,21 +6387,60 @@ module.exports = function (chai, util) { * * @name increases * @param {Function} modifier function - * @param {Object} object - * @param {String} property name + * @param {Object} object or getter function + * @param {String} property name _optional_ * @param {String} message _optional_ * @namespace Assert * @api public */ - assert.increases = function (fn, obj, prop) { - new Assertion(fn).to.increase(obj, prop); + assert.increases = function (fn, obj, prop, msg) { + if (arguments.length === 3 && typeof obj === 'function') { + msg = prop; + prop = null; + } + + return new Assertion(fn, msg, assert.increases, true) + .to.increase(obj, prop); } - /** - * ### .doesNotIncrease(function, object, property) + /** + * ### .increasesBy(function, object, property, delta, [message]) * - * Asserts that a function does not increase object property + * Asserts that a function increases a numeric object property or a function's return value by an amount (delta). + * + * var obj = { val: 10 }; + * var fn = function() { obj.val += 10 }; + * assert.increasesBy(fn, obj, 'val', 10); + * + * @name increasesBy + * @param {Function} modifier function + * @param {Object} object or getter function + * @param {String} property name _optional_ + * @param {Number} change amount (delta) + * @param {String} message _optional_ + * @namespace Assert + * @api public + */ + + assert.increasesBy = function (fn, obj, prop, delta, msg) { + if (arguments.length === 4 && typeof obj === 'function') { + var tmpMsg = delta; + delta = prop; + msg = tmpMsg; + } else if (arguments.length === 3) { + delta = prop; + prop = null; + } + + new Assertion(fn, msg, assert.increasesBy, true) + .to.increase(obj, prop).by(delta); + } + + /** + * ### .doesNotIncrease(function, object, property, [message]) + * + * Asserts that a function does not increase a numeric object property. * * var obj = { val: 10 }; * var fn = function() { obj.val = 8 }; @@ -3587,21 +6448,60 @@ module.exports = function (chai, util) { * * @name doesNotIncrease * @param {Function} modifier function - * @param {Object} object - * @param {String} property name + * @param {Object} object or getter function + * @param {String} property name _optional_ * @param {String} message _optional_ * @namespace Assert * @api public */ - assert.doesNotIncrease = function (fn, obj, prop) { - new Assertion(fn).to.not.increase(obj, prop); + assert.doesNotIncrease = function (fn, obj, prop, msg) { + if (arguments.length === 3 && typeof obj === 'function') { + msg = prop; + prop = null; + } + + return new Assertion(fn, msg, assert.doesNotIncrease, true) + .to.not.increase(obj, prop); } - /** - * ### .decreases(function, object, property) + /** + * ### .increasesButNotBy(function, object, property, [message]) * - * Asserts that a function decreases an object property + * Asserts that a function does not increase a numeric object property or function's return value by an amount (delta). + * + * var obj = { val: 10 }; + * var fn = function() { obj.val = 15 }; + * assert.increasesButNotBy(fn, obj, 'val', 10); + * + * @name increasesButNotBy + * @param {Function} modifier function + * @param {Object} object or getter function + * @param {String} property name _optional_ + * @param {Number} change amount (delta) + * @param {String} message _optional_ + * @namespace Assert + * @api public + */ + + assert.increasesButNotBy = function (fn, obj, prop, delta, msg) { + if (arguments.length === 4 && typeof obj === 'function') { + var tmpMsg = delta; + delta = prop; + msg = tmpMsg; + } else if (arguments.length === 3) { + delta = prop; + prop = null; + } + + new Assertion(fn, msg, assert.increasesButNotBy, true) + .to.increase(obj, prop).but.not.by(delta); + } + + /** + * ### .decreases(function, object, property, [message]) + * + * Asserts that a function decreases a numeric object property. * * var obj = { val: 10 }; * var fn = function() { obj.val = 5 }; @@ -3609,21 +6509,60 @@ module.exports = function (chai, util) { * * @name decreases * @param {Function} modifier function - * @param {Object} object - * @param {String} property name + * @param {Object} object or getter function + * @param {String} property name _optional_ * @param {String} message _optional_ * @namespace Assert * @api public */ - assert.decreases = function (fn, obj, prop) { - new Assertion(fn).to.decrease(obj, prop); + assert.decreases = function (fn, obj, prop, msg) { + if (arguments.length === 3 && typeof obj === 'function') { + msg = prop; + prop = null; + } + + return new Assertion(fn, msg, assert.decreases, true) + .to.decrease(obj, prop); } - /** - * ### .doesNotDecrease(function, object, property) + /** + * ### .decreasesBy(function, object, property, delta, [message]) * - * Asserts that a function does not decreases an object property + * Asserts that a function decreases a numeric object property or a function's return value by an amount (delta) + * + * var obj = { val: 10 }; + * var fn = function() { obj.val -= 5 }; + * assert.decreasesBy(fn, obj, 'val', 5); + * + * @name decreasesBy + * @param {Function} modifier function + * @param {Object} object or getter function + * @param {String} property name _optional_ + * @param {Number} change amount (delta) + * @param {String} message _optional_ + * @namespace Assert + * @api public + */ + + assert.decreasesBy = function (fn, obj, prop, delta, msg) { + if (arguments.length === 4 && typeof obj === 'function') { + var tmpMsg = delta; + delta = prop; + msg = tmpMsg; + } else if (arguments.length === 3) { + delta = prop; + prop = null; + } + + new Assertion(fn, msg, assert.decreasesBy, true) + .to.decrease(obj, prop).by(delta); + } + + /** + * ### .doesNotDecrease(function, object, property, [message]) + * + * Asserts that a function does not decreases a numeric object property. * * var obj = { val: 10 }; * var fn = function() { obj.val = 15 }; @@ -3631,15 +6570,87 @@ module.exports = function (chai, util) { * * @name doesNotDecrease * @param {Function} modifier function - * @param {Object} object - * @param {String} property name + * @param {Object} object or getter function + * @param {String} property name _optional_ * @param {String} message _optional_ * @namespace Assert * @api public */ - assert.doesNotDecrease = function (fn, obj, prop) { - new Assertion(fn).to.not.decrease(obj, prop); + assert.doesNotDecrease = function (fn, obj, prop, msg) { + if (arguments.length === 3 && typeof obj === 'function') { + msg = prop; + prop = null; + } + + return new Assertion(fn, msg, assert.doesNotDecrease, true) + .to.not.decrease(obj, prop); + } + + /** + * ### .doesNotDecreaseBy(function, object, property, delta, [message]) + * + * Asserts that a function does not decreases a numeric object property or a function's return value by an amount (delta) + * + * var obj = { val: 10 }; + * var fn = function() { obj.val = 5 }; + * assert.doesNotDecreaseBy(fn, obj, 'val', 1); + * + * @name doesNotDecrease + * @param {Function} modifier function + * @param {Object} object or getter function + * @param {String} property name _optional_ + * @param {Number} change amount (delta) + * @param {String} message _optional_ + * @namespace Assert + * @api public + */ + + assert.doesNotDecreaseBy = function (fn, obj, prop, delta, msg) { + if (arguments.length === 4 && typeof obj === 'function') { + var tmpMsg = delta; + delta = prop; + msg = tmpMsg; + } else if (arguments.length === 3) { + delta = prop; + prop = null; + } + + return new Assertion(fn, msg, assert.doesNotDecreaseBy, true) + .to.not.decrease(obj, prop).by(delta); + } + + /** + * ### .decreasesButNotBy(function, object, property, delta, [message]) + * + * Asserts that a function does not decreases a numeric object property or a function's return value by an amount (delta) + * + * var obj = { val: 10 }; + * var fn = function() { obj.val = 5 }; + * assert.decreasesButNotBy(fn, obj, 'val', 1); + * + * @name decreasesButNotBy + * @param {Function} modifier function + * @param {Object} object or getter function + * @param {String} property name _optional_ + * @param {Number} change amount (delta) + * @param {String} message _optional_ + * @namespace Assert + * @api public + */ + + assert.decreasesButNotBy = function (fn, obj, prop, delta, msg) { + if (arguments.length === 4 && typeof obj === 'function') { + var tmpMsg = delta; + delta = prop; + msg = tmpMsg; + } else if (arguments.length === 3) { + delta = prop; + prop = null; + } + + new Assertion(fn, msg, assert.decreasesButNotBy, true) + .to.decrease(obj, prop).but.not.by(delta); } /*! @@ -3680,7 +6691,7 @@ module.exports = function (chai, util) { */ assert.isExtensible = function (obj, msg) { - new Assertion(obj, msg).to.be.extensible; + new Assertion(obj, msg, assert.isExtensible, true).to.be.extensible; }; /** @@ -3690,7 +6701,7 @@ module.exports = function (chai, util) { * * var nonExtensibleObject = Object.preventExtensions({}); * var sealedObject = Object.seal({}); - * var frozenObject = Object.freese({}); + * var frozenObject = Object.freeze({}); * * assert.isNotExtensible(nonExtensibleObject); * assert.isNotExtensible(sealedObject); @@ -3705,7 +6716,7 @@ module.exports = function (chai, util) { */ assert.isNotExtensible = function (obj, msg) { - new Assertion(obj, msg).to.not.be.extensible; + new Assertion(obj, msg, assert.isNotExtensible, true).to.not.be.extensible; }; /** @@ -3729,7 +6740,7 @@ module.exports = function (chai, util) { */ assert.isSealed = function (obj, msg) { - new Assertion(obj, msg).to.be.sealed; + new Assertion(obj, msg, assert.isSealed, true).to.be.sealed; }; /** @@ -3748,7 +6759,7 @@ module.exports = function (chai, util) { */ assert.isNotSealed = function (obj, msg) { - new Assertion(obj, msg).to.not.be.sealed; + new Assertion(obj, msg, assert.isNotSealed, true).to.not.be.sealed; }; /** @@ -3769,7 +6780,7 @@ module.exports = function (chai, util) { */ assert.isFrozen = function (obj, msg) { - new Assertion(obj, msg).to.be.frozen; + new Assertion(obj, msg, assert.isFrozen, true).to.be.frozen; }; /** @@ -3788,7 +6799,59 @@ module.exports = function (chai, util) { */ assert.isNotFrozen = function (obj, msg) { - new Assertion(obj, msg).to.not.be.frozen; + new Assertion(obj, msg, assert.isNotFrozen, true).to.not.be.frozen; + }; + + /** + * ### .isEmpty(target) + * + * Asserts that the target does not contain any values. + * For arrays and strings, it checks the `length` property. + * For `Map` and `Set` instances, it checks the `size` property. + * For non-function objects, it gets the count of own + * enumerable string keys. + * + * assert.isEmpty([]); + * assert.isEmpty(''); + * assert.isEmpty(new Map); + * assert.isEmpty({}); + * + * @name isEmpty + * @alias empty + * @param {Object|Array|String|Map|Set} target + * @param {String} message _optional_ + * @namespace Assert + * @api public + */ + + assert.isEmpty = function(val, msg) { + new Assertion(val, msg, assert.isEmpty, true).to.be.empty; + }; + + /** + * ### .isNotEmpty(target) + * + * Asserts that the target contains values. + * For arrays and strings, it checks the `length` property. + * For `Map` and `Set` instances, it checks the `size` property. + * For non-function objects, it gets the count of own + * enumerable string keys. + * + * assert.isNotEmpty([1, 2]); + * assert.isNotEmpty('34'); + * assert.isNotEmpty(new Set([5, 6])); + * assert.isNotEmpty({ key: 7 }); + * + * @name isNotEmpty + * @alias notEmpty + * @param {Object|Array|String|Map|Set} target + * @param {String} message _optional_ + * @namespace Assert + * @api public + */ + + assert.isNotEmpty = function(val, msg) { + new Assertion(val, msg, assert.isNotEmpty, true).to.not.be.empty; }; /*! @@ -3808,7 +6871,9 @@ module.exports = function (chai, util) { ('isSealed', 'sealed') ('isNotSealed', 'notSealed') ('isFrozen', 'frozen') - ('isNotFrozen', 'notFrozen'); + ('isNotFrozen', 'notFrozen') + ('isEmpty', 'empty') + ('isNotEmpty', 'notEmpty'); }; },{}],7:[function(require,module,exports){ @@ -3833,7 +6898,7 @@ module.exports = function (chai, util) { * @param {Mixed} expected * @param {String} message * @param {String} operator - * @namespace Expect + * @namespace BDD * @api public */ @@ -3860,7 +6925,10 @@ module.exports = function (chai, util) { function loadShould () { // explicitly define this method as function as to have it's name to include as `ssfi` function shouldGetter() { - if (this instanceof String || this instanceof Number || this instanceof Boolean ) { + if (this instanceof String + || this instanceof Number + || this instanceof Boolean + || typeof Symbol === 'function' && this instanceof Symbol) { return new Assertion(this.valueOf(), null, shouldGetter); } return new Assertion(this, null, shouldGetter); @@ -3898,7 +6966,7 @@ module.exports = function (chai, util) { * @param {Mixed} expected * @param {String} message * @param {String} operator - * @namespace Should + * @namespace BDD * @api public */ @@ -4061,28 +7129,32 @@ module.exports = function (chai, util) { * Module dependencies */ -var transferFlags = require('./transferFlags'); +var addLengthGuard = require('./addLengthGuard'); +var chai = require('../../chai'); var flag = require('./flag'); -var config = require('../config'); +var proxify = require('./proxify'); +var transferFlags = require('./transferFlags'); /*! * Module variables */ -// Check whether `__proto__` is supported -var hasProtoSupport = '__proto__' in Object; +// Check whether `Object.setPrototypeOf` is supported +var canSetPrototype = typeof Object.setPrototypeOf === 'function'; -// Without `__proto__` support, this module will need to add properties to a function. -// However, some Function.prototype methods cannot be overwritten, -// and there seems no easy cross-platform way to detect them (@see chaijs/chai/issues/69). -var excludeNames = /^(?:length|name|arguments|caller)$/; +// Without `Object.setPrototypeOf` support, this module will need to add properties to a function. +// However, some of functions' own props are not configurable and should be skipped. +var testFn = function() {}; +var excludeNames = Object.getOwnPropertyNames(testFn).filter(function(name) { + return !Object.getOwnPropertyDescriptor(testFn, name).configurable; +}); // Cache `Function` properties var call = Function.prototype.call, apply = Function.prototype.apply; /** - * ### addChainableMethod (ctx, name, method, chainingBehavior) + * ### .addChainableMethod(ctx, name, method, chainingBehavior) * * Adds a method to an object, such that the method can also be chained. * @@ -4110,7 +7182,7 @@ var call = Function.prototype.call, * @api public */ -module.exports = function (ctx, name, method, chainingBehavior) { +module.exports = function addChainableMethod(ctx, name, method, chainingBehavior) { if (typeof chainingBehavior !== 'function') { chainingBehavior = function () { }; } @@ -4127,54 +7199,149 @@ module.exports = function (ctx, name, method, chainingBehavior) { ctx.__methods[name] = chainableBehavior; Object.defineProperty(ctx, name, - { get: function () { + { get: function chainableMethodGetter() { chainableBehavior.chainingBehavior.call(this); - var assert = function assert() { - var old_ssfi = flag(this, 'ssfi'); - if (old_ssfi && config.includeStack === false) - flag(this, 'ssfi', assert); + var chainableMethodWrapper = function () { + // Setting the `ssfi` flag to `chainableMethodWrapper` causes this + // function to be the starting point for removing implementation + // frames from the stack trace of a failed assertion. + // + // However, we only want to use this function as the starting point if + // the `lockSsfi` flag isn't set. + // + // If the `lockSsfi` flag is set, then this assertion is being + // invoked from inside of another assertion. In this case, the `ssfi` + // flag has already been set by the outer assertion. + // + // Note that overwriting a chainable method merely replaces the saved + // methods in `ctx.__methods` instead of completely replacing the + // overwritten assertion. Therefore, an overwriting assertion won't + // set the `ssfi` or `lockSsfi` flags. + if (!flag(this, 'lockSsfi')) { + flag(this, 'ssfi', chainableMethodWrapper); + } + var result = chainableBehavior.method.apply(this, arguments); - return result === undefined ? this : result; + if (result !== undefined) { + return result; + } + + var newAssertion = new chai.Assertion(); + transferFlags(this, newAssertion); + return newAssertion; }; - // Use `__proto__` if available - if (hasProtoSupport) { + addLengthGuard(chainableMethodWrapper, name, true); + + // Use `Object.setPrototypeOf` if available + if (canSetPrototype) { // Inherit all properties from the object by replacing the `Function` prototype - var prototype = assert.__proto__ = Object.create(this); + var prototype = Object.create(this); // Restore the `call` and `apply` methods from `Function` prototype.call = call; prototype.apply = apply; + Object.setPrototypeOf(chainableMethodWrapper, prototype); } // Otherwise, redefine all properties (slow!) else { var asserterNames = Object.getOwnPropertyNames(ctx); asserterNames.forEach(function (asserterName) { - if (!excludeNames.test(asserterName)) { - var pd = Object.getOwnPropertyDescriptor(ctx, asserterName); - Object.defineProperty(assert, asserterName, pd); + if (excludeNames.indexOf(asserterName) !== -1) { + return; } + + var pd = Object.getOwnPropertyDescriptor(ctx, asserterName); + Object.defineProperty(chainableMethodWrapper, asserterName, pd); }); } - transferFlags(this, assert); - return assert; + transferFlags(this, chainableMethodWrapper); + return proxify(chainableMethodWrapper); } , configurable: true }); }; -},{"../config":4,"./flag":13,"./transferFlags":29}],10:[function(require,module,exports){ +},{"../../chai":2,"./addLengthGuard":10,"./flag":15,"./proxify":30,"./transferFlags":32}],10:[function(require,module,exports){ +var config = require('../config'); + +var fnLengthDesc = Object.getOwnPropertyDescriptor(function () {}, 'length'); + +/*! + * Chai - addLengthGuard utility + * Copyright(c) 2012-2014 Jake Luer + * MIT Licensed + */ + +/** + * ### .addLengthGuard(fn, assertionName, isChainable) + * + * Define `length` as a getter on the given uninvoked method assertion. The + * getter acts as a guard against chaining `length` directly off of an uninvoked + * method assertion, which is a problem because it references `function`'s + * built-in `length` property instead of Chai's `length` assertion. When the + * getter catches the user making this mistake, it throws an error with a + * helpful message. + * + * There are two ways in which this mistake can be made. The first way is by + * chaining the `length` assertion directly off of an uninvoked chainable + * method. In this case, Chai suggests that the user use `lengthOf` instead. The + * second way is by chaining the `length` assertion directly off of an uninvoked + * non-chainable method. Non-chainable methods must be invoked prior to + * chaining. In this case, Chai suggests that the user consult the docs for the + * given assertion. + * + * If the `length` property of functions is unconfigurable, then return `fn` + * without modification. + * + * Note that in ES6, the function's `length` property is configurable, so once + * support for legacy environments is dropped, Chai's `length` property can + * replace the built-in function's `length` property, and this length guard will + * no longer be necessary. In the mean time, maintaining consistency across all + * environments is the priority. + * + * @param {Function} fn + * @param {String} assertionName + * @param {Boolean} isChainable + * @namespace Utils + * @name addLengthGuard + */ + +module.exports = function addLengthGuard (fn, assertionName, isChainable) { + if (!fnLengthDesc.configurable) return fn; + + Object.defineProperty(fn, 'length', { + get: function () { + if (isChainable) { + throw Error('Invalid Chai property: ' + assertionName + '.length. Due' + + ' to a compatibility issue, "length" cannot directly follow "' + + assertionName + '". Use "' + assertionName + '.lengthOf" instead.'); + } + + throw Error('Invalid Chai property: ' + assertionName + '.length. See' + + ' docs for proper usage of "' + assertionName + '".'); + } + }); + + return fn; +}; + +},{"../config":4}],11:[function(require,module,exports){ /*! * Chai - addMethod utility * Copyright(c) 2012-2014 Jake Luer * MIT Licensed */ -var config = require('../config'); +var addLengthGuard = require('./addLengthGuard'); +var chai = require('../../chai'); +var flag = require('./flag'); +var proxify = require('./proxify'); +var transferFlags = require('./transferFlags'); /** - * ### .addMethod (ctx, name, method) + * ### .addMethod(ctx, name, method) * * Adds a method to the prototype of an object. * @@ -4198,30 +7365,52 @@ var config = require('../config'); * @name addMethod * @api public */ -var flag = require('./flag'); -module.exports = function (ctx, name, method) { - ctx[name] = function () { - var old_ssfi = flag(this, 'ssfi'); - if (old_ssfi && config.includeStack === false) - flag(this, 'ssfi', ctx[name]); +module.exports = function addMethod(ctx, name, method) { + var methodWrapper = function () { + // Setting the `ssfi` flag to `methodWrapper` causes this function to be the + // starting point for removing implementation frames from the stack trace of + // a failed assertion. + // + // However, we only want to use this function as the starting point if the + // `lockSsfi` flag isn't set. + // + // If the `lockSsfi` flag is set, then either this assertion has been + // overwritten by another assertion, or this assertion is being invoked from + // inside of another assertion. In the first case, the `ssfi` flag has + // already been set by the overwriting assertion. In the second case, the + // `ssfi` flag has already been set by the outer assertion. + if (!flag(this, 'lockSsfi')) { + flag(this, 'ssfi', methodWrapper); + } + var result = method.apply(this, arguments); - return result === undefined ? this : result; + if (result !== undefined) + return result; + + var newAssertion = new chai.Assertion(); + transferFlags(this, newAssertion); + return newAssertion; }; + + addLengthGuard(methodWrapper, name, false); + ctx[name] = proxify(methodWrapper, name); }; -},{"../config":4,"./flag":13}],11:[function(require,module,exports){ +},{"../../chai":2,"./addLengthGuard":10,"./flag":15,"./proxify":30,"./transferFlags":32}],12:[function(require,module,exports){ /*! * Chai - addProperty utility * Copyright(c) 2012-2014 Jake Luer * MIT Licensed */ -var config = require('../config'); +var chai = require('../../chai'); var flag = require('./flag'); +var isProxyEnabled = require('./isProxyEnabled'); +var transferFlags = require('./transferFlags'); /** - * ### addProperty (ctx, name, getter) + * ### .addProperty(ctx, name, getter) * * Adds a property to the prototype of an object. * @@ -4246,21 +7435,76 @@ var flag = require('./flag'); * @api public */ -module.exports = function (ctx, name, getter) { +module.exports = function addProperty(ctx, name, getter) { + getter = getter === undefined ? new Function() : getter; + Object.defineProperty(ctx, name, - { get: function addProperty() { - var old_ssfi = flag(this, 'ssfi'); - if (old_ssfi && config.includeStack === false) - flag(this, 'ssfi', addProperty); + { get: function propertyGetter() { + // Setting the `ssfi` flag to `propertyGetter` causes this function to + // be the starting point for removing implementation frames from the + // stack trace of a failed assertion. + // + // However, we only want to use this function as the starting point if + // the `lockSsfi` flag isn't set and proxy protection is disabled. + // + // If the `lockSsfi` flag is set, then either this assertion has been + // overwritten by another assertion, or this assertion is being invoked + // from inside of another assertion. In the first case, the `ssfi` flag + // has already been set by the overwriting assertion. In the second + // case, the `ssfi` flag has already been set by the outer assertion. + // + // If proxy protection is enabled, then the `ssfi` flag has already been + // set by the proxy getter. + if (!isProxyEnabled() && !flag(this, 'lockSsfi')) { + flag(this, 'ssfi', propertyGetter); + } var result = getter.call(this); - return result === undefined ? this : result; + if (result !== undefined) + return result; + + var newAssertion = new chai.Assertion(); + transferFlags(this, newAssertion); + return newAssertion; } , configurable: true }); }; -},{"../config":4,"./flag":13}],12:[function(require,module,exports){ +},{"../../chai":2,"./flag":15,"./isProxyEnabled":25,"./transferFlags":32}],13:[function(require,module,exports){ +/*! + * Chai - compareByInspect utility + * Copyright(c) 2011-2016 Jake Luer + * MIT Licensed + */ + +/*! + * Module dependancies + */ + +var inspect = require('./inspect'); + +/** + * ### .compareByInspect(mixed, mixed) + * + * To be used as a compareFunction with Array.prototype.sort. Compares elements + * using inspect instead of default behavior of using toString so that Symbols + * and objects with irregular/missing toString can still be sorted without a + * TypeError. + * + * @param {Mixed} first element to compare + * @param {Mixed} second element to compare + * @returns {Number} -1 if 'a' should come before 'b'; otherwise 1 + * @name compareByInspect + * @namespace Utils + * @api public + */ + +module.exports = function compareByInspect(a, b) { + return inspect(a) < inspect(b) ? -1 : 1; +}; + +},{"./inspect":23}],14:[function(require,module,exports){ /*! * Chai - expectTypes utility * Copyright(c) 2012-2014 Jake Luer @@ -4268,7 +7512,7 @@ module.exports = function (ctx, name, getter) { */ /** - * ### expectTypes(obj, types) + * ### .expectTypes(obj, types) * * Ensures that the object being tested against is of a valid type. * @@ -4276,6 +7520,8 @@ module.exports = function (ctx, name, getter) { * * @param {Mixed} obj constructed Assertion * @param {Array} type A list of allowed types for this assertion + * @param {Function} ssfi starting point for removing implementation frames from + * stack trace of AssertionError * @namespace Utils * @name expectTypes * @api public @@ -4285,8 +7531,12 @@ var AssertionError = require('assertion-error'); var flag = require('./flag'); var type = require('type-detect'); -module.exports = function (obj, types) { - var obj = flag(obj, 'object'); +module.exports = function expectTypes(obj, types, ssfi) { + var flagMsg = flag(obj, 'message'); + + flagMsg = flagMsg ? flagMsg + ': ' : ''; + + obj = flag(obj, 'object'); types = types.map(function (t) { return t.toLowerCase(); }); types.sort(); @@ -4297,14 +7547,18 @@ module.exports = function (obj, types) { return or + art + ' ' + t; }).join(', '); - if (!types.some(function (expected) { return type(obj) === expected; })) { + var objType = type(obj).toLowerCase(); + + if (!types.some(function (expected) { return objType === expected; })) { throw new AssertionError( - 'object tested must be ' + str + ', but ' + type(obj) + ' given' + flagMsg + 'object tested must be ' + str + ', but ' + objType + ' given', + undefined, + ssfi ); } }; -},{"./flag":13,"assertion-error":30,"type-detect":35}],13:[function(require,module,exports){ +},{"./flag":15,"assertion-error":33,"type-detect":39}],15:[function(require,module,exports){ /*! * Chai - flag utility * Copyright(c) 2012-2014 Jake Luer @@ -4312,7 +7566,7 @@ module.exports = function (obj, types) { */ /** - * ### flag(object, key, [value]) + * ### .flag(object, key, [value]) * * Get or set a flag value on an object. If a * value is provided it will be set, else it will @@ -4330,7 +7584,7 @@ module.exports = function (obj, types) { * @api private */ -module.exports = function (obj, key, value) { +module.exports = function flag(obj, key, value) { var flags = obj.__flags || (obj.__flags = Object.create(null)); if (arguments.length === 3) { flags[key] = value; @@ -4339,7 +7593,7 @@ module.exports = function (obj, key, value) { } }; -},{}],14:[function(require,module,exports){ +},{}],16:[function(require,module,exports){ /*! * Chai - getActual utility * Copyright(c) 2012-2014 Jake Luer @@ -4347,9 +7601,9 @@ module.exports = function (obj, key, value) { */ /** - * # getActual(object, [actual]) + * ### .getActual(object, [actual]) * - * Returns the `actual` value for an Assertion + * Returns the `actual` value for an Assertion. * * @param {Object} object (constructed Assertion) * @param {Arguments} chai.Assertion.prototype.assert arguments @@ -4357,11 +7611,11 @@ module.exports = function (obj, key, value) { * @name getActual */ -module.exports = function (obj, args) { +module.exports = function getActual(obj, args) { return args.length > 4 ? args[4] : obj._obj; }; -},{}],15:[function(require,module,exports){ +},{}],17:[function(require,module,exports){ /*! * Chai - getEnumerableProperties utility * Copyright(c) 2012-2014 Jake Luer @@ -4389,7 +7643,7 @@ module.exports = function getEnumerableProperties(object) { return result; }; -},{}],16:[function(require,module,exports){ +},{}],18:[function(require,module,exports){ /*! * Chai - message composition utility * Copyright(c) 2012-2014 Jake Luer @@ -4424,7 +7678,7 @@ var flag = require('./flag') * @api public */ -module.exports = function (obj, args) { +module.exports = function getMessage(obj, args) { var negate = flag(obj, 'negate') , val = flag(obj, 'object') , expected = args[3] @@ -4442,189 +7696,67 @@ module.exports = function (obj, args) { return flagMsg ? flagMsg + ': ' + msg : msg; }; -},{"./flag":13,"./getActual":14,"./inspect":23,"./objDisplay":24}],17:[function(require,module,exports){ +},{"./flag":15,"./getActual":16,"./inspect":23,"./objDisplay":26}],19:[function(require,module,exports){ /*! - * Chai - getName utility - * Copyright(c) 2012-2014 Jake Luer + * Chai - getOwnEnumerableProperties utility + * Copyright(c) 2011-2016 Jake Luer * MIT Licensed */ -/** - * # getName(func) - * - * Gets the name of a function, in a cross-browser way. - * - * @param {Function} a function (usually a constructor) - * @namespace Utils - * @name getName - */ - -module.exports = function (func) { - if (func.name) return func.name; - - var match = /^\s?function ([^(]*)\(/.exec(func); - return match && match[1] ? match[1] : ""; -}; - -},{}],18:[function(require,module,exports){ /*! - * Chai - getPathInfo utility - * Copyright(c) 2012-2014 Jake Luer - * MIT Licensed + * Module dependancies */ -var hasProperty = require('./hasProperty'); +var getOwnEnumerablePropertySymbols = require('./getOwnEnumerablePropertySymbols'); /** - * ### .getPathInfo(path, object) + * ### .getOwnEnumerableProperties(object) * - * This allows the retrieval of property info in an - * object given a string path. + * This allows the retrieval of directly-owned enumerable property names and + * symbols of an object. This function is necessary because Object.keys only + * returns enumerable property names, not enumerable property symbols. * - * The path info consists of an object with the - * following properties: - * - * * parent - The parent object of the property referenced by `path` - * * name - The name of the final property, a number if it was an array indexer - * * value - The value of the property, if it exists, otherwise `undefined` - * * exists - Whether the property exists or not - * - * @param {String} path * @param {Object} object - * @returns {Object} info + * @returns {Array} * @namespace Utils - * @name getPathInfo + * @name getOwnEnumerableProperties * @api public */ -module.exports = function getPathInfo(path, obj) { - var parsed = parsePath(path), - last = parsed[parsed.length - 1]; - - var info = { - parent: parsed.length > 1 ? _getPathValue(parsed, obj, parsed.length - 1) : obj, - name: last.p || last.i, - value: _getPathValue(parsed, obj) - }; - info.exists = hasProperty(info.name, info.parent); - - return info; +module.exports = function getOwnEnumerableProperties(obj) { + return Object.keys(obj).concat(getOwnEnumerablePropertySymbols(obj)); }; - +},{"./getOwnEnumerablePropertySymbols":20}],20:[function(require,module,exports){ /*! - * ## parsePath(path) - * - * Helper function used to parse string object - * paths. Use in conjunction with `_getPathValue`. - * - * var parsed = parsePath('myobject.property.subprop'); - * - * ### Paths: - * - * * Can be as near infinitely deep and nested - * * Arrays are also valid using the formal `myobject.document[3].property`. - * * Literal dots and brackets (not delimiter) must be backslash-escaped. - * - * @param {String} path - * @returns {Object} parsed - * @api private + * Chai - getOwnEnumerablePropertySymbols utility + * Copyright(c) 2011-2016 Jake Luer + * MIT Licensed */ -function parsePath (path) { - var str = path.replace(/([^\\])\[/g, '$1.[') - , parts = str.match(/(\\\.|[^.]+?)+/g); - return parts.map(function (value) { - var re = /^\[(\d+)\]$/ - , mArr = re.exec(value); - if (mArr) return { i: parseFloat(mArr[1]) }; - else return { p: value.replace(/\\([.\[\]])/g, '$1') }; +/** + * ### .getOwnEnumerablePropertySymbols(object) + * + * This allows the retrieval of directly-owned enumerable property symbols of an + * object. This function is necessary because Object.getOwnPropertySymbols + * returns both enumerable and non-enumerable property symbols. + * + * @param {Object} object + * @returns {Array} + * @namespace Utils + * @name getOwnEnumerablePropertySymbols + * @api public + */ + +module.exports = function getOwnEnumerablePropertySymbols(obj) { + if (typeof Object.getOwnPropertySymbols !== 'function') return []; + + return Object.getOwnPropertySymbols(obj).filter(function (sym) { + return Object.getOwnPropertyDescriptor(obj, sym).enumerable; }); -} - - -/*! - * ## _getPathValue(parsed, obj) - * - * Helper companion function for `.parsePath` that returns - * the value located at the parsed address. - * - * var value = getPathValue(parsed, obj); - * - * @param {Object} parsed definition from `parsePath`. - * @param {Object} object to search against - * @param {Number} object to search against - * @returns {Object|Undefined} value - * @api private - */ - -function _getPathValue (parsed, obj, index) { - var tmp = obj - , res; - - index = (index === undefined ? parsed.length : index); - - for (var i = 0, l = index; i < l; i++) { - var part = parsed[i]; - if (tmp) { - if ('undefined' !== typeof part.p) - tmp = tmp[part.p]; - else if ('undefined' !== typeof part.i) - tmp = tmp[part.i]; - if (i == (l - 1)) res = tmp; - } else { - res = undefined; - } - } - return res; -} - -},{"./hasProperty":21}],19:[function(require,module,exports){ -/*! - * Chai - getPathValue utility - * Copyright(c) 2012-2014 Jake Luer - * @see https://github.com/logicalparadox/filtr - * MIT Licensed - */ - -var getPathInfo = require('./getPathInfo'); - -/** - * ### .getPathValue(path, object) - * - * This allows the retrieval of values in an - * object given a string path. - * - * var obj = { - * prop1: { - * arr: ['a', 'b', 'c'] - * , str: 'Hello' - * } - * , prop2: { - * arr: [ { nested: 'Universe' } ] - * , str: 'Hello again!' - * } - * } - * - * The following would be the results. - * - * getPathValue('prop1.str', obj); // Hello - * getPathValue('prop1.att[2]', obj); // b - * getPathValue('prop2.arr[0].nested', obj); // Universe - * - * @param {String} path - * @param {Object} object - * @returns {Object} value or `undefined` - * @namespace Utils - * @name getPathValue - * @api public - */ -module.exports = function(path, obj) { - var info = getPathInfo(path, obj); - return info.value; }; -},{"./getPathInfo":18}],20:[function(require,module,exports){ +},{}],21:[function(require,module,exports){ /*! * Chai - getProperties utility * Copyright(c) 2012-2014 Jake Luer @@ -4662,73 +7794,7 @@ module.exports = function getProperties(object) { return result; }; -},{}],21:[function(require,module,exports){ -/*! - * Chai - hasProperty utility - * Copyright(c) 2012-2014 Jake Luer - * MIT Licensed - */ - -var type = require('type-detect'); - -/** - * ### .hasProperty(object, name) - * - * This allows checking whether an object has - * named property or numeric array index. - * - * Basically does the same thing as the `in` - * operator but works properly with natives - * and null/undefined values. - * - * var obj = { - * arr: ['a', 'b', 'c'] - * , str: 'Hello' - * } - * - * The following would be the results. - * - * hasProperty('str', obj); // true - * hasProperty('constructor', obj); // true - * hasProperty('bar', obj); // false - * - * hasProperty('length', obj.str); // true - * hasProperty(1, obj.str); // true - * hasProperty(5, obj.str); // false - * - * hasProperty('length', obj.arr); // true - * hasProperty(2, obj.arr); // true - * hasProperty(3, obj.arr); // false - * - * @param {Objuect} object - * @param {String|Number} name - * @returns {Boolean} whether it exists - * @namespace Utils - * @name getPathInfo - * @api public - */ - -var literals = { - 'number': Number - , 'string': String -}; - -module.exports = function hasProperty(name, obj) { - var ot = type(obj); - - // Bad Object, obviously no props at all - if(ot === 'null' || ot === 'undefined') - return false; - - // The `in` operator does not work with certain literals - // box these before the check - if(literals[ot] && typeof obj !== 'object') - obj = new literals[ot](obj); - - return name in obj; -}; - -},{"type-detect":35}],22:[function(require,module,exports){ +},{}],22:[function(require,module,exports){ /*! * chai * Copyright(c) 2011 Jake Luer @@ -4736,10 +7802,10 @@ module.exports = function hasProperty(name, obj) { */ /*! - * Main exports + * Dependencies that are used for multiple exports are required here only once */ -var exports = module.exports = {}; +var pathval = require('pathval'); /*! * test utility @@ -4800,29 +7866,23 @@ exports.transferFlags = require('./transferFlags'); exports.eql = require('deep-eql'); -/*! - * Deep path value - */ - -exports.getPathValue = require('./getPathValue'); - /*! * Deep path info */ -exports.getPathInfo = require('./getPathInfo'); +exports.getPathInfo = pathval.getPathInfo; /*! * Check if a property exists */ -exports.hasProperty = require('./hasProperty'); +exports.hasProperty = pathval.hasProperty; /*! * Function name */ -exports.getName = require('./getName'); +exports.getName = require('get-func-name'); /*! * add Property @@ -4860,23 +7920,74 @@ exports.addChainableMethod = require('./addChainableMethod'); exports.overwriteChainableMethod = require('./overwriteChainableMethod'); -},{"./addChainableMethod":9,"./addMethod":10,"./addProperty":11,"./expectTypes":12,"./flag":13,"./getActual":14,"./getMessage":16,"./getName":17,"./getPathInfo":18,"./getPathValue":19,"./hasProperty":21,"./inspect":23,"./objDisplay":24,"./overwriteChainableMethod":25,"./overwriteMethod":26,"./overwriteProperty":27,"./test":28,"./transferFlags":29,"deep-eql":31,"type-detect":35}],23:[function(require,module,exports){ +/*! + * Compare by inspect method + */ + +exports.compareByInspect = require('./compareByInspect'); + +/*! + * Get own enumerable property symbols method + */ + +exports.getOwnEnumerablePropertySymbols = require('./getOwnEnumerablePropertySymbols'); + +/*! + * Get own enumerable properties method + */ + +exports.getOwnEnumerableProperties = require('./getOwnEnumerableProperties'); + +/*! + * Checks error against a given set of criteria + */ + +exports.checkError = require('check-error'); + +/*! + * Proxify util + */ + +exports.proxify = require('./proxify'); + +/*! + * addLengthGuard util + */ + +exports.addLengthGuard = require('./addLengthGuard'); + +/*! + * isProxyEnabled helper + */ + +exports.isProxyEnabled = require('./isProxyEnabled'); + +/*! + * isNaN method + */ + +exports.isNaN = require('./isNaN'); + +},{"./addChainableMethod":9,"./addLengthGuard":10,"./addMethod":11,"./addProperty":12,"./compareByInspect":13,"./expectTypes":14,"./flag":15,"./getActual":16,"./getMessage":18,"./getOwnEnumerableProperties":19,"./getOwnEnumerablePropertySymbols":20,"./inspect":23,"./isNaN":24,"./isProxyEnabled":25,"./objDisplay":26,"./overwriteChainableMethod":27,"./overwriteMethod":28,"./overwriteProperty":29,"./proxify":30,"./test":31,"./transferFlags":32,"check-error":34,"deep-eql":35,"get-func-name":37,"pathval":38,"type-detect":39}],23:[function(require,module,exports){ // This is (almost) directly from Node.js utils // https://github.com/joyent/node/blob/f8c335d0caf47f16d31413f89aa28eda3878e3aa/lib/util.js -var getName = require('./getName'); +var getName = require('get-func-name'); var getProperties = require('./getProperties'); var getEnumerableProperties = require('./getEnumerableProperties'); +var config = require('../config'); module.exports = inspect; /** - * Echos the value of a value. Trys to print the value out + * ### .inspect(obj, [showHidden], [depth], [colors]) + * + * Echoes the value of a value. Tries to print the value out * in the best way possible given the different types. * * @param {Object} obj The object to print out. * @param {Boolean} showHidden Flag that shows hidden (not enumerable) - * properties of objects. + * properties of objects. Default is false. * @param {Number} depth Depth in which to descend in object. Default is 2. * @param {Boolean} colors Flag to turn on ANSI escape codes to color the * output. Default is false (no coloring). @@ -4899,6 +8010,7 @@ var isDOMElement = function (object) { } else { return object && typeof object === 'object' && + 'nodeType' in object && object.nodeType === 1 && typeof object.nodeName === 'string'; } @@ -4912,7 +8024,7 @@ function formatValue(ctx, value, recurseTimes) { value.inspect !== exports.inspect && // Also filter out any prototype objects using the circular check. !(value.constructor && value.constructor.prototype === value)) { - var ret = value.inspect(recurseTimes); + var ret = value.inspect(recurseTimes, ctx); if (typeof ret !== 'string') { ret = formatValue(ctx, ret, recurseTimes); } @@ -4962,6 +8074,8 @@ function formatValue(ctx, value, recurseTimes) { var visibleKeys = getEnumerableProperties(value); var keys = ctx.showHidden ? getProperties(value) : visibleKeys; + var name, nameSuffix; + // Some type of object without properties can be shortcutted. // In IE, errors have a single `stack` property, or if they are vanilla `Error`, // a `stack` plus `description` property; ignore those for consistency. @@ -4970,8 +8084,8 @@ function formatValue(ctx, value, recurseTimes) { (keys.length === 2 && keys[0] === 'description' && keys[1] === 'stack') ))) { if (typeof value === 'function') { - var name = getName(value); - var nameSuffix = name ? ': ' + name : ''; + name = getName(value); + nameSuffix = name ? ': ' + name : ''; return ctx.stylize('[Function' + nameSuffix + ']', 'special'); } if (isRegExp(value)) { @@ -4985,7 +8099,15 @@ function formatValue(ctx, value, recurseTimes) { } } - var base = '', array = false, braces = ['{', '}']; + var base = '' + , array = false + , typedArray = false + , braces = ['{', '}']; + + if (isTypedArray(value)) { + typedArray = true; + braces = ['[', ']']; + } // Make Array say that they are Array if (isArray(value)) { @@ -4995,8 +8117,8 @@ function formatValue(ctx, value, recurseTimes) { // Make functions say that they are functions if (typeof value === 'function') { - var name = getName(value); - var nameSuffix = name ? ': ' + name : ''; + name = getName(value); + nameSuffix = name ? ': ' + name : ''; base = ' [Function' + nameSuffix + ']'; } @@ -5032,6 +8154,8 @@ function formatValue(ctx, value, recurseTimes) { var output; if (array) { output = formatArray(ctx, value, recurseTimes, visibleKeys, keys); + } else if (typedArray) { + return formatTypedArray(value); } else { output = keys.map(function(key) { return formatProperty(ctx, value, recurseTimes, visibleKeys, key, array); @@ -5063,6 +8187,9 @@ function formatPrimitive(ctx, value) { case 'boolean': return ctx.stylize('' + value, 'boolean'); + + case 'symbol': + return ctx.stylize(value.toString(), 'symbol'); } // For some reason typeof null is "object", so special case here. if (value === null) { @@ -5086,6 +8213,7 @@ function formatArray(ctx, value, recurseTimes, visibleKeys, keys) { output.push(''); } } + keys.forEach(function(key) { if (!key.match(/^\d+$/)) { output.push(formatProperty(ctx, value, recurseTimes, visibleKeys, @@ -5095,18 +8223,40 @@ function formatArray(ctx, value, recurseTimes, visibleKeys, keys) { return output; } +function formatTypedArray(value) { + var str = '[ '; + + for (var i = 0; i < value.length; ++i) { + if (str.length >= config.truncateThreshold - 7) { + str += '...'; + break; + } + str += value[i] + ', '; + } + str += ' ]'; + + // Removing trailing `, ` if the array was not truncated + if (str.indexOf(', ]') !== -1) { + str = str.replace(', ]', ' ]'); + } + + return str; +} function formatProperty(ctx, value, recurseTimes, visibleKeys, key, array) { - var name, str; - if (value.__lookupGetter__) { - if (value.__lookupGetter__(key)) { - if (value.__lookupSetter__(key)) { + var name; + var propDescriptor = Object.getOwnPropertyDescriptor(value, key); + var str; + + if (propDescriptor) { + if (propDescriptor.get) { + if (propDescriptor.set) { str = ctx.stylize('[Getter/Setter]', 'special'); } else { str = ctx.stylize('[Getter]', 'special'); } } else { - if (value.__lookupSetter__(key)) { + if (propDescriptor.set) { str = ctx.stylize('[Setter]', 'special'); } } @@ -5176,6 +8326,12 @@ function reduceToSingleString(output, base, braces) { return braces[0] + base + ' ' + output.join(', ') + ' ' + braces[1]; } +function isTypedArray(ar) { + // Unfortunately there's no way to check if an object is a TypedArray + // We have to check if it's one of these types + return (typeof ar === 'object' && /\w+Array]$/.test(objectToString(ar))); +} + function isArray(ar) { return Array.isArray(ar) || (typeof ar === 'object' && objectToString(ar) === '[object Array]'); @@ -5197,7 +8353,61 @@ function objectToString(o) { return Object.prototype.toString.call(o); } -},{"./getEnumerableProperties":15,"./getName":17,"./getProperties":20}],24:[function(require,module,exports){ +},{"../config":4,"./getEnumerableProperties":17,"./getProperties":21,"get-func-name":37}],24:[function(require,module,exports){ +/*! + * Chai - isNaN utility + * Copyright(c) 2012-2015 Sakthipriyan Vairamani + * MIT Licensed + */ + +/** + * ### .isNaN(value) + * + * Checks if the given value is NaN or not. + * + * utils.isNaN(NaN); // true + * + * @param {Value} The value which has to be checked if it is NaN + * @name isNaN + * @api private + */ + +function isNaN(value) { + // Refer http://www.ecma-international.org/ecma-262/6.0/#sec-isnan-number + // section's NOTE. + return value !== value; +} + +// If ECMAScript 6's Number.isNaN is present, prefer that. +module.exports = Number.isNaN || isNaN; + +},{}],25:[function(require,module,exports){ +var config = require('../config'); + +/*! + * Chai - isProxyEnabled helper + * Copyright(c) 2012-2014 Jake Luer + * MIT Licensed + */ + +/** + * ### .isProxyEnabled() + * + * Helper function to check if Chai's proxy protection feature is enabled. If + * proxies are unsupported or disabled via the user's Chai config, then return + * false. Otherwise, return true. + * + * @namespace Utils + * @name isProxyEnabled + */ + +module.exports = function isProxyEnabled() { + return config.useProxy && + typeof Proxy !== 'undefined' && + typeof Reflect !== 'undefined'; +}; + +},{"../config":4}],26:[function(require,module,exports){ /*! * Chai - flag utility * Copyright(c) 2012-2014 Jake Luer @@ -5212,7 +8422,7 @@ var inspect = require('./inspect'); var config = require('../config'); /** - * ### .objDisplay (object) + * ### .objDisplay(object) * * Determines if an object or an array matches * criteria to be inspected in-line for error @@ -5224,7 +8434,7 @@ var config = require('../config'); * @api public */ -module.exports = function (obj) { +module.exports = function objDisplay(obj) { var str = inspect(obj) , type = Object.prototype.toString.call(obj); @@ -5249,22 +8459,25 @@ module.exports = function (obj) { } }; -},{"../config":4,"./inspect":23}],25:[function(require,module,exports){ +},{"../config":4,"./inspect":23}],27:[function(require,module,exports){ /*! * Chai - overwriteChainableMethod utility * Copyright(c) 2012-2014 Jake Luer * MIT Licensed */ +var chai = require('../../chai'); +var transferFlags = require('./transferFlags'); + /** - * ### overwriteChainableMethod (ctx, name, method, chainingBehavior) + * ### .overwriteChainableMethod(ctx, name, method, chainingBehavior) * * Overwites an already existing chainable method * and provides access to the previous function or * property. Must return functions to be used for * name. * - * utils.overwriteChainableMethod(chai.Assertion.prototype, 'length', + * utils.overwriteChainableMethod(chai.Assertion.prototype, 'lengthOf', * function (_super) { * } * , function (_super) { @@ -5277,8 +8490,8 @@ module.exports = function (obj) { * * Then can be used as any other assertion. * - * expect(myFoo).to.have.length(3); - * expect(myFoo).to.have.length.above(3); + * expect(myFoo).to.have.lengthOf(3); + * expect(myFoo).to.have.lengthOf.above(3); * * @param {Object} ctx object whose method / property is to be overwritten * @param {String} name of method / property to overwrite @@ -5289,31 +8502,49 @@ module.exports = function (obj) { * @api public */ -module.exports = function (ctx, name, method, chainingBehavior) { +module.exports = function overwriteChainableMethod(ctx, name, method, chainingBehavior) { var chainableBehavior = ctx.__methods[name]; var _chainingBehavior = chainableBehavior.chainingBehavior; - chainableBehavior.chainingBehavior = function () { + chainableBehavior.chainingBehavior = function overwritingChainableMethodGetter() { var result = chainingBehavior(_chainingBehavior).call(this); - return result === undefined ? this : result; + if (result !== undefined) { + return result; + } + + var newAssertion = new chai.Assertion(); + transferFlags(this, newAssertion); + return newAssertion; }; var _method = chainableBehavior.method; - chainableBehavior.method = function () { + chainableBehavior.method = function overwritingChainableMethodWrapper() { var result = method(_method).apply(this, arguments); - return result === undefined ? this : result; + if (result !== undefined) { + return result; + } + + var newAssertion = new chai.Assertion(); + transferFlags(this, newAssertion); + return newAssertion; }; }; -},{}],26:[function(require,module,exports){ +},{"../../chai":2,"./transferFlags":32}],28:[function(require,module,exports){ /*! * Chai - overwriteMethod utility * Copyright(c) 2012-2014 Jake Luer * MIT Licensed */ +var addLengthGuard = require('./addLengthGuard'); +var chai = require('../../chai'); +var flag = require('./flag'); +var proxify = require('./proxify'); +var transferFlags = require('./transferFlags'); + /** - * ### overwriteMethod (ctx, name, fn) + * ### .overwriteMethod(ctx, name, fn) * * Overwites an already existing method and provides * access to previous function. Must return function @@ -5346,28 +8577,67 @@ module.exports = function (ctx, name, method, chainingBehavior) { * @api public */ -module.exports = function (ctx, name, method) { +module.exports = function overwriteMethod(ctx, name, method) { var _method = ctx[name] - , _super = function () { return this; }; + , _super = function () { + throw new Error(name + ' is not a function'); + }; if (_method && 'function' === typeof _method) _super = _method; - ctx[name] = function () { + var overwritingMethodWrapper = function () { + // Setting the `ssfi` flag to `overwritingMethodWrapper` causes this + // function to be the starting point for removing implementation frames from + // the stack trace of a failed assertion. + // + // However, we only want to use this function as the starting point if the + // `lockSsfi` flag isn't set. + // + // If the `lockSsfi` flag is set, then either this assertion has been + // overwritten by another assertion, or this assertion is being invoked from + // inside of another assertion. In the first case, the `ssfi` flag has + // already been set by the overwriting assertion. In the second case, the + // `ssfi` flag has already been set by the outer assertion. + if (!flag(this, 'lockSsfi')) { + flag(this, 'ssfi', overwritingMethodWrapper); + } + + // Setting the `lockSsfi` flag to `true` prevents the overwritten assertion + // from changing the `ssfi` flag. By this point, the `ssfi` flag is already + // set to the correct starting point for this assertion. + var origLockSsfi = flag(this, 'lockSsfi'); + flag(this, 'lockSsfi', true); var result = method(_super).apply(this, arguments); - return result === undefined ? this : result; + flag(this, 'lockSsfi', origLockSsfi); + + if (result !== undefined) { + return result; + } + + var newAssertion = new chai.Assertion(); + transferFlags(this, newAssertion); + return newAssertion; } + + addLengthGuard(overwritingMethodWrapper, name, false); + ctx[name] = proxify(overwritingMethodWrapper, name); }; -},{}],27:[function(require,module,exports){ +},{"../../chai":2,"./addLengthGuard":10,"./flag":15,"./proxify":30,"./transferFlags":32}],29:[function(require,module,exports){ /*! * Chai - overwriteProperty utility * Copyright(c) 2012-2014 Jake Luer * MIT Licensed */ +var chai = require('../../chai'); +var flag = require('./flag'); +var isProxyEnabled = require('./isProxyEnabled'); +var transferFlags = require('./transferFlags'); + /** - * ### overwriteProperty (ctx, name, fn) + * ### .overwriteProperty(ctx, name, fn) * * Overwites an already existing property getter and provides * access to previous value. Must return function to use as getter. @@ -5400,7 +8670,7 @@ module.exports = function (ctx, name, method) { * @api public */ -module.exports = function (ctx, name, getter) { +module.exports = function overwriteProperty(ctx, name, getter) { var _get = Object.getOwnPropertyDescriptor(ctx, name) , _super = function () {}; @@ -5408,15 +8678,174 @@ module.exports = function (ctx, name, getter) { _super = _get.get Object.defineProperty(ctx, name, - { get: function () { + { get: function overwritingPropertyGetter() { + // Setting the `ssfi` flag to `overwritingPropertyGetter` causes this + // function to be the starting point for removing implementation frames + // from the stack trace of a failed assertion. + // + // However, we only want to use this function as the starting point if + // the `lockSsfi` flag isn't set and proxy protection is disabled. + // + // If the `lockSsfi` flag is set, then either this assertion has been + // overwritten by another assertion, or this assertion is being invoked + // from inside of another assertion. In the first case, the `ssfi` flag + // has already been set by the overwriting assertion. In the second + // case, the `ssfi` flag has already been set by the outer assertion. + // + // If proxy protection is enabled, then the `ssfi` flag has already been + // set by the proxy getter. + if (!isProxyEnabled() && !flag(this, 'lockSsfi')) { + flag(this, 'ssfi', overwritingPropertyGetter); + } + + // Setting the `lockSsfi` flag to `true` prevents the overwritten + // assertion from changing the `ssfi` flag. By this point, the `ssfi` + // flag is already set to the correct starting point for this assertion. + var origLockSsfi = flag(this, 'lockSsfi'); + flag(this, 'lockSsfi', true); var result = getter(_super).call(this); - return result === undefined ? this : result; + flag(this, 'lockSsfi', origLockSsfi); + + if (result !== undefined) { + return result; + } + + var newAssertion = new chai.Assertion(); + transferFlags(this, newAssertion); + return newAssertion; } , configurable: true }); }; -},{}],28:[function(require,module,exports){ +},{"../../chai":2,"./flag":15,"./isProxyEnabled":25,"./transferFlags":32}],30:[function(require,module,exports){ +var config = require('../config'); +var flag = require('./flag'); +var getProperties = require('./getProperties'); +var isProxyEnabled = require('./isProxyEnabled'); + +/*! + * Chai - proxify utility + * Copyright(c) 2012-2014 Jake Luer + * MIT Licensed + */ + +/** + * ### .proxify(object) + * + * Return a proxy of given object that throws an error when a non-existent + * property is read. By default, the root cause is assumed to be a misspelled + * property, and thus an attempt is made to offer a reasonable suggestion from + * the list of existing properties. However, if a nonChainableMethodName is + * provided, then the root cause is instead a failure to invoke a non-chainable + * method prior to reading the non-existent property. + * + * If proxies are unsupported or disabled via the user's Chai config, then + * return object without modification. + * + * @param {Object} obj + * @param {String} nonChainableMethodName + * @namespace Utils + * @name proxify + */ + +var builtins = ['__flags', '__methods', '_obj', 'assert']; + +module.exports = function proxify(obj, nonChainableMethodName) { + if (!isProxyEnabled()) return obj; + + return new Proxy(obj, { + get: function proxyGetter(target, property) { + // This check is here because we should not throw errors on Symbol properties + // such as `Symbol.toStringTag`. + // The values for which an error should be thrown can be configured using + // the `config.proxyExcludedKeys` setting. + if (typeof property === 'string' && + config.proxyExcludedKeys.indexOf(property) === -1 && + !Reflect.has(target, property)) { + // Special message for invalid property access of non-chainable methods. + if (nonChainableMethodName) { + throw Error('Invalid Chai property: ' + nonChainableMethodName + '.' + + property + '. See docs for proper usage of "' + + nonChainableMethodName + '".'); + } + + var orderedProperties = getProperties(target).filter(function(property) { + return !Object.prototype.hasOwnProperty(property) && + builtins.indexOf(property) === -1; + }).sort(function(a, b) { + return stringDistance(property, a) - stringDistance(property, b); + }); + + if (orderedProperties.length && + stringDistance(orderedProperties[0], property) < 4) { + // If the property is reasonably close to an existing Chai property, + // suggest that property to the user. + throw Error('Invalid Chai property: ' + property + + '. Did you mean "' + orderedProperties[0] + '"?'); + } else { + throw Error('Invalid Chai property: ' + property); + } + } + + // Use this proxy getter as the starting point for removing implementation + // frames from the stack trace of a failed assertion. For property + // assertions, this prevents the proxy getter from showing up in the stack + // trace since it's invoked before the property getter. For method and + // chainable method assertions, this flag will end up getting changed to + // the method wrapper, which is good since this frame will no longer be in + // the stack once the method is invoked. Note that Chai builtin assertion + // properties such as `__flags` are skipped since this is only meant to + // capture the starting point of an assertion. This step is also skipped + // if the `lockSsfi` flag is set, thus indicating that this assertion is + // being called from within another assertion. In that case, the `ssfi` + // flag is already set to the outer assertion's starting point. + if (builtins.indexOf(property) === -1 && !flag(target, 'lockSsfi')) { + flag(target, 'ssfi', proxyGetter); + } + + return Reflect.get(target, property); + } + }); +}; + +/** + * # stringDistance(strA, strB) + * Return the Levenshtein distance between two strings. + * @param {string} strA + * @param {string} strB + * @return {number} the string distance between strA and strB + * @api private + */ + +function stringDistance(strA, strB, memo) { + if (!memo) { + // `memo` is a two-dimensional array containing a cache of distances + // memo[i][j] is the distance between strA.slice(0, i) and + // strB.slice(0, j). + memo = []; + for (var i = 0; i <= strA.length; i++) { + memo[i] = []; + } + } + + if (!memo[strA.length] || !memo[strA.length][strB.length]) { + if (strA.length === 0 || strB.length === 0) { + memo[strA.length][strB.length] = Math.max(strA.length, strB.length); + } else { + memo[strA.length][strB.length] = Math.min( + stringDistance(strA.slice(0, -1), strB, memo) + 1, + stringDistance(strA, strB.slice(0, -1), memo) + 1, + stringDistance(strA.slice(0, -1), strB.slice(0, -1), memo) + + (strA.slice(-1) === strB.slice(-1) ? 0 : 1) + ); + } + } + + return memo[strA.length][strB.length]; +} + +},{"../config":4,"./flag":15,"./getProperties":21,"./isProxyEnabled":25}],31:[function(require,module,exports){ /*! * Chai - test utility * Copyright(c) 2012-2014 Jake Luer @@ -5430,7 +8859,7 @@ module.exports = function (ctx, name, getter) { var flag = require('./flag'); /** - * # test(object, expression) + * ### .test(object, expression) * * Test and object for expression. * @@ -5440,13 +8869,13 @@ var flag = require('./flag'); * @name test */ -module.exports = function (obj, args) { +module.exports = function test(obj, args) { var negate = flag(obj, 'negate') , expr = args[0]; return negate ? !expr : expr; }; -},{"./flag":13}],29:[function(require,module,exports){ +},{"./flag":15}],32:[function(require,module,exports){ /*! * Chai - transferFlags utility * Copyright(c) 2012-2014 Jake Luer @@ -5454,12 +8883,12 @@ module.exports = function (obj, args) { */ /** - * ### transferFlags(assertion, object, includeAll = true) + * ### .transferFlags(assertion, object, includeAll = true) * * Transfer all the flags for `assertion` to `object`. If * `includeAll` is set to `false`, then the base Chai - * assertion flags (namely `object`, `ssfi`, and `message`) - * will not be transferred. + * assertion flags (namely `object`, `ssfi`, `lockSsfi`, + * and `message`) will not be transferred. * * * var newAssertion = new Assertion(); @@ -5476,7 +8905,7 @@ module.exports = function (obj, args) { * @api private */ -module.exports = function (assertion, object, includeAll) { +module.exports = function transferFlags(assertion, object, includeAll) { var flags = assertion.__flags || (assertion.__flags = Object.create(null)); if (!object.__flags) { @@ -5487,13 +8916,13 @@ module.exports = function (assertion, object, includeAll) { for (var flag in flags) { if (includeAll || - (flag !== 'object' && flag !== 'ssfi' && flag != 'message')) { + (flag !== 'object' && flag !== 'ssfi' && flag !== 'lockSsfi' && flag != 'message')) { object.__flags[flag] = flags[flag]; } } }; -},{}],30:[function(require,module,exports){ +},{}],33:[function(require,module,exports){ /*! * assertion-error * Copyright(c) 2013 Jake Luer @@ -5566,7 +8995,11 @@ function AssertionError (message, _props, ssf) { if (ssf && Error.captureStackTrace) { Error.captureStackTrace(this, ssf); } else { - this.stack = new Error().stack; + try { + throw new Error(); + } catch(e) { + this.stack = e.stack; + } } } @@ -5607,10 +9040,183 @@ AssertionError.prototype.toJSON = function (stack) { return props; }; -},{}],31:[function(require,module,exports){ -module.exports = require('./lib/eql'); +},{}],34:[function(require,module,exports){ +'use strict'; -},{"./lib/eql":32}],32:[function(require,module,exports){ +/* ! + * Chai - checkError utility + * Copyright(c) 2012-2016 Jake Luer + * MIT Licensed + */ + +/** + * ### .checkError + * + * Checks that an error conforms to a given set of criteria and/or retrieves information about it. + * + * @api public + */ + +/** + * ### .compatibleInstance(thrown, errorLike) + * + * Checks if two instances are compatible (strict equal). + * Returns false if errorLike is not an instance of Error, because instances + * can only be compatible if they're both error instances. + * + * @name compatibleInstance + * @param {Error} thrown error + * @param {Error|ErrorConstructor} errorLike object to compare against + * @namespace Utils + * @api public + */ + +function compatibleInstance(thrown, errorLike) { + return errorLike instanceof Error && thrown === errorLike; +} + +/** + * ### .compatibleConstructor(thrown, errorLike) + * + * Checks if two constructors are compatible. + * This function can receive either an error constructor or + * an error instance as the `errorLike` argument. + * Constructors are compatible if they're the same or if one is + * an instance of another. + * + * @name compatibleConstructor + * @param {Error} thrown error + * @param {Error|ErrorConstructor} errorLike object to compare against + * @namespace Utils + * @api public + */ + +function compatibleConstructor(thrown, errorLike) { + if (errorLike instanceof Error) { + // If `errorLike` is an instance of any error we compare their constructors + return thrown.constructor === errorLike.constructor || thrown instanceof errorLike.constructor; + } else if (errorLike.prototype instanceof Error || errorLike === Error) { + // If `errorLike` is a constructor that inherits from Error, we compare `thrown` to `errorLike` directly + return thrown.constructor === errorLike || thrown instanceof errorLike; + } + + return false; +} + +/** + * ### .compatibleMessage(thrown, errMatcher) + * + * Checks if an error's message is compatible with a matcher (String or RegExp). + * If the message contains the String or passes the RegExp test, + * it is considered compatible. + * + * @name compatibleMessage + * @param {Error} thrown error + * @param {String|RegExp} errMatcher to look for into the message + * @namespace Utils + * @api public + */ + +function compatibleMessage(thrown, errMatcher) { + var comparisonString = typeof thrown === 'string' ? thrown : thrown.message; + if (errMatcher instanceof RegExp) { + return errMatcher.test(comparisonString); + } else if (typeof errMatcher === 'string') { + return comparisonString.indexOf(errMatcher) !== -1; // eslint-disable-line no-magic-numbers + } + + return false; +} + +/** + * ### .getFunctionName(constructorFn) + * + * Returns the name of a function. + * This also includes a polyfill function if `constructorFn.name` is not defined. + * + * @name getFunctionName + * @param {Function} constructorFn + * @namespace Utils + * @api private + */ + +var functionNameMatch = /\s*function(?:\s|\s*\/\*[^(?:*\/)]+\*\/\s*)*([^\(\/]+)/; +function getFunctionName(constructorFn) { + var name = ''; + if (typeof constructorFn.name === 'undefined') { + // Here we run a polyfill if constructorFn.name is not defined + var match = String(constructorFn).match(functionNameMatch); + if (match) { + name = match[1]; + } + } else { + name = constructorFn.name; + } + + return name; +} + +/** + * ### .getConstructorName(errorLike) + * + * Gets the constructor name for an Error instance or constructor itself. + * + * @name getConstructorName + * @param {Error|ErrorConstructor} errorLike + * @namespace Utils + * @api public + */ + +function getConstructorName(errorLike) { + var constructorName = errorLike; + if (errorLike instanceof Error) { + constructorName = getFunctionName(errorLike.constructor); + } else if (typeof errorLike === 'function') { + // If `err` is not an instance of Error it is an error constructor itself or another function. + // If we've got a common function we get its name, otherwise we may need to create a new instance + // of the error just in case it's a poorly-constructed error. Please see chaijs/chai/issues/45 to know more. + constructorName = getFunctionName(errorLike).trim() || + getFunctionName(new errorLike()); // eslint-disable-line new-cap + } + + return constructorName; +} + +/** + * ### .getMessage(errorLike) + * + * Gets the error message from an error. + * If `err` is a String itself, we return it. + * If the error has no message, we return an empty string. + * + * @name getMessage + * @param {Error|String} errorLike + * @namespace Utils + * @api public + */ + +function getMessage(errorLike) { + var msg = ''; + if (errorLike && errorLike.message) { + msg = errorLike.message; + } else if (typeof errorLike === 'string') { + msg = errorLike; + } + + return msg; +} + +module.exports = { + compatibleInstance: compatibleInstance, + compatibleConstructor: compatibleConstructor, + compatibleMessage: compatibleMessage, + getMessage: getMessage, + getConstructorName: getConstructorName, +}; + +},{}],35:[function(require,module,exports){ +'use strict'; +/* globals Symbol: true, Uint8Array: true, WeakMap: true */ /*! * deep-eql * Copyright(c) 2013 Jake Luer @@ -5622,16 +9228,89 @@ module.exports = require('./lib/eql'); */ var type = require('type-detect'); +function FakeMap() { + this.clear(); +} +FakeMap.prototype = { + clear: function clearMap() { + this.keys = []; + this.values = []; + return this; + }, + set: function setMap(key, value) { + var index = this.keys.indexOf(key); + if (index >= 0) { + this.values[index] = value; + } else { + this.keys.push(key); + this.values.push(value); + } + return this; + }, + get: function getMap(key) { + return this.values[this.keys.indexOf(key)]; + }, + delete: function deleteMap(key) { + var index = this.keys.indexOf(key); + if (index >= 0) { + this.values = this.values.slice(0, index).concat(this.values.slice(index + 1)); + this.keys = this.keys.slice(0, index).concat(this.keys.slice(index + 1)); + } + return this; + }, +}; + +var MemoizeMap = null; +if (typeof WeakMap === 'function') { + MemoizeMap = WeakMap; +} else { + MemoizeMap = FakeMap; +} /*! - * Buffer.isBuffer browser shim - */ + * Check to see if the MemoizeMap has recorded a result of the two operands + * + * @param {Mixed} leftHandOperand + * @param {Mixed} rightHandOperand + * @param {MemoizeMap} memoizeMap + * @returns {Boolean|null} result +*/ +function memoizeCompare(leftHandOperand, rightHandOperand, memoizeMap) { + // Technically, WeakMap keys can *only* be objects, not primitives. + if (!memoizeMap || isPrimitive(leftHandOperand) || isPrimitive(rightHandOperand)) { + return null; + } + var leftHandMap = memoizeMap.get(leftHandOperand); + if (leftHandMap) { + var result = leftHandMap.get(rightHandOperand); + if (typeof result === 'boolean') { + return result; + } + } + return null; +} -var Buffer; -try { Buffer = require('buffer').Buffer; } -catch(ex) { - Buffer = {}; - Buffer.isBuffer = function() { return false; } +/*! + * Set the result of the equality into the MemoizeMap + * + * @param {Mixed} leftHandOperand + * @param {Mixed} rightHandOperand + * @param {MemoizeMap} memoizeMap + * @param {Boolean} result +*/ +function memoizeSet(leftHandOperand, rightHandOperand, memoizeMap, result) { + // Technically, WeakMap keys can *only* be objects, not primitives. + if (!memoizeMap || isPrimitive(leftHandOperand) || isPrimitive(rightHandOperand)) { + return; + } + var leftHandMap = memoizeMap.get(leftHandOperand); + if (leftHandMap) { + leftHandMap.set(rightHandOperand, result); + } else { + leftHandMap = new MemoizeMap(); + leftHandMap.set(rightHandOperand, result); + memoizeMap.set(leftHandOperand, leftHandMap); + } } /*! @@ -5639,520 +9318,1519 @@ catch(ex) { */ module.exports = deepEqual; +module.exports.MemoizeMap = MemoizeMap; /** - * Assert super-strict (egal) equality between - * two objects of any type. + * Assert deeply nested sameValue equality between two objects of any type. * - * @param {Mixed} a - * @param {Mixed} b - * @param {Array} memoised (optional) + * @param {Mixed} leftHandOperand + * @param {Mixed} rightHandOperand + * @param {Object} [options] (optional) Additional options + * @param {Array} [options.comparator] (optional) Override default algorithm, determining custom equality. + * @param {Array} [options.memoize] (optional) Provide a custom memoization object which will cache the results of + complex objects for a speed boost. By passing `false` you can disable memoization, but this will cause circular + references to blow the stack. * @return {Boolean} equal match */ +function deepEqual(leftHandOperand, rightHandOperand, options) { + // If we have a comparator, we can't assume anything; so bail to its check first. + if (options && options.comparator) { + return extensiveDeepEqual(leftHandOperand, rightHandOperand, options); + } -function deepEqual(a, b, m) { - if (sameValue(a, b)) { + var simpleResult = simpleEqual(leftHandOperand, rightHandOperand); + if (simpleResult !== null) { + return simpleResult; + } + + // Deeper comparisons are pushed through to a larger function + return extensiveDeepEqual(leftHandOperand, rightHandOperand, options); +} + +/** + * Many comparisons can be canceled out early via simple equality or primitive checks. + * @param {Mixed} leftHandOperand + * @param {Mixed} rightHandOperand + * @return {Boolean|null} equal match + */ +function simpleEqual(leftHandOperand, rightHandOperand) { + // Equal references (except for Numbers) can be returned early + if (leftHandOperand === rightHandOperand) { + // Handle +-0 cases + return leftHandOperand !== 0 || 1 / leftHandOperand === 1 / rightHandOperand; + } + + // handle NaN cases + if ( + leftHandOperand !== leftHandOperand && // eslint-disable-line no-self-compare + rightHandOperand !== rightHandOperand // eslint-disable-line no-self-compare + ) { return true; - } else if ('date' === type(a)) { - return dateEqual(a, b); - } else if ('regexp' === type(a)) { - return regexpEqual(a, b); - } else if (Buffer.isBuffer(a)) { - return bufferEqual(a, b); - } else if ('arguments' === type(a)) { - return argumentsEqual(a, b, m); - } else if (!typeEqual(a, b)) { - return false; - } else if (('object' !== type(a) && 'object' !== type(b)) - && ('array' !== type(a) && 'array' !== type(b))) { - return sameValue(a, b); - } else { - return objectEqual(a, b, m); } + + // Anything that is not an 'object', i.e. symbols, functions, booleans, numbers, + // strings, and undefined, can be compared by reference. + if (isPrimitive(leftHandOperand) || isPrimitive(rightHandOperand)) { + // Easy out b/c it would have passed the first equality check + return false; + } + return null; } /*! - * Strict (egal) equality test. Ensures that NaN always - * equals NaN and `-0` does not equal `+0`. + * The main logic of the `deepEqual` function. * - * @param {Mixed} a - * @param {Mixed} b + * @param {Mixed} leftHandOperand + * @param {Mixed} rightHandOperand + * @param {Object} [options] (optional) Additional options + * @param {Array} [options.comparator] (optional) Override default algorithm, determining custom equality. + * @param {Array} [options.memoize] (optional) Provide a custom memoization object which will cache the results of + complex objects for a speed boost. By passing `false` you can disable memoization, but this will cause circular + references to blow the stack. * @return {Boolean} equal match - */ +*/ +function extensiveDeepEqual(leftHandOperand, rightHandOperand, options) { + options = options || {}; + options.memoize = options.memoize === false ? false : options.memoize || new MemoizeMap(); + var comparator = options && options.comparator; -function sameValue(a, b) { - if (a === b) return a !== 0 || 1 / a === 1 / b; - return a !== a && b !== b; -} + // Check if a memoized result exists. + var memoizeResultLeft = memoizeCompare(leftHandOperand, rightHandOperand, options.memoize); + if (memoizeResultLeft !== null) { + return memoizeResultLeft; + } + var memoizeResultRight = memoizeCompare(rightHandOperand, leftHandOperand, options.memoize); + if (memoizeResultRight !== null) { + return memoizeResultRight; + } -/*! - * Compare the types of two given objects and - * return if they are equal. Note that an Array - * has a type of `array` (not `object`) and arguments - * have a type of `arguments` (not `array`/`object`). - * - * @param {Mixed} a - * @param {Mixed} b - * @return {Boolean} result - */ - -function typeEqual(a, b) { - return type(a) === type(b); -} - -/*! - * Compare two Date objects by asserting that - * the time values are equal using `saveValue`. - * - * @param {Date} a - * @param {Date} b - * @return {Boolean} result - */ - -function dateEqual(a, b) { - if ('date' !== type(b)) return false; - return sameValue(a.getTime(), b.getTime()); -} - -/*! - * Compare two regular expressions by converting them - * to string and checking for `sameValue`. - * - * @param {RegExp} a - * @param {RegExp} b - * @return {Boolean} result - */ - -function regexpEqual(a, b) { - if ('regexp' !== type(b)) return false; - return sameValue(a.toString(), b.toString()); -} - -/*! - * Assert deep equality of two `arguments` objects. - * Unfortunately, these must be sliced to arrays - * prior to test to ensure no bad behavior. - * - * @param {Arguments} a - * @param {Arguments} b - * @param {Array} memoize (optional) - * @return {Boolean} result - */ - -function argumentsEqual(a, b, m) { - if ('arguments' !== type(b)) return false; - a = [].slice.call(a); - b = [].slice.call(b); - return deepEqual(a, b, m); -} - -/*! - * Get enumerable properties of a given object. - * - * @param {Object} a - * @return {Array} property names - */ - -function enumerable(a) { - var res = []; - for (var key in a) res.push(key); - return res; -} - -/*! - * Simple equality for flat iterable objects - * such as Arrays or Node.js buffers. - * - * @param {Iterable} a - * @param {Iterable} b - * @return {Boolean} result - */ - -function iterableEqual(a, b) { - if (a.length !== b.length) return false; - - var i = 0; - var match = true; - - for (; i < a.length; i++) { - if (a[i] !== b[i]) { - match = false; - break; + // If a comparator is present, use it. + if (comparator) { + var comparatorResult = comparator(leftHandOperand, rightHandOperand); + // Comparators may return null, in which case we want to go back to default behavior. + if (comparatorResult === false || comparatorResult === true) { + memoizeSet(leftHandOperand, rightHandOperand, options.memoize, comparatorResult); + return comparatorResult; + } + // To allow comparators to override *any* behavior, we ran them first. Since it didn't decide + // what to do, we need to make sure to return the basic tests first before we move on. + var simpleResult = simpleEqual(leftHandOperand, rightHandOperand); + if (simpleResult !== null) { + // Don't memoize this, it takes longer to set/retrieve than to just compare. + return simpleResult; } } - return match; + var leftHandType = type(leftHandOperand); + if (leftHandType !== type(rightHandOperand)) { + memoizeSet(leftHandOperand, rightHandOperand, options.memoize, false); + return false; + } + + // Temporarily set the operands in the memoize object to prevent blowing the stack + memoizeSet(leftHandOperand, rightHandOperand, options.memoize, true); + + var result = extensiveDeepEqualByType(leftHandOperand, rightHandOperand, leftHandType, options); + memoizeSet(leftHandOperand, rightHandOperand, options.memoize, result); + return result; +} + +function extensiveDeepEqualByType(leftHandOperand, rightHandOperand, leftHandType, options) { + switch (leftHandType) { + case 'String': + case 'Number': + case 'Boolean': + case 'Date': + // If these types are their instance types (e.g. `new Number`) then re-deepEqual against their values + return deepEqual(leftHandOperand.valueOf(), rightHandOperand.valueOf()); + case 'Promise': + case 'Symbol': + case 'function': + case 'WeakMap': + case 'WeakSet': + case 'Error': + return leftHandOperand === rightHandOperand; + case 'Arguments': + case 'Int8Array': + case 'Uint8Array': + case 'Uint8ClampedArray': + case 'Int16Array': + case 'Uint16Array': + case 'Int32Array': + case 'Uint32Array': + case 'Float32Array': + case 'Float64Array': + case 'Array': + return iterableEqual(leftHandOperand, rightHandOperand, options); + case 'RegExp': + return regexpEqual(leftHandOperand, rightHandOperand); + case 'Generator': + return generatorEqual(leftHandOperand, rightHandOperand, options); + case 'DataView': + return iterableEqual(new Uint8Array(leftHandOperand.buffer), new Uint8Array(rightHandOperand.buffer), options); + case 'ArrayBuffer': + return iterableEqual(new Uint8Array(leftHandOperand), new Uint8Array(rightHandOperand), options); + case 'Set': + return entriesEqual(leftHandOperand, rightHandOperand, options); + case 'Map': + return entriesEqual(leftHandOperand, rightHandOperand, options); + default: + return objectEqual(leftHandOperand, rightHandOperand, options); + } } /*! - * Extension to `iterableEqual` specifically - * for Node.js Buffers. + * Compare two Regular Expressions for equality. * - * @param {Buffer} a - * @param {Mixed} b + * @param {RegExp} leftHandOperand + * @param {RegExp} rightHandOperand * @return {Boolean} result */ -function bufferEqual(a, b) { - if (!Buffer.isBuffer(b)) return false; - return iterableEqual(a, b); +function regexpEqual(leftHandOperand, rightHandOperand) { + return leftHandOperand.toString() === rightHandOperand.toString(); } /*! - * Block for `objectEqual` ensuring non-existing - * values don't get in. + * Compare two Sets/Maps for equality. Faster than other equality functions. * - * @param {Mixed} object + * @param {Set} leftHandOperand + * @param {Set} rightHandOperand + * @param {Object} [options] (Optional) * @return {Boolean} result */ -function isValue(a) { - return a !== null && a !== undefined; +function entriesEqual(leftHandOperand, rightHandOperand, options) { + // IE11 doesn't support Set#entries or Set#@@iterator, so we need manually populate using Set#forEach + if (leftHandOperand.size !== rightHandOperand.size) { + return false; + } + if (leftHandOperand.size === 0) { + return true; + } + var leftHandItems = []; + var rightHandItems = []; + leftHandOperand.forEach(function gatherEntries(key, value) { + leftHandItems.push([ key, value ]); + }); + rightHandOperand.forEach(function gatherEntries(key, value) { + rightHandItems.push([ key, value ]); + }); + return iterableEqual(leftHandItems.sort(), rightHandItems.sort(), options); } /*! - * Recursively check the equality of two objects. - * Once basic sameness has been established it will - * defer to `deepEqual` for each enumerable key - * in the object. + * Simple equality for flat iterable objects such as Arrays, TypedArrays or Node.js buffers. * - * @param {Mixed} a - * @param {Mixed} b + * @param {Iterable} leftHandOperand + * @param {Iterable} rightHandOperand + * @param {Object} [options] (Optional) * @return {Boolean} result */ -function objectEqual(a, b, m) { - if (!isValue(a) || !isValue(b)) { +function iterableEqual(leftHandOperand, rightHandOperand, options) { + var length = leftHandOperand.length; + if (length !== rightHandOperand.length) { return false; } - - if (a.prototype !== b.prototype) { - return false; + if (length === 0) { + return true; } - - var i; - if (m) { - for (i = 0; i < m.length; i++) { - if ((m[i][0] === a && m[i][1] === b) - || (m[i][0] === b && m[i][1] === a)) { - return true; - } - } - } else { - m = []; - } - - try { - var ka = enumerable(a); - var kb = enumerable(b); - } catch (ex) { - return false; - } - - ka.sort(); - kb.sort(); - - if (!iterableEqual(ka, kb)) { - return false; - } - - m.push([ a, b ]); - - var key; - for (i = ka.length - 1; i >= 0; i--) { - key = ka[i]; - if (!deepEqual(a[key], b[key], m)) { + var index = -1; + while (++index < length) { + if (deepEqual(leftHandOperand[index], rightHandOperand[index], options) === false) { return false; } } - return true; } -},{"buffer":undefined,"type-detect":33}],33:[function(require,module,exports){ -module.exports = require('./lib/type'); - -},{"./lib/type":34}],34:[function(require,module,exports){ /*! + * Simple equality for generator objects such as those returned by generator functions. + * + * @param {Iterable} leftHandOperand + * @param {Iterable} rightHandOperand + * @param {Object} [options] (Optional) + * @return {Boolean} result + */ + +function generatorEqual(leftHandOperand, rightHandOperand, options) { + return iterableEqual(getGeneratorEntries(leftHandOperand), getGeneratorEntries(rightHandOperand), options); +} + +/*! + * Determine if the given object has an @@iterator function. + * + * @param {Object} target + * @return {Boolean} `true` if the object has an @@iterator function. + */ +function hasIteratorFunction(target) { + return typeof Symbol !== 'undefined' && + typeof target === 'object' && + typeof Symbol.iterator !== 'undefined' && + typeof target[Symbol.iterator] === 'function'; +} + +/*! + * Gets all iterator entries from the given Object. If the Object has no @@iterator function, returns an empty array. + * This will consume the iterator - which could have side effects depending on the @@iterator implementation. + * + * @param {Object} target + * @returns {Array} an array of entries from the @@iterator function + */ +function getIteratorEntries(target) { + if (hasIteratorFunction(target)) { + try { + return getGeneratorEntries(target[Symbol.iterator]()); + } catch (iteratorError) { + return []; + } + } + return []; +} + +/*! + * Gets all entries from a Generator. This will consume the generator - which could have side effects. + * + * @param {Generator} target + * @returns {Array} an array of entries from the Generator. + */ +function getGeneratorEntries(generator) { + var generatorResult = generator.next(); + var accumulator = [ generatorResult.value ]; + while (generatorResult.done === false) { + generatorResult = generator.next(); + accumulator.push(generatorResult.value); + } + return accumulator; +} + +/*! + * Gets all own and inherited enumerable keys from a target. + * + * @param {Object} target + * @returns {Array} an array of own and inherited enumerable keys from the target. + */ +function getEnumerableKeys(target) { + var keys = []; + for (var key in target) { + keys.push(key); + } + return keys; +} + +/*! + * Determines if two objects have matching values, given a set of keys. Defers to deepEqual for the equality check of + * each key. If any value of the given key is not equal, the function will return false (early). + * + * @param {Mixed} leftHandOperand + * @param {Mixed} rightHandOperand + * @param {Array} keys An array of keys to compare the values of leftHandOperand and rightHandOperand against + * @param {Object} [options] (Optional) + * @return {Boolean} result + */ +function keysEqual(leftHandOperand, rightHandOperand, keys, options) { + var length = keys.length; + if (length === 0) { + return true; + } + for (var i = 0; i < length; i += 1) { + if (deepEqual(leftHandOperand[keys[i]], rightHandOperand[keys[i]], options) === false) { + return false; + } + } + return true; +} + +/*! + * Recursively check the equality of two Objects. Once basic sameness has been established it will defer to `deepEqual` + * for each enumerable key in the object. + * + * @param {Mixed} leftHandOperand + * @param {Mixed} rightHandOperand + * @param {Object} [options] (Optional) + * @return {Boolean} result + */ + +function objectEqual(leftHandOperand, rightHandOperand, options) { + var leftHandKeys = getEnumerableKeys(leftHandOperand); + var rightHandKeys = getEnumerableKeys(rightHandOperand); + if (leftHandKeys.length && leftHandKeys.length === rightHandKeys.length) { + leftHandKeys.sort(); + rightHandKeys.sort(); + if (iterableEqual(leftHandKeys, rightHandKeys) === false) { + return false; + } + return keysEqual(leftHandOperand, rightHandOperand, leftHandKeys, options); + } + + var leftHandEntries = getIteratorEntries(leftHandOperand); + var rightHandEntries = getIteratorEntries(rightHandOperand); + if (leftHandEntries.length && leftHandEntries.length === rightHandEntries.length) { + leftHandEntries.sort(); + rightHandEntries.sort(); + return iterableEqual(leftHandEntries, rightHandEntries, options); + } + + if (leftHandKeys.length === 0 && + leftHandEntries.length === 0 && + rightHandKeys.length === 0 && + rightHandEntries.length === 0) { + return true; + } + + return false; +} + +/*! + * Returns true if the argument is a primitive. + * + * This intentionally returns true for all objects that can be compared by reference, + * including functions and symbols. + * + * @param {Mixed} value + * @return {Boolean} result + */ +function isPrimitive(value) { + return value === null || typeof value !== 'object'; +} + +},{"type-detect":36}],36:[function(require,module,exports){ +'use strict'; +/* ! * type-detect * Copyright(c) 2013 jake luer * MIT Licensed */ - -/*! - * Primary Exports - */ - -var exports = module.exports = getType; - -/*! - * Detectable javascript natives - */ - -var natives = { - '[object Array]': 'array' - , '[object RegExp]': 'regexp' - , '[object Function]': 'function' - , '[object Arguments]': 'arguments' - , '[object Date]': 'date' -}; - +var getPrototypeOfExists = typeof Object.getPrototypeOf === 'function'; +var promiseExists = typeof Promise === 'function'; +var globalObject = typeof window !== 'undefined' ? window : typeof global !== 'undefined' ? global : self; // eslint-disable-line +var isDom = 'location' in globalObject && 'document' in globalObject; +var htmlElementExists = typeof HTMLElement !== 'undefined'; +var isArrayExists = typeof Array.isArray === 'function'; +var symbolExists = typeof Symbol !== 'undefined'; +var mapExists = typeof Map !== 'undefined'; +var setExists = typeof Set !== 'undefined'; +var weakMapExists = typeof WeakMap !== 'undefined'; +var weakSetExists = typeof WeakSet !== 'undefined'; +var dataViewExists = typeof DataView !== 'undefined'; +var symbolIteratorExists = symbolExists && typeof Symbol.iterator !== 'undefined'; +var symbolToStringTagExists = symbolExists && typeof Symbol.toStringTag !== 'undefined'; +var setEntriesExists = setExists && typeof Set.prototype.entries === 'function'; +var mapEntriesExists = mapExists && typeof Map.prototype.entries === 'function'; +var setIteratorPrototype = getPrototypeOfExists && setEntriesExists && Object.getPrototypeOf(new Set().entries()); +var mapIteratorPrototype = getPrototypeOfExists && mapEntriesExists && Object.getPrototypeOf(new Map().entries()); +var arrayIteratorExists = symbolIteratorExists && typeof Array.prototype[Symbol.iterator] === 'function'; +var arrayIteratorPrototype = arrayIteratorExists && Object.getPrototypeOf([][Symbol.iterator]()); +var stringIteratorExists = symbolIteratorExists && typeof Array.prototype[Symbol.iterator] === 'function'; +var stringIteratorPrototype = stringIteratorExists && Object.getPrototypeOf(''[Symbol.iterator]()); +var toStringLeftSliceLength = 8; +var toStringRightSliceLength = -1; /** * ### typeOf (obj) * - * Use several different techniques to determine - * the type of object being tested. - * + * Uses `Object.prototype.toString` to determine the type of an object, + * normalising behaviour across engine versions & well optimised. * * @param {Mixed} object * @return {String} object type * @api public */ - -function getType (obj) { - var str = Object.prototype.toString.call(obj); - if (natives[str]) return natives[str]; - if (obj === null) return 'null'; - if (obj === undefined) return 'undefined'; - if (obj === Object(obj)) return 'object'; - return typeof obj; -} - -exports.Library = Library; - -/** - * ### Library - * - * Create a repository for custom type detection. - * - * ```js - * var lib = new type.Library; - * ``` - * - */ - -function Library () { - this.tests = {}; -} - -/** - * #### .of (obj) - * - * Expose replacement `typeof` detection to the library. - * - * ```js - * if ('string' === lib.of('hello world')) { - * // ... - * } - * ``` - * - * @param {Mixed} object to test - * @return {String} type - */ - -Library.prototype.of = getType; - -/** - * #### .define (type, test) - * - * Add a test to for the `.test()` assertion. - * - * Can be defined as a regular expression: - * - * ```js - * lib.define('int', /^[0-9]+$/); - * ``` - * - * ... or as a function: - * - * ```js - * lib.define('bln', function (obj) { - * if ('boolean' === lib.of(obj)) return true; - * var blns = [ 'yes', 'no', 'true', 'false', 1, 0 ]; - * if ('string' === lib.of(obj)) obj = obj.toLowerCase(); - * return !! ~blns.indexOf(obj); - * }); - * ``` - * - * @param {String} type - * @param {RegExp|Function} test - * @api public - */ - -Library.prototype.define = function (type, test) { - if (arguments.length === 1) return this.tests[type]; - this.tests[type] = test; - return this; -}; - -/** - * #### .test (obj, test) - * - * Assert that an object is of type. Will first - * check natives, and if that does not pass it will - * use the user defined custom tests. - * - * ```js - * assert(lib.test('1', 'int')); - * assert(lib.test('yes', 'bln')); - * ``` - * - * @param {Mixed} object - * @param {String} type - * @return {Boolean} result - * @api public - */ - -Library.prototype.test = function (obj, type) { - if (type === getType(obj)) return true; - var test = this.tests[type]; - - if (test && 'regexp' === getType(test)) { - return test.test(obj); - } else if (test && 'function' === getType(test)) { - return test(obj); - } else { - throw new ReferenceError('Type test "' + type + '" not defined or invalid.'); +module.exports = function typeDetect(obj) { + /* ! Speed optimisation + * Pre: + * string literal x 3,039,035 ops/sec ±1.62% (78 runs sampled) + * boolean literal x 1,424,138 ops/sec ±4.54% (75 runs sampled) + * number literal x 1,653,153 ops/sec ±1.91% (82 runs sampled) + * undefined x 9,978,660 ops/sec ±1.92% (75 runs sampled) + * function x 2,556,769 ops/sec ±1.73% (77 runs sampled) + * Post: + * string literal x 38,564,796 ops/sec ±1.15% (79 runs sampled) + * boolean literal x 31,148,940 ops/sec ±1.10% (79 runs sampled) + * number literal x 32,679,330 ops/sec ±1.90% (78 runs sampled) + * undefined x 32,363,368 ops/sec ±1.07% (82 runs sampled) + * function x 31,296,870 ops/sec ±0.96% (83 runs sampled) + */ + var typeofObj = typeof obj; + if (typeofObj !== 'object') { + return typeofObj; } + + /* ! Speed optimisation + * Pre: + * null x 28,645,765 ops/sec ±1.17% (82 runs sampled) + * Post: + * null x 36,428,962 ops/sec ±1.37% (84 runs sampled) + */ + if (obj === null) { + return 'null'; + } + + /* ! Spec Conformance + * Test: `Object.prototype.toString.call(window)`` + * - Node === "[object global]" + * - Chrome === "[object global]" + * - Firefox === "[object Window]" + * - PhantomJS === "[object Window]" + * - Safari === "[object Window]" + * - IE 11 === "[object Window]" + * - IE Edge === "[object Window]" + * Test: `Object.prototype.toString.call(this)`` + * - Chrome Worker === "[object global]" + * - Firefox Worker === "[object DedicatedWorkerGlobalScope]" + * - Safari Worker === "[object DedicatedWorkerGlobalScope]" + * - IE 11 Worker === "[object WorkerGlobalScope]" + * - IE Edge Worker === "[object WorkerGlobalScope]" + */ + if (obj === globalObject) { + return 'global'; + } + + /* ! Speed optimisation + * Pre: + * array literal x 2,888,352 ops/sec ±0.67% (82 runs sampled) + * Post: + * array literal x 22,479,650 ops/sec ±0.96% (81 runs sampled) + */ + if (isArrayExists && Array.isArray(obj)) { + return 'Array'; + } + + if (isDom) { + /* ! Spec Conformance + * (https://html.spec.whatwg.org/multipage/browsers.html#location) + * WhatWG HTML$7.7.3 - The `Location` interface + * Test: `Object.prototype.toString.call(window.location)`` + * - IE <=11 === "[object Object]" + * - IE Edge <=13 === "[object Object]" + */ + if (obj === globalObject.location) { + return 'Location'; + } + + /* ! Spec Conformance + * (https://html.spec.whatwg.org/#document) + * WhatWG HTML$3.1.1 - The `Document` object + * Note: Most browsers currently adher to the W3C DOM Level 2 spec + * (https://www.w3.org/TR/DOM-Level-2-HTML/html.html#ID-26809268) + * which suggests that browsers should use HTMLTableCellElement for + * both TD and TH elements. WhatWG separates these. + * WhatWG HTML states: + * > For historical reasons, Window objects must also have a + * > writable, configurable, non-enumerable property named + * > HTMLDocument whose value is the Document interface object. + * Test: `Object.prototype.toString.call(document)`` + * - Chrome === "[object HTMLDocument]" + * - Firefox === "[object HTMLDocument]" + * - Safari === "[object HTMLDocument]" + * - IE <=10 === "[object Document]" + * - IE 11 === "[object HTMLDocument]" + * - IE Edge <=13 === "[object HTMLDocument]" + */ + if (obj === globalObject.document) { + return 'Document'; + } + + /* ! Spec Conformance + * (https://html.spec.whatwg.org/multipage/webappapis.html#mimetypearray) + * WhatWG HTML$8.6.1.5 - Plugins - Interface MimeTypeArray + * Test: `Object.prototype.toString.call(navigator.mimeTypes)`` + * - IE <=10 === "[object MSMimeTypesCollection]" + */ + if (obj === (globalObject.navigator || {}).mimeTypes) { + return 'MimeTypeArray'; + } + + /* ! Spec Conformance + * (https://html.spec.whatwg.org/multipage/webappapis.html#pluginarray) + * WhatWG HTML$8.6.1.5 - Plugins - Interface PluginArray + * Test: `Object.prototype.toString.call(navigator.plugins)`` + * - IE <=10 === "[object MSPluginsCollection]" + */ + if (obj === (globalObject.navigator || {}).plugins) { + return 'PluginArray'; + } + + /* ! Spec Conformance + * (https://html.spec.whatwg.org/multipage/webappapis.html#pluginarray) + * WhatWG HTML$4.4.4 - The `blockquote` element - Interface `HTMLQuoteElement` + * Test: `Object.prototype.toString.call(document.createElement('blockquote'))`` + * - IE <=10 === "[object HTMLBlockElement]" + */ + if (htmlElementExists && obj instanceof HTMLElement && obj.tagName === 'BLOCKQUOTE') { + return 'HTMLQuoteElement'; + } + + /* ! Spec Conformance + * (https://html.spec.whatwg.org/#htmltabledatacellelement) + * WhatWG HTML$4.9.9 - The `td` element - Interface `HTMLTableDataCellElement` + * Note: Most browsers currently adher to the W3C DOM Level 2 spec + * (https://www.w3.org/TR/DOM-Level-2-HTML/html.html#ID-82915075) + * which suggests that browsers should use HTMLTableCellElement for + * both TD and TH elements. WhatWG separates these. + * Test: Object.prototype.toString.call(document.createElement('td')) + * - Chrome === "[object HTMLTableCellElement]" + * - Firefox === "[object HTMLTableCellElement]" + * - Safari === "[object HTMLTableCellElement]" + */ + if (htmlElementExists && obj instanceof HTMLElement && obj.tagName === 'TD') { + return 'HTMLTableDataCellElement'; + } + + /* ! Spec Conformance + * (https://html.spec.whatwg.org/#htmltableheadercellelement) + * WhatWG HTML$4.9.9 - The `td` element - Interface `HTMLTableHeaderCellElement` + * Note: Most browsers currently adher to the W3C DOM Level 2 spec + * (https://www.w3.org/TR/DOM-Level-2-HTML/html.html#ID-82915075) + * which suggests that browsers should use HTMLTableCellElement for + * both TD and TH elements. WhatWG separates these. + * Test: Object.prototype.toString.call(document.createElement('th')) + * - Chrome === "[object HTMLTableCellElement]" + * - Firefox === "[object HTMLTableCellElement]" + * - Safari === "[object HTMLTableCellElement]" + */ + if (htmlElementExists && obj instanceof HTMLElement && obj.tagName === 'TH') { + return 'HTMLTableHeaderCellElement'; + } + } + + /* ! Speed optimisation + * Pre: + * Float64Array x 625,644 ops/sec ±1.58% (80 runs sampled) + * Float32Array x 1,279,852 ops/sec ±2.91% (77 runs sampled) + * Uint32Array x 1,178,185 ops/sec ±1.95% (83 runs sampled) + * Uint16Array x 1,008,380 ops/sec ±2.25% (80 runs sampled) + * Uint8Array x 1,128,040 ops/sec ±2.11% (81 runs sampled) + * Int32Array x 1,170,119 ops/sec ±2.88% (80 runs sampled) + * Int16Array x 1,176,348 ops/sec ±5.79% (86 runs sampled) + * Int8Array x 1,058,707 ops/sec ±4.94% (77 runs sampled) + * Uint8ClampedArray x 1,110,633 ops/sec ±4.20% (80 runs sampled) + * Post: + * Float64Array x 7,105,671 ops/sec ±13.47% (64 runs sampled) + * Float32Array x 5,887,912 ops/sec ±1.46% (82 runs sampled) + * Uint32Array x 6,491,661 ops/sec ±1.76% (79 runs sampled) + * Uint16Array x 6,559,795 ops/sec ±1.67% (82 runs sampled) + * Uint8Array x 6,463,966 ops/sec ±1.43% (85 runs sampled) + * Int32Array x 5,641,841 ops/sec ±3.49% (81 runs sampled) + * Int16Array x 6,583,511 ops/sec ±1.98% (80 runs sampled) + * Int8Array x 6,606,078 ops/sec ±1.74% (81 runs sampled) + * Uint8ClampedArray x 6,602,224 ops/sec ±1.77% (83 runs sampled) + */ + var stringTag = (symbolToStringTagExists && obj[Symbol.toStringTag]); + if (typeof stringTag === 'string') { + return stringTag; + } + + if (getPrototypeOfExists) { + var objPrototype = Object.getPrototypeOf(obj); + /* ! Speed optimisation + * Pre: + * regex literal x 1,772,385 ops/sec ±1.85% (77 runs sampled) + * regex constructor x 2,143,634 ops/sec ±2.46% (78 runs sampled) + * Post: + * regex literal x 3,928,009 ops/sec ±0.65% (78 runs sampled) + * regex constructor x 3,931,108 ops/sec ±0.58% (84 runs sampled) + */ + if (objPrototype === RegExp.prototype) { + return 'RegExp'; + } + + /* ! Speed optimisation + * Pre: + * date x 2,130,074 ops/sec ±4.42% (68 runs sampled) + * Post: + * date x 3,953,779 ops/sec ±1.35% (77 runs sampled) + */ + if (objPrototype === Date.prototype) { + return 'Date'; + } + + /* ! Spec Conformance + * (http://www.ecma-international.org/ecma-262/6.0/index.html#sec-promise.prototype-@@tostringtag) + * ES6$25.4.5.4 - Promise.prototype[@@toStringTag] should be "Promise": + * Test: `Object.prototype.toString.call(Promise.resolve())`` + * - Chrome <=47 === "[object Object]" + * - Edge <=20 === "[object Object]" + * - Firefox 29-Latest === "[object Promise]" + * - Safari 7.1-Latest === "[object Promise]" + */ + if (promiseExists && objPrototype === Promise.prototype) { + return 'Promise'; + } + + /* ! Speed optimisation + * Pre: + * set x 2,222,186 ops/sec ±1.31% (82 runs sampled) + * Post: + * set x 4,545,879 ops/sec ±1.13% (83 runs sampled) + */ + if (setExists && objPrototype === Set.prototype) { + return 'Set'; + } + + /* ! Speed optimisation + * Pre: + * map x 2,396,842 ops/sec ±1.59% (81 runs sampled) + * Post: + * map x 4,183,945 ops/sec ±6.59% (82 runs sampled) + */ + if (mapExists && objPrototype === Map.prototype) { + return 'Map'; + } + + /* ! Speed optimisation + * Pre: + * weakset x 1,323,220 ops/sec ±2.17% (76 runs sampled) + * Post: + * weakset x 4,237,510 ops/sec ±2.01% (77 runs sampled) + */ + if (weakSetExists && objPrototype === WeakSet.prototype) { + return 'WeakSet'; + } + + /* ! Speed optimisation + * Pre: + * weakmap x 1,500,260 ops/sec ±2.02% (78 runs sampled) + * Post: + * weakmap x 3,881,384 ops/sec ±1.45% (82 runs sampled) + */ + if (weakMapExists && objPrototype === WeakMap.prototype) { + return 'WeakMap'; + } + + /* ! Spec Conformance + * (http://www.ecma-international.org/ecma-262/6.0/index.html#sec-dataview.prototype-@@tostringtag) + * ES6$24.2.4.21 - DataView.prototype[@@toStringTag] should be "DataView": + * Test: `Object.prototype.toString.call(new DataView(new ArrayBuffer(1)))`` + * - Edge <=13 === "[object Object]" + */ + if (dataViewExists && objPrototype === DataView.prototype) { + return 'DataView'; + } + + /* ! Spec Conformance + * (http://www.ecma-international.org/ecma-262/6.0/index.html#sec-%mapiteratorprototype%-@@tostringtag) + * ES6$23.1.5.2.2 - %MapIteratorPrototype%[@@toStringTag] should be "Map Iterator": + * Test: `Object.prototype.toString.call(new Map().entries())`` + * - Edge <=13 === "[object Object]" + */ + if (mapExists && objPrototype === mapIteratorPrototype) { + return 'Map Iterator'; + } + + /* ! Spec Conformance + * (http://www.ecma-international.org/ecma-262/6.0/index.html#sec-%setiteratorprototype%-@@tostringtag) + * ES6$23.2.5.2.2 - %SetIteratorPrototype%[@@toStringTag] should be "Set Iterator": + * Test: `Object.prototype.toString.call(new Set().entries())`` + * - Edge <=13 === "[object Object]" + */ + if (setExists && objPrototype === setIteratorPrototype) { + return 'Set Iterator'; + } + + /* ! Spec Conformance + * (http://www.ecma-international.org/ecma-262/6.0/index.html#sec-%arrayiteratorprototype%-@@tostringtag) + * ES6$22.1.5.2.2 - %ArrayIteratorPrototype%[@@toStringTag] should be "Array Iterator": + * Test: `Object.prototype.toString.call([][Symbol.iterator]())`` + * - Edge <=13 === "[object Object]" + */ + if (arrayIteratorExists && objPrototype === arrayIteratorPrototype) { + return 'Array Iterator'; + } + + /* ! Spec Conformance + * (http://www.ecma-international.org/ecma-262/6.0/index.html#sec-%stringiteratorprototype%-@@tostringtag) + * ES6$21.1.5.2.2 - %StringIteratorPrototype%[@@toStringTag] should be "String Iterator": + * Test: `Object.prototype.toString.call(''[Symbol.iterator]())`` + * - Edge <=13 === "[object Object]" + */ + if (stringIteratorExists && objPrototype === stringIteratorPrototype) { + return 'String Iterator'; + } + + /* ! Speed optimisation + * Pre: + * object from null x 2,424,320 ops/sec ±1.67% (76 runs sampled) + * Post: + * object from null x 5,838,000 ops/sec ±0.99% (84 runs sampled) + */ + if (objPrototype === null) { + return 'Object'; + } + } + + return Object + .prototype + .toString + .call(obj) + .slice(toStringLeftSliceLength, toStringRightSliceLength); }; -},{}],35:[function(require,module,exports){ -arguments[4][33][0].apply(exports,arguments) -},{"./lib/type":36,"dup":33}],36:[function(require,module,exports){ -/*! +module.exports.typeDetect = module.exports; + +},{}],37:[function(require,module,exports){ +'use strict'; + +/* ! + * Chai - getFuncName utility + * Copyright(c) 2012-2016 Jake Luer + * MIT Licensed + */ + +/** + * ### .getFuncName(constructorFn) + * + * Returns the name of a function. + * When a non-function instance is passed, returns `null`. + * This also includes a polyfill function if `aFunc.name` is not defined. + * + * @name getFuncName + * @param {Function} funct + * @namespace Utils + * @api public + */ + +var toString = Function.prototype.toString; +var functionNameMatch = /\s*function(?:\s|\s*\/\*[^(?:*\/)]+\*\/\s*)*([^\s\(\/]+)/; +function getFuncName(aFunc) { + if (typeof aFunc !== 'function') { + return null; + } + + var name = ''; + if (typeof Function.prototype.name === 'undefined' && typeof aFunc.name === 'undefined') { + // Here we run a polyfill if Function does not support the `name` property and if aFunc.name is not defined + var match = toString.call(aFunc).match(functionNameMatch); + if (match) { + name = match[1]; + } + } else { + // If we've got a `name` property we just use it + name = aFunc.name; + } + + return name; +} + +module.exports = getFuncName; + +},{}],38:[function(require,module,exports){ +'use strict'; + +/* ! + * Chai - pathval utility + * Copyright(c) 2012-2014 Jake Luer + * @see https://github.com/logicalparadox/filtr + * MIT Licensed + */ + +/** + * ### .hasProperty(object, name) + * + * This allows checking whether an object has own + * or inherited from prototype chain named property. + * + * Basically does the same thing as the `in` + * operator but works properly with null/undefined values + * and other primitives. + * + * var obj = { + * arr: ['a', 'b', 'c'] + * , str: 'Hello' + * } + * + * The following would be the results. + * + * hasProperty(obj, 'str'); // true + * hasProperty(obj, 'constructor'); // true + * hasProperty(obj, 'bar'); // false + * + * hasProperty(obj.str, 'length'); // true + * hasProperty(obj.str, 1); // true + * hasProperty(obj.str, 5); // false + * + * hasProperty(obj.arr, 'length'); // true + * hasProperty(obj.arr, 2); // true + * hasProperty(obj.arr, 3); // false + * + * @param {Object} object + * @param {String|Symbol} name + * @returns {Boolean} whether it exists + * @namespace Utils + * @name hasProperty + * @api public + */ + +function hasProperty(obj, name) { + if (typeof obj === 'undefined' || obj === null) { + return false; + } + + // The `in` operator does not work with primitives. + return name in Object(obj); +} + +/* ! + * ## parsePath(path) + * + * Helper function used to parse string object + * paths. Use in conjunction with `internalGetPathValue`. + * + * var parsed = parsePath('myobject.property.subprop'); + * + * ### Paths: + * + * * Can be infinitely deep and nested. + * * Arrays are also valid using the formal `myobject.document[3].property`. + * * Literal dots and brackets (not delimiter) must be backslash-escaped. + * + * @param {String} path + * @returns {Object} parsed + * @api private + */ + +function parsePath(path) { + var str = path.replace(/([^\\])\[/g, '$1.['); + var parts = str.match(/(\\\.|[^.]+?)+/g); + return parts.map(function mapMatches(value) { + var regexp = /^\[(\d+)\]$/; + var mArr = regexp.exec(value); + var parsed = null; + if (mArr) { + parsed = { i: parseFloat(mArr[1]) }; + } else { + parsed = { p: value.replace(/\\([.\[\]])/g, '$1') }; + } + + return parsed; + }); +} + +/* ! + * ## internalGetPathValue(obj, parsed[, pathDepth]) + * + * Helper companion function for `.parsePath` that returns + * the value located at the parsed address. + * + * var value = getPathValue(obj, parsed); + * + * @param {Object} object to search against + * @param {Object} parsed definition from `parsePath`. + * @param {Number} depth (nesting level) of the property we want to retrieve + * @returns {Object|Undefined} value + * @api private + */ + +function internalGetPathValue(obj, parsed, pathDepth) { + var temporaryValue = obj; + var res = null; + pathDepth = (typeof pathDepth === 'undefined' ? parsed.length : pathDepth); + + for (var i = 0; i < pathDepth; i++) { + var part = parsed[i]; + if (temporaryValue) { + if (typeof part.p === 'undefined') { + temporaryValue = temporaryValue[part.i]; + } else { + temporaryValue = temporaryValue[part.p]; + } + + if (i === (pathDepth - 1)) { + res = temporaryValue; + } + } + } + + return res; +} + +/* ! + * ## internalSetPathValue(obj, value, parsed) + * + * Companion function for `parsePath` that sets + * the value located at a parsed address. + * + * internalSetPathValue(obj, 'value', parsed); + * + * @param {Object} object to search and define on + * @param {*} value to use upon set + * @param {Object} parsed definition from `parsePath` + * @api private + */ + +function internalSetPathValue(obj, val, parsed) { + var tempObj = obj; + var pathDepth = parsed.length; + var part = null; + // Here we iterate through every part of the path + for (var i = 0; i < pathDepth; i++) { + var propName = null; + var propVal = null; + part = parsed[i]; + + // If it's the last part of the path, we set the 'propName' value with the property name + if (i === (pathDepth - 1)) { + propName = typeof part.p === 'undefined' ? part.i : part.p; + // Now we set the property with the name held by 'propName' on object with the desired val + tempObj[propName] = val; + } else if (typeof part.p !== 'undefined' && tempObj[part.p]) { + tempObj = tempObj[part.p]; + } else if (typeof part.i !== 'undefined' && tempObj[part.i]) { + tempObj = tempObj[part.i]; + } else { + // If the obj doesn't have the property we create one with that name to define it + var next = parsed[i + 1]; + // Here we set the name of the property which will be defined + propName = typeof part.p === 'undefined' ? part.i : part.p; + // Here we decide if this property will be an array or a new object + propVal = typeof next.p === 'undefined' ? [] : {}; + tempObj[propName] = propVal; + tempObj = tempObj[propName]; + } + } +} + +/** + * ### .getPathInfo(object, path) + * + * This allows the retrieval of property info in an + * object given a string path. + * + * The path info consists of an object with the + * following properties: + * + * * parent - The parent object of the property referenced by `path` + * * name - The name of the final property, a number if it was an array indexer + * * value - The value of the property, if it exists, otherwise `undefined` + * * exists - Whether the property exists or not + * + * @param {Object} object + * @param {String} path + * @returns {Object} info + * @namespace Utils + * @name getPathInfo + * @api public + */ + +function getPathInfo(obj, path) { + var parsed = parsePath(path); + var last = parsed[parsed.length - 1]; + var info = { + parent: parsed.length > 1 ? internalGetPathValue(obj, parsed, parsed.length - 1) : obj, + name: last.p || last.i, + value: internalGetPathValue(obj, parsed), + }; + info.exists = hasProperty(info.parent, info.name); + + return info; +} + +/** + * ### .getPathValue(object, path) + * + * This allows the retrieval of values in an + * object given a string path. + * + * var obj = { + * prop1: { + * arr: ['a', 'b', 'c'] + * , str: 'Hello' + * } + * , prop2: { + * arr: [ { nested: 'Universe' } ] + * , str: 'Hello again!' + * } + * } + * + * The following would be the results. + * + * getPathValue(obj, 'prop1.str'); // Hello + * getPathValue(obj, 'prop1.att[2]'); // b + * getPathValue(obj, 'prop2.arr[0].nested'); // Universe + * + * @param {Object} object + * @param {String} path + * @returns {Object} value or `undefined` + * @namespace Utils + * @name getPathValue + * @api public + */ + +function getPathValue(obj, path) { + var info = getPathInfo(obj, path); + return info.value; +} + +/** + * ### .setPathValue(object, path, value) + * + * Define the value in an object at a given string path. + * + * ```js + * var obj = { + * prop1: { + * arr: ['a', 'b', 'c'] + * , str: 'Hello' + * } + * , prop2: { + * arr: [ { nested: 'Universe' } ] + * , str: 'Hello again!' + * } + * }; + * ``` + * + * The following would be acceptable. + * + * ```js + * var properties = require('tea-properties'); + * properties.set(obj, 'prop1.str', 'Hello Universe!'); + * properties.set(obj, 'prop1.arr[2]', 'B'); + * properties.set(obj, 'prop2.arr[0].nested.value', { hello: 'universe' }); + * ``` + * + * @param {Object} object + * @param {String} path + * @param {Mixed} value + * @api private + */ + +function setPathValue(obj, path, val) { + var parsed = parsePath(path); + internalSetPathValue(obj, val, parsed); + return obj; +} + +module.exports = { + hasProperty: hasProperty, + getPathInfo: getPathInfo, + getPathValue: getPathValue, + setPathValue: setPathValue, +}; + +},{}],39:[function(require,module,exports){ +'use strict'; +/* ! * type-detect * Copyright(c) 2013 jake luer * MIT Licensed */ - -/*! - * Primary Exports - */ - -var exports = module.exports = getType; - +var promiseExists = typeof Promise === 'function'; +var globalObject = typeof window !== 'undefined' ? window : typeof global !== 'undefined' ? global : self; // eslint-disable-line +var isDom = 'location' in globalObject && 'document' in globalObject; +var symbolExists = typeof Symbol !== 'undefined'; +var mapExists = typeof Map !== 'undefined'; +var setExists = typeof Set !== 'undefined'; +var weakMapExists = typeof WeakMap !== 'undefined'; +var weakSetExists = typeof WeakSet !== 'undefined'; +var dataViewExists = typeof DataView !== 'undefined'; +var symbolIteratorExists = symbolExists && typeof Symbol.iterator !== 'undefined'; +var symbolToStringTagExists = symbolExists && typeof Symbol.toStringTag !== 'undefined'; +var setEntriesExists = setExists && typeof Set.prototype.entries === 'function'; +var mapEntriesExists = mapExists && typeof Map.prototype.entries === 'function'; +var setIteratorPrototype = setEntriesExists && Object.getPrototypeOf(new Set().entries()); +var mapIteratorPrototype = mapEntriesExists && Object.getPrototypeOf(new Map().entries()); +var arrayIteratorExists = symbolIteratorExists && typeof Array.prototype[Symbol.iterator] === 'function'; +var arrayIteratorPrototype = arrayIteratorExists && Object.getPrototypeOf([][Symbol.iterator]()); +var stringIteratorExists = symbolIteratorExists && typeof Array.prototype[Symbol.iterator] === 'function'; +var stringIteratorPrototype = stringIteratorExists && Object.getPrototypeOf(''[Symbol.iterator]()); +var toStringLeftSliceLength = 8; +var toStringRightSliceLength = -1; /** * ### typeOf (obj) * - * Use several different techniques to determine - * the type of object being tested. - * + * Uses `Object.prototype.toString` to determine the type of an object, + * normalising behaviour across engine versions & well optimised. * * @param {Mixed} object * @return {String} object type * @api public */ -var objectTypeRegexp = /^\[object (.*)\]$/; - -function getType(obj) { - var type = Object.prototype.toString.call(obj).match(objectTypeRegexp)[1].toLowerCase(); - // Let "new String('')" return 'object' - if (typeof Promise === 'function' && obj instanceof Promise) return 'promise'; - // PhantomJS has type "DOMWindow" for null - if (obj === null) return 'null'; - // PhantomJS has type "DOMWindow" for undefined - if (obj === undefined) return 'undefined'; - return type; -} - -exports.Library = Library; - -/** - * ### Library - * - * Create a repository for custom type detection. - * - * ```js - * var lib = new type.Library; - * ``` - * - */ - -function Library() { - if (!(this instanceof Library)) return new Library(); - this.tests = {}; -} - -/** - * #### .of (obj) - * - * Expose replacement `typeof` detection to the library. - * - * ```js - * if ('string' === lib.of('hello world')) { - * // ... - * } - * ``` - * - * @param {Mixed} object to test - * @return {String} type - */ - -Library.prototype.of = getType; - -/** - * #### .define (type, test) - * - * Add a test to for the `.test()` assertion. - * - * Can be defined as a regular expression: - * - * ```js - * lib.define('int', /^[0-9]+$/); - * ``` - * - * ... or as a function: - * - * ```js - * lib.define('bln', function (obj) { - * if ('boolean' === lib.of(obj)) return true; - * var blns = [ 'yes', 'no', 'true', 'false', 1, 0 ]; - * if ('string' === lib.of(obj)) obj = obj.toLowerCase(); - * return !! ~blns.indexOf(obj); - * }); - * ``` - * - * @param {String} type - * @param {RegExp|Function} test - * @api public - */ - -Library.prototype.define = function(type, test) { - if (arguments.length === 1) return this.tests[type]; - this.tests[type] = test; - return this; -}; - -/** - * #### .test (obj, test) - * - * Assert that an object is of type. Will first - * check natives, and if that does not pass it will - * use the user defined custom tests. - * - * ```js - * assert(lib.test('1', 'int')); - * assert(lib.test('yes', 'bln')); - * ``` - * - * @param {Mixed} object - * @param {String} type - * @return {Boolean} result - * @api public - */ - -Library.prototype.test = function(obj, type) { - if (type === getType(obj)) return true; - var test = this.tests[type]; - - if (test && 'regexp' === getType(test)) { - return test.test(obj); - } else if (test && 'function' === getType(test)) { - return test(obj); - } else { - throw new ReferenceError('Type test "' + type + '" not defined or invalid.'); +module.exports = function typeDetect(obj) { + /* ! Speed optimisation + * Pre: + * string literal x 3,039,035 ops/sec ±1.62% (78 runs sampled) + * boolean literal x 1,424,138 ops/sec ±4.54% (75 runs sampled) + * number literal x 1,653,153 ops/sec ±1.91% (82 runs sampled) + * undefined x 9,978,660 ops/sec ±1.92% (75 runs sampled) + * function x 2,556,769 ops/sec ±1.73% (77 runs sampled) + * Post: + * string literal x 38,564,796 ops/sec ±1.15% (79 runs sampled) + * boolean literal x 31,148,940 ops/sec ±1.10% (79 runs sampled) + * number literal x 32,679,330 ops/sec ±1.90% (78 runs sampled) + * undefined x 32,363,368 ops/sec ±1.07% (82 runs sampled) + * function x 31,296,870 ops/sec ±0.96% (83 runs sampled) + */ + var typeofObj = typeof obj; + if (typeofObj !== 'object') { + return typeofObj; } + + /* ! Speed optimisation + * Pre: + * null x 28,645,765 ops/sec ±1.17% (82 runs sampled) + * Post: + * null x 36,428,962 ops/sec ±1.37% (84 runs sampled) + */ + if (obj === null) { + return 'null'; + } + + /* ! Spec Conformance + * Test: `Object.prototype.toString.call(window)`` + * - Node === "[object global]" + * - Chrome === "[object global]" + * - Firefox === "[object Window]" + * - PhantomJS === "[object Window]" + * - Safari === "[object Window]" + * - IE 11 === "[object Window]" + * - IE Edge === "[object Window]" + * Test: `Object.prototype.toString.call(this)`` + * - Chrome Worker === "[object global]" + * - Firefox Worker === "[object DedicatedWorkerGlobalScope]" + * - Safari Worker === "[object DedicatedWorkerGlobalScope]" + * - IE 11 Worker === "[object WorkerGlobalScope]" + * - IE Edge Worker === "[object WorkerGlobalScope]" + */ + if (obj === globalObject) { + return 'global'; + } + + /* ! Speed optimisation + * Pre: + * array literal x 2,888,352 ops/sec ±0.67% (82 runs sampled) + * Post: + * array literal x 22,479,650 ops/sec ±0.96% (81 runs sampled) + */ + if (Array.isArray(obj)) { + return 'Array'; + } + + if (isDom) { + /* ! Spec Conformance + * (https://html.spec.whatwg.org/multipage/browsers.html#location) + * WhatWG HTML$7.7.3 - The `Location` interface + * Test: `Object.prototype.toString.call(window.location)`` + * - IE <=11 === "[object Object]" + * - IE Edge <=13 === "[object Object]" + */ + if (obj === globalObject.location) { + return 'Location'; + } + + /* ! Spec Conformance + * (https://html.spec.whatwg.org/#document) + * WhatWG HTML$3.1.1 - The `Document` object + * Note: Most browsers currently adher to the W3C DOM Level 2 spec + * (https://www.w3.org/TR/DOM-Level-2-HTML/html.html#ID-26809268) + * which suggests that browsers should use HTMLTableCellElement for + * both TD and TH elements. WhatWG separates these. + * WhatWG HTML states: + * > For historical reasons, Window objects must also have a + * > writable, configurable, non-enumerable property named + * > HTMLDocument whose value is the Document interface object. + * Test: `Object.prototype.toString.call(document)`` + * - Chrome === "[object HTMLDocument]" + * - Firefox === "[object HTMLDocument]" + * - Safari === "[object HTMLDocument]" + * - IE <=10 === "[object Document]" + * - IE 11 === "[object HTMLDocument]" + * - IE Edge <=13 === "[object HTMLDocument]" + */ + if (obj === globalObject.document) { + return 'Document'; + } + + /* ! Spec Conformance + * (https://html.spec.whatwg.org/multipage/webappapis.html#mimetypearray) + * WhatWG HTML$8.6.1.5 - Plugins - Interface MimeTypeArray + * Test: `Object.prototype.toString.call(navigator.mimeTypes)`` + * - IE <=10 === "[object MSMimeTypesCollection]" + */ + if (obj === (globalObject.navigator || {}).mimeTypes) { + return 'MimeTypeArray'; + } + + /* ! Spec Conformance + * (https://html.spec.whatwg.org/multipage/webappapis.html#pluginarray) + * WhatWG HTML$8.6.1.5 - Plugins - Interface PluginArray + * Test: `Object.prototype.toString.call(navigator.plugins)`` + * - IE <=10 === "[object MSPluginsCollection]" + */ + if (obj === (globalObject.navigator || {}).plugins) { + return 'PluginArray'; + } + + /* ! Spec Conformance + * (https://html.spec.whatwg.org/multipage/webappapis.html#pluginarray) + * WhatWG HTML$4.4.4 - The `blockquote` element - Interface `HTMLQuoteElement` + * Test: `Object.prototype.toString.call(document.createElement('blockquote'))`` + * - IE <=10 === "[object HTMLBlockElement]" + */ + if (obj instanceof HTMLElement && obj.tagName === 'BLOCKQUOTE') { + return 'HTMLQuoteElement'; + } + + /* ! Spec Conformance + * (https://html.spec.whatwg.org/#htmltabledatacellelement) + * WhatWG HTML$4.9.9 - The `td` element - Interface `HTMLTableDataCellElement` + * Note: Most browsers currently adher to the W3C DOM Level 2 spec + * (https://www.w3.org/TR/DOM-Level-2-HTML/html.html#ID-82915075) + * which suggests that browsers should use HTMLTableCellElement for + * both TD and TH elements. WhatWG separates these. + * Test: Object.prototype.toString.call(document.createElement('td')) + * - Chrome === "[object HTMLTableCellElement]" + * - Firefox === "[object HTMLTableCellElement]" + * - Safari === "[object HTMLTableCellElement]" + */ + if (obj instanceof HTMLElement && obj.tagName === 'TD') { + return 'HTMLTableDataCellElement'; + } + + /* ! Spec Conformance + * (https://html.spec.whatwg.org/#htmltableheadercellelement) + * WhatWG HTML$4.9.9 - The `td` element - Interface `HTMLTableHeaderCellElement` + * Note: Most browsers currently adher to the W3C DOM Level 2 spec + * (https://www.w3.org/TR/DOM-Level-2-HTML/html.html#ID-82915075) + * which suggests that browsers should use HTMLTableCellElement for + * both TD and TH elements. WhatWG separates these. + * Test: Object.prototype.toString.call(document.createElement('th')) + * - Chrome === "[object HTMLTableCellElement]" + * - Firefox === "[object HTMLTableCellElement]" + * - Safari === "[object HTMLTableCellElement]" + */ + if (obj instanceof HTMLElement && obj.tagName === 'TH') { + return 'HTMLTableHeaderCellElement'; + } + } + + /* ! Speed optimisation + * Pre: + * Float64Array x 625,644 ops/sec ±1.58% (80 runs sampled) + * Float32Array x 1,279,852 ops/sec ±2.91% (77 runs sampled) + * Uint32Array x 1,178,185 ops/sec ±1.95% (83 runs sampled) + * Uint16Array x 1,008,380 ops/sec ±2.25% (80 runs sampled) + * Uint8Array x 1,128,040 ops/sec ±2.11% (81 runs sampled) + * Int32Array x 1,170,119 ops/sec ±2.88% (80 runs sampled) + * Int16Array x 1,176,348 ops/sec ±5.79% (86 runs sampled) + * Int8Array x 1,058,707 ops/sec ±4.94% (77 runs sampled) + * Uint8ClampedArray x 1,110,633 ops/sec ±4.20% (80 runs sampled) + * Post: + * Float64Array x 7,105,671 ops/sec ±13.47% (64 runs sampled) + * Float32Array x 5,887,912 ops/sec ±1.46% (82 runs sampled) + * Uint32Array x 6,491,661 ops/sec ±1.76% (79 runs sampled) + * Uint16Array x 6,559,795 ops/sec ±1.67% (82 runs sampled) + * Uint8Array x 6,463,966 ops/sec ±1.43% (85 runs sampled) + * Int32Array x 5,641,841 ops/sec ±3.49% (81 runs sampled) + * Int16Array x 6,583,511 ops/sec ±1.98% (80 runs sampled) + * Int8Array x 6,606,078 ops/sec ±1.74% (81 runs sampled) + * Uint8ClampedArray x 6,602,224 ops/sec ±1.77% (83 runs sampled) + */ + var stringTag = (symbolToStringTagExists && obj[Symbol.toStringTag]); + if (typeof stringTag === 'string') { + return stringTag; + } + + var objPrototype = Object.getPrototypeOf(obj); + /* ! Speed optimisation + * Pre: + * regex literal x 1,772,385 ops/sec ±1.85% (77 runs sampled) + * regex constructor x 2,143,634 ops/sec ±2.46% (78 runs sampled) + * Post: + * regex literal x 3,928,009 ops/sec ±0.65% (78 runs sampled) + * regex constructor x 3,931,108 ops/sec ±0.58% (84 runs sampled) + */ + if (objPrototype === RegExp.prototype) { + return 'RegExp'; + } + + /* ! Speed optimisation + * Pre: + * date x 2,130,074 ops/sec ±4.42% (68 runs sampled) + * Post: + * date x 3,953,779 ops/sec ±1.35% (77 runs sampled) + */ + if (objPrototype === Date.prototype) { + return 'Date'; + } + + /* ! Spec Conformance + * (http://www.ecma-international.org/ecma-262/6.0/index.html#sec-promise.prototype-@@tostringtag) + * ES6$25.4.5.4 - Promise.prototype[@@toStringTag] should be "Promise": + * Test: `Object.prototype.toString.call(Promise.resolve())`` + * - Chrome <=47 === "[object Object]" + * - Edge <=20 === "[object Object]" + * - Firefox 29-Latest === "[object Promise]" + * - Safari 7.1-Latest === "[object Promise]" + */ + if (promiseExists && objPrototype === Promise.prototype) { + return 'Promise'; + } + + /* ! Speed optimisation + * Pre: + * set x 2,222,186 ops/sec ±1.31% (82 runs sampled) + * Post: + * set x 4,545,879 ops/sec ±1.13% (83 runs sampled) + */ + if (setExists && objPrototype === Set.prototype) { + return 'Set'; + } + + /* ! Speed optimisation + * Pre: + * map x 2,396,842 ops/sec ±1.59% (81 runs sampled) + * Post: + * map x 4,183,945 ops/sec ±6.59% (82 runs sampled) + */ + if (mapExists && objPrototype === Map.prototype) { + return 'Map'; + } + + /* ! Speed optimisation + * Pre: + * weakset x 1,323,220 ops/sec ±2.17% (76 runs sampled) + * Post: + * weakset x 4,237,510 ops/sec ±2.01% (77 runs sampled) + */ + if (weakSetExists && objPrototype === WeakSet.prototype) { + return 'WeakSet'; + } + + /* ! Speed optimisation + * Pre: + * weakmap x 1,500,260 ops/sec ±2.02% (78 runs sampled) + * Post: + * weakmap x 3,881,384 ops/sec ±1.45% (82 runs sampled) + */ + if (weakMapExists && objPrototype === WeakMap.prototype) { + return 'WeakMap'; + } + + /* ! Spec Conformance + * (http://www.ecma-international.org/ecma-262/6.0/index.html#sec-dataview.prototype-@@tostringtag) + * ES6$24.2.4.21 - DataView.prototype[@@toStringTag] should be "DataView": + * Test: `Object.prototype.toString.call(new DataView(new ArrayBuffer(1)))`` + * - Edge <=13 === "[object Object]" + */ + if (dataViewExists && objPrototype === DataView.prototype) { + return 'DataView'; + } + + /* ! Spec Conformance + * (http://www.ecma-international.org/ecma-262/6.0/index.html#sec-%mapiteratorprototype%-@@tostringtag) + * ES6$23.1.5.2.2 - %MapIteratorPrototype%[@@toStringTag] should be "Map Iterator": + * Test: `Object.prototype.toString.call(new Map().entries())`` + * - Edge <=13 === "[object Object]" + */ + if (mapExists && objPrototype === mapIteratorPrototype) { + return 'Map Iterator'; + } + + /* ! Spec Conformance + * (http://www.ecma-international.org/ecma-262/6.0/index.html#sec-%setiteratorprototype%-@@tostringtag) + * ES6$23.2.5.2.2 - %SetIteratorPrototype%[@@toStringTag] should be "Set Iterator": + * Test: `Object.prototype.toString.call(new Set().entries())`` + * - Edge <=13 === "[object Object]" + */ + if (setExists && objPrototype === setIteratorPrototype) { + return 'Set Iterator'; + } + + /* ! Spec Conformance + * (http://www.ecma-international.org/ecma-262/6.0/index.html#sec-%arrayiteratorprototype%-@@tostringtag) + * ES6$22.1.5.2.2 - %ArrayIteratorPrototype%[@@toStringTag] should be "Array Iterator": + * Test: `Object.prototype.toString.call([][Symbol.iterator]())`` + * - Edge <=13 === "[object Object]" + */ + if (arrayIteratorExists && objPrototype === arrayIteratorPrototype) { + return 'Array Iterator'; + } + + /* ! Spec Conformance + * (http://www.ecma-international.org/ecma-262/6.0/index.html#sec-%stringiteratorprototype%-@@tostringtag) + * ES6$21.1.5.2.2 - %StringIteratorPrototype%[@@toStringTag] should be "String Iterator": + * Test: `Object.prototype.toString.call(''[Symbol.iterator]())`` + * - Edge <=13 === "[object Object]" + */ + if (stringIteratorExists && objPrototype === stringIteratorPrototype) { + return 'String Iterator'; + } + + /* ! Speed optimisation + * Pre: + * object from null x 2,424,320 ops/sec ±1.67% (76 runs sampled) + * Post: + * object from null x 5,838,000 ops/sec ±0.99% (84 runs sampled) + */ + if (objPrototype === null) { + return 'Object'; + } + + return Object + .prototype + .toString + .call(obj) + .slice(toStringLeftSliceLength, toStringRightSliceLength); }; +module.exports.typeDetect = module.exports; + +},{}],40:[function(require,module,exports){ +module.exports={ + "author": "Jake Luer ", + "name": "chai", + "description": "BDD/TDD assertion library for node.js and the browser. Test framework agnostic.", + "keywords": [ + "test", + "assertion", + "assert", + "testing", + "chai" + ], + "homepage": "http://chaijs.com", + "license": "MIT", + "contributors": [ + "Jake Luer ", + "Domenic Denicola (http://domenicdenicola.com)", + "Veselin Todorov ", + "John Firebaugh " + ], + "version": "4.0.0-canary.2", + "repository": { + "type": "git", + "url": "https://github.com/chaijs/chai" + }, + "bugs": { + "url": "https://github.com/chaijs/chai/issues" + }, + "main": "./index", + "browser": "./chai.js", + "scripts": { + "test": "make test" + }, + "engines": { + "node": ">=4" + }, + "dependencies": { + "assertion-error": "^1.0.1", + "check-error": "^1.0.1", + "deep-eql": "^2.0.1", + "get-func-name": "^2.0.0", + "pathval": "^1.0.0", + "type-detect": "^4.0.0" + }, + "devDependencies": { + "browserify": "^13.0.1", + "bump-cli": "^1.1.3", + "istanbul": "^0.4.3", + "karma": "^1.0.0", + "karma-firefox-launcher": "^1.0.0", + "karma-mocha": "^1.0.1", + "karma-phantomjs-launcher": "^1.0.0", + "karma-sauce-launcher": "^1.0.0", + "mocha": "^3.0.0" + } +} + },{}]},{},[1])(1) -}); +}); \ No newline at end of file diff --git a/package.json b/package.json index a6bd6bc..d77c24d 100644 --- a/package.json +++ b/package.json @@ -17,7 +17,7 @@ "Veselin Todorov ", "John Firebaugh " ], - "version": "4.0.0-canary.1", + "version": "4.0.0-canary.2", "repository": { "type": "git", "url": "https://github.com/chaijs/chai" From 7db8c6ce28b0ae236497e0f51d70d7a73fb065a8 Mon Sep 17 00:00:00 2001 From: Keith Cirkel Date: Wed, 12 Apr 2017 10:31:08 +0100 Subject: [PATCH 2/2] chore: temporarily comment out deploy whlile we're canary --- .travis.yml | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/.travis.yml b/.travis.yml index d26fa02..4bbc38d 100644 --- a/.travis.yml +++ b/.travis.yml @@ -19,13 +19,13 @@ env: - SAUCE_CONNECT_READY_FILE=/tmp/sauce-connect-ready - LOGS_DIR=/tmp/chai-build/logs -deploy: - provider: npm - email: - secure: "M9J56tqJwOxn6D3PSh8BNGac/8XjTCRGCFexN5QFl7Ha9AKnyQTy1ZAaJonMGoClBhh37Dy/89wVWmqg4NPckrD5Ej7NvwhTjVfEHusllaCROqVu5m7ijS3WKlIxM/xtBzU7SqRY41JxloP+CLKxRiKRWQJDvgwUZW7OHdmAkAY=" - api_key: - secure: ihzLWemQDFmsF4iQ3sS5/Wt0GQy6v15WgsxYfocpLuYWjxaAWgB4vQ3gUar8UpnE7lEFtNUOTIkPob0MSgNrFjoH2PB1sh5cW/acDE7kF7Z7hVxr8zGWkgiWXeNd08lz1mruizK1L/I0o2k/7o7zEDzShpsYSwgWfekzbJxLtC8= - on: - tags: true - repo: chaijs/chai - all_branches: true +# deploy: +# provider: npm +# email: +# secure: "M9J56tqJwOxn6D3PSh8BNGac/8XjTCRGCFexN5QFl7Ha9AKnyQTy1ZAaJonMGoClBhh37Dy/89wVWmqg4NPckrD5Ej7NvwhTjVfEHusllaCROqVu5m7ijS3WKlIxM/xtBzU7SqRY41JxloP+CLKxRiKRWQJDvgwUZW7OHdmAkAY=" +# api_key: +# secure: ihzLWemQDFmsF4iQ3sS5/Wt0GQy6v15WgsxYfocpLuYWjxaAWgB4vQ3gUar8UpnE7lEFtNUOTIkPob0MSgNrFjoH2PB1sh5cW/acDE7kF7Z7hVxr8zGWkgiWXeNd08lz1mruizK1L/I0o2k/7o7zEDzShpsYSwgWfekzbJxLtC8= +# on: +# tags: true +# repo: chaijs/chai +# all_branches: true