From e0ee3def233b2ea316a0af83733f57b44440cf1b Mon Sep 17 00:00:00 2001 From: Dai Date: Mon, 29 Jun 2015 23:15:14 +0100 Subject: [PATCH 01/19] Added handling for undefined and null (fails on negate flag too) --- lib/chai/core/assertions.js | 12 +++++++----- test/expect.js | 29 +++++++++++++++++++++++++++++ 2 files changed, 36 insertions(+), 5 deletions(-) diff --git a/lib/chai/core/assertions.js b/lib/chai/core/assertions.js index d38aaaed6..30e68a215 100644 --- a/lib/chai/core/assertions.js +++ b/lib/chai/core/assertions.js @@ -510,14 +510,16 @@ module.exports = function (chai, _) { 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; + if (obj == null) { + expected = flag(this, 'negate'); + } else if (Array.isArray(obj) || 'string' === typeof obj) { + expected = obj.length === 0; + } else if ('object' === typeof obj) { + expected = Object.keys(obj).length === 0; } this.assert( - !expected + expected , 'expected #{this} to be empty' , 'expected #{this} not to be empty' ); diff --git a/test/expect.js b/test/expect.js index 42b50e632..e152cf677 100644 --- a/test/expect.js +++ b/test/expect.js @@ -471,6 +471,35 @@ describe('expect', function () { err(function(){ expect({foo: 'bar'}).to.be.empty; }, "expected { foo: \'bar\' } to be empty"); + + err(function(){ + expect(0).to.be.empty; + }, "expected 0 to be empty"); + + err(function(){ + expect(null).to.be.empty; + }, "expected null to be empty"); + + err(function(){ + expect(undefined).to.be.empty; + }, "expected undefined to be empty"); + + err(function(){ + expect().to.be.empty; + }, "expected undefined to be empty"); + + err(function(){ + expect(null).to.not.be.empty; + }, "expected null not to be empty"); + + err(function(){ + expect(undefined).to.not.be.empty; + }, "expected undefined not to be empty"); + + err(function(){ + expect().to.not.be.empty; + }, "expected undefined not to be empty"); + }); it('NaN', function() { From 128c1126ac14bc1c3c90b48f7110e4708bb4779f Mon Sep 17 00:00:00 2001 From: Dai Date: Tue, 30 Jun 2015 17:14:46 +0100 Subject: [PATCH 02/19] Updated code based on discussion --- lib/chai/core/assertions.js | 15 +++------------ test/expect.js | 16 ++++++---------- 2 files changed, 9 insertions(+), 22 deletions(-) diff --git a/lib/chai/core/assertions.js b/lib/chai/core/assertions.js index 30e68a215..490916256 100644 --- a/lib/chai/core/assertions.js +++ b/lib/chai/core/assertions.js @@ -507,19 +507,10 @@ module.exports = function (chai, _) { */ Assertion.addProperty('empty', function () { - var obj = flag(this, 'object') - , expected = obj; - - if (obj == null) { - expected = flag(this, 'negate'); - } else if (Array.isArray(obj) || 'string' === typeof obj) { - expected = obj.length === 0; - } else if ('object' === typeof obj) { - expected = Object.keys(obj).length === 0; - } - + 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' ); diff --git a/test/expect.js b/test/expect.js index e152cf677..5eeb58f3b 100644 --- a/test/expect.js +++ b/test/expect.js @@ -472,33 +472,29 @@ describe('expect', function () { expect({foo: 'bar'}).to.be.empty; }, "expected { foo: \'bar\' } to be empty"); - err(function(){ - expect(0).to.be.empty; - }, "expected 0 to be empty"); - err(function(){ expect(null).to.be.empty; - }, "expected null to be empty"); + }, "expected null to exist"); err(function(){ expect(undefined).to.be.empty; - }, "expected undefined to be empty"); + }, "expected undefined to exist"); err(function(){ expect().to.be.empty; - }, "expected undefined to be empty"); + }, "expected undefined to exist"); err(function(){ expect(null).to.not.be.empty; - }, "expected null not to be empty"); + }, "expected null to exist"); err(function(){ expect(undefined).to.not.be.empty; - }, "expected undefined not to be empty"); + }, "expected undefined to exist"); err(function(){ expect().to.not.be.empty; - }, "expected undefined not to be empty"); + }, "expected undefined to exist"); }); From d2884a6baa31fbcddfe20d005c882d975294d51c Mon Sep 17 00:00:00 2001 From: Sakthipriyan Vairamani Date: Thu, 20 Aug 2015 02:01:42 +0530 Subject: [PATCH 03/19] handle NaN properly in assertions Fixes: #498 --- lib/chai/core/assertions.js | 4 ++-- lib/chai/interface/assert.js | 2 +- lib/chai/utils/index.js | 6 ++++++ lib/chai/utils/isNaN.js | 26 ++++++++++++++++++++++++++ test/assert.js | 24 +++++++++++++++++++++--- test/expect.js | 31 +++++++++++++++++++++++++------ test/should.js | 30 ++++++++++++++++++++++++++++-- 7 files changed, 109 insertions(+), 14 deletions(-) create mode 100644 lib/chai/utils/isNaN.js diff --git a/lib/chai/core/assertions.js b/lib/chai/core/assertions.js index 490916256..4c0ba44fb 100644 --- a/lib/chai/core/assertions.js +++ b/lib/chai/core/assertions.js @@ -446,7 +446,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 @@ -456,7 +456,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' ); diff --git a/lib/chai/interface/assert.js b/lib/chai/interface/assert.js index 3d1f69295..28dde285f 100644 --- a/lib/chai/interface/assert.js +++ b/lib/chai/interface/assert.js @@ -431,7 +431,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 diff --git a/lib/chai/utils/index.js b/lib/chai/utils/index.js index 57ea5ed05..932547d26 100644 --- a/lib/chai/utils/index.js +++ b/lib/chai/utils/index.js @@ -158,3 +158,9 @@ exports.checkError = require('check-error'); */ exports.proxify = require('./proxify'); + +/*! + * isNaN method + */ + +exports.isNaN = require('./isNaN'); diff --git a/lib/chai/utils/isNaN.js b/lib/chai/utils/isNaN.js new file mode 100644 index 000000000..113256d7f --- /dev/null +++ b/lib/chai/utils/isNaN.js @@ -0,0 +1,26 @@ +/*! + * 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; diff --git a/test/assert.js b/test/assert.js index 8140b7a07..8ceebb4b9 100644 --- a/test/assert.js +++ b/test/assert.js @@ -233,6 +233,9 @@ describe('assert', function () { assert.deepEqual({tea: 'chai'}, {tea: 'chai'}); assert.deepStrictEqual({tea: 'chai'}, {tea: 'chai'}); // Alias of deepEqual + assert.deepEqual([NaN], [NaN]); + assert.deepEqual({tea: NaN}, {tea: NaN}); + err(function () { assert.deepEqual({tea: 'chai'}, {tea: 'black'}); }, "expected { tea: \'chai\' } to deeply equal { tea: \'black\' }"); @@ -329,7 +332,19 @@ describe('assert', function () { }); it('isNaN', function() { - assert.isNaN('hello'); + assert.isNaN(NaN); + + err(function (){ + assert.isNaN(Infinity); + }, "expected Infinity to be NaN"); + + err(function (){ + assert.isNaN(undefined); + }, "expected undefined to be NaN"); + + err(function (){ + assert.isNaN({}); + }, "expected {} to be NaN"); err(function (){ assert.isNaN(4); @@ -338,10 +353,13 @@ describe('assert', function () { it('isNotNaN', function() { assert.isNotNaN(4); + assert.isNotNaN(Infinity); + assert.isNotNaN(undefined); + assert.isNotNaN({}); err(function (){ - assert.isNotNaN('hello'); - }, "expected 'hello' not to be NaN"); + assert.isNotNaN(NaN); + }, "expected NaN not to be NaN"); }); it('exists', function() { diff --git a/test/expect.js b/test/expect.js index 5eeb58f3b..00bee817b 100644 --- a/test/expect.js +++ b/test/expect.js @@ -500,11 +500,34 @@ describe('expect', function () { it('NaN', function() { expect(NaN).to.be.NaN; - expect('foo').to.be.NaN; - expect({}).to.be.NaN; + + expect(undefined).not.to.be.NaN; + expect(Infinity).not.to.be.NaN; + expect('foo').not.to.be.NaN; + expect({}).not.to.be.NaN; expect(4).not.to.be.NaN; expect([]).not.to.be.NaN; + err(function(){ + expect(NaN).not.to.be.NaN; + }, "expected NaN not to be NaN"); + + err(function(){ + expect(undefined).to.be.NaN; + }, "expected undefined to be NaN"); + + err(function(){ + expect(Infinity).to.be.NaN; + }, "expected Infinity to be NaN"); + + err(function(){ + expect('foo').to.be.NaN; + }, "expected 'foo' to be NaN"); + + err(function(){ + expect({}).to.be.NaN; + }, "expected {} to be NaN"); + err(function(){ expect(4).to.be.NaN; }, "expected 4 to be NaN"); @@ -512,10 +535,6 @@ describe('expect', function () { err(function(){ expect([]).to.be.NaN; }, "expected [] to be NaN"); - - err(function(){ - expect('foo').not.to.be.NaN; - }, "expected 'foo' not to be NaN"); }); it('finite', function() { diff --git a/test/should.js b/test/should.js index c32a9e3ac..d78e023a2 100644 --- a/test/should.js +++ b/test/should.js @@ -154,12 +154,38 @@ describe('should', function() { }); it('NaN', function(){ - 'foo'.should.be.NaN; + NaN.should.be.NaN; + + Infinity.should.not.be.NaN; + 'foo'.should.not.be.NaN; + ({}).should.not.be.NaN; + should.not.equal(undefined, NaN); (4).should.not.be.NaN; + err(function(){ + NaN.should.not.be.NaN; + }, "expected NaN not to be NaN"); + + err(function(){ + Infinity.should.be.NaN; + }, "expected Infinity to be NaN"); + + err(function(){ + 'foo'.should.be.NaN; + }, "expected 'foo' to be NaN"); + + err(function(){ + ({}).should.be.NaN; + }, "expected {} to be NaN"); + + err(function(){ + should.equal(undefined, NaN); + }, "expected undefined to equal NaN"); + err(function(){ (4).should.be.NaN; - }, "expected 4 to be NaN") + }, "expected 4 to be NaN"); + }); it('undefined', function(){ From a1190c92568fa750eaef977eed0603ce4477d2d9 Mon Sep 17 00:00:00 2001 From: Tristan Patch Date: Fri, 28 Aug 2015 16:24:33 -0700 Subject: [PATCH 04/19] Deep members comparisons show diff. --- lib/chai/core/assertions.js | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/chai/core/assertions.js b/lib/chai/core/assertions.js index 4c0ba44fb..ca4dcd5e0 100644 --- a/lib/chai/core/assertions.js +++ b/lib/chai/core/assertions.js @@ -1767,6 +1767,7 @@ module.exports = function (chai, _) { , failNegateMsg , subset , obj + , true ); }); From 04aa795b73084df79961b8749e424daa434eaea3 Mon Sep 17 00:00:00 2001 From: Whitney Young Date: Sun, 4 Oct 2015 14:49:33 -0700 Subject: [PATCH 05/19] No longer silently allowing undefined method calls. Fixes #467. --- lib/chai/utils/overwriteMethod.js | 4 +++- test/utilities.js | 19 ++++++++++++++++++- 2 files changed, 21 insertions(+), 2 deletions(-) diff --git a/lib/chai/utils/overwriteMethod.js b/lib/chai/utils/overwriteMethod.js index 20657a57c..4c61c181e 100644 --- a/lib/chai/utils/overwriteMethod.js +++ b/lib/chai/utils/overwriteMethod.js @@ -42,7 +42,9 @@ var flag = require('./flag'); 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 77db79b01..52182ae89 100644 --- a/test/utilities.js +++ b/test/utilities.js @@ -302,13 +302,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 () { From 0a8869643c16a0c4348d212258bd0df6b35fc7f3 Mon Sep 17 00:00:00 2001 From: lucasfcosta Date: Sat, 19 Dec 2015 15:09:07 -0200 Subject: [PATCH 06/19] New showDiff behaviour also depending on expected and actual values --- lib/chai/assertion.js | 3 ++- test/assert.js | 29 +++++++++++++++++++++++++++++ 2 files changed, 31 insertions(+), 1 deletion(-) diff --git a/lib/chai/assertion.js b/lib/chai/assertion.js index 9a3846b4b..2a97d21a6 100644 --- a/lib/chai/assertion.js +++ b/lib/chai/assertion.js @@ -100,7 +100,8 @@ 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) { diff --git a/test/assert.js b/test/assert.js index 8ceebb4b9..e2ce224b6 100644 --- a/test/assert.js +++ b/test/assert.js @@ -1890,4 +1890,33 @@ describe('assert', function () { }, 'expected undefined to not be frozen'); }); }); + + it('showDiff true with actual and expected args', function() { + try { + new chai.Assertion().assert( + 'one' === 'two' + , 'expected #{this} to equal #{exp}' + , 'expected #{this} to not equal #{act}' + , 'one' + , 'two' + ); + } catch(e) { + assert.isTrue(e.showDiff); + } + }); + + it('showDiff false without expected and actual', function() { + try { + new chai.Assertion().assert( + 'one' === 'two' + , 'expected #{this} to equal #{exp}' + , 'expected #{this} to not equal #{act}' + , 'one' + , 'two' + , false + ); + } catch(e) { + assert.isFalse(e.showDiff); + } + }); }); From f20c7b5b014c3bfc6c6e4dad33487c10acaf2fa2 Mon Sep 17 00:00:00 2001 From: lucasfcosta Date: Tue, 22 Dec 2015 21:30:22 -0200 Subject: [PATCH 07/19] isTypedArray verification --- lib/chai/utils/inspect.js | 26 +++++++++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/lib/chai/utils/inspect.js b/lib/chai/utils/inspect.js index 1a7967732..edea645d2 100644 --- a/lib/chai/utils/inspect.js +++ b/lib/chai/utils/inspect.js @@ -124,7 +124,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)) { @@ -318,6 +326,22 @@ 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' && + objectToString(ar) === '[object Int8Array]' || + objectToString(ar) === '[object Uint8Array]' || + objectToString(ar) === '[object Uint8ClampedArray]' || + objectToString(ar) === '[object Int16Array]' || + objectToString(ar) === '[object Uint16Array]' || + objectToString(ar) === '[object Int32Array]' || + objectToString(ar) === '[object Uint32Array]' || + objectToString(ar) === '[object Float32Array]' || + objectToString(ar) === '[object Float64Array]' + ) +} + function isArray(ar) { return Array.isArray(ar) || (typeof ar === 'object' && objectToString(ar) === '[object Array]'); From 8123bf778cf253febed5beed57209a1c6f7b1ec0 Mon Sep 17 00:00:00 2001 From: lucasfcosta Date: Tue, 22 Dec 2015 23:14:53 -0200 Subject: [PATCH 08/19] formatTypedArray function --- lib/chai/utils/inspect.js | 16 ++++++++++++++++ test/utilities.js | 36 ++++++++++++++++++++++++++++++++++++ 2 files changed, 52 insertions(+) diff --git a/lib/chai/utils/inspect.js b/lib/chai/utils/inspect.js index edea645d2..7a3cb6dda 100644 --- a/lib/chai/utils/inspect.js +++ b/lib/chai/utils/inspect.js @@ -179,6 +179,8 @@ function formatValue(ctx, value, recurseTimes) { var output; if (array) { output = formatArray(ctx, value, recurseTimes, visibleKeys, keys); + } else if (typedArray) { + output = formatTypedArray(ctx, value, recurseTimes, visibleKeys, keys); } else { output = keys.map(function(key) { return formatProperty(ctx, value, recurseTimes, visibleKeys, key, array); @@ -245,6 +247,20 @@ function formatArray(ctx, value, recurseTimes, visibleKeys, keys) { return output; } +function formatTypedArray(ctx, value, recurseTimes, visibleKeys, keys) { + var output = []; + + for (var i = 0; i < l; ++i) { + if (Object.prototype.hasOwnProperty.call(value, String(i))) { + output.push(formatProperty(ctx, value, recurseTimes, visibleKeys, + String(i), true)); + } else { + output.push(''); + } + } + + return output; +} function formatProperty(ctx, value, recurseTimes, visibleKeys, key, array) { var name, str; diff --git a/test/utilities.js b/test/utilities.js index 52182ae89..270b9dffc 100644 --- a/test/utilities.js +++ b/test/utilities.js @@ -672,6 +672,42 @@ describe('utilities', function () { }); }); + it('inspect typedArray conversion', function () { + chai.use(function (_chai, _) { + var arr = [1, 2, 3] + , exp = '[ 1, 2, 3 ]' + , isNode = true; + + if (typeof window !== 'undefined') { + isNode = false; + } + + // Checks if engine supports common TypedArrays + if ((!isNode && 'Int8Array' in window) || + isNode && typeof 'Int8Array' !== undefined) { + // Typed array inspections should work as array inspections do + expect(_.inspect(new Int8Array(arr))).to.equal(exp); + expect(_.inspect(new Uint8Array(arr))).to.equal(exp); + expect(_.inspect(new Int16Array(arr))).to.equal(exp); + expect(_.inspect(new Uint16Array(arr))).to.equal(exp); + expect(_.inspect(new Int32Array(arr))).to.equal(exp); + expect(_.inspect(new Uint32Array(arr))).to.equal(exp); + expect(_.inspect(new Float32Array(arr))).to.equal(exp); + } + + // These ones may not be available alongside the others above + if ((!isNode && 'Uint8ClampedArray' in window) || + isNode && typeof 'Uint8ClampedArray' !== undefined) { + expect(_.inspect(new Uint8ClampedArray(arr))).to.equal(exp); + } + + if ((!isNode && 'Float64Array' in window) || + isNode && typeof 'Float64Array' !== undefined) { + expect(_.inspect(new Float64Array(arr))).to.equal(exp); + } + }); + }); + it('addChainableMethod', function () { chai.use(function (_chai, _) { _chai.Assertion.addChainableMethod('x', From b90ba4e5a00e4843375667cb2acbc69300e5299a Mon Sep 17 00:00:00 2001 From: lucasfcosta Date: Tue, 22 Dec 2015 23:15:47 -0200 Subject: [PATCH 09/19] Truncate long typedArray --- lib/chai/utils/inspect.js | 11 +++++++++++ test/utilities.js | 24 ++++++++++++++++++++++++ 2 files changed, 35 insertions(+) diff --git a/lib/chai/utils/inspect.js b/lib/chai/utils/inspect.js index 7a3cb6dda..7a72d14d5 100644 --- a/lib/chai/utils/inspect.js +++ b/lib/chai/utils/inspect.js @@ -250,6 +250,12 @@ function formatArray(ctx, value, recurseTimes, visibleKeys, keys) { function formatTypedArray(ctx, value, recurseTimes, visibleKeys, keys) { var output = []; + // Truncating TypedArray if it has more than 1000 elements + var l = value.length; + if (l > 1000) { + l = 5 + } + for (var i = 0; i < l; ++i) { if (Object.prototype.hasOwnProperty.call(value, String(i))) { output.push(formatProperty(ctx, value, recurseTimes, visibleKeys, @@ -259,6 +265,11 @@ function formatTypedArray(ctx, value, recurseTimes, visibleKeys, keys) { } } + // If TypedArray has been truncated we add '...' to it's end + if (l !== value.length) { + output.push('...'); + } + return output; } diff --git a/test/utilities.js b/test/utilities.js index 270b9dffc..5eeb0e39f 100644 --- a/test/utilities.js +++ b/test/utilities.js @@ -708,6 +708,30 @@ describe('utilities', function () { }); }); + it('truncate long typedArray', function () { + chai.use(function (_chai, _) { + + var arr = [] + , exp = '[ 1, 2, 3, 4, 5, ... ]' + , isNode = true; + + // Filling arr with lots of elements + for (var i = 1; i <= 1001; i++) { + arr.push(i); + } + + if (typeof window !== 'undefined') { + isNode = false; + } + + if ((!isNode && 'Int8Array' in window) || + isNode && typeof 'Int8Array' !== undefined) { + + expect(_.inspect(new Int8Array(arr))).to.equal(exp); + } + }); + }); + it('addChainableMethod', function () { chai.use(function (_chai, _) { _chai.Assertion.addChainableMethod('x', From aae52f2f4252d587704229c1895bbf7c1583b8a6 Mon Sep 17 00:00:00 2001 From: lucasfcosta Date: Tue, 22 Dec 2015 23:23:30 -0200 Subject: [PATCH 10/19] Minor inspect code style fixes --- lib/chai/utils/inspect.js | 11 ++++------- test/utilities.js | 9 ++++----- 2 files changed, 8 insertions(+), 12 deletions(-) diff --git a/lib/chai/utils/inspect.js b/lib/chai/utils/inspect.js index 7a72d14d5..0b485c69c 100644 --- a/lib/chai/utils/inspect.js +++ b/lib/chai/utils/inspect.js @@ -238,6 +238,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, @@ -251,10 +252,7 @@ function formatTypedArray(ctx, value, recurseTimes, visibleKeys, keys) { var output = []; // Truncating TypedArray if it has more than 1000 elements - var l = value.length; - if (l > 1000) { - l = 5 - } + var l = value.length > 1000 ? 5 : value.length; for (var i = 0; i < l; ++i) { if (Object.prototype.hasOwnProperty.call(value, String(i))) { @@ -265,7 +263,7 @@ function formatTypedArray(ctx, value, recurseTimes, visibleKeys, keys) { } } - // If TypedArray has been truncated we add '...' to it's end + // If TypedArray has been truncated we add '...' to its end if (l !== value.length) { output.push('...'); } @@ -365,8 +363,7 @@ function isTypedArray(ar) { objectToString(ar) === '[object Int32Array]' || objectToString(ar) === '[object Uint32Array]' || objectToString(ar) === '[object Float32Array]' || - objectToString(ar) === '[object Float64Array]' - ) + objectToString(ar) === '[object Float64Array]'); } function isArray(ar) { diff --git a/test/utilities.js b/test/utilities.js index 5eeb0e39f..5e1237604 100644 --- a/test/utilities.js +++ b/test/utilities.js @@ -672,7 +672,7 @@ describe('utilities', function () { }); }); - it('inspect typedArray conversion', function () { + it('inspect every kind of available TypedArray', function () { chai.use(function (_chai, _) { var arr = [1, 2, 3] , exp = '[ 1, 2, 3 ]' @@ -708,7 +708,7 @@ describe('utilities', function () { }); }); - it('truncate long typedArray', function () { + it('truncate long TypedArray', function () { chai.use(function (_chai, _) { var arr = [] @@ -726,9 +726,8 @@ describe('utilities', function () { if ((!isNode && 'Int8Array' in window) || isNode && typeof 'Int8Array' !== undefined) { - - expect(_.inspect(new Int8Array(arr))).to.equal(exp); - } + expect(_.inspect(new Int8Array(arr))).to.equal(exp); + } }); }); From 4d314b614b3017c1a9d78fd42120419136aa54dd Mon Sep 17 00:00:00 2001 From: lucasfcosta Date: Wed, 30 Dec 2015 13:18:11 -0200 Subject: [PATCH 11/19] Fix truncateThreshold --- lib/chai/utils/inspect.js | 6 ++++-- test/utilities.js | 4 +++- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/lib/chai/utils/inspect.js b/lib/chai/utils/inspect.js index 0b485c69c..8cdf0343f 100644 --- a/lib/chai/utils/inspect.js +++ b/lib/chai/utils/inspect.js @@ -4,6 +4,7 @@ var getName = require('./getName'); var getProperties = require('./getProperties'); var getEnumerableProperties = require('./getEnumerableProperties'); +var config = require('../config'); module.exports = inspect; @@ -251,8 +252,9 @@ function formatArray(ctx, value, recurseTimes, visibleKeys, keys) { function formatTypedArray(ctx, value, recurseTimes, visibleKeys, keys) { var output = []; - // Truncating TypedArray if it has more than 1000 elements - var l = value.length > 1000 ? 5 : value.length; + // Truncating TypedArray if it has more elements than the configured threshold + var t = config.truncateThreshold; + var l = value.length > t ? t : value.length; for (var i = 0; i < l; ++i) { if (Object.prototype.hasOwnProperty.call(value, String(i))) { diff --git a/test/utilities.js b/test/utilities.js index 5e1237604..d279ab40a 100644 --- a/test/utilities.js +++ b/test/utilities.js @@ -711,12 +711,14 @@ describe('utilities', function () { it('truncate long TypedArray', function () { chai.use(function (_chai, _) { + _chai.config.truncateThreshold = 5; + var arr = [] , exp = '[ 1, 2, 3, 4, 5, ... ]' , isNode = true; // Filling arr with lots of elements - for (var i = 1; i <= 1001; i++) { + for (var i = 1; i <= 1000; i++) { arr.push(i); } From 3ff3cc73c82415d931d0115991a9587653b9463f Mon Sep 17 00:00:00 2001 From: lucasfcosta Date: Wed, 30 Dec 2015 13:21:50 -0200 Subject: [PATCH 12/19] Use regex.test method to check for TypedArrays --- lib/chai/utils/inspect.js | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/lib/chai/utils/inspect.js b/lib/chai/utils/inspect.js index 8cdf0343f..dd61b78a2 100644 --- a/lib/chai/utils/inspect.js +++ b/lib/chai/utils/inspect.js @@ -356,16 +356,7 @@ function reduceToSingleString(output, base, braces) { 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' && - objectToString(ar) === '[object Int8Array]' || - objectToString(ar) === '[object Uint8Array]' || - objectToString(ar) === '[object Uint8ClampedArray]' || - objectToString(ar) === '[object Int16Array]' || - objectToString(ar) === '[object Uint16Array]' || - objectToString(ar) === '[object Int32Array]' || - objectToString(ar) === '[object Uint32Array]' || - objectToString(ar) === '[object Float32Array]' || - objectToString(ar) === '[object Float64Array]'); + return (typeof ar === 'object' && /\w+Array]$/.test(objectToString(ar))); } function isArray(ar) { From 0e68e8d488fb796dc96c7d5ad599ba9d3bab3422 Mon Sep 17 00:00:00 2001 From: lucasfcosta Date: Thu, 31 Dec 2015 15:28:49 -0200 Subject: [PATCH 13/19] Use getOwnPropertyDescriptor instead of __lookup methods --- lib/chai/utils/inspect.js | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/lib/chai/utils/inspect.js b/lib/chai/utils/inspect.js index dd61b78a2..237f5e656 100644 --- a/lib/chai/utils/inspect.js +++ b/lib/chai/utils/inspect.js @@ -274,16 +274,19 @@ function formatTypedArray(ctx, value, recurseTimes, visibleKeys, keys) { } 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'); } } From 01189490bd7f288e2916264bf2ce06fcf224853b Mon Sep 17 00:00:00 2001 From: lucasfcosta Date: Tue, 5 Jan 2016 00:47:50 -0200 Subject: [PATCH 14/19] Truncate typedArrays according to their string representation --- lib/chai/utils/inspect.js | 30 +++++++++++++----------------- test/utilities.js | 4 +--- 2 files changed, 14 insertions(+), 20 deletions(-) diff --git a/lib/chai/utils/inspect.js b/lib/chai/utils/inspect.js index 237f5e656..ef3590cc9 100644 --- a/lib/chai/utils/inspect.js +++ b/lib/chai/utils/inspect.js @@ -181,7 +181,7 @@ function formatValue(ctx, value, recurseTimes) { if (array) { output = formatArray(ctx, value, recurseTimes, visibleKeys, keys); } else if (typedArray) { - output = formatTypedArray(ctx, value, recurseTimes, visibleKeys, keys); + return formatTypedArray(value); } else { output = keys.map(function(key) { return formatProperty(ctx, value, recurseTimes, visibleKeys, key, array); @@ -249,28 +249,24 @@ function formatArray(ctx, value, recurseTimes, visibleKeys, keys) { return output; } -function formatTypedArray(ctx, value, recurseTimes, visibleKeys, keys) { - var output = []; - - // Truncating TypedArray if it has more elements than the configured threshold - var t = config.truncateThreshold; - var l = value.length > t ? t : value.length; +function formatTypedArray(value) { + var str = '[ '; - for (var i = 0; i < l; ++i) { - if (Object.prototype.hasOwnProperty.call(value, String(i))) { - output.push(formatProperty(ctx, value, recurseTimes, visibleKeys, - String(i), true)); - } else { - output.push(''); + for (var i = 0; i < value.length; ++i) { + if (str.length >= config.truncateThreshold - 7) { + str += '...'; + break; } + str += value[i] + ', '; } + str += ' ]'; - // If TypedArray has been truncated we add '...' to its end - if (l !== value.length) { - output.push('...'); + // Removing trailing `, ` if the array was not truncated + if (str.indexOf(', ]') !== -1) { + str = str.replace(', ]', ' ]'); } - return output; + return str; } function formatProperty(ctx, value, recurseTimes, visibleKeys, key, array) { diff --git a/test/utilities.js b/test/utilities.js index d279ab40a..38c6c5d8a 100644 --- a/test/utilities.js +++ b/test/utilities.js @@ -711,10 +711,8 @@ describe('utilities', function () { it('truncate long TypedArray', function () { chai.use(function (_chai, _) { - _chai.config.truncateThreshold = 5; - var arr = [] - , exp = '[ 1, 2, 3, 4, 5, ... ]' + , exp = '[ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, ... ]' , isNode = true; // Filling arr with lots of elements From 16101af34739dfd372c373be05043358a4b810ee Mon Sep 17 00:00:00 2001 From: lucasfcosta Date: Mon, 14 Mar 2016 17:25:00 -0300 Subject: [PATCH 15/19] Assertion subject (obj) changes when using ownProperty assertion --- lib/chai/core/assertions.js | 47 +++++++++++++++++++---- test/expect.js | 74 ++++++++++++++++++++++++++++++++++++- test/should.js | 74 ++++++++++++++++++++++++++++++++++++- 3 files changed, 183 insertions(+), 12 deletions(-) diff --git a/lib/chai/core/assertions.js b/lib/chai/core/assertions.js index ca4dcd5e0..6aa61e8c1 100644 --- a/lib/chai/core/assertions.js +++ b/lib/chai/core/assertions.js @@ -1021,28 +1021,59 @@ module.exports = function (chai, _) { /** - * ### .ownProperty(name) + * ### .ownProperty(name, [value]) * - * Asserts that the target has an own property `name`. + * Asserts that the target has an own property `name` and, optionally, if it has + * (or not, if using `.not`) the desired `value`. * * expect('test').to.have.ownProperty('length'); + * expect('test').to.haveOwnProperty('length'); + * expect('test').to.not.have.ownProperty('foo'); + * expect('test').to.not.haveOwnProperty('foo'); + * expect({ length: 12 }).to.have.ownProperty('length', 12); + * expect({ length: 1337 }).to.not.have.ownProperty('length', 20); + * expect({ length: 12 }).to.haveOwnProperty('length', 12); + * expect({ length: 1337 }).to.not.haveOwnProperty('length', 20); * * @name ownProperty * @alias haveOwnProperty * @param {String} name + * @param {Mixed} value (optional) * @param {String} message _optional_ * @namespace BDD * @api public */ - function assertOwnProperty (name, msg) { + function assertOwnProperty (name, value, msg) { if (msg) flag(this, 'message', msg); var obj = flag(this, 'object'); - this.assert( - Object.prototype.hasOwnProperty.call(obj, name) - , 'expected #{this} to have own property ' + _.inspect(name) - , 'expected #{this} to not have own property ' + _.inspect(name) - ); + var negate = flag(this, 'negate'); + var objHasProperty = Object.prototype.hasOwnProperty.call(obj, name) + var actualValue = obj[name]; + + if (negate && value !== undefined) { + if (actualValue === undefined) { + throw new Error(_.inspect(obj) + ' does not have own property ' + _.inspect(name)); + } + } else { + this.assert( + objHasProperty + , 'expected #{this} to have own property ' + _.inspect(name) + , 'expected #{this} to not have own property ' + _.inspect(name) + ); + } + + if (value !== undefined) { + this.assert( + actualValue === value + , 'expected #{this} to have own property ' + _.inspect(name) + ' of #{exp}, but got #{act}' + , 'expected #{this} to not have own property ' + _.inspect(name) + ' of #{act}' + , value + , actualValue + ); + } + + flag(this, 'object', actualValue); } Assertion.addMethod('ownProperty', assertOwnProperty); diff --git a/test/expect.js b/test/expect.js index 00bee817b..62597fc50 100644 --- a/test/expect.js +++ b/test/expect.js @@ -732,15 +732,85 @@ describe('expect', function () { it('ownProperty(name)', function(){ expect('test').to.have.ownProperty('length'); expect('test').to.haveOwnProperty('length'); + expect('test').to.not.have.ownProperty('iDontExist'); + expect('test').to.not.haveOwnProperty('iDontExist'); + expect({ length: 12 }).to.have.ownProperty('length'); + expect({ length: 12 }).to.haveOwnProperty('length'); + expect({ length: 12 }).to.not.have.ownProperty('iDontExist'); + expect({ length: 12 }).to.not.haveOwnProperty('iDontExist'); + + // Chaining property's value + expect('test').to.have.ownProperty('length').that.is.a('number'); + expect('test').to.haveOwnProperty('length').that.is.a('number'); + + err(function(){ + expect({ length: 12 }).to.have.ownProperty('iDontExist'); + }, "expected { length: 12 } to have own property 'iDontExist'"); + + err(function(){ + expect({ length: 12 }).to.not.have.ownProperty('length'); + }, "expected { length: 12 } to not have own property 'length'"); + + err(function(){ + expect({ length: 12 }).to.haveOwnProperty('iDontExist'); + }, "expected { length: 12 } to have own property 'iDontExist'"); + + err(function(){ + expect({ length: 12 }).to.not.haveOwnProperty('length'); + }, "expected { length: 12 } to not have own property 'length'"); + }); + + it('ownProperty(name, value)', function(){ + expect('test').to.have.ownProperty('length', 4); + expect('test').to.haveOwnProperty('length', 4); + expect('test').to.not.have.ownProperty('length', 1337); + expect('test').to.not.haveOwnProperty('length', 1337); + + expect({ length: 12 }).to.have.ownProperty('length', 12); + expect({ length: 12 }).to.haveOwnProperty('length', 12); + expect({ length: 12 }).to.not.have.ownProperty('length', 15); + expect({ length: 12 }).to.not.haveOwnProperty('length', 15); + + // Chaining property's value + expect('test').to.have.ownProperty('length', 4).that.is.a('number'); + expect('test').to.haveOwnProperty('length', 4).that.is.a('number'); var objNoProto = Object.create(null); objNoProto.a = 'a'; expect(objNoProto).to.have.ownProperty('a'); err(function(){ - expect({ length: 12 }).to.not.have.ownProperty('length', 'blah'); - }, "blah: expected { length: 12 } to not have own property 'length'"); + expect({ length: 12 }).to.have.ownProperty('iDontExist', 12); + }, "expected { length: 12 } to have own property 'iDontExist'"); + + err(function() { + expect({ length: 12 }).to.not.have.ownProperty('length', 12); + }, "expected { length: 12 } to not have own property 'length' of 12"); + + err(function() { + expect({ length: 12 }).to.have.ownProperty('length', 15); + }, "expected { length: 12 } to have own property 'length' of 15, but got 12"); + + err(function() { + expect({ length: 12 }).to.not.have.ownProperty('iDontExist', 15); + }, "{ length: 12 } does not have own property 'iDontExist'"); + + err(function(){ + expect({ length: 12 }).to.haveOwnProperty('iDontExist', 12); + }, "expected { length: 12 } to have own property 'iDontExist'"); + + err(function() { + expect({ length: 12 }).to.not.haveOwnProperty('length', 12); + }, "expected { length: 12 } to not have own property 'length' of 12"); + + err(function() { + expect({ length: 12 }).to.haveOwnProperty('length', 15); + }, "expected { length: 12 } to have own property 'length' of 15, but got 12"); + + err(function() { + expect({ length: 12 }).to.not.haveOwnProperty('iDontExist', 15); + }, "{ length: 12 } does not have own property 'iDontExist'"); }); it('ownPropertyDescriptor(name)', function(){ diff --git a/test/should.js b/test/should.js index d78e023a2..da1c00a8a 100644 --- a/test/should.js +++ b/test/should.js @@ -583,16 +583,86 @@ describe('should', function() { it('ownProperty(name)', function(){ 'test'.should.have.ownProperty('length'); 'test'.should.haveOwnProperty('length'); + 'test'.should.not.have.ownProperty('iDontExist'); + 'test'.should.not.haveOwnProperty('iDontExist'); + ({ length: 12 }).should.have.ownProperty('length'); + ({ length: 12 }).should.haveOwnProperty('length'); + ({ length: 12 }).should.not.have.ownProperty('iDontExist'); + ({ length: 12 }).should.not.haveOwnProperty('iDontExist'); ({ 1: 1 }).should.have.ownProperty(1); var objNoHasOwnProperty = {hasOwnProperty: null}; objNoHasOwnProperty.a = 'a'; objNoHasOwnProperty.should.have.ownProperty('a'); + // Chaining property's value + 'test'.should.have.ownProperty('length').that.is.a('number'); + 'test'.should.haveOwnProperty('length').that.is.a('number'); + + err(function(){ + ({ length: 12 }).should.have.ownProperty('iDontExist'); + }, "expected { length: 12 } to have own property 'iDontExist'"); + + err(function(){ + ({ length: 12 }).should.not.have.ownProperty('length'); + }, "expected { length: 12 } to not have own property 'length'"); + + err(function(){ + ({ length: 12 }).should.haveOwnProperty('iDontExist'); + }, "expected { length: 12 } to have own property 'iDontExist'"); + + err(function(){ + ({ length: 12 }).should.not.haveOwnProperty('length'); + }, "expected { length: 12 } to not have own property 'length'"); + }); + + it('ownProperty(name, value)', function(){ + 'test'.should.have.ownProperty('length', 4); + 'test'.should.haveOwnProperty('length', 4); + 'test'.should.not.have.ownProperty('length', 1337); + 'test'.should.not.haveOwnProperty('length', 1337); + + ({ length: 12 }).should.have.ownProperty('length', 12); + ({ length: 12 }).should.haveOwnProperty('length', 12); + ({ length: 12 }).should.not.have.ownProperty('length', 15); + ({ length: 12 }).should.not.haveOwnProperty('length', 15); + + // Chaining property's value + 'test'.should.have.ownProperty('length', 4).that.is.a('number'); + 'test'.should.haveOwnProperty('length', 4).that.is.a('number'); + err(function(){ - ({ length: 12 }).should.not.have.ownProperty('length', 'blah'); - }, "blah: expected { length: 12 } to not have own property 'length'"); + ({ length: 12 }).should.have.ownProperty('iDontExist', 12); + }, "expected { length: 12 } to have own property 'iDontExist'"); + + err(function() { + ({ length: 12 }).should.not.have.ownProperty('length', 12); + }, "expected { length: 12 } to not have own property 'length' of 12"); + + err(function() { + ({ length: 12 }).should.have.ownProperty('length', 15); + }, "expected { length: 12 } to have own property 'length' of 15, but got 12"); + + err(function() { + ({ length: 12 }).should.not.have.ownProperty('iDontExist', 15); + }, "{ length: 12 } does not have own property 'iDontExist'"); + + err(function(){ + ({ length: 12 }).should.haveOwnProperty('iDontExist', 12); + }, "expected { length: 12 } to have own property 'iDontExist'"); + + err(function() { + ({ length: 12 }).should.not.haveOwnProperty('length', 12); + }, "expected { length: 12 } to not have own property 'length' of 12"); + + err(function() { + ({ length: 12 }).should.haveOwnProperty('length', 15); + }, "expected { length: 12 } to have own property 'length' of 15, but got 12"); + + err(function() { + ({ length: 12 }).should.not.haveOwnProperty('iDontExist', 15); + }, "{ length: 12 } does not have own property 'iDontExist'"); }); it('ownPropertyDescriptor(name)', function(){ From df1a89241b91e488763539884041d6e9943c4dfe Mon Sep 17 00:00:00 2001 From: lucasfcosta Date: Mon, 14 Mar 2016 21:15:37 -0300 Subject: [PATCH 16/19] ownProperty assertions on Assert interface --- lib/chai/interface/assert.js | 76 ++++++++++++++++++++++++++++++++++++ test/assert.js | 47 ++++++++++++++++++++++ 2 files changed, 123 insertions(+) diff --git a/lib/chai/interface/assert.js b/lib/chai/interface/assert.js index 28dde285f..fa9a3b9d0 100644 --- a/lib/chai/interface/assert.js +++ b/lib/chai/interface/assert.js @@ -1301,6 +1301,82 @@ module.exports = function (chai, util) { new Assertion(obj, msg).to.not.have.deep.nested.property(prop, val); } + /** + * ### .ownProperty(object, property, [message]) + * + * Asserts that `object` has an own property named by `property`. + * + * 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).to.have.ownProperty(prop); + }; + + /** + * ### .notOwnProperty(object, property, [message]) + * + * Asserts that `object` does not have an own property named by `property`. + * + * assert.notOwnProperty({ tea: { green: 'matcha' }}, 'coffee'); + * + * @name notOwnProperty + * @param {Object} object + * @param {String} property + * @param {String} message + * @api public + */ + + assert.notOwnProperty = function (obj, prop, msg) { + new Assertion(obj, msg).to.not.have.ownProperty(prop); + }; + + /** + * ### .ownPropertyVal(object, property, value, [message]) + * + * Asserts that `object` has an own property named by `property` and a value + * equal to the provided `value`. + * + * 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).to.have.ownProperty(prop, value); + }; + + /** + * ### .notOwnPropertyVal(object, property, value, [message]) + * + * Asserts that `object` has an own property named by `property`, but with a value + * different from that given by `value`. + * + * assert.notOwnPropertyVal({ tea: 'is better'}, 'tea', 'is worse'); + * + * @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).to.not.have.ownProperty(prop, value); + }; + /** * ### .lengthOf(object, length, [message]) * diff --git a/test/assert.js b/test/assert.js index e2ce224b6..79843ff07 100644 --- a/test/assert.js +++ b/test/assert.js @@ -1159,6 +1159,53 @@ describe('assert', function () { }, "blah: expected { a: { b: { c: 1 } } } to not have a deep nested property 'a.b' of { c: 1 }"); }); + it('ownProperty', function() { + var coffeeObj = { coffee: 'is good' }; + + // This has length = 17 + var teaObj = 'but tea is better'; + + assert.ownProperty(coffeeObj, 'coffee'); + assert.ownProperty(teaObj, 'length'); + + assert.ownPropertyVal(coffeeObj, 'coffee', 'is good'); + assert.ownPropertyVal(teaObj, 'length', 17); + + assert.notOwnProperty(coffeeObj, 'length'); + assert.notOwnProperty(teaObj, 'calories'); + + assert.notOwnPropertyVal(coffeeObj, 'coffee', 'is bad'); + assert.notOwnPropertyVal(teaObj, 'length', 1); + + err(function () { + assert.ownProperty(coffeeObj, 'calories'); + }, "expected { coffee: 'is good' } to have own property 'calories'"); + + err(function () { + assert.notOwnProperty(coffeeObj, 'coffee'); + }, "expected { coffee: 'is good' } to not have own property 'coffee'"); + + err(function () { + assert.ownPropertyVal(teaObj, 'length', 1); + }, "expected 'but tea is better' to have own property 'length' of 1, but got 17"); + + err(function () { + assert.notOwnPropertyVal(teaObj, 'length', 17); + }, "expected 'but tea is better' to not have own property 'length' of 17"); + + err(function () { + assert.ownPropertyVal(teaObj, 'calories', 17); + }, "expected 'but tea is better' to have own property 'calories'"); + + err(function () { + assert.ownPropertyVal(teaObj, 'calories', 17); + }, "expected 'but tea is better' to have own property 'calories'"); + + err(function () { + assert.notOwnPropertyVal(coffeeObj, 'sugar', 1337); + }, "{ coffee: 'is good' } does not have own property 'sugar'"); + }); + it('throws / throw / Throw', function() { ['throws', 'throw', 'Throw'].forEach(function (throws) { assert[throws](function() { throw new Error('foo'); }); From c3e828534bd79bbc23d4804c1a7775c2558cb4a4 Mon Sep 17 00:00:00 2001 From: Lucas Vieira Date: Sat, 30 Apr 2016 17:46:33 -0300 Subject: [PATCH 17/19] Verify types on within and related assertions --- lib/chai/core/assertions.js | 75 +++++++++++++++++++++---- test/assert.js | 15 +++++ test/expect.js | 108 ++++++++++++++++++++++++++++++++++++ test/should.js | 104 ++++++++++++++++++++++++++++++++++ 4 files changed, 292 insertions(+), 10 deletions(-) diff --git a/lib/chai/core/assertions.js b/lib/chai/core/assertions.js index 6aa61e8c1..5050d62e1 100644 --- a/lib/chai/core/assertions.js +++ b/lib/chai/core/assertions.js @@ -645,9 +645,20 @@ module.exports = function (chai, _) { function assertAbove (n, msg) { if (msg) flag(this, 'message', msg); - var obj = flag(this, 'object'); - if (flag(this, 'doLength')) { + var obj = flag(this, 'object') + , doLength = flag(this, 'doLength'); + + if (doLength) { new Assertion(obj, msg).to.have.property('length'); + } else { + new Assertion(obj, msg).is.a('number'); + } + + if (_.type(n) !== 'number') { + throw new Error('the argument to above must be a number'); + } + + if (doLength) { var len = obj.length; this.assert( len > n @@ -694,9 +705,20 @@ module.exports = function (chai, _) { function assertLeast (n, msg) { if (msg) flag(this, 'message', msg); - var obj = flag(this, 'object'); - if (flag(this, 'doLength')) { + var obj = flag(this, 'object') + , doLength = flag(this, 'doLength'); + + if (doLength) { new Assertion(obj, msg).to.have.property('length'); + } else { + new Assertion(obj, msg).is.a('number'); + } + + if (_.type(n) !== 'number') { + throw new Error('the argument to least must be a number'); + } + + if (doLength) { var len = obj.length; this.assert( len >= n @@ -743,9 +765,20 @@ module.exports = function (chai, _) { function assertBelow (n, msg) { if (msg) flag(this, 'message', msg); - var obj = flag(this, 'object'); - if (flag(this, 'doLength')) { + var obj = flag(this, 'object') + , doLength = flag(this, 'doLength'); + + if (doLength) { new Assertion(obj, msg).to.have.property('length'); + } else { + new Assertion(obj, msg).is.a('number'); + } + + if (_.type(n) !== 'number') { + throw new Error('the argument to below must be a number'); + } + + if (doLength) { var len = obj.length; this.assert( len < n @@ -792,9 +825,20 @@ module.exports = function (chai, _) { function assertMost (n, msg) { if (msg) flag(this, 'message', msg); - var obj = flag(this, 'object'); - if (flag(this, 'doLength')) { + var obj = flag(this, 'object') + , doLength = flag(this, 'doLength'); + + if (doLength) { new Assertion(obj, msg).to.have.property('length'); + } else { + new Assertion(obj, msg).is.a('number'); + } + + if (_.type(n) !== 'number') { + throw new Error('the argument to most must be a number'); + } + + if (doLength) { var len = obj.length; this.assert( len <= n @@ -841,9 +885,20 @@ 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')) { + , range = start + '..' + finish + , doLength = flag(this, 'doLength'); + + if (doLength) { new Assertion(obj, msg).to.have.property('length'); + } else { + new Assertion(obj, msg).is.a('number'); + } + + if (_.type(start) !== 'number' || _.type(finish) !== 'number') { + throw new Error('the arguments to within must be numbers'); + } + + if (doLength) { var len = obj.length; this.assert( len >= start && len <= finish diff --git a/test/assert.js b/test/assert.js index 79843ff07..449a1789c 100644 --- a/test/assert.js +++ b/test/assert.js @@ -1676,6 +1676,14 @@ describe('assert', function () { err(function() { assert.isAbove(1, 1); }, 'expected 1 to be above 1'); + + err(function() { + assert.isAbove(null, 1); + }, 'expected null to be a number'); + + err(function() { + assert.isAbove(1, null); + }, 'the argument to above must be a number'); }); it('atLeast', function() { @@ -1698,6 +1706,13 @@ describe('assert', function () { assert.isBelow(1, 1); }, 'expected 1 to be below 1'); + err(function() { + assert.isBelow(null, 1); + }, 'expected null to be a number'); + + err(function() { + assert.isBelow(1, null); + }, 'the argument to below must be a number'); }); it('atMost', function() { diff --git a/test/expect.js b/test/expect.js index 62597fc50..14e6ec2f7 100644 --- a/test/expect.js +++ b/test/expect.js @@ -217,6 +217,34 @@ describe('expect', function () { err(function () { expect([ 1, 2, 3 ]).to.have.length.within(5,7, 'blah'); }, "blah: expected [ 1, 2, 3 ] to have a length within 5..7"); + + err(function () { + expect(null).to.be.within(0, 1, 'blah'); + }, "blah: expected null to be a number"); + + err(function () { + expect(1).to.be.within(null, 1, 'blah'); + }, "the arguments to within must be numbers"); + + err(function () { + expect(1).to.be.within(0, null, 'blah'); + }, "the arguments to within must be numbers"); + + err(function () { + expect(null).to.not.be.within(0, 1, 'blah'); + }, "blah: expected null to be a number"); + + err(function () { + expect(1).to.not.be.within(null, 1, 'blah'); + }, "the arguments to within must be numbers"); + + err(function () { + expect(1).to.not.be.within(0, null, 'blah'); + }, "the arguments to within must be numbers"); + + err(function () { + expect(1).to.have.length.within(5,7, 'blah'); + }, "blah: expected 1 to have a property 'length'"); }); it('above(n)', function(){ @@ -242,6 +270,26 @@ describe('expect', function () { err(function () { expect([ 1, 2, 3 ]).to.have.length.above(4, 'blah'); }, "blah: expected [ 1, 2, 3 ] to have a length above 4 but got 3"); + + err(function () { + expect(null).to.be.above(0, 'blah'); + }, "blah: expected null to be a number"); + + err(function () { + expect(1).to.be.above(null, 'blah'); + }, "the argument to above must be a number"); + + err(function () { + expect(null).to.not.be.above(0, 'blah'); + }, "blah: expected null to be a number"); + + err(function () { + expect(1).to.not.be.above(null, 'blah'); + }, "the argument to above must be a number"); + + err(function () { + expect(1).to.have.length.above(0, 'blah'); + }, "blah: expected 1 to have a property 'length'"); }); it('least(n)', function(){ @@ -270,6 +318,26 @@ describe('expect', function () { err(function () { expect([ 1, 2, 3, 4 ]).to.not.have.length.of.at.least(4, 'blah'); }, "blah: expected [ 1, 2, 3, 4 ] to have a length below 4"); + + err(function () { + expect(null).to.be.at.least(0, 'blah'); + }, "blah: expected null to be a number"); + + err(function () { + expect(1).to.be.at.least(null, 'blah'); + }, "the argument to least must be a number"); + + err(function () { + expect(null).to.not.be.at.least(0, 'blah'); + }, "blah: expected null to be a number"); + + err(function () { + expect(1).to.not.be.at.least(null, 'blah'); + }, "the argument to least must be a number"); + + err(function () { + expect(1).to.have.length.at.least(0, 'blah'); + }, "blah: expected 1 to have a property 'length'"); }); it('below(n)', function(){ @@ -295,6 +363,26 @@ describe('expect', function () { err(function () { expect([ 1, 2, 3 ]).to.have.length.below(2, 'blah'); }, "blah: expected [ 1, 2, 3 ] to have a length below 2 but got 3"); + + err(function () { + expect(null).to.be.below(0, 'blah'); + }, "blah: expected null to be a number"); + + err(function () { + expect(1).to.be.below(null, 'blah'); + }, "the argument to below must be a number"); + + err(function () { + expect(null).to.not.be.below(0, 'blah'); + }, "blah: expected null to be a number"); + + err(function () { + expect(1).to.not.be.below(null, 'blah'); + }, "the argument to below must be a number"); + + err(function () { + expect(1).to.have.length.below(0, 'blah'); + }, "blah: expected 1 to have a property 'length'"); }); it('most(n)', function(){ @@ -324,6 +412,26 @@ describe('expect', function () { err(function () { expect([ 1, 2 ]).to.not.have.length.of.at.most(2, 'blah'); }, "blah: expected [ 1, 2 ] to have a length above 2"); + + err(function () { + expect(null).to.be.at.most(0, 'blah'); + }, "blah: expected null to be a number"); + + err(function () { + expect(1).to.be.at.most(null, 'blah'); + }, "the argument to most must be a number"); + + err(function () { + expect(null).to.not.be.at.most(0, 'blah'); + }, "blah: expected null to be a number"); + + err(function () { + expect(1).to.not.be.at.most(null, 'blah'); + }, "the argument to most must be a number"); + + err(function () { + expect(1).to.have.length.of.at.most(0, 'blah'); + }, "blah: expected 1 to have a property 'length'"); }); it('match(regexp)', function(){ diff --git a/test/should.js b/test/should.js index da1c00a8a..4995264f9 100644 --- a/test/should.js +++ b/test/should.js @@ -261,6 +261,34 @@ describe('should', function() { err(function(){ ({ foo: 1 }).should.have.length.within(50,100, 'blah'); }, "blah: expected { foo: 1 } to have a property 'length'"); + + err(function () { + ('string').should.be.within(0, 1, 'blah'); + }, "blah: expected 'string' to be a number"); + + err(function () { + (1).should.be.within(null, 1, 'blah'); + }, "the arguments to within must be numbers"); + + err(function () { + (1).should.be.within(0, null, 'blah'); + }, "the arguments to within must be numbers"); + + err(function () { + ('string').should.not.be.within(0, 1, 'blah'); + }, "blah: expected 'string' to be a number"); + + err(function () { + (1).should.not.be.within(null, 1, 'blah'); + }, "the arguments to within must be numbers"); + + err(function () { + (1).should.not.be.within(0, null, 'blah'); + }, "the arguments to within must be numbers"); + + err(function () { + (1).should.have.length.within(5,7, 'blah'); + }, "blah: expected 1 to have a property 'length'"); }); it('above(n)', function(){ @@ -280,6 +308,26 @@ describe('should', function() { err(function(){ ({foo: 1}).should.have.length.above(3, 'blah'); }, "blah: expected { foo: 1 } to have a property 'length'"); + + err(function () { + ('string').should.be.above(0, 'blah'); + }, "blah: expected 'string' to be a number"); + + err(function () { + (1).should.be.above(null, 'blah'); + }, "the argument to above must be a number"); + + err(function () { + ('string').should.not.be.above(0, 'blah'); + }, "blah: expected 'string' to be a number"); + + err(function () { + (1).should.not.be.above(null, 'blah'); + }, "the argument to above must be a number"); + + err(function () { + (1).should.have.length.above(0, 'blah'); + }, "blah: expected 1 to have a property 'length'"); }); it('least(n)', function(){ @@ -297,6 +345,22 @@ describe('should', function() { err(function(){ ({foo: 1}).should.have.length.of.at.least(3, 'blah'); }, "blah: expected { foo: 1 } to have a property 'length'"); + + err(function () { + ('string').should.be.at.least(0, 'blah'); + }, "blah: expected 'string' to be a number"); + + err(function () { + (1).should.be.at.least(null, 'blah'); + }, "the argument to least must be a number"); + + err(function () { + ('string').should.not.be.at.least(0, 'blah'); + }, "blah: expected 'string' to be a number"); + + err(function () { + (1).should.not.be.at.least(null, 'blah'); + }, "the argument to least must be a number"); }); it('below(n)', function(){ @@ -316,6 +380,26 @@ describe('should', function() { err(function(){ ({foo: 1}).should.have.length.below(3, 'blah'); }, "blah: expected { foo: 1 } to have a property 'length'"); + + err(function () { + ('string').should.be.below(0, 'blah'); + }, "blah: expected 'string' to be a number"); + + err(function () { + (1).should.be.below(null, 'blah'); + }, "the argument to below must be a number"); + + err(function () { + ('string').should.not.be.below(0, 'blah'); + }, "blah: expected 'string' to be a number"); + + err(function () { + (1).should.not.be.below(null, 'blah'); + }, "the argument to below must be a number"); + + err(function () { + (1).should.have.length.below(0, 'blah'); + }, "blah: expected 1 to have a property 'length'"); }); it('most(n)', function(){ @@ -333,6 +417,26 @@ describe('should', function() { err(function(){ ({foo: 1}).should.have.length.of.at.most(3, 'blah'); }, "blah: expected { foo: 1 } to have a property 'length'"); + + err(function () { + ('string').should.be.at.most(0, 'blah'); + }, "blah: expected 'string' to be a number"); + + err(function () { + (1).should.be.at.most(null, 'blah'); + }, "the argument to most must be a number"); + + err(function () { + ('string').should.not.be.at.most(0, 'blah'); + }, "blah: expected 'string' to be a number"); + + err(function () { + (1).should.not.be.at.most(null, 'blah'); + }, "the argument to most must be a number"); + + err(function () { + (1).should.have.length.of.at.most(0, 'blah'); + }, "blah: expected 1 to have a property 'length'"); }); it('match(regexp)', function(){ From 62309d38c49ca134ea9e69fa9043e091ee53c340 Mon Sep 17 00:00:00 2001 From: Lucas Vieira Date: Sun, 1 May 2016 13:16:54 -0300 Subject: [PATCH 18/19] Documentation update to indicate type requirements --- lib/chai/core/assertions.js | 25 +++++++++++++++---------- 1 file changed, 15 insertions(+), 10 deletions(-) diff --git a/lib/chai/core/assertions.js b/lib/chai/core/assertions.js index 5050d62e1..ab6a9f648 100644 --- a/lib/chai/core/assertions.js +++ b/lib/chai/core/assertions.js @@ -622,14 +622,15 @@ module.exports = function (chai, _) { /** * ### .above(value) * - * Asserts that the target is greater than `value`. + * Asserts that the number target is greater than `value`. * * expect(10).to.be.above(5); * * 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. + * was supplied directly. In this case the target must + * have a length property. * * expect('foo').to.have.length.above(2); * expect([ 1, 2, 3 ]).to.have.length.above(2); @@ -683,14 +684,15 @@ module.exports = function (chai, _) { /** * ### .least(value) * - * Asserts that the target is greater than or equal to `value`. + * Asserts that the number target is greater than or equal to `value`. * * expect(10).to.be.at.least(10); * * 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. + * was supplied directly. In this case the target must + * have a length property. * * expect('foo').to.have.length.of.at.least(2); * expect([ 1, 2, 3 ]).to.have.length.of.at.least(3); @@ -742,14 +744,15 @@ module.exports = function (chai, _) { /** * ### .below(value) * - * Asserts that the target is less than `value`. + * Asserts that the number target is less than `value`. * * expect(5).to.be.below(10); * * 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. + * was supplied directly. In this case the target must + * have a length property. * * expect('foo').to.have.length.below(4); * expect([ 1, 2, 3 ]).to.have.length.below(4); @@ -803,14 +806,15 @@ module.exports = function (chai, _) { /** * ### .most(value) * - * Asserts that the target is less than or equal to `value`. + * Asserts that the number target is less than or equal to `value`. * * expect(5).to.be.at.most(5); * * 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. + * was supplied directly. In this case the target must + * have a length property. * * expect('foo').to.have.length.of.at.most(4); * expect([ 1, 2, 3 ]).to.have.length.of.at.most(3); @@ -862,14 +866,15 @@ module.exports = function (chai, _) { /** * ### .within(start, finish) * - * Asserts that the target is within a range. + * Asserts that the number target is within a range of numbers. * * expect(7).to.be.within(5,10); * * 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. + * was supplied directly. In this case the target must + * have a length property. * * expect('foo').to.have.length.within(2,4); * expect([ 1, 2, 3 ]).to.have.length.within(2,4); From 4a009decbf4c1c551119ae1404271127ee03afd3 Mon Sep 17 00:00:00 2001 From: Grant Snodgrass Date: Sun, 4 Sep 2016 05:10:33 -0400 Subject: [PATCH 19/19] Fix broken test after rebasing 4.x.x branch --- test/utilities.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/utilities.js b/test/utilities.js index 38c6c5d8a..7201eef6f 100644 --- a/test/utilities.js +++ b/test/utilities.js @@ -322,7 +322,7 @@ describe('utilities', function () { var dneFail = expect('something'); var dneError; - try { dneFail.to.doesnotexistfail(); } + try { dneFail.doesnotexistfail(); } catch (e) { dneError = e; } expect(dneFail.__flags).to.have.property('doesnt'); expect(dneError.message).to.eql('doesnotexistfail is not a function');