Skip to content

Commit 1b17805

Browse files
authored
Support big int in approximently (#1606)
* Add `numeric` assertion * Use `numeric` assertion in `approximately` * Use home-made `abs` to support BigInt in `approximately` * support bigint in "above" assertion * add bigint test for typeOf * add isNumeric and isNotNumeric to assert.js * support BigInt in `atLeast` * support bigint in `below` * add support for bigint in `atMost` * add bigint support to `within`
1 parent 346421f commit 1b17805

File tree

6 files changed

+167
-68
lines changed

6 files changed

+167
-68
lines changed

lib/chai/core/assertions.js

Lines changed: 41 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -233,7 +233,6 @@ Assertion.addProperty('any', function () {
233233
* @namespace BDD
234234
* @public
235235
*/
236-
237236
Assertion.addProperty('all', function () {
238237
flag(this, 'all', true);
239238
flag(this, 'any', false);
@@ -694,6 +693,17 @@ Assertion.addProperty('true', function () {
694693
);
695694
});
696695

696+
Assertion.addProperty('numeric', function () {
697+
const object = flag(this, 'object');
698+
699+
this.assert(
700+
['Number', 'BigInt'].includes(_.type(object))
701+
, 'expected #{this} to be numeric'
702+
, 'expected #{this} to not be numeric'
703+
, flag(this, 'negate') ? false : true
704+
);
705+
});
706+
697707
/**
698708
* ### .callable
699709
*
@@ -1208,27 +1218,19 @@ function assertAbove (n, msg) {
12081218
, msgPrefix = ((flagMsg) ? flagMsg + ': ' : '')
12091219
, ssfi = flag(this, 'ssfi')
12101220
, objType = _.type(obj).toLowerCase()
1211-
, nType = _.type(n).toLowerCase()
1212-
, errorMessage
1213-
, shouldThrow = true;
1221+
, nType = _.type(n).toLowerCase();
12141222

12151223
if (doLength && objType !== 'map' && objType !== 'set') {
12161224
new Assertion(obj, flagMsg, ssfi, true).to.have.property('length');
12171225
}
12181226

12191227
if (!doLength && (objType === 'date' && nType !== 'date')) {
1220-
errorMessage = msgPrefix + 'the argument to above must be a date';
1221-
} else if (nType !== 'number' && (doLength || objType === 'number')) {
1222-
errorMessage = msgPrefix + 'the argument to above must be a number';
1223-
} else if (!doLength && (objType !== 'date' && objType !== 'number')) {
1228+
throw new AssertionError(msgPrefix + 'the argument to above must be a date', undefined, ssfi);
1229+
} else if (!_.isNumeric(n) && (doLength || _.isNumeric(obj))) {
1230+
throw new AssertionError(msgPrefix + 'the argument to above must be a number', undefined, ssfi);
1231+
} else if (!doLength && (objType !== 'date' && !_.isNumeric(obj))) {
12241232
var printObj = (objType === 'string') ? "'" + obj + "'" : obj;
1225-
errorMessage = msgPrefix + 'expected ' + printObj + ' to be a number or a date';
1226-
} else {
1227-
shouldThrow = false;
1228-
}
1229-
1230-
if (shouldThrow) {
1231-
throw new AssertionError(errorMessage, undefined, ssfi);
1233+
throw new AssertionError(msgPrefix + 'expected ' + printObj + ' to be a number or a date', undefined, ssfi);
12321234
}
12331235

12341236
if (doLength) {
@@ -1299,7 +1301,7 @@ Assertion.addMethod('greaterThan', assertAbove);
12991301
* @name least
13001302
* @alias gte
13011303
* @alias greaterThanOrEqual
1302-
* @param {number} n
1304+
* @param {unknown} n
13031305
* @param {string} msg _optional_
13041306
* @namespace BDD
13051307
* @public
@@ -1322,9 +1324,9 @@ function assertLeast (n, msg) {
13221324

13231325
if (!doLength && (objType === 'date' && nType !== 'date')) {
13241326
errorMessage = msgPrefix + 'the argument to least must be a date';
1325-
} else if (nType !== 'number' && (doLength || objType === 'number')) {
1327+
} else if (!_.isNumeric(n) && (doLength || _.isNumeric(obj))) {
13261328
errorMessage = msgPrefix + 'the argument to least must be a number';
1327-
} else if (!doLength && (objType !== 'date' && objType !== 'number')) {
1329+
} else if (!doLength && (objType !== 'date' && !_.isNumeric(obj))) {
13281330
var printObj = (objType === 'string') ? "'" + obj + "'" : obj;
13291331
errorMessage = msgPrefix + 'expected ' + printObj + ' to be a number or a date';
13301332
} else {
@@ -1402,7 +1404,7 @@ Assertion.addMethod('greaterThanOrEqual', assertLeast);
14021404
* @name below
14031405
* @alias lt
14041406
* @alias lessThan
1405-
* @param {number} n
1407+
* @param {unknown} n
14061408
* @param {string} msg _optional_
14071409
* @namespace BDD
14081410
* @public
@@ -1422,12 +1424,12 @@ function assertBelow (n, msg) {
14221424
if (doLength && objType !== 'map' && objType !== 'set') {
14231425
new Assertion(obj, flagMsg, ssfi, true).to.have.property('length');
14241426
}
1425-
1427+
14261428
if (!doLength && (objType === 'date' && nType !== 'date')) {
14271429
errorMessage = msgPrefix + 'the argument to below must be a date';
1428-
} else if (nType !== 'number' && (doLength || objType === 'number')) {
1430+
} else if (!_.isNumeric(n) && (doLength || _.isNumeric(obj))) {
14291431
errorMessage = msgPrefix + 'the argument to below must be a number';
1430-
} else if (!doLength && (objType !== 'date' && objType !== 'number')) {
1432+
} else if (!doLength && (objType !== 'date' && !_.isNumeric(obj))) {
14311433
var printObj = (objType === 'string') ? "'" + obj + "'" : obj;
14321434
errorMessage = msgPrefix + 'expected ' + printObj + ' to be a number or a date';
14331435
} else {
@@ -1506,7 +1508,7 @@ Assertion.addMethod('lessThan', assertBelow);
15061508
* @name most
15071509
* @alias lte
15081510
* @alias lessThanOrEqual
1509-
* @param {number} n
1511+
* @param {unknown} n
15101512
* @param {string} msg _optional_
15111513
* @namespace BDD
15121514
* @public
@@ -1529,9 +1531,9 @@ function assertMost (n, msg) {
15291531

15301532
if (!doLength && (objType === 'date' && nType !== 'date')) {
15311533
errorMessage = msgPrefix + 'the argument to most must be a date';
1532-
} else if (nType !== 'number' && (doLength || objType === 'number')) {
1534+
} else if (!_.isNumeric(n) && (doLength || _.isNumeric(obj))) {
15331535
errorMessage = msgPrefix + 'the argument to most must be a number';
1534-
} else if (!doLength && (objType !== 'date' && objType !== 'number')) {
1536+
} else if (!doLength && (objType !== 'date' && !_.isNumeric(obj))) {
15351537
var printObj = (objType === 'string') ? "'" + obj + "'" : obj;
15361538
errorMessage = msgPrefix + 'expected ' + printObj + ' to be a number or a date';
15371539
} else {
@@ -1608,8 +1610,8 @@ Assertion.addMethod('lessThanOrEqual', assertMost);
16081610
* expect(4, 'nooo why fail??').to.be.within(1, 3);
16091611
*
16101612
* @name within
1611-
* @param {number} start lower bound inclusive
1612-
* @param {number} finish upper bound inclusive
1613+
* @param {unknown} start lower bound inclusive
1614+
* @param {unknown} finish upper bound inclusive
16131615
* @param {string} msg _optional_
16141616
* @namespace BDD
16151617
* @public
@@ -1636,9 +1638,9 @@ Assertion.addMethod('within', function (start, finish, msg) {
16361638

16371639
if (!doLength && (objType === 'date' && (startType !== 'date' || finishType !== 'date'))) {
16381640
errorMessage = msgPrefix + 'the arguments to within must be dates';
1639-
} else if ((startType !== 'number' || finishType !== 'number') && (doLength || objType === 'number')) {
1641+
} else if ((!_.isNumeric(start) || !_.isNumeric(finish)) && (doLength || _.isNumeric(obj))) {
16401642
errorMessage = msgPrefix + 'the arguments to within must be numbers';
1641-
} else if (!doLength && (objType !== 'date' && objType !== 'number')) {
1643+
} else if (!doLength && (objType !== 'date' && !_.isNumeric(obj))) {
16421644
var printObj = (objType === 'string') ? "'" + obj + "'" : obj;
16431645
errorMessage = msgPrefix + 'expected ' + printObj + ' to be a number or a date';
16441646
} else {
@@ -3013,19 +3015,18 @@ function closeTo(expected, delta, msg) {
30133015
, flagMsg = flag(this, 'message')
30143016
, ssfi = flag(this, 'ssfi');
30153017

3016-
new Assertion(obj, flagMsg, ssfi, true).is.a('number');
3017-
if (typeof expected !== 'number' || typeof delta !== 'number') {
3018-
flagMsg = flagMsg ? flagMsg + ': ' : '';
3019-
var deltaMessage = delta === undefined ? ", and a delta is required" : "";
3020-
throw new AssertionError(
3021-
flagMsg + 'the arguments to closeTo or approximately must be numbers' + deltaMessage,
3022-
undefined,
3023-
ssfi
3024-
);
3025-
}
3018+
new Assertion(obj, flagMsg, ssfi, true).is.numeric;
3019+
let message = 'A `delta` value is required for `closeTo`';
3020+
if (delta == undefined) throw new AssertionError(flagMsg ? `${flagMsg}: ${message}` : message, undefined, ssfi);
3021+
new Assertion(delta, flagMsg, ssfi, true).is.numeric;
3022+
message = 'A `expected` value is required for `closeTo`';
3023+
if (expected == undefined) throw new AssertionError(flagMsg ? `${flagMsg}: ${message}` : message, undefined, ssfi);
3024+
new Assertion(expected, flagMsg, ssfi, true).is.numeric;
3025+
3026+
const abs = (x) => x < 0n ? -x : x;
30263027

30273028
this.assert(
3028-
Math.abs(obj - expected) <= delta
3029+
abs(obj - expected) <= delta
30293030
, 'expected #{this} to be close to ' + expected + ' +/- ' + delta
30303031
, 'expected #{this} not to be close to ' + expected + ' +/- ' + delta
30313032
);

lib/chai/interface/assert.js

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -706,6 +706,45 @@ assert.isNotNumber = function (val, msg) {
706706
new Assertion(val, msg, assert.isNotNumber, true).to.not.be.a('number');
707707
};
708708

709+
/**
710+
* ### .isNumeric(value, [message])
711+
*
712+
* Asserts that `value` is a number or BigInt.
713+
*
714+
* var cups = 2;
715+
* assert.isNumeric(cups, 'how many cups');
716+
*
717+
* var cups = 10n;
718+
* assert.isNumeric(cups, 'how many cups');
719+
*
720+
* @name isNumeric
721+
* @param {unknown} val
722+
* @param {string} msg
723+
* @namespace Assert
724+
* @public
725+
*/
726+
assert.isNumeric = function (val, msg) {
727+
new Assertion(val, msg, assert.isNumeric, true).is.numeric;
728+
};
729+
730+
/**
731+
* ### .isNotNumeric(value, [message])
732+
*
733+
* Asserts that `value` is _not_ a number or BigInt.
734+
*
735+
* var cups = '2 cups please';
736+
* assert.isNotNumeric(cups, 'how many cups');
737+
*
738+
* @name isNotNumeric
739+
* @param {unknown} val
740+
* @param {string} msg
741+
* @namespace Assert
742+
* @public
743+
*/
744+
assert.isNotNumeric = function (val, msg) {
745+
new Assertion(val, msg, assert.isNotNumeric, true).is.not.numeric;
746+
};
747+
709748
/**
710749
* ### .isFinite(value, [message])
711750
*

lib/chai/utils/index.js

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,8 @@ import * as checkError from 'check-error';
1111
export {test} from './test.js';
1212

1313
// type utility
14-
export {type} from './type-detect.js';
14+
import {type} from './type-detect.js';
15+
export {type};
1516

1617
// expectTypes utility
1718
export {expectTypes} from './expectTypes.js';
@@ -105,3 +106,7 @@ export {getOperator} from './getOperator.js';
105106
export function isRegExp(obj) {
106107
return Object.prototype.toString.call(obj) === '[object RegExp]';
107108
}
109+
110+
export function isNumeric(obj) {
111+
return ['Number', 'BigInt'].includes(type(obj))
112+
}

test/assert.js

Lines changed: 59 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -153,6 +153,23 @@ describe('assert', function () {
153153
assert.typeOf(function() {}, 'asyncfunction', 'blah');
154154
}, "blah: expected [Function] to be an asyncfunction");
155155

156+
assert.typeOf(5n, 'bigint');
157+
158+
assert.typeOf(() => {}, 'function');
159+
assert.typeOf(function() {}, 'function');
160+
assert.typeOf(async function() {}, 'asyncfunction');
161+
assert.typeOf(function*() {}, 'generatorfunction');
162+
assert.typeOf(async function*() {}, 'asyncgeneratorfunction');
163+
assert.typeOf(Symbol(), 'symbol');
164+
165+
err(function () {
166+
assert.typeOf(5, 'function', 'blah');
167+
}, "blah: expected 5 to be a function");
168+
169+
err(function () {
170+
assert.typeOf(function() {}, 'asyncfunction', 'blah');
171+
}, "blah: expected [Function] to be an asyncfunction");
172+
156173
err(function () {
157174
assert.typeOf(5, 'string', 'blah');
158175
}, "blah: expected 5 to be a string");
@@ -632,6 +649,27 @@ describe('assert', function () {
632649
}, "blah: expected 4 not to be a number");
633650
});
634651

652+
653+
it('isNumeric', function() {
654+
assert.isNumeric(1);
655+
assert.isNumeric(Number('3'));
656+
assert.isNumeric(6n);
657+
assert.isNumeric(BigInt(9));
658+
659+
err(function () {
660+
assert.isNumeric('1', 'blah');
661+
}, "blah: expected \'1\' to be numeric");
662+
});
663+
664+
it('isNotNumeric', function () {
665+
assert.isNotNumeric('hello');
666+
assert.isNotNumeric([ 5 ]);
667+
668+
err(function () {
669+
assert.isNotNumeric(4, 'blah');
670+
}, "blah: expected 4 to not be numeric");
671+
});
672+
635673
it('isFinite', function() {
636674
assert.isFinite(4);
637675
assert.isFinite(-10);
@@ -1855,6 +1893,7 @@ describe('assert', function () {
18551893
assert.closeTo(1.5, 1.0, 0.5);
18561894
assert.closeTo(10, 20, 20);
18571895
assert.closeTo(-10, 20, 30);
1896+
assert.closeTo(10, 10, 0);
18581897

18591898
err(function(){
18601899
assert.closeTo(2, 1.0, 0.5, 'blah');
@@ -1866,25 +1905,26 @@ describe('assert', function () {
18661905

18671906
err(function() {
18681907
assert.closeTo([1.5], 1.0, 0.5, 'blah');
1869-
}, "blah: expected [ 1.5 ] to be a number");
1908+
}, "blah: expected [ 1.5 ] to be numeric");
18701909

18711910
err(function() {
18721911
assert.closeTo(1.5, "1.0", 0.5, 'blah');
1873-
}, "blah: the arguments to closeTo or approximately must be numbers");
1912+
}, "blah: expected '1.0' to be numeric");
18741913

18751914
err(function() {
18761915
assert.closeTo(1.5, 1.0, true, 'blah');
1877-
}, "blah: the arguments to closeTo or approximately must be numbers");
1916+
}, "blah: expected true to be numeric");
18781917

18791918
err(function() {
18801919
assert.closeTo(1.5, 1.0, undefined, 'blah');
1881-
}, "blah: the arguments to closeTo or approximately must be numbers, and a delta is required");
1920+
}, "blah: A `delta` value is required for `closeTo`");
18821921
});
18831922

18841923
it('approximately', function(){
18851924
assert.approximately(1.5, 1.0, 0.5);
18861925
assert.approximately(10, 20, 20);
18871926
assert.approximately(-10, 20, 30);
1927+
assert.approximately(1n, 2n, 1n);
18881928

18891929
err(function(){
18901930
assert.approximately(2, 1.0, 0.5, 'blah');
@@ -1896,19 +1936,19 @@ describe('assert', function () {
18961936

18971937
err(function() {
18981938
assert.approximately([1.5], 1.0, 0.5);
1899-
}, "expected [ 1.5 ] to be a number");
1939+
}, "expected [ 1.5 ] to be numeric");
19001940

19011941
err(function() {
19021942
assert.approximately(1.5, "1.0", 0.5, 'blah');
1903-
}, "blah: the arguments to closeTo or approximately must be numbers");
1943+
}, "blah: expected '1.0' to be numeric");
19041944

19051945
err(function() {
19061946
assert.approximately(1.5, 1.0, true, 'blah');
1907-
}, "blah: the arguments to closeTo or approximately must be numbers");
1947+
}, "blah: expected true to be numeric");
19081948

19091949
err(function() {
19101950
assert.approximately(1.5, 1.0, undefined, 'blah');
1911-
}, "blah: the arguments to closeTo or approximately must be numbers, and a delta is required");
1951+
}, "blah: A `delta` value is required for `closeTo`");
19121952
});
19131953

19141954
it('sameMembers', function() {
@@ -2135,6 +2175,10 @@ describe('assert', function () {
21352175

21362176
it('above', function() {
21372177
assert.isAbove(5, 2, '5 should be above 2');
2178+
assert.isAbove(5n, 2, '5 should be above 2');
2179+
assert.isAbove(5, 2n, '5 should be above 2');
2180+
assert.isAbove(5n, 2n, '5 should be above 2');
2181+
assert.isAbove(9007199254740994n, 2, '9007199254740994 should be above 2');
21382182

21392183
err(function() {
21402184
assert.isAbove(1, 3, 'blah');
@@ -2186,6 +2230,8 @@ describe('assert', function () {
21862230
it('atLeast', function() {
21872231
assert.isAtLeast(5, 2, '5 should be above 2');
21882232
assert.isAtLeast(1, 1, '1 should be equal to 1');
2233+
assert.isAtLeast(5n, 2, '5 should be above 2');
2234+
assert.isAtLeast(1, 1n, '1 should be equal to 1');
21892235

21902236
err(function() {
21912237
assert.isAtLeast(1, 3, 'blah');
@@ -2231,6 +2277,9 @@ describe('assert', function () {
22312277

22322278
it('below', function() {
22332279
assert.isBelow(2, 5, '2 should be below 5');
2280+
assert.isBelow(2, 5n, '2 should be below 5');
2281+
assert.isBelow(2n, 5, '2 should be below 5');
2282+
assert.isBelow(2n, 5n, '2 should be below 5');
22342283

22352284
err(function() {
22362285
assert.isBelow(3, 1, 'blah');
@@ -2282,6 +2331,8 @@ describe('assert', function () {
22822331
it('atMost', function() {
22832332
assert.isAtMost(2, 5, '2 should be below 5');
22842333
assert.isAtMost(1, 1, '1 should be equal to 1');
2334+
assert.isAtMost(2n, 5, '2 should be below 5');
2335+
assert.isAtMost(1, 1n, '1 should be equal to 1');
22852336

22862337
err(function() {
22872338
assert.isAtMost(3, 1, 'blah');

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy