From 3ee4ac5bf93374b91df43ea6cac72d65009d8154 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kristj=C3=A1n=20Oddsson?= Date: Tue, 20 Feb 2024 21:24:36 +0100 Subject: [PATCH 01/13] Add `numeric` assertion --- lib/chai/core/assertions.js | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/lib/chai/core/assertions.js b/lib/chai/core/assertions.js index 4da99927..c1cf3ab9 100644 --- a/lib/chai/core/assertions.js +++ b/lib/chai/core/assertions.js @@ -233,7 +233,6 @@ Assertion.addProperty('any', function () { * @namespace BDD * @api public */ - Assertion.addProperty('all', function () { flag(this, 'all', true); flag(this, 'any', false); @@ -672,6 +671,17 @@ Assertion.addProperty('true', function () { ); }); +Assertion.addProperty('numeric', function () { + const object = flag(this, 'object'); + + this.assert( + ['Number', 'BigInt'].includes(_.type(object)) + , 'expected #{this} to be numeric' + , 'expected #{this} to not be numeric' + , flag(this, 'negate') ? false : true + ); +}); + /** * ### .false * From 339274df6932e1d128c7a36e50d2ad6d51feb9db Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kristj=C3=A1n=20Oddsson?= Date: Tue, 20 Feb 2024 21:27:50 +0100 Subject: [PATCH 02/13] Use `numeric` assertion in `approximately` --- lib/chai/core/assertions.js | 13 +++---------- test/assert.js | 16 ++++++++-------- test/expect.js | 22 +++++++++++----------- test/should.js | 16 ++++++++-------- 4 files changed, 30 insertions(+), 37 deletions(-) diff --git a/lib/chai/core/assertions.js b/lib/chai/core/assertions.js index c1cf3ab9..fd5f1db6 100644 --- a/lib/chai/core/assertions.js +++ b/lib/chai/core/assertions.js @@ -2972,16 +2972,9 @@ function closeTo(expected, delta, msg) { , flagMsg = flag(this, 'message') , ssfi = flag(this, 'ssfi'); - new Assertion(obj, flagMsg, ssfi, true).is.a('number'); - if (typeof expected !== 'number' || typeof delta !== 'number') { - flagMsg = flagMsg ? flagMsg + ': ' : ''; - var deltaMessage = delta === undefined ? ", and a delta is required" : ""; - throw new AssertionError( - flagMsg + 'the arguments to closeTo or approximately must be numbers' + deltaMessage, - undefined, - ssfi - ); - } + new Assertion(obj, flagMsg, ssfi, true).is.numeric; + new Assertion(delta, flagMsg, ssfi, true).is.numeric; + new Assertion(expected, flagMsg, ssfi, true).is.numeric; this.assert( Math.abs(obj - expected) <= delta diff --git a/test/assert.js b/test/assert.js index 8462fd5e..149cc5e9 100644 --- a/test/assert.js +++ b/test/assert.js @@ -1866,19 +1866,19 @@ describe('assert', function () { err(function() { assert.closeTo([1.5], 1.0, 0.5, 'blah'); - }, "blah: expected [ 1.5 ] to be a number"); + }, "blah: expected [ 1.5 ] to be numeric"); err(function() { assert.closeTo(1.5, "1.0", 0.5, 'blah'); - }, "blah: the arguments to closeTo or approximately must be numbers"); + }, "blah: expected '1.0' to be numeric"); err(function() { assert.closeTo(1.5, 1.0, true, 'blah'); - }, "blah: the arguments to closeTo or approximately must be numbers"); + }, "blah: expected true to be numeric"); err(function() { assert.closeTo(1.5, 1.0, undefined, 'blah'); - }, "blah: the arguments to closeTo or approximately must be numbers, and a delta is required"); + }, "blah: expected undefined to be numeric"); }); it('approximately', function(){ @@ -1896,19 +1896,19 @@ describe('assert', function () { err(function() { assert.approximately([1.5], 1.0, 0.5); - }, "expected [ 1.5 ] to be a number"); + }, "expected [ 1.5 ] to be numeric"); err(function() { assert.approximately(1.5, "1.0", 0.5, 'blah'); - }, "blah: the arguments to closeTo or approximately must be numbers"); + }, "blah: expected '1.0' to be numeric"); err(function() { assert.approximately(1.5, 1.0, true, 'blah'); - }, "blah: the arguments to closeTo or approximately must be numbers"); + }, "blah: expected true to be numeric"); err(function() { assert.approximately(1.5, 1.0, undefined, 'blah'); - }, "blah: the arguments to closeTo or approximately must be numbers, and a delta is required"); + }, "blah: expected undefined to be numeric"); }); it('sameMembers', function() { diff --git a/test/expect.js b/test/expect.js index 2dd08ef2..c7aeef0a 100644 --- a/test/expect.js +++ b/test/expect.js @@ -3267,31 +3267,31 @@ describe('expect', function () { err(function() { expect([1.5]).to.be.closeTo(1.0, 0.5, 'blah'); - }, "blah: expected [ 1.5 ] to be a number"); + }, "blah: expected [ 1.5 ] to be numeric"); err(function() { expect([1.5], 'blah').to.be.closeTo(1.0, 0.5); - }, "blah: expected [ 1.5 ] to be a number"); + }, "blah: expected [ 1.5 ] to be numeric"); err(function() { expect(1.5).to.be.closeTo("1.0", 0.5, 'blah'); - }, "blah: the arguments to closeTo or approximately must be numbers"); + }, "blah: expected '1.0' to be numeric"); err(function() { expect(1.5, 'blah').to.be.closeTo("1.0", 0.5); - }, "blah: the arguments to closeTo or approximately must be numbers"); + }, "blah: expected '1.0' to be numeric"); err(function() { expect(1.5).to.be.closeTo(1.0, true, 'blah'); - }, "blah: the arguments to closeTo or approximately must be numbers"); + }, "blah: expected true to be numeric"); err(function() { expect(1.5, 'blah').to.be.closeTo(1.0, true); - }, "blah: the arguments to closeTo or approximately must be numbers"); + }, "blah: expected true to be numeric"); err(function() { expect(1.5, 'blah').to.be.closeTo(1.0); - }, "blah: the arguments to closeTo or approximately must be numbers, and a delta is required"); + }, "blah: expected undefined to be numeric"); }); it('approximately', function(){ @@ -3309,19 +3309,19 @@ describe('expect', function () { err(function() { expect([1.5]).to.be.approximately(1.0, 0.5); - }, "expected [ 1.5 ] to be a number"); + }, "expected [ 1.5 ] to be numeric"); err(function() { expect(1.5).to.be.approximately("1.0", 0.5); - }, "the arguments to closeTo or approximately must be numbers"); + }, "expected '1.0' to be numeric"); err(function() { expect(1.5).to.be.approximately(1.0, true); - }, "the arguments to closeTo or approximately must be numbers"); + }, "expected true to be numeric"); err(function() { expect(1.5).to.be.approximately(1.0); - }, "the arguments to closeTo or approximately must be numbers, and a delta is required"); + }, "expected undefined to be numeric"); }); it('oneOf', function() { diff --git a/test/should.js b/test/should.js index 17429961..983b15d8 100644 --- a/test/should.js +++ b/test/should.js @@ -2823,19 +2823,19 @@ describe('should', function() { err(function() { [1.5].should.be.closeTo(1.0, 0.5, 'blah'); - }, "blah: expected [ 1.5 ] to be a number"); + }, "blah: expected [ 1.5 ] to be numeric"); err(function() { (1.5).should.be.closeTo("1.0", 0.5, 'blah'); - }, "blah: the arguments to closeTo or approximately must be numbers"); + }, "blah: expected '1.0' to be numeric"); err(function() { (1.5).should.be.closeTo(1.0, true, 'blah'); - }, "blah: the arguments to closeTo or approximately must be numbers"); + }, "blah: expected true to be numeric"); err(function() { (1.5).should.be.closeTo(1.0, undefined, 'blah'); - }, "blah: the arguments to closeTo or approximately must be numbers, and a delta is required"); + }, "blah: expected undefined to be numeric"); }); it('approximately', function(){ @@ -2847,19 +2847,19 @@ describe('should', function() { err(function() { [1.5].should.be.approximately(1.0, 0.5); - }, "expected [ 1.5 ] to be a number"); + }, "expected [ 1.5 ] to be numeric"); err(function() { (1.5).should.be.approximately("1.0", 0.5); - }, "the arguments to closeTo or approximately must be numbers"); + }, "expected '1.0' to be numeric"); err(function() { (1.5).should.be.approximately(1.0, true); - }, "the arguments to closeTo or approximately must be numbers"); + }, "expected true to be numeric"); err(function() { (1.5).should.be.approximately(1.0); - }, "the arguments to closeTo or approximately must be numbers, and a delta is required"); + }, "expected undefined to be numeric"); }); it('include.members', function() { From 1bd4f750e589cbfac7ee0b11d4f71868cd8b87fb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kristj=C3=A1n=20Oddsson?= Date: Tue, 20 Feb 2024 21:30:24 +0100 Subject: [PATCH 03/13] Use home-made `abs` to support BigInt in `approximately` --- lib/chai/core/assertions.js | 4 +++- test/assert.js | 1 + 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/lib/chai/core/assertions.js b/lib/chai/core/assertions.js index fd5f1db6..ba40db18 100644 --- a/lib/chai/core/assertions.js +++ b/lib/chai/core/assertions.js @@ -2976,8 +2976,10 @@ function closeTo(expected, delta, msg) { new Assertion(delta, flagMsg, ssfi, true).is.numeric; new Assertion(expected, flagMsg, ssfi, true).is.numeric; + const abs = (x) => x < 0n ? -x : x; + this.assert( - Math.abs(obj - expected) <= delta + abs(obj - expected) <= delta , 'expected #{this} to be close to ' + expected + ' +/- ' + delta , 'expected #{this} not to be close to ' + expected + ' +/- ' + delta ); diff --git a/test/assert.js b/test/assert.js index 149cc5e9..e77a333c 100644 --- a/test/assert.js +++ b/test/assert.js @@ -1885,6 +1885,7 @@ describe('assert', function () { assert.approximately(1.5, 1.0, 0.5); assert.approximately(10, 20, 20); assert.approximately(-10, 20, 30); + assert.approximately(1n, 2n, 1n); err(function(){ assert.approximately(2, 1.0, 0.5, 'blah'); From a4dab5353c739b2ea8cd5827eb6df585a769fb72 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kristj=C3=A1n=20Oddsson?= Date: Sat, 7 Sep 2024 13:41:21 +0000 Subject: [PATCH 04/13] support bigint in "above" assertion --- lib/chai/core/assertions.js | 22 ++++++++-------------- test/assert.js | 4 ++++ 2 files changed, 12 insertions(+), 14 deletions(-) diff --git a/lib/chai/core/assertions.js b/lib/chai/core/assertions.js index ba40db18..89f0af28 100644 --- a/lib/chai/core/assertions.js +++ b/lib/chai/core/assertions.js @@ -1169,27 +1169,21 @@ function assertAbove (n, msg) { , msgPrefix = ((flagMsg) ? flagMsg + ': ' : '') , ssfi = flag(this, 'ssfi') , objType = _.type(obj).toLowerCase() - , nType = _.type(n).toLowerCase() - , errorMessage - , shouldThrow = true; + , nType = _.type(n).toLowerCase(); if (doLength && objType !== 'map' && objType !== 'set') { new Assertion(obj, flagMsg, ssfi, true).to.have.property('length'); } + const isNumeric = x => ['Number', 'BigInt'].includes(_.type(x)) + if (!doLength && (objType === 'date' && nType !== 'date')) { - errorMessage = msgPrefix + 'the argument to above must be a date'; - } else if (nType !== 'number' && (doLength || objType === 'number')) { - errorMessage = msgPrefix + 'the argument to above must be a number'; - } else if (!doLength && (objType !== 'date' && objType !== 'number')) { + throw new AssertionError(msgPrefix + 'the argument to above must be a date', undefined, ssfi); + } else if (!isNumeric(n) && (doLength || isNumeric(obj))) { + throw new AssertionError(msgPrefix + 'the argument to above must be a number', undefined, ssfi); + } else if (!doLength && (objType !== 'date' && !isNumeric(obj))) { var printObj = (objType === 'string') ? "'" + obj + "'" : obj; - errorMessage = msgPrefix + 'expected ' + printObj + ' to be a number or a date'; - } else { - shouldThrow = false; - } - - if (shouldThrow) { - throw new AssertionError(errorMessage, undefined, ssfi); + throw new AssertionError(msgPrefix + 'expected ' + printObj + ' to be a number or a date', undefined, ssfi); } if (doLength) { diff --git a/test/assert.js b/test/assert.js index e77a333c..74e88be6 100644 --- a/test/assert.js +++ b/test/assert.js @@ -2134,6 +2134,10 @@ describe('assert', function () { it('above', function() { assert.isAbove(5, 2, '5 should be above 2'); + assert.isAbove(5n, 2, '5 should be above 2'); + assert.isAbove(5, 2n, '5 should be above 2'); + assert.isAbove(5n, 2n, '5 should be above 2'); + assert.isAbove(9007199254740994n, 2, '9007199254740994 should be above 2'); err(function() { assert.isAbove(1, 3, 'blah'); From ae27a8980ffaf140a50e5ee8b7f64879c1d0de12 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kristj=C3=A1n=20Oddsson?= Date: Sat, 7 Sep 2024 13:46:33 +0000 Subject: [PATCH 05/13] add bigint test for typeOf --- test/assert.js | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/test/assert.js b/test/assert.js index 74e88be6..f575af46 100644 --- a/test/assert.js +++ b/test/assert.js @@ -144,6 +144,23 @@ describe('assert', function () { assert.typeOf(Symbol(), 'symbol'); } + assert.typeOf(5n, 'bigint'); + + assert.typeOf(() => {}, 'function'); + assert.typeOf(function() {}, 'function'); + assert.typeOf(async function() {}, 'asyncfunction'); + assert.typeOf(function*() {}, 'generatorfunction'); + assert.typeOf(async function*() {}, 'asyncgeneratorfunction'); + assert.typeOf(Symbol(), 'symbol'); + + err(function () { + assert.typeOf(5, 'function', 'blah'); + }, "blah: expected 5 to be a function"); + + err(function () { + assert.typeOf(function() {}, 'asyncfunction', 'blah'); + }, "blah: expected [Function] to be an asyncfunction"); + err(function () { assert.typeOf(5, 'string', 'blah'); }, "blah: expected 5 to be a string"); From 9cbb1e2034054156468449205e424f819e49165c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kristj=C3=A1n=20Oddsson?= Date: Sat, 7 Sep 2024 13:52:55 +0000 Subject: [PATCH 06/13] add isNumeric and isNotNumeric to assert.js --- lib/chai/interface/assert.js | 39 ++++++++++++++++++++++++++++++++++++ test/assert.js | 21 +++++++++++++++++++ 2 files changed, 60 insertions(+) diff --git a/lib/chai/interface/assert.js b/lib/chai/interface/assert.js index 77af8ead..380ed592 100644 --- a/lib/chai/interface/assert.js +++ b/lib/chai/interface/assert.js @@ -744,6 +744,45 @@ assert.isNotNumber = function (val, msg) { new Assertion(val, msg, assert.isNotNumber, true).to.not.be.a('number'); }; +/** + * ### .isNumeric(value, [message]) + * + * Asserts that `value` is a number or BigInt. + * + * var cups = 2; + * assert.isNumeric(cups, 'how many cups'); + * + * var cups = 10n; + * assert.isNumeric(cups, 'how many cups'); + * + * @name isNumeric + * @param {unknown} val + * @param {string} msg + * @namespace Assert + * @public + */ +assert.isNumeric = function (val, msg) { + new Assertion(val, msg, assert.isNumeric, true).is.numeric; +}; + +/** + * ### .isNotNumeric(value, [message]) + * + * Asserts that `value` is _not_ a number or BigInt. + * + * var cups = '2 cups please'; + * assert.isNotNumeric(cups, 'how many cups'); + * + * @name isNotNumeric + * @param {unknown} val + * @param {string} msg + * @namespace Assert + * @public + */ +assert.isNotNumeric = function (val, msg) { + new Assertion(val, msg, assert.isNotNumeric, true).is.not.numeric; +}; + /** * ### .isFinite(value, [message]) * diff --git a/test/assert.js b/test/assert.js index f575af46..224f648c 100644 --- a/test/assert.js +++ b/test/assert.js @@ -612,6 +612,27 @@ describe('assert', function () { }, "blah: expected 4 not to be a number"); }); + + it('isNumeric', function() { + assert.isNumeric(1); + assert.isNumeric(Number('3')); + assert.isNumeric(6n); + assert.isNumeric(BigInt(9)); + + err(function () { + assert.isNumeric('1', 'blah'); + }, "blah: expected \'1\' to be numeric"); + }); + + it('isNotNumeric', function () { + assert.isNotNumeric('hello'); + assert.isNotNumeric([ 5 ]); + + err(function () { + assert.isNotNumeric(4, 'blah'); + }, "blah: expected 4 to not be numeric"); + }); + it('isFinite', function() { assert.isFinite(4); assert.isFinite(-10); From 0398d045ebc28999dc39b9c7fbfbecdd9237cbd4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kristj=C3=A1n=20Oddsson?= Date: Sat, 7 Sep 2024 13:57:48 +0000 Subject: [PATCH 07/13] support BigInt in `atLeast` --- lib/chai/core/assertions.js | 6 ++++-- test/assert.js | 2 ++ 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/lib/chai/core/assertions.js b/lib/chai/core/assertions.js index 89f0af28..d22de876 100644 --- a/lib/chai/core/assertions.js +++ b/lib/chai/core/assertions.js @@ -1276,11 +1276,13 @@ function assertLeast (n, msg) { new Assertion(obj, flagMsg, ssfi, true).to.have.property('length'); } + const isNumeric = x => ['Number', 'BigInt'].includes(_.type(x)) + if (!doLength && (objType === 'date' && nType !== 'date')) { errorMessage = msgPrefix + 'the argument to least must be a date'; - } else if (nType !== 'number' && (doLength || objType === 'number')) { + } else if (!isNumeric(n) && (doLength || isNumeric(obj))) { errorMessage = msgPrefix + 'the argument to least must be a number'; - } else if (!doLength && (objType !== 'date' && objType !== 'number')) { + } else if (!doLength && (objType !== 'date' && !isNumeric(obj))) { var printObj = (objType === 'string') ? "'" + obj + "'" : obj; errorMessage = msgPrefix + 'expected ' + printObj + ' to be a number or a date'; } else { diff --git a/test/assert.js b/test/assert.js index 224f648c..6445a1c2 100644 --- a/test/assert.js +++ b/test/assert.js @@ -2227,6 +2227,8 @@ describe('assert', function () { it('atLeast', function() { assert.isAtLeast(5, 2, '5 should be above 2'); assert.isAtLeast(1, 1, '1 should be equal to 1'); + assert.isAtLeast(5n, 2, '5 should be above 2'); + assert.isAtLeast(1, 1n, '1 should be equal to 1'); err(function() { assert.isAtLeast(1, 3, 'blah'); From 8dde6dcfad6a3bd16d4f51a82dfdffdc27f4f64e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kristj=C3=A1n=20Oddsson?= Date: Sat, 7 Sep 2024 13:59:58 +0000 Subject: [PATCH 08/13] support bigint in `below` --- lib/chai/core/assertions.js | 14 ++++++++------ test/assert.js | 3 +++ 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/lib/chai/core/assertions.js b/lib/chai/core/assertions.js index d22de876..19ae165f 100644 --- a/lib/chai/core/assertions.js +++ b/lib/chai/core/assertions.js @@ -1254,8 +1254,8 @@ Assertion.addMethod('greaterThan', assertAbove); * @name least * @alias gte * @alias greaterThanOrEqual - * @param {Number} n - * @param {String} msg _optional_ + * @param {unknown} n + * @param {string} msg _optional_ * @namespace BDD * @api public */ @@ -1360,8 +1360,8 @@ Assertion.addMethod('greaterThanOrEqual', assertLeast); * @name below * @alias lt * @alias lessThan - * @param {Number} n - * @param {String} msg _optional_ + * @param {unknown} n + * @param {string} msg _optional_ * @namespace BDD * @api public */ @@ -1381,12 +1381,14 @@ function assertBelow (n, msg) { if (doLength && objType !== 'map' && objType !== 'set') { new Assertion(obj, flagMsg, ssfi, true).to.have.property('length'); } + + const isNumeric = x => ['Number', 'BigInt'].includes(_.type(x)) if (!doLength && (objType === 'date' && nType !== 'date')) { errorMessage = msgPrefix + 'the argument to below must be a date'; - } else if (nType !== 'number' && (doLength || objType === 'number')) { + } else if (!isNumeric(n) && (doLength || isNumeric(obj))) { errorMessage = msgPrefix + 'the argument to below must be a number'; - } else if (!doLength && (objType !== 'date' && objType !== 'number')) { + } else if (!doLength && (objType !== 'date' && !isNumeric(obj))) { var printObj = (objType === 'string') ? "'" + obj + "'" : obj; errorMessage = msgPrefix + 'expected ' + printObj + ' to be a number or a date'; } else { diff --git a/test/assert.js b/test/assert.js index 6445a1c2..dc4b108f 100644 --- a/test/assert.js +++ b/test/assert.js @@ -2274,6 +2274,9 @@ describe('assert', function () { it('below', function() { assert.isBelow(2, 5, '2 should be below 5'); + assert.isBelow(2, 5n, '2 should be below 5'); + assert.isBelow(2n, 5, '2 should be below 5'); + assert.isBelow(2n, 5n, '2 should be below 5'); err(function() { assert.isBelow(3, 1, 'blah'); From 90e3ae7c8887c5dd4361f0c0a41809be5e8afd62 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kristj=C3=A1n=20Oddsson?= Date: Sat, 7 Sep 2024 14:01:49 +0000 Subject: [PATCH 09/13] add support for bigint in `atMost` --- lib/chai/core/assertions.js | 10 ++++++---- test/assert.js | 2 ++ 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/lib/chai/core/assertions.js b/lib/chai/core/assertions.js index 19ae165f..75d19b23 100644 --- a/lib/chai/core/assertions.js +++ b/lib/chai/core/assertions.js @@ -1467,8 +1467,8 @@ Assertion.addMethod('lessThan', assertBelow); * @name most * @alias lte * @alias lessThanOrEqual - * @param {Number} n - * @param {String} msg _optional_ + * @param {unknown} n + * @param {string} msg _optional_ * @namespace BDD * @api public */ @@ -1489,11 +1489,13 @@ function assertMost (n, msg) { new Assertion(obj, flagMsg, ssfi, true).to.have.property('length'); } + const isNumeric = x => ['Number', 'BigInt'].includes(_.type(x)) + if (!doLength && (objType === 'date' && nType !== 'date')) { errorMessage = msgPrefix + 'the argument to most must be a date'; - } else if (nType !== 'number' && (doLength || objType === 'number')) { + } else if (!isNumeric(n) && (doLength || isNumeric(obj))) { errorMessage = msgPrefix + 'the argument to most must be a number'; - } else if (!doLength && (objType !== 'date' && objType !== 'number')) { + } else if (!doLength && (objType !== 'date' && !isNumeric(obj))) { var printObj = (objType === 'string') ? "'" + obj + "'" : obj; errorMessage = msgPrefix + 'expected ' + printObj + ' to be a number or a date'; } else { diff --git a/test/assert.js b/test/assert.js index dc4b108f..63d323b0 100644 --- a/test/assert.js +++ b/test/assert.js @@ -2328,6 +2328,8 @@ describe('assert', function () { it('atMost', function() { assert.isAtMost(2, 5, '2 should be below 5'); assert.isAtMost(1, 1, '1 should be equal to 1'); + assert.isAtMost(2n, 5, '2 should be below 5'); + assert.isAtMost(1, 1n, '1 should be equal to 1'); err(function() { assert.isAtMost(3, 1, 'blah'); From 3587410298e21d9aec2acc9a101e9dddc1480ea0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kristj=C3=A1n=20Oddsson?= Date: Sat, 7 Sep 2024 14:05:38 +0000 Subject: [PATCH 10/13] add bigint support to `within` --- lib/chai/core/assertions.js | 12 +++++++----- test/expect.js | 3 +++ 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/lib/chai/core/assertions.js b/lib/chai/core/assertions.js index 75d19b23..610b5934 100644 --- a/lib/chai/core/assertions.js +++ b/lib/chai/core/assertions.js @@ -1572,9 +1572,9 @@ Assertion.addMethod('lessThanOrEqual', assertMost); * expect(4, 'nooo why fail??').to.be.within(1, 3); * * @name within - * @param {Number} start lower bound inclusive - * @param {Number} finish upper bound inclusive - * @param {String} msg _optional_ + * @param {unknown} start lower bound inclusive + * @param {unknown} finish upper bound inclusive + * @param {string} msg _optional_ * @namespace BDD * @api public */ @@ -1598,12 +1598,14 @@ Assertion.addMethod('within', function (start, finish, msg) { if (doLength && objType !== 'map' && objType !== 'set') { new Assertion(obj, flagMsg, ssfi, true).to.have.property('length'); } + + const isNumeric = x => ['Number', 'BigInt'].includes(_.type(x)) if (!doLength && (objType === 'date' && (startType !== 'date' || finishType !== 'date'))) { errorMessage = msgPrefix + 'the arguments to within must be dates'; - } else if ((startType !== 'number' || finishType !== 'number') && (doLength || objType === 'number')) { + } else if ((!isNumeric(start) || !isNumeric(finish)) && (doLength || isNumeric(obj))) { errorMessage = msgPrefix + 'the arguments to within must be numbers'; - } else if (!doLength && (objType !== 'date' && objType !== 'number')) { + } else if (!doLength && (objType !== 'date' && !isNumeric(obj))) { var printObj = (objType === 'string') ? "'" + obj + "'" : obj; errorMessage = msgPrefix + 'expected ' + printObj + ' to be a number or a date'; } else { diff --git a/test/expect.js b/test/expect.js index c7aeef0a..18db7f93 100644 --- a/test/expect.js +++ b/test/expect.js @@ -479,6 +479,9 @@ describe('expect', function () { expect('foo').to.have.lengthOf.within(2, 4); expect([ 1, 2, 3 ]).to.have.length.within(2, 4); expect([ 1, 2, 3 ]).to.have.lengthOf.within(2, 4); + expect(5n).to.be.within(5, 10); + expect(5).to.be.within(3n, 6); + expect(5).to.be.within(3, 5n); err(function(){ expect(5).to.not.be.within(4, 6, 'blah'); From 45f8825b1bf1b2ab3dca9f74692f2a192bf78802 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kristj=C3=A1n=20Oddsson?= Date: Tue, 8 Oct 2024 08:45:03 +0000 Subject: [PATCH 11/13] update error message for `closeTo` --- lib/chai/core/assertions.js | 4 ++++ test/assert.js | 4 ++-- test/expect.js | 4 ++-- test/should.js | 4 ++-- 4 files changed, 10 insertions(+), 6 deletions(-) diff --git a/lib/chai/core/assertions.js b/lib/chai/core/assertions.js index f99557b1..81d74a35 100644 --- a/lib/chai/core/assertions.js +++ b/lib/chai/core/assertions.js @@ -3026,7 +3026,11 @@ function closeTo(expected, delta, msg) { , ssfi = flag(this, 'ssfi'); new Assertion(obj, flagMsg, ssfi, true).is.numeric; + let message = 'A `delta` value is required for `closeTo`'; + if (!delta) throw new AssertionError(flagMsg ? `${flagMsg}: ${message}` : message, undefined, ssfi); new Assertion(delta, flagMsg, ssfi, true).is.numeric; + message = 'A `expected` value is required for `closeTo`'; + if (!delta) throw new AssertionError(flagMsg ? `${flagMsg}: ${message}` : message, undefined, ssfi); new Assertion(expected, flagMsg, ssfi, true).is.numeric; const abs = (x) => x < 0n ? -x : x; diff --git a/test/assert.js b/test/assert.js index c5aee3ec..c1c528cb 100644 --- a/test/assert.js +++ b/test/assert.js @@ -1916,7 +1916,7 @@ describe('assert', function () { err(function() { assert.closeTo(1.5, 1.0, undefined, 'blah'); - }, "blah: expected undefined to be numeric"); + }, "blah: A `delta` value is required for `closeTo`"); }); it('approximately', function(){ @@ -1947,7 +1947,7 @@ describe('assert', function () { err(function() { assert.approximately(1.5, 1.0, undefined, 'blah'); - }, "blah: expected undefined to be numeric"); + }, "blah: A `delta` value is required for `closeTo`"); }); it('sameMembers', function() { diff --git a/test/expect.js b/test/expect.js index 67802145..c9549d77 100644 --- a/test/expect.js +++ b/test/expect.js @@ -3306,7 +3306,7 @@ describe('expect', function () { err(function() { expect(1.5, 'blah').to.be.closeTo(1.0); - }, "blah: expected undefined to be numeric"); + }, "blah: A `delta` value is required for `closeTo`"); }); it('approximately', function(){ @@ -3336,7 +3336,7 @@ describe('expect', function () { err(function() { expect(1.5).to.be.approximately(1.0); - }, "expected undefined to be numeric"); + }, "A `delta` value is required for `closeTo`"); }); it('oneOf', function() { diff --git a/test/should.js b/test/should.js index c47a5cd8..4b7fbc48 100644 --- a/test/should.js +++ b/test/should.js @@ -2768,7 +2768,7 @@ describe('should', function() { err(function() { (1.5).should.be.closeTo(1.0, undefined, 'blah'); - }, "blah: expected undefined to be numeric"); + }, "blah: A `delta` value is required for `closeTo`"); }); it('approximately', function(){ @@ -2792,7 +2792,7 @@ describe('should', function() { err(function() { (1.5).should.be.approximately(1.0); - }, "expected undefined to be numeric"); + }, "A `delta` value is required for `closeTo`"); }); it('include.members', function() { From 0005660b94e8b5b2eab79fb9b65d48f2f5fa420b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kristj=C3=A1n=20Oddsson?= Date: Wed, 9 Oct 2024 09:59:48 +0000 Subject: [PATCH 12/13] explicitly allow a delta of `0` --- lib/chai/core/assertions.js | 4 ++-- test/assert.js | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/lib/chai/core/assertions.js b/lib/chai/core/assertions.js index 81d74a35..e87789b0 100644 --- a/lib/chai/core/assertions.js +++ b/lib/chai/core/assertions.js @@ -3027,10 +3027,10 @@ function closeTo(expected, delta, msg) { new Assertion(obj, flagMsg, ssfi, true).is.numeric; let message = 'A `delta` value is required for `closeTo`'; - if (!delta) throw new AssertionError(flagMsg ? `${flagMsg}: ${message}` : message, undefined, ssfi); + if (delta == undefined) throw new AssertionError(flagMsg ? `${flagMsg}: ${message}` : message, undefined, ssfi); new Assertion(delta, flagMsg, ssfi, true).is.numeric; message = 'A `expected` value is required for `closeTo`'; - if (!delta) throw new AssertionError(flagMsg ? `${flagMsg}: ${message}` : message, undefined, ssfi); + if (expected == undefined) throw new AssertionError(flagMsg ? `${flagMsg}: ${message}` : message, undefined, ssfi); new Assertion(expected, flagMsg, ssfi, true).is.numeric; const abs = (x) => x < 0n ? -x : x; diff --git a/test/assert.js b/test/assert.js index c1c528cb..89395b05 100644 --- a/test/assert.js +++ b/test/assert.js @@ -1893,6 +1893,7 @@ describe('assert', function () { assert.closeTo(1.5, 1.0, 0.5); assert.closeTo(10, 20, 20); assert.closeTo(-10, 20, 30); + assert.closeTo(10, 10, 0); err(function(){ assert.closeTo(2, 1.0, 0.5, 'blah'); From d953553cb76cac5ae4d46893c74dfbbd01a61a04 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kristj=C3=A1n=20Oddsson?= Date: Wed, 9 Oct 2024 13:26:06 +0000 Subject: [PATCH 13/13] extract `isNumeric` into `utils` --- lib/chai/core/assertions.js | 30 ++++++++++-------------------- lib/chai/utils/index.js | 7 ++++++- 2 files changed, 16 insertions(+), 21 deletions(-) diff --git a/lib/chai/core/assertions.js b/lib/chai/core/assertions.js index e87789b0..e2971726 100644 --- a/lib/chai/core/assertions.js +++ b/lib/chai/core/assertions.js @@ -1224,13 +1224,11 @@ function assertAbove (n, msg) { new Assertion(obj, flagMsg, ssfi, true).to.have.property('length'); } - const isNumeric = x => ['Number', 'BigInt'].includes(_.type(x)) - if (!doLength && (objType === 'date' && nType !== 'date')) { throw new AssertionError(msgPrefix + 'the argument to above must be a date', undefined, ssfi); - } else if (!isNumeric(n) && (doLength || isNumeric(obj))) { + } else if (!_.isNumeric(n) && (doLength || _.isNumeric(obj))) { throw new AssertionError(msgPrefix + 'the argument to above must be a number', undefined, ssfi); - } else if (!doLength && (objType !== 'date' && !isNumeric(obj))) { + } else if (!doLength && (objType !== 'date' && !_.isNumeric(obj))) { var printObj = (objType === 'string') ? "'" + obj + "'" : obj; throw new AssertionError(msgPrefix + 'expected ' + printObj + ' to be a number or a date', undefined, ssfi); } @@ -1324,13 +1322,11 @@ function assertLeast (n, msg) { new Assertion(obj, flagMsg, ssfi, true).to.have.property('length'); } - const isNumeric = x => ['Number', 'BigInt'].includes(_.type(x)) - if (!doLength && (objType === 'date' && nType !== 'date')) { errorMessage = msgPrefix + 'the argument to least must be a date'; - } else if (!isNumeric(n) && (doLength || isNumeric(obj))) { + } else if (!_.isNumeric(n) && (doLength || _.isNumeric(obj))) { errorMessage = msgPrefix + 'the argument to least must be a number'; - } else if (!doLength && (objType !== 'date' && !isNumeric(obj))) { + } else if (!doLength && (objType !== 'date' && !_.isNumeric(obj))) { var printObj = (objType === 'string') ? "'" + obj + "'" : obj; errorMessage = msgPrefix + 'expected ' + printObj + ' to be a number or a date'; } else { @@ -1429,13 +1425,11 @@ function assertBelow (n, msg) { new Assertion(obj, flagMsg, ssfi, true).to.have.property('length'); } - const isNumeric = x => ['Number', 'BigInt'].includes(_.type(x)) - if (!doLength && (objType === 'date' && nType !== 'date')) { errorMessage = msgPrefix + 'the argument to below must be a date'; - } else if (!isNumeric(n) && (doLength || isNumeric(obj))) { + } else if (!_.isNumeric(n) && (doLength || _.isNumeric(obj))) { errorMessage = msgPrefix + 'the argument to below must be a number'; - } else if (!doLength && (objType !== 'date' && !isNumeric(obj))) { + } else if (!doLength && (objType !== 'date' && !_.isNumeric(obj))) { var printObj = (objType === 'string') ? "'" + obj + "'" : obj; errorMessage = msgPrefix + 'expected ' + printObj + ' to be a number or a date'; } else { @@ -1535,13 +1529,11 @@ function assertMost (n, msg) { new Assertion(obj, flagMsg, ssfi, true).to.have.property('length'); } - const isNumeric = x => ['Number', 'BigInt'].includes(_.type(x)) - if (!doLength && (objType === 'date' && nType !== 'date')) { errorMessage = msgPrefix + 'the argument to most must be a date'; - } else if (!isNumeric(n) && (doLength || isNumeric(obj))) { + } else if (!_.isNumeric(n) && (doLength || _.isNumeric(obj))) { errorMessage = msgPrefix + 'the argument to most must be a number'; - } else if (!doLength && (objType !== 'date' && !isNumeric(obj))) { + } else if (!doLength && (objType !== 'date' && !_.isNumeric(obj))) { var printObj = (objType === 'string') ? "'" + obj + "'" : obj; errorMessage = msgPrefix + 'expected ' + printObj + ' to be a number or a date'; } else { @@ -1643,14 +1635,12 @@ Assertion.addMethod('within', function (start, finish, msg) { if (doLength && objType !== 'map' && objType !== 'set') { new Assertion(obj, flagMsg, ssfi, true).to.have.property('length'); } - - const isNumeric = x => ['Number', 'BigInt'].includes(_.type(x)) if (!doLength && (objType === 'date' && (startType !== 'date' || finishType !== 'date'))) { errorMessage = msgPrefix + 'the arguments to within must be dates'; - } else if ((!isNumeric(start) || !isNumeric(finish)) && (doLength || isNumeric(obj))) { + } else if ((!_.isNumeric(start) || !_.isNumeric(finish)) && (doLength || _.isNumeric(obj))) { errorMessage = msgPrefix + 'the arguments to within must be numbers'; - } else if (!doLength && (objType !== 'date' && !isNumeric(obj))) { + } else if (!doLength && (objType !== 'date' && !_.isNumeric(obj))) { var printObj = (objType === 'string') ? "'" + obj + "'" : obj; errorMessage = msgPrefix + 'expected ' + printObj + ' to be a number or a date'; } else { diff --git a/lib/chai/utils/index.js b/lib/chai/utils/index.js index 80306f79..fd4e6358 100644 --- a/lib/chai/utils/index.js +++ b/lib/chai/utils/index.js @@ -11,7 +11,8 @@ import * as checkError from 'check-error'; export {test} from './test.js'; // type utility -export {type} from './type-detect.js'; +import {type} from './type-detect.js'; +export {type}; // expectTypes utility export {expectTypes} from './expectTypes.js'; @@ -105,3 +106,7 @@ export {getOperator} from './getOperator.js'; export function isRegExp(obj) { return Object.prototype.toString.call(obj) === '[object RegExp]'; } + +export function isNumeric(obj) { + return ['Number', 'BigInt'].includes(type(obj)) +}