From 09858fba2af18ada33773b1f03f7cd543b793727 Mon Sep 17 00:00:00 2001 From: Whitney Young Date: Sun, 4 Oct 2015 14:44:29 -0700 Subject: [PATCH] No longer silently allowing undefined method calls. Fixes #467. --- chai.js | 168 +++++++++++++++++++++--------- lib/chai/utils/overwriteMethod.js | 4 +- test/utilities.js | 19 +++- 3 files changed, 138 insertions(+), 53 deletions(-) diff --git a/chai.js b/chai.js index b7bcdb58d..e5e747f31 100644 --- a/chai.js +++ b/chai.js @@ -96,7 +96,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":21,"assertion-error":29}],3:[function(require,module,exports){ +},{"./chai/assertion":3,"./chai/config":4,"./chai/core/assertions":5,"./chai/interface/assert":6,"./chai/interface/expect":7,"./chai/interface/should":8,"./chai/utils":21,"assertion-error":30}],3:[function(require,module,exports){ /*! * chai * http://chaijs.com @@ -624,7 +624,7 @@ module.exports = function (chai, _) { * ### .NaN * Asserts that the target is `NaN`. * - * expect('foo').to.be.NaN; + * expect(NaN).to.be.NaN; * expect(4).not.to.be.NaN; * * @name NaN @@ -633,7 +633,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' ); @@ -681,17 +681,10 @@ module.exports = function (chai, _) { */ Assertion.addProperty('empty', function () { - var obj = flag(this, 'object') - , expected = obj; - - if (Array.isArray(obj) || 'string' === typeof object) { - expected = obj.length; - } else if (typeof obj === 'object') { - expected = Object.keys(obj).length; - } - + var obj = flag(this, 'object'); + new Assertion(obj).to.exist; this.assert( - !expected + Object.keys(Object(obj)).length === 0 , 'expected #{this} to be empty' , 'expected #{this} not to be empty' ); @@ -1809,6 +1802,7 @@ module.exports = function (chai, _) { , 'expected #{this} to not be a superset of #{act}' , obj , subset + , true ); } @@ -1818,6 +1812,7 @@ module.exports = function (chai, _) { , 'expected #{this} to not have the same members as #{act}' , obj , subset + , true ); }); @@ -2245,16 +2240,16 @@ module.exports = function (chai, util) { new Assertion(act, msg).to.not.eql(exp); }; - /** - * ### .isTrue(value, [message]) + /** + * ### .isAbove(valueToCheck, valueToBeAbove, [message]) * - * Asserts that `value` is true. + * Asserts `valueToCheck` is strictly greater than (>) `valueToBeAbove` * - * var teaServed = true; - * assert.isTrue(teaServed, 'the tea has been served'); + * assert.isAbove(5, 2, '5 is strictly greater than 2'); * - * @name isTrue - * @param {Mixed} value + * @name isAbove + * @param {Mixed} valueToCheck + * @param {Mixed} valueToBeAbove * @param {String} message * @api public */ @@ -2264,15 +2259,15 @@ module.exports = function (chai, util) { }; /** - * ### .isAbove(valueToCheck, valueToBeAbove, [message]) + * ### .isBelow(valueToCheck, valueToBeBelow, [message]) * - * Asserts `valueToCheck` is strictly greater than (>) `valueToBeAbove` + * Asserts `valueToCheck` is strictly less than (<) `valueToBeBelow` * - * assert.isAbove(5, 2, '5 is strictly greater than 2'); + * assert.isBelow(3, 6, '3 is strictly less than 6'); * - * @name isAbove + * @name isBelow * @param {Mixed} valueToCheck - * @param {Mixed} valueToBeAbove + * @param {Mixed} valueToBeBelow * @param {String} message * @api public */ @@ -2281,16 +2276,16 @@ module.exports = function (chai, util) { new Assertion(val, msg).to.be.below(blw); }; - /** - * ### .isBelow(valueToCheck, valueToBeBelow, [message]) + /** + * ### .isTrue(value, [message]) * - * Asserts `valueToCheck` is strictly less than (<) `valueToBeBelow` + * Asserts that `value` is true. * - * assert.isBelow(3, 6, '3 is strictly less than 6'); + * var teaServed = true; + * assert.isTrue(teaServed, 'the tea has been served'); * - * @name isBelow - * @param {Mixed} valueToCheck - * @param {Mixed} valueToBeBelow + * @name isTrue + * @param {Mixed} value * @param {String} message * @api public */ @@ -2299,6 +2294,24 @@ module.exports = function (chai, util) { new Assertion(val, msg).is['true']; }; + /** + * ### .isNotTrue(value, [message]) + * + * Asserts that `value` is not true. + * + * var tea = 'tasty chai'; + * assert.isNotTrue(tea, 'great, time for tea!'); + * + * @name isNotTrue + * @param {Mixed} value + * @param {String} message + * @api public + */ + + assert.isNotTrue = function (val, msg) { + new Assertion(val, msg).to.not.equal(true); + }; + /** * ### .isFalse(value, [message]) * @@ -2317,6 +2330,24 @@ module.exports = function (chai, util) { new Assertion(val, msg).is['false']; }; + /** + * ### .isNotFalse(value, [message]) + * + * Asserts that `value` is not false. + * + * var tea = 'tasty chai'; + * assert.isNotFalse(tea, 'great, time for tea!'); + * + * @name isNotFalse + * @param {Mixed} value + * @param {String} message + * @api public + */ + + assert.isNotFalse = function (val, msg) { + new Assertion(val, msg).to.not.equal(false); + }; + /** * ### .isNull(value, [message]) * @@ -2356,7 +2387,7 @@ module.exports = function (chai, util) { * ### .isNaN * Asserts that value is NaN * - * assert.isNaN('foo', 'foo is NaN'); + * assert.isNaN(NaN, 'NaN is NaN'); * * @name isNaN * @param {Mixed} value @@ -3705,7 +3736,7 @@ module.exports = function (ctx, name, method, chainingBehavior) { }); }; -},{"../config":4,"./flag":12,"./transferFlags":28}],10:[function(require,module,exports){ +},{"../config":4,"./flag":12,"./transferFlags":29}],10:[function(require,module,exports){ /*! * Chai - addMethod utility * Copyright(c) 2012-2014 Jake Luer @@ -3925,7 +3956,7 @@ module.exports = function (obj, args) { return flagMsg ? flagMsg + ': ' + msg : msg; }; -},{"./flag":12,"./getActual":13,"./inspect":22,"./objDisplay":23}],16:[function(require,module,exports){ +},{"./flag":12,"./getActual":13,"./inspect":22,"./objDisplay":24}],16:[function(require,module,exports){ /*! * Chai - getName utility * Copyright(c) 2012-2014 Jake Luer @@ -4205,7 +4236,7 @@ module.exports = function hasProperty(name, obj) { return name in obj; }; -},{"type-detect":34}],21:[function(require,module,exports){ +},{"type-detect":35}],21:[function(require,module,exports){ /*! * chai * Copyright(c) 2011 Jake Luer @@ -4332,8 +4363,13 @@ exports.addChainableMethod = require('./addChainableMethod'); exports.overwriteChainableMethod = require('./overwriteChainableMethod'); +/*! + * isNaN method + */ + +exports.isNaN = require('./isNaN'); -},{"./addChainableMethod":9,"./addMethod":10,"./addProperty":11,"./flag":12,"./getActual":13,"./getMessage":15,"./getName":16,"./getPathInfo":17,"./getPathValue":18,"./hasProperty":20,"./inspect":22,"./objDisplay":23,"./overwriteChainableMethod":24,"./overwriteMethod":25,"./overwriteProperty":26,"./test":27,"./transferFlags":28,"deep-eql":30,"type-detect":34}],22:[function(require,module,exports){ +},{"./addChainableMethod":9,"./addMethod":10,"./addProperty":11,"./flag":12,"./getActual":13,"./getMessage":15,"./getName":16,"./getPathInfo":17,"./getPathValue":18,"./hasProperty":20,"./inspect":22,"./isNaN":23,"./objDisplay":24,"./overwriteChainableMethod":25,"./overwriteMethod":26,"./overwriteProperty":27,"./test":28,"./transferFlags":29,"deep-eql":31,"type-detect":35}],22:[function(require,module,exports){ // This is (almost) directly from Node.js utils // https://github.com/joyent/node/blob/f8c335d0caf47f16d31413f89aa28eda3878e3aa/lib/util.js @@ -4669,6 +4705,34 @@ function objectToString(o) { } },{"./getEnumerableProperties":14,"./getName":16,"./getProperties":19}],23:[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; + +},{}],24:[function(require,module,exports){ /*! * Chai - flag utility * Copyright(c) 2012-2014 Jake Luer @@ -4719,7 +4783,7 @@ module.exports = function (obj) { } }; -},{"../config":4,"./inspect":22}],24:[function(require,module,exports){ +},{"../config":4,"./inspect":22}],25:[function(require,module,exports){ /*! * Chai - overwriteChainableMethod utility * Copyright(c) 2012-2014 Jake Luer @@ -4774,7 +4838,7 @@ module.exports = function (ctx, name, method, chainingBehavior) { }; }; -},{}],25:[function(require,module,exports){ +},{}],26:[function(require,module,exports){ /*! * Chai - overwriteMethod utility * Copyright(c) 2012-2014 Jake Luer @@ -4816,7 +4880,9 @@ module.exports = function (ctx, name, method, chainingBehavior) { module.exports = function (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; @@ -4827,7 +4893,7 @@ module.exports = function (ctx, name, method) { } }; -},{}],26:[function(require,module,exports){ +},{}],27:[function(require,module,exports){ /*! * Chai - overwriteProperty utility * Copyright(c) 2012-2014 Jake Luer @@ -4883,7 +4949,7 @@ module.exports = function (ctx, name, getter) { }); }; -},{}],27:[function(require,module,exports){ +},{}],28:[function(require,module,exports){ /*! * Chai - test utility * Copyright(c) 2012-2014 Jake Luer @@ -4911,7 +4977,7 @@ module.exports = function (obj, args) { return negate ? !expr : expr; }; -},{"./flag":12}],28:[function(require,module,exports){ +},{"./flag":12}],29:[function(require,module,exports){ /*! * Chai - transferFlags utility * Copyright(c) 2012-2014 Jake Luer @@ -4957,7 +5023,7 @@ module.exports = function (assertion, object, includeAll) { } }; -},{}],29:[function(require,module,exports){ +},{}],30:[function(require,module,exports){ /*! * assertion-error * Copyright(c) 2013 Jake Luer @@ -5071,10 +5137,10 @@ AssertionError.prototype.toJSON = function (stack) { return props; }; -},{}],30:[function(require,module,exports){ +},{}],31:[function(require,module,exports){ module.exports = require('./lib/eql'); -},{"./lib/eql":31}],31:[function(require,module,exports){ +},{"./lib/eql":32}],32:[function(require,module,exports){ /*! * deep-eql * Copyright(c) 2013 Jake Luer @@ -5333,10 +5399,10 @@ function objectEqual(a, b, m) { return true; } -},{"buffer":undefined,"type-detect":32}],32:[function(require,module,exports){ +},{"buffer":undefined,"type-detect":33}],33:[function(require,module,exports){ module.exports = require('./lib/type'); -},{"./lib/type":33}],33:[function(require,module,exports){ +},{"./lib/type":34}],34:[function(require,module,exports){ /*! * type-detect * Copyright(c) 2013 jake luer @@ -5480,9 +5546,9 @@ Library.prototype.test = function (obj, type) { } }; -},{}],34:[function(require,module,exports){ -arguments[4][32][0].apply(exports,arguments) -},{"./lib/type":35,"dup":32}],35:[function(require,module,exports){ +},{}],35:[function(require,module,exports){ +arguments[4][33][0].apply(exports,arguments) +},{"./lib/type":36,"dup":33}],36:[function(require,module,exports){ /*! * type-detect * Copyright(c) 2013 jake luer diff --git a/lib/chai/utils/overwriteMethod.js b/lib/chai/utils/overwriteMethod.js index 66b158916..73f65c671 100644 --- a/lib/chai/utils/overwriteMethod.js +++ b/lib/chai/utils/overwriteMethod.js @@ -39,7 +39,9 @@ module.exports = function (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; diff --git a/test/utilities.js b/test/utilities.js index 9c6b8e69b..255fa13ea 100644 --- a/test/utilities.js +++ b/test/utilities.js @@ -259,13 +259,30 @@ describe('utilities', function () { expect(_super).to.be.a('function'); return function () { _.flag(this, 'doesnt', true); - _super.apply(this, arguments); } }); }); var dne = expect('something').to.doesnotexist(); expect(dne.__flags).to.have.property('doesnt'); + + chai.use(function (_chai, _) { + expect(_chai.Assertion).to.not.respondTo('doesnotexistfail'); + _chai.Assertion.overwriteMethod('doesnotexistfail', function (_super) { + expect(_super).to.be.a('function'); + return function () { + _.flag(this, 'doesnt', true); + _super.apply(this, arguments); + } + }); + }); + + var dneFail = expect('something'); + var dneError; + try { dneFail.to.doesnotexistfail(); } + catch (e) { dneError = e; } + expect(dneFail.__flags).to.have.property('doesnt'); + expect(dneError.message).to.eql('doesnotexistfail is not a function'); }); it('overwriteMethod returning result', function () {