Skip to content

Commit fb5fd85

Browse files
authored
Allow deepEqual fonction to be configured globally (#1553)
1 parent 744a16e commit fb5fd85

File tree

5 files changed

+117
-46
lines changed

5 files changed

+117
-46
lines changed

chai.js

+49-23
Original file line numberDiff line numberDiff line change
@@ -150,6 +150,8 @@ module.exports = function (_chai, util) {
150150
* from within another assertion. It's also temporarily set to `true` before
151151
* an overwritten assertion gets called by the overwriting assertion.
152152
*
153+
* - `eql`: This flag contains the deepEqual function to be used by the assertion.
154+
*
153155
* @param {Mixed} obj target of the assertion
154156
* @param {String} msg (optional) custom error message
155157
* @param {Function} ssfi (optional) starting point for removing stack frames
@@ -162,6 +164,7 @@ module.exports = function (_chai, util) {
162164
flag(this, 'lockSsfi', lockSsfi);
163165
flag(this, 'object', obj);
164166
flag(this, 'message', msg);
167+
flag(this, 'eql', config.deepEqual ?? util.eql);
165168

166169
return util.proxify(this);
167170
}
@@ -365,7 +368,33 @@ module.exports = {
365368
* @api public
366369
*/
367370

368-
proxyExcludedKeys: ['then', 'catch', 'inspect', 'toJSON']
371+
proxyExcludedKeys: ['then', 'catch', 'inspect', 'toJSON'],
372+
373+
/**
374+
* ### config.deepEqual
375+
*
376+
* User configurable property, defines which a custom function to use for deepEqual
377+
* comparisons.
378+
* By default, the function used is the one from the `deep-eql` package without custom comparator.
379+
*
380+
* // use a custom comparator
381+
* chai.config.deepEqual = (expected, actual) => {
382+
* return chai.util.eql(expected, actual, {
383+
* comparator: (expected, actual) => {
384+
* // for non number comparison, use the default behavior
385+
* if(typeof expected !== 'number') return null;
386+
* // allow a difference of 10 between compared numbers
387+
* return typeof actual === 'number' && Math.abs(actual - expected) < 10
388+
* }
389+
* })
390+
* };
391+
*
392+
* @param {Function}
393+
* @api public
394+
*/
395+
396+
deepEqual: null
397+
369398
};
370399

371400
},{}],5:[function(require,module,exports){
@@ -849,7 +878,8 @@ module.exports = function (chai, _) {
849878
, negate = flag(this, 'negate')
850879
, ssfi = flag(this, 'ssfi')
851880
, isDeep = flag(this, 'deep')
852-
, descriptor = isDeep ? 'deep ' : '';
881+
, descriptor = isDeep ? 'deep ' : ''
882+
, isEql = isDeep ? flag(this, 'eql') : SameValueZero;
853883

854884
flagMsg = flagMsg ? flagMsg + ': ' : '';
855885

@@ -873,7 +903,6 @@ module.exports = function (chai, _) {
873903
break;
874904

875905
case 'map':
876-
var isEql = isDeep ? _.eql : SameValueZero;
877906
obj.forEach(function (item) {
878907
included = included || isEql(item, val);
879908
});
@@ -882,7 +911,7 @@ module.exports = function (chai, _) {
882911
case 'set':
883912
if (isDeep) {
884913
obj.forEach(function (item) {
885-
included = included || _.eql(item, val);
914+
included = included || isEql(item, val);
886915
});
887916
} else {
888917
included = obj.has(val);
@@ -892,7 +921,7 @@ module.exports = function (chai, _) {
892921
case 'array':
893922
if (isDeep) {
894923
included = obj.some(function (item) {
895-
return _.eql(item, val);
924+
return isEql(item, val);
896925
})
897926
} else {
898927
included = obj.indexOf(val) !== -1;
@@ -1464,8 +1493,9 @@ module.exports = function (chai, _) {
14641493

14651494
function assertEql(obj, msg) {
14661495
if (msg) flag(this, 'message', msg);
1496+
var eql = flag(this, 'eql');
14671497
this.assert(
1468-
_.eql(obj, flag(this, 'object'))
1498+
eql(obj, flag(this, 'object'))
14691499
, 'expected #{this} to deeply equal #{exp}'
14701500
, 'expected #{this} to not deeply equal #{exp}'
14711501
, obj
@@ -2233,7 +2263,8 @@ module.exports = function (chai, _) {
22332263
var isDeep = flag(this, 'deep')
22342264
, negate = flag(this, 'negate')
22352265
, pathInfo = isNested ? _.getPathInfo(obj, name) : null
2236-
, value = isNested ? pathInfo.value : obj[name];
2266+
, value = isNested ? pathInfo.value : obj[name]
2267+
, isEql = isDeep ? flag(this, 'eql') : (val1, val2) => val1 === val2;;
22372268

22382269
var descriptor = '';
22392270
if (isDeep) descriptor += 'deep ';
@@ -2260,7 +2291,7 @@ module.exports = function (chai, _) {
22602291

22612292
if (arguments.length > 1) {
22622293
this.assert(
2263-
hasProperty && (isDeep ? _.eql(val, value) : val === value)
2294+
hasProperty && isEql(val, value)
22642295
, 'expected #{this} to have ' + descriptor + _.inspect(name) + ' of #{exp}, but got #{act}'
22652296
, 'expected #{this} to not have ' + descriptor + _.inspect(name) + ' of #{act}'
22662297
, val
@@ -2408,9 +2439,10 @@ module.exports = function (chai, _) {
24082439
if (msg) flag(this, 'message', msg);
24092440
var obj = flag(this, 'object');
24102441
var actualDescriptor = Object.getOwnPropertyDescriptor(Object(obj), name);
2442+
var eql = flag(this, 'eql');
24112443
if (actualDescriptor && descriptor) {
24122444
this.assert(
2413-
_.eql(descriptor, actualDescriptor)
2445+
eql(descriptor, actualDescriptor)
24142446
, 'expected the own property descriptor for ' + _.inspect(name) + ' on #{this} to match ' + _.inspect(descriptor) + ', got ' + _.inspect(actualDescriptor)
24152447
, 'expected the own property descriptor for ' + _.inspect(name) + ' on #{this} to not match ' + _.inspect(descriptor)
24162448
, descriptor
@@ -2764,7 +2796,8 @@ module.exports = function (chai, _) {
27642796
var len = keys.length
27652797
, any = flag(this, 'any')
27662798
, all = flag(this, 'all')
2767-
, expected = keys;
2799+
, expected = keys
2800+
, isEql = isDeep ? flag(this, 'eql') : (val1, val2) => val1 === val2;
27682801

27692802
if (!any && !all) {
27702803
all = true;
@@ -2774,11 +2807,7 @@ module.exports = function (chai, _) {
27742807
if (any) {
27752808
ok = expected.some(function(expectedKey) {
27762809
return actual.some(function(actualKey) {
2777-
if (isDeep) {
2778-
return _.eql(expectedKey, actualKey);
2779-
} else {
2780-
return expectedKey === actualKey;
2781-
}
2810+
return isEql(expectedKey, actualKey);
27822811
});
27832812
});
27842813
}
@@ -2787,11 +2816,7 @@ module.exports = function (chai, _) {
27872816
if (all) {
27882817
ok = expected.every(function(expectedKey) {
27892818
return actual.some(function(actualKey) {
2790-
if (isDeep) {
2791-
return _.eql(expectedKey, actualKey);
2792-
} else {
2793-
return expectedKey === actualKey;
2794-
}
2819+
return isEql(expectedKey, actualKey);
27952820
});
27962821
});
27972822

@@ -3479,7 +3504,7 @@ module.exports = function (chai, _) {
34793504
failNegateMsg = 'expected #{this} to not have the same ' + subject + ' as #{exp}';
34803505
}
34813506

3482-
var cmp = flag(this, 'deep') ? _.eql : undefined;
3507+
var cmp = flag(this, 'deep') ? flag(this, 'eql') : undefined;
34833508

34843509
this.assert(
34853510
isSubsetOf(subset, obj, cmp, contains, ordered)
@@ -3535,7 +3560,8 @@ module.exports = function (chai, _) {
35353560
, flagMsg = flag(this, 'message')
35363561
, ssfi = flag(this, 'ssfi')
35373562
, contains = flag(this, 'contains')
3538-
, isDeep = flag(this, 'deep');
3563+
, isDeep = flag(this, 'deep')
3564+
, eql = flag(this, 'eql');
35393565
new Assertion(list, flagMsg, ssfi, true).to.be.an('array');
35403566

35413567
if (contains) {
@@ -3549,7 +3575,7 @@ module.exports = function (chai, _) {
35493575
} else {
35503576
if (isDeep) {
35513577
this.assert(
3552-
list.some(function(possibility) { return _.eql(expected, possibility) })
3578+
list.some(function(possibility) { return eql(expected, possibility) })
35533579
, 'expected #{this} to deeply equal one of #{exp}'
35543580
, 'expected #{this} to deeply equal one of #{exp}'
35553581
, list

lib/chai/assertion.js

+3
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,8 @@ module.exports = function (_chai, util) {
5252
* from within another assertion. It's also temporarily set to `true` before
5353
* an overwritten assertion gets called by the overwriting assertion.
5454
*
55+
* - `eql`: This flag contains the deepEqual function to be used by the assertion.
56+
*
5557
* @param {Mixed} obj target of the assertion
5658
* @param {String} msg (optional) custom error message
5759
* @param {Function} ssfi (optional) starting point for removing stack frames
@@ -64,6 +66,7 @@ module.exports = function (_chai, util) {
6466
flag(this, 'lockSsfi', lockSsfi);
6567
flag(this, 'object', obj);
6668
flag(this, 'message', msg);
69+
flag(this, 'eql', config.deepEqual ?? util.eql);
6770

6871
return util.proxify(this);
6972
}

lib/chai/config.js

+27-1
Original file line numberDiff line numberDiff line change
@@ -90,5 +90,31 @@ module.exports = {
9090
* @api public
9191
*/
9292

93-
proxyExcludedKeys: ['then', 'catch', 'inspect', 'toJSON']
93+
proxyExcludedKeys: ['then', 'catch', 'inspect', 'toJSON'],
94+
95+
/**
96+
* ### config.deepEqual
97+
*
98+
* User configurable property, defines which a custom function to use for deepEqual
99+
* comparisons.
100+
* By default, the function used is the one from the `deep-eql` package without custom comparator.
101+
*
102+
* // use a custom comparator
103+
* chai.config.deepEqual = (expected, actual) => {
104+
* return chai.util.eql(expected, actual, {
105+
* comparator: (expected, actual) => {
106+
* // for non number comparison, use the default behavior
107+
* if(typeof expected !== 'number') return null;
108+
* // allow a difference of 10 between compared numbers
109+
* return typeof actual === 'number' && Math.abs(actual - expected) < 10
110+
* }
111+
* })
112+
* };
113+
*
114+
* @param {Function}
115+
* @api public
116+
*/
117+
118+
deepEqual: null
119+
94120
};

lib/chai/core/assertions.js

+19-22
Original file line numberDiff line numberDiff line change
@@ -478,7 +478,8 @@ module.exports = function (chai, _) {
478478
, negate = flag(this, 'negate')
479479
, ssfi = flag(this, 'ssfi')
480480
, isDeep = flag(this, 'deep')
481-
, descriptor = isDeep ? 'deep ' : '';
481+
, descriptor = isDeep ? 'deep ' : ''
482+
, isEql = isDeep ? flag(this, 'eql') : SameValueZero;
482483

483484
flagMsg = flagMsg ? flagMsg + ': ' : '';
484485

@@ -502,7 +503,6 @@ module.exports = function (chai, _) {
502503
break;
503504

504505
case 'map':
505-
var isEql = isDeep ? _.eql : SameValueZero;
506506
obj.forEach(function (item) {
507507
included = included || isEql(item, val);
508508
});
@@ -511,7 +511,7 @@ module.exports = function (chai, _) {
511511
case 'set':
512512
if (isDeep) {
513513
obj.forEach(function (item) {
514-
included = included || _.eql(item, val);
514+
included = included || isEql(item, val);
515515
});
516516
} else {
517517
included = obj.has(val);
@@ -521,7 +521,7 @@ module.exports = function (chai, _) {
521521
case 'array':
522522
if (isDeep) {
523523
included = obj.some(function (item) {
524-
return _.eql(item, val);
524+
return isEql(item, val);
525525
})
526526
} else {
527527
included = obj.indexOf(val) !== -1;
@@ -1093,8 +1093,9 @@ module.exports = function (chai, _) {
10931093

10941094
function assertEql(obj, msg) {
10951095
if (msg) flag(this, 'message', msg);
1096+
var eql = flag(this, 'eql');
10961097
this.assert(
1097-
_.eql(obj, flag(this, 'object'))
1098+
eql(obj, flag(this, 'object'))
10981099
, 'expected #{this} to deeply equal #{exp}'
10991100
, 'expected #{this} to not deeply equal #{exp}'
11001101
, obj
@@ -1862,7 +1863,8 @@ module.exports = function (chai, _) {
18621863
var isDeep = flag(this, 'deep')
18631864
, negate = flag(this, 'negate')
18641865
, pathInfo = isNested ? _.getPathInfo(obj, name) : null
1865-
, value = isNested ? pathInfo.value : obj[name];
1866+
, value = isNested ? pathInfo.value : obj[name]
1867+
, isEql = isDeep ? flag(this, 'eql') : (val1, val2) => val1 === val2;;
18661868

18671869
var descriptor = '';
18681870
if (isDeep) descriptor += 'deep ';
@@ -1889,7 +1891,7 @@ module.exports = function (chai, _) {
18891891

18901892
if (arguments.length > 1) {
18911893
this.assert(
1892-
hasProperty && (isDeep ? _.eql(val, value) : val === value)
1894+
hasProperty && isEql(val, value)
18931895
, 'expected #{this} to have ' + descriptor + _.inspect(name) + ' of #{exp}, but got #{act}'
18941896
, 'expected #{this} to not have ' + descriptor + _.inspect(name) + ' of #{act}'
18951897
, val
@@ -2037,9 +2039,10 @@ module.exports = function (chai, _) {
20372039
if (msg) flag(this, 'message', msg);
20382040
var obj = flag(this, 'object');
20392041
var actualDescriptor = Object.getOwnPropertyDescriptor(Object(obj), name);
2042+
var eql = flag(this, 'eql');
20402043
if (actualDescriptor && descriptor) {
20412044
this.assert(
2042-
_.eql(descriptor, actualDescriptor)
2045+
eql(descriptor, actualDescriptor)
20432046
, 'expected the own property descriptor for ' + _.inspect(name) + ' on #{this} to match ' + _.inspect(descriptor) + ', got ' + _.inspect(actualDescriptor)
20442047
, 'expected the own property descriptor for ' + _.inspect(name) + ' on #{this} to not match ' + _.inspect(descriptor)
20452048
, descriptor
@@ -2393,7 +2396,8 @@ module.exports = function (chai, _) {
23932396
var len = keys.length
23942397
, any = flag(this, 'any')
23952398
, all = flag(this, 'all')
2396-
, expected = keys;
2399+
, expected = keys
2400+
, isEql = isDeep ? flag(this, 'eql') : (val1, val2) => val1 === val2;
23972401

23982402
if (!any && !all) {
23992403
all = true;
@@ -2403,11 +2407,7 @@ module.exports = function (chai, _) {
24032407
if (any) {
24042408
ok = expected.some(function(expectedKey) {
24052409
return actual.some(function(actualKey) {
2406-
if (isDeep) {
2407-
return _.eql(expectedKey, actualKey);
2408-
} else {
2409-
return expectedKey === actualKey;
2410-
}
2410+
return isEql(expectedKey, actualKey);
24112411
});
24122412
});
24132413
}
@@ -2416,11 +2416,7 @@ module.exports = function (chai, _) {
24162416
if (all) {
24172417
ok = expected.every(function(expectedKey) {
24182418
return actual.some(function(actualKey) {
2419-
if (isDeep) {
2420-
return _.eql(expectedKey, actualKey);
2421-
} else {
2422-
return expectedKey === actualKey;
2423-
}
2419+
return isEql(expectedKey, actualKey);
24242420
});
24252421
});
24262422

@@ -3108,7 +3104,7 @@ module.exports = function (chai, _) {
31083104
failNegateMsg = 'expected #{this} to not have the same ' + subject + ' as #{exp}';
31093105
}
31103106

3111-
var cmp = flag(this, 'deep') ? _.eql : undefined;
3107+
var cmp = flag(this, 'deep') ? flag(this, 'eql') : undefined;
31123108

31133109
this.assert(
31143110
isSubsetOf(subset, obj, cmp, contains, ordered)
@@ -3164,7 +3160,8 @@ module.exports = function (chai, _) {
31643160
, flagMsg = flag(this, 'message')
31653161
, ssfi = flag(this, 'ssfi')
31663162
, contains = flag(this, 'contains')
3167-
, isDeep = flag(this, 'deep');
3163+
, isDeep = flag(this, 'deep')
3164+
, eql = flag(this, 'eql');
31683165
new Assertion(list, flagMsg, ssfi, true).to.be.an('array');
31693166

31703167
if (contains) {
@@ -3178,7 +3175,7 @@ module.exports = function (chai, _) {
31783175
} else {
31793176
if (isDeep) {
31803177
this.assert(
3181-
list.some(function(possibility) { return _.eql(expected, possibility) })
3178+
list.some(function(possibility) { return eql(expected, possibility) })
31823179
, 'expected #{this} to deeply equal one of #{exp}'
31833180
, 'expected #{this} to deeply equal one of #{exp}'
31843181
, list

0 commit comments

Comments
 (0)