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

feat: change error comparison algorithm #57

Merged
merged 2 commits into from
Sep 10, 2018
Merged

feat: change error comparison algorithm #57

merged 2 commits into from
Sep 10, 2018

Conversation

meeber
Copy link
Contributor

@meeber meeber commented Sep 8, 2018

BREAKING CHANGE: Previously, Error objects were compared using
strict equality. This commit causes Error objects to be compared
using deep equality instead, but treats them as a special case by
including their name and message properties in the comparison,
regardless of enumerability.

This PR is intended as a tamer, Error-specific alternative to #56. While working on #56, I decided to shift course due to the following:

  • I don't feel 100% confident that comparing constructors is the correct thing to do, and I'm worried such a change could be a big breaking change for some existing users.
  • I realized that comparing the non-enumerable name property of Error objects achieved something similar to comparing constructors, and was similar in nature to comparing the non-enumerable message property.
  • There was no need to special case an Error's stack property because it's already skipped by default due to being non-enumerable.

var leftHandKeys = getEnumerableKeys(leftHandOperand);
var rightHandKeys = getEnumerableKeys(rightHandOperand);
if (leftHandType === 'Error') {
addExtraErrorKeys(leftHandKeys);
addExtraErrorKeys(rightHandKeys);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it'd be simpler just to do:

leftHandkeys.push('name');
leftHandKeys.push('message');
rightHandKeys.push('name');
rightHandKeys.push('message');

Calling out to a function and checking if those keys are already in the index is "probably" (my hand-wavy intuition) more costly than just checking the property for equality twice over. Certainly the code would be cleaner though

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like the simpler form aesthetically, but the problem is that if one Error object is assigned a custom name property (thus shadowing its prototype's name) and the other one isn't, then name gets added twice to the list of keys for the object that has the custom name property (once from getEnumerableKeys, and once from the manual push), whereas the other object only gets it added once from the manual push. The comparison then fails due to a difference in length of the two key arrays.

Copy link
Member

@keithamus keithamus left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm 👍 on this. Looks like a good change. I've got a nit to pick (see above) but otherwise :shipit:

@meeber
Copy link
Contributor Author

meeber commented Sep 8, 2018

Whoops, looks like Object.assign isn't supported in PhantomJS. Guess I should refactor the tests.

BREAKING CHANGE: This commit drops support for versions of Node
that are no longer maintained.
@meeber meeber added the breaking label Sep 8, 2018
@meeber
Copy link
Contributor Author

meeber commented Sep 8, 2018

Refactored the tests and snuck in another commit to drop support for unmaintained Node versions and update Travis config.

@keithamus
Copy link
Member

Still LGTM. Another @chaijs/deep-eql member should review+merge.

Copy link
Member

@lucasfcosta lucasfcosta left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm very happy with this change.

I've left two minor comments which are up to you to change or not if you agree with them.

I think that especially the one about moving EXTRA_ERROR_KEYS to inside the function could be useful, but I won't block merging because of that.

@@ -108,9 +108,10 @@ The primary export of `deep-eql` is function that can be given two objects to co
- All own and inherited enumerable properties are considered:
- `eql(Object.create({ foo: { a: 1 } }), Object.create({ foo: { a: 1 } })).should.be.true;`
- `eql(Object.create({ foo: { a: 1 } }), Object.create({ foo: { a: 2 } })).should.be.false;`
- When comparing `Error` objects, `name` and `message` properties are also considered, regardless of enumerability:
- `eql(Error('foo'), Error('foo')).should.be.true;`
- `eql(Error('foo'), Error('bar')).should.be.false;`
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe worth using an example of an error with a property being set explicitly different, such as code, but with the same message and type being false.

index.js Outdated
@@ -25,6 +25,9 @@ FakeMap.prototype = {
},
};

// Non-enumerable keys to include in comparison of Error objects
var EXTRA_ERROR_KEYS = [ 'name', 'message' ];
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Perhaps we could move this to be just above addExtraErrorKeys since it's only used there?

Also, I wast thinking that these could be used inline on addExtraErrorKeys or even just declared inside that function since they are only used there if that doesn't hurt performance.

BREAKING CHANGE: Previously, `Error` objects were compared using
strict equality. This commit causes `Error` objects to be compared
using deep equality instead, but treats them as a special case by
including their `name` and `message` properties in the comparison,
regardless of enumerability.
@meeber
Copy link
Contributor Author

meeber commented Sep 10, 2018

Just pushed another update:

  • Simplified the adding of Error-specific keys; the loop was overkill given that the number of extra keys being added should always be 2.
  • Added an example to README of comparing two Error objects with same name and message but different code.

@keithamus keithamus merged commit 62e2d51 into chaijs:master Sep 10, 2018
@keithamus
Copy link
Member

👍

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants