Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support big int in approximently #1606

Merged
merged 14 commits into from
Oct 9, 2024
81 changes: 41 additions & 40 deletions lib/chai/core/assertions.js
Original file line number Diff line number Diff line change
Expand Up @@ -233,7 +233,6 @@ Assertion.addProperty('any', function () {
* @namespace BDD
* @public
*/

Assertion.addProperty('all', function () {
flag(this, 'all', true);
flag(this, 'any', false);
Expand Down Expand Up @@ -694,6 +693,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
);
});

/**
* ### .callable
*
Expand Down Expand Up @@ -1208,27 +1218,19 @@ 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');
}

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) {
Expand Down Expand Up @@ -1299,7 +1301,7 @@ Assertion.addMethod('greaterThan', assertAbove);
* @name least
* @alias gte
* @alias greaterThanOrEqual
* @param {number} n
* @param {unknown} n
* @param {string} msg _optional_
* @namespace BDD
* @public
Expand All @@ -1322,9 +1324,9 @@ function assertLeast (n, msg) {

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 {
Expand Down Expand Up @@ -1402,7 +1404,7 @@ Assertion.addMethod('greaterThanOrEqual', assertLeast);
* @name below
* @alias lt
* @alias lessThan
* @param {number} n
* @param {unknown} n
* @param {string} msg _optional_
* @namespace BDD
* @public
Expand All @@ -1422,12 +1424,12 @@ function assertBelow (n, msg) {
if (doLength && objType !== 'map' && objType !== 'set') {
new Assertion(obj, flagMsg, ssfi, true).to.have.property('length');
}

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 {
Expand Down Expand Up @@ -1506,7 +1508,7 @@ Assertion.addMethod('lessThan', assertBelow);
* @name most
* @alias lte
* @alias lessThanOrEqual
* @param {number} n
* @param {unknown} n
* @param {string} msg _optional_
* @namespace BDD
* @public
Expand All @@ -1529,9 +1531,9 @@ function assertMost (n, msg) {

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 {
Expand Down Expand Up @@ -1608,8 +1610,8 @@ 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 {unknown} start lower bound inclusive
* @param {unknown} finish upper bound inclusive
* @param {string} msg _optional_
* @namespace BDD
* @public
Expand All @@ -1636,9 +1638,9 @@ Assertion.addMethod('within', function (start, finish, msg) {

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 {
Expand Down Expand Up @@ -3013,19 +3015,18 @@ 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" : "";
koddsson marked this conversation as resolved.
Show resolved Hide resolved
throw new AssertionError(
flagMsg + 'the arguments to closeTo or approximately must be numbers' + deltaMessage,
undefined,
ssfi
);
}
new Assertion(obj, flagMsg, ssfi, true).is.numeric;
let message = 'A `delta` value is required for `closeTo`';
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 (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;

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
);
Expand Down
39 changes: 39 additions & 0 deletions lib/chai/interface/assert.js
Original file line number Diff line number Diff line change
Expand Up @@ -706,6 +706,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])
*
Expand Down
7 changes: 6 additions & 1 deletion lib/chai/utils/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -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))
}
67 changes: 59 additions & 8 deletions test/assert.js
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,23 @@ describe('assert', function () {
assert.typeOf(function() {}, 'asyncfunction', 'blah');
}, "blah: expected [Function] to be an asyncfunction");

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");
Expand Down Expand Up @@ -632,6 +649,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);
Expand Down Expand Up @@ -1855,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');
Expand All @@ -1866,25 +1905,26 @@ 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: A `delta` value is required for `closeTo`");
});

it('approximately', 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');
Expand All @@ -1896,19 +1936,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: A `delta` value is required for `closeTo`");
});

it('sameMembers', function() {
Expand Down Expand Up @@ -2135,6 +2175,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');
Expand Down Expand Up @@ -2186,6 +2230,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');
Expand Down Expand Up @@ -2231,6 +2277,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');
Expand Down Expand Up @@ -2282,6 +2331,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');
Expand Down
Loading