Skip to content

Commit 50f229d

Browse files
meeberkeithamus
authored andcommitted
feat: change error comparison algorithm again (#59)
BREAKING CHANGE: As described in GH Issue #58, the previous change to the error comparison algorithm isn't compatible with IE and Safari due to those browsers adding extra enumerable properties onto `Error` objects. This commit causes `Error` objects to only include their `name`, `message`, and `code` properties in the comparison, regardless of enumerability.
1 parent 62e2d51 commit 50f229d

3 files changed

Lines changed: 19 additions & 27 deletions

File tree

README.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -108,11 +108,13 @@ The primary export of `deep-eql` is function that can be given two objects to co
108108
- All own and inherited enumerable properties are considered:
109109
- `eql(Object.create({ foo: { a: 1 } }), Object.create({ foo: { a: 1 } })).should.be.true;`
110110
- `eql(Object.create({ foo: { a: 1 } }), Object.create({ foo: { a: 2 } })).should.be.false;`
111-
- When comparing `Error` objects, `name` and `message` properties are also considered, regardless of enumerability:
111+
- When comparing `Error` objects, only `name`, `message`, and `code` properties are considered, regardless of enumerability:
112112
- `eql(Error('foo'), Error('foo')).should.be.true;`
113113
- `eql(Error('foo'), Error('bar')).should.be.false;`
114114
- `eql(Error('foo'), TypeError('foo')).should.be.false;`
115+
- `eql(Object.assign(Error('foo'), { code: 42 }), Object.assign(Error('foo'), { code: 42 })).should.be.true;`
115116
- `eql(Object.assign(Error('foo'), { code: 42 }), Object.assign(Error('foo'), { code: 13 })).should.be.false;`
117+
- `eql(Object.assign(Error('foo'), { otherProp: 42 }), Object.assign(Error('foo'), { otherProp: 13 })).should.be.true;`
116118
- Arguments are not Arrays:
117119
- `eql([], arguments).should.be.false;`
118120
- `eql([], Array.prototype.slice.call(arguments)).should.be.true;`

index.js

Lines changed: 5 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -208,6 +208,8 @@ function extensiveDeepEqualByType(leftHandOperand, rightHandOperand, leftHandTyp
208208
case 'WeakMap':
209209
case 'WeakSet':
210210
return leftHandOperand === rightHandOperand;
211+
case 'Error':
212+
return keysEqual(leftHandOperand, rightHandOperand, [ 'name', 'message', 'code' ], options);
211213
case 'Arguments':
212214
case 'Int8Array':
213215
case 'Uint8Array':
@@ -233,7 +235,7 @@ function extensiveDeepEqualByType(leftHandOperand, rightHandOperand, leftHandTyp
233235
case 'Map':
234236
return entriesEqual(leftHandOperand, rightHandOperand, options);
235237
default:
236-
return objectEqual(leftHandOperand, rightHandOperand, leftHandType, options);
238+
return objectEqual(leftHandOperand, rightHandOperand, options);
237239
}
238240
}
239241

@@ -377,21 +379,6 @@ function getEnumerableKeys(target) {
377379
return keys;
378380
}
379381

380-
/*!
381-
* Adds `Error`-specific keys to an array of object keys. We want to include these keys when comparing `Error` objects
382-
* despite these keys being non-enumerable by default (and thus not added by `getEnumerableKeys`).
383-
*
384-
* @param {Array} keys An array of keys
385-
*/
386-
function addExtraErrorKeys(keys) {
387-
if (keys.indexOf('name') === -1) { // eslint-disable-line no-magic-numbers
388-
keys.push('name');
389-
}
390-
if (keys.indexOf('message') === -1) { // eslint-disable-line no-magic-numbers
391-
keys.push('message');
392-
}
393-
}
394-
395382
/*!
396383
* Determines if two objects have matching values, given a set of keys. Defers to deepEqual for the equality check of
397384
* each key. If any value of the given key is not equal, the function will return false (early).
@@ -417,22 +404,16 @@ function keysEqual(leftHandOperand, rightHandOperand, keys, options) {
417404

418405
/*!
419406
* Recursively check the equality of two Objects. Once basic sameness has been established it will defer to `deepEqual`
420-
* for each enumerable key in the object. For `Error` objects, also compare `name` and `message` keys, regardless of
421-
* enumerability.
407+
* for each enumerable key in the object.
422408
*
423409
* @param {Mixed} leftHandOperand
424410
* @param {Mixed} rightHandOperand
425-
* @param {String} type of leftHandOperand
426411
* @param {Object} [options] (Optional)
427412
* @return {Boolean} result
428413
*/
429-
function objectEqual(leftHandOperand, rightHandOperand, leftHandType, options) {
414+
function objectEqual(leftHandOperand, rightHandOperand, options) {
430415
var leftHandKeys = getEnumerableKeys(leftHandOperand);
431416
var rightHandKeys = getEnumerableKeys(rightHandOperand);
432-
if (leftHandType === 'Error') {
433-
addExtraErrorKeys(leftHandKeys);
434-
addExtraErrorKeys(rightHandKeys);
435-
}
436417
if (leftHandKeys.length && leftHandKeys.length === rightHandKeys.length) {
437418
leftHandKeys.sort();
438419
rightHandKeys.sort();

test/index.js

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -414,7 +414,7 @@ describe('Generic', function () {
414414
'eql(Error("foo"), Object.assign(Error("foo"), { name: "TypeError" })) === false');
415415
});
416416

417-
it('returns true for errors with same custom property', function () {
417+
it('returns true for errors with same code', function () {
418418
var err1 = Error('foo');
419419
var err2 = Error('foo');
420420
err1.code = 42;
@@ -423,7 +423,7 @@ describe('Generic', function () {
423423
'eql(Object.assign(Error("foo"), { code: 42 }), Object.assign(Error("foo"), { code: 42 }))');
424424
});
425425

426-
it('returns false for errors with different custom property', function () {
426+
it('returns false for errors with different code', function () {
427427
var err1 = Error('foo');
428428
var err2 = Error('foo');
429429
err1.code = 42;
@@ -432,6 +432,15 @@ describe('Generic', function () {
432432
'eql(Object.assign(new Error("foo"), { code: 42 }), Object.assign(new Error("foo"), { code: 13 })) === false');
433433
});
434434

435+
it('returns true for errors with same name and message despite different otherProp', function () {
436+
var err1 = Error('foo');
437+
var err2 = Error('foo');
438+
err1.otherProp = 42;
439+
err2.otherProp = 13;
440+
assert(eql(err1, err2),
441+
'eql(Object.assign(Error("foo"), { otherProp: 42 }), Object.assign(Error("foo"), { otherProp: 13 }))');
442+
});
443+
435444
});
436445

437446
});

0 commit comments

Comments
 (0)