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

Allow deepEqual fonction to be configured globally (4.x.x branch) #1553

Merged
merged 1 commit into from Jan 5, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
72 changes: 49 additions & 23 deletions chai.js
Expand Up @@ -150,6 +150,8 @@ module.exports = function (_chai, util) {
* from within another assertion. It's also temporarily set to `true` before
* an overwritten assertion gets called by the overwriting assertion.
*
* - `eql`: This flag contains the deepEqual function to be used by the assertion.
*
* @param {Mixed} obj target of the assertion
* @param {String} msg (optional) custom error message
* @param {Function} ssfi (optional) starting point for removing stack frames
Expand All @@ -162,6 +164,7 @@ module.exports = function (_chai, util) {
flag(this, 'lockSsfi', lockSsfi);
flag(this, 'object', obj);
flag(this, 'message', msg);
flag(this, 'eql', config.deepEqual ?? util.eql);

return util.proxify(this);
}
Expand Down Expand Up @@ -365,7 +368,33 @@ module.exports = {
* @api public
*/

proxyExcludedKeys: ['then', 'catch', 'inspect', 'toJSON']
proxyExcludedKeys: ['then', 'catch', 'inspect', 'toJSON'],

/**
* ### config.deepEqual
*
* User configurable property, defines which a custom function to use for deepEqual
* comparisons.
* By default, the function used is the one from the `deep-eql` package without custom comparator.
*
* // use a custom comparator
* chai.config.deepEqual = (expected, actual) => {
* return chai.util.eql(expected, actual, {
* comparator: (expected, actual) => {
* // for non number comparison, use the default behavior
* if(typeof expected !== 'number') return null;
* // allow a difference of 10 between compared numbers
* return typeof actual === 'number' && Math.abs(actual - expected) < 10
* }
* })
* };
*
* @param {Function}
* @api public
*/

deepEqual: null

};

},{}],5:[function(require,module,exports){
Expand Down Expand Up @@ -849,7 +878,8 @@ module.exports = function (chai, _) {
, negate = flag(this, 'negate')
, ssfi = flag(this, 'ssfi')
, isDeep = flag(this, 'deep')
, descriptor = isDeep ? 'deep ' : '';
, descriptor = isDeep ? 'deep ' : ''
, isEql = isDeep ? flag(this, 'eql') : SameValueZero;

flagMsg = flagMsg ? flagMsg + ': ' : '';

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

case 'map':
var isEql = isDeep ? _.eql : SameValueZero;
obj.forEach(function (item) {
included = included || isEql(item, val);
});
Expand All @@ -882,7 +911,7 @@ module.exports = function (chai, _) {
case 'set':
if (isDeep) {
obj.forEach(function (item) {
included = included || _.eql(item, val);
included = included || isEql(item, val);
});
} else {
included = obj.has(val);
Expand All @@ -892,7 +921,7 @@ module.exports = function (chai, _) {
case 'array':
if (isDeep) {
included = obj.some(function (item) {
return _.eql(item, val);
return isEql(item, val);
})
} else {
included = obj.indexOf(val) !== -1;
Expand Down Expand Up @@ -1464,8 +1493,9 @@ module.exports = function (chai, _) {

function assertEql(obj, msg) {
if (msg) flag(this, 'message', msg);
var eql = flag(this, 'eql');
this.assert(
_.eql(obj, flag(this, 'object'))
eql(obj, flag(this, 'object'))
, 'expected #{this} to deeply equal #{exp}'
, 'expected #{this} to not deeply equal #{exp}'
, obj
Expand Down Expand Up @@ -2233,7 +2263,8 @@ module.exports = function (chai, _) {
var isDeep = flag(this, 'deep')
, negate = flag(this, 'negate')
, pathInfo = isNested ? _.getPathInfo(obj, name) : null
, value = isNested ? pathInfo.value : obj[name];
, value = isNested ? pathInfo.value : obj[name]
, isEql = isDeep ? flag(this, 'eql') : (val1, val2) => val1 === val2;;

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

if (arguments.length > 1) {
this.assert(
hasProperty && (isDeep ? _.eql(val, value) : val === value)
hasProperty && isEql(val, value)
, 'expected #{this} to have ' + descriptor + _.inspect(name) + ' of #{exp}, but got #{act}'
, 'expected #{this} to not have ' + descriptor + _.inspect(name) + ' of #{act}'
, val
Expand Down Expand Up @@ -2408,9 +2439,10 @@ module.exports = function (chai, _) {
if (msg) flag(this, 'message', msg);
var obj = flag(this, 'object');
var actualDescriptor = Object.getOwnPropertyDescriptor(Object(obj), name);
var eql = flag(this, 'eql');
if (actualDescriptor && descriptor) {
this.assert(
_.eql(descriptor, actualDescriptor)
eql(descriptor, actualDescriptor)
, 'expected the own property descriptor for ' + _.inspect(name) + ' on #{this} to match ' + _.inspect(descriptor) + ', got ' + _.inspect(actualDescriptor)
, 'expected the own property descriptor for ' + _.inspect(name) + ' on #{this} to not match ' + _.inspect(descriptor)
, descriptor
Expand Down Expand Up @@ -2764,7 +2796,8 @@ module.exports = function (chai, _) {
var len = keys.length
, any = flag(this, 'any')
, all = flag(this, 'all')
, expected = keys;
, expected = keys
, isEql = isDeep ? flag(this, 'eql') : (val1, val2) => val1 === val2;

if (!any && !all) {
all = true;
Expand All @@ -2774,11 +2807,7 @@ module.exports = function (chai, _) {
if (any) {
ok = expected.some(function(expectedKey) {
return actual.some(function(actualKey) {
if (isDeep) {
return _.eql(expectedKey, actualKey);
} else {
return expectedKey === actualKey;
}
return isEql(expectedKey, actualKey);
});
});
}
Expand All @@ -2787,11 +2816,7 @@ module.exports = function (chai, _) {
if (all) {
ok = expected.every(function(expectedKey) {
return actual.some(function(actualKey) {
if (isDeep) {
return _.eql(expectedKey, actualKey);
} else {
return expectedKey === actualKey;
}
return isEql(expectedKey, actualKey);
});
});

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

var cmp = flag(this, 'deep') ? _.eql : undefined;
var cmp = flag(this, 'deep') ? flag(this, 'eql') : undefined;

this.assert(
isSubsetOf(subset, obj, cmp, contains, ordered)
Expand Down Expand Up @@ -3535,7 +3560,8 @@ module.exports = function (chai, _) {
, flagMsg = flag(this, 'message')
, ssfi = flag(this, 'ssfi')
, contains = flag(this, 'contains')
, isDeep = flag(this, 'deep');
, isDeep = flag(this, 'deep')
, eql = flag(this, 'eql');
new Assertion(list, flagMsg, ssfi, true).to.be.an('array');

if (contains) {
Expand All @@ -3549,7 +3575,7 @@ module.exports = function (chai, _) {
} else {
if (isDeep) {
this.assert(
list.some(function(possibility) { return _.eql(expected, possibility) })
list.some(function(possibility) { return eql(expected, possibility) })
, 'expected #{this} to deeply equal one of #{exp}'
, 'expected #{this} to deeply equal one of #{exp}'
, list
Expand Down
3 changes: 3 additions & 0 deletions lib/chai/assertion.js
Expand Up @@ -52,6 +52,8 @@ module.exports = function (_chai, util) {
* from within another assertion. It's also temporarily set to `true` before
* an overwritten assertion gets called by the overwriting assertion.
*
* - `eql`: This flag contains the deepEqual function to be used by the assertion.
*
* @param {Mixed} obj target of the assertion
* @param {String} msg (optional) custom error message
* @param {Function} ssfi (optional) starting point for removing stack frames
Expand All @@ -64,6 +66,7 @@ module.exports = function (_chai, util) {
flag(this, 'lockSsfi', lockSsfi);
flag(this, 'object', obj);
flag(this, 'message', msg);
flag(this, 'eql', config.deepEqual ?? util.eql);

return util.proxify(this);
}
Expand Down
28 changes: 27 additions & 1 deletion lib/chai/config.js
Expand Up @@ -90,5 +90,31 @@ module.exports = {
* @api public
*/

proxyExcludedKeys: ['then', 'catch', 'inspect', 'toJSON']
proxyExcludedKeys: ['then', 'catch', 'inspect', 'toJSON'],

/**
* ### config.deepEqual
*
* User configurable property, defines which a custom function to use for deepEqual
* comparisons.
* By default, the function used is the one from the `deep-eql` package without custom comparator.
*
* // use a custom comparator
* chai.config.deepEqual = (expected, actual) => {
* return chai.util.eql(expected, actual, {
* comparator: (expected, actual) => {
* // for non number comparison, use the default behavior
* if(typeof expected !== 'number') return null;
* // allow a difference of 10 between compared numbers
* return typeof actual === 'number' && Math.abs(actual - expected) < 10
* }
* })
* };
*
* @param {Function}
* @api public
*/

deepEqual: null

};
41 changes: 19 additions & 22 deletions lib/chai/core/assertions.js
Expand Up @@ -478,7 +478,8 @@ module.exports = function (chai, _) {
, negate = flag(this, 'negate')
, ssfi = flag(this, 'ssfi')
, isDeep = flag(this, 'deep')
, descriptor = isDeep ? 'deep ' : '';
, descriptor = isDeep ? 'deep ' : ''
, isEql = isDeep ? flag(this, 'eql') : SameValueZero;

flagMsg = flagMsg ? flagMsg + ': ' : '';

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

case 'map':
var isEql = isDeep ? _.eql : SameValueZero;
obj.forEach(function (item) {
included = included || isEql(item, val);
});
Expand All @@ -511,7 +511,7 @@ module.exports = function (chai, _) {
case 'set':
if (isDeep) {
obj.forEach(function (item) {
included = included || _.eql(item, val);
included = included || isEql(item, val);
});
} else {
included = obj.has(val);
Expand All @@ -521,7 +521,7 @@ module.exports = function (chai, _) {
case 'array':
if (isDeep) {
included = obj.some(function (item) {
return _.eql(item, val);
return isEql(item, val);
})
} else {
included = obj.indexOf(val) !== -1;
Expand Down Expand Up @@ -1093,8 +1093,9 @@ module.exports = function (chai, _) {

function assertEql(obj, msg) {
if (msg) flag(this, 'message', msg);
var eql = flag(this, 'eql');
this.assert(
_.eql(obj, flag(this, 'object'))
eql(obj, flag(this, 'object'))
, 'expected #{this} to deeply equal #{exp}'
, 'expected #{this} to not deeply equal #{exp}'
, obj
Expand Down Expand Up @@ -1862,7 +1863,8 @@ module.exports = function (chai, _) {
var isDeep = flag(this, 'deep')
, negate = flag(this, 'negate')
, pathInfo = isNested ? _.getPathInfo(obj, name) : null
, value = isNested ? pathInfo.value : obj[name];
, value = isNested ? pathInfo.value : obj[name]
, isEql = isDeep ? flag(this, 'eql') : (val1, val2) => val1 === val2;;

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

if (arguments.length > 1) {
this.assert(
hasProperty && (isDeep ? _.eql(val, value) : val === value)
hasProperty && isEql(val, value)
, 'expected #{this} to have ' + descriptor + _.inspect(name) + ' of #{exp}, but got #{act}'
, 'expected #{this} to not have ' + descriptor + _.inspect(name) + ' of #{act}'
, val
Expand Down Expand Up @@ -2037,9 +2039,10 @@ module.exports = function (chai, _) {
if (msg) flag(this, 'message', msg);
var obj = flag(this, 'object');
var actualDescriptor = Object.getOwnPropertyDescriptor(Object(obj), name);
var eql = flag(this, 'eql');
if (actualDescriptor && descriptor) {
this.assert(
_.eql(descriptor, actualDescriptor)
eql(descriptor, actualDescriptor)
, 'expected the own property descriptor for ' + _.inspect(name) + ' on #{this} to match ' + _.inspect(descriptor) + ', got ' + _.inspect(actualDescriptor)
, 'expected the own property descriptor for ' + _.inspect(name) + ' on #{this} to not match ' + _.inspect(descriptor)
, descriptor
Expand Down Expand Up @@ -2393,7 +2396,8 @@ module.exports = function (chai, _) {
var len = keys.length
, any = flag(this, 'any')
, all = flag(this, 'all')
, expected = keys;
, expected = keys
, isEql = isDeep ? flag(this, 'eql') : (val1, val2) => val1 === val2;

if (!any && !all) {
all = true;
Expand All @@ -2403,11 +2407,7 @@ module.exports = function (chai, _) {
if (any) {
ok = expected.some(function(expectedKey) {
return actual.some(function(actualKey) {
if (isDeep) {
return _.eql(expectedKey, actualKey);
} else {
return expectedKey === actualKey;
}
return isEql(expectedKey, actualKey);
});
});
}
Expand All @@ -2416,11 +2416,7 @@ module.exports = function (chai, _) {
if (all) {
ok = expected.every(function(expectedKey) {
return actual.some(function(actualKey) {
if (isDeep) {
return _.eql(expectedKey, actualKey);
} else {
return expectedKey === actualKey;
}
return isEql(expectedKey, actualKey);
});
});

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

var cmp = flag(this, 'deep') ? _.eql : undefined;
var cmp = flag(this, 'deep') ? flag(this, 'eql') : undefined;

this.assert(
isSubsetOf(subset, obj, cmp, contains, ordered)
Expand Down Expand Up @@ -3164,7 +3160,8 @@ module.exports = function (chai, _) {
, flagMsg = flag(this, 'message')
, ssfi = flag(this, 'ssfi')
, contains = flag(this, 'contains')
, isDeep = flag(this, 'deep');
, isDeep = flag(this, 'deep')
, eql = flag(this, 'eql');
new Assertion(list, flagMsg, ssfi, true).to.be.an('array');

if (contains) {
Expand All @@ -3178,7 +3175,7 @@ module.exports = function (chai, _) {
} else {
if (isDeep) {
this.assert(
list.some(function(possibility) { return _.eql(expected, possibility) })
list.some(function(possibility) { return eql(expected, possibility) })
, 'expected #{this} to deeply equal one of #{exp}'
, 'expected #{this} to deeply equal one of #{exp}'
, list
Expand Down