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
Evaluate symbolic keys for deep equality assertion #64
Conversation
…properties that are missing from the expectation. -Added unit tests. -Added myself to the authors list.
…ts object. -Fixed linting errors.
Pull Request Test Coverage Report for Build 256
💛 - Coveralls |
Thank you! If you're reversing the symbol property checking as I suggested, I guess the additional |
@mantoni I believe the additional |
That is why I'm saying you need to reverse the additional property check. Only properties in the expectation should be considered. Right now, you are checking the symbol properties in the actual. |
lib/deep-equal.test.js
Outdated
it("returns true if object contains additional symbolic properties", function() { | ||
var obj1 = {}; | ||
var obj2 = {}; | ||
obj2[symbol] = 42; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this check (and also the implementation) need to be the other way around. The first argument passed to deepEquals
is the actual object, the second one the expectation. We want to ignore additional symbol properties on the actual.
Oups. I just now saw that my review wasn't submitted. Sorry. 🤦♂️ |
…d is the expected.
You're right @mantoni, I mixed up the parameters. Thanks for calling that out! I just reversed the logic. However, if I comment out the additional |
Oooh, that's an interesting one. I mean, backward compatibility aside, one could argue that we're now expecting an @mroderick I'd like to hear your opinion on this. |
If backwards compatibility is critical, we could alternatively only assert that two objects are not deeply equal if both of them have the same symbols defined but the values don't match. This solution wouldn't have caught the real bug in one of my applications that inspired me to open this issue, though. 😄 |
Yeah, the thing we're doing here right now is, we break the symetry of the function in this point. We did already break the symetry a while back with the matcher support, so I'd say we should maybe rename the parameters to |
I think that is much clearer, as that's how we've been using this for a long time anyway. |
The non-strict comparison of this library is a double edged sword, that makes solutions to some situations more of a judgement call and not so much something that can just be "pick the stricter option" (often my preferred solution). I always think of this library as "if I squint and the things look the same, then that's good enough". If we decide to not support comparing samsam.deepEqual([1, 2], arguments);
// throw new TypeError("comparing Array to arguments is not supported, because arguments have symbol properties that Array does not. You could convert arguments to an Array with Array.from" I wonder if that is too strict though, for a library that aims for "same-same" comparisons.... I too wonder if there are additional symbolic keys that we're not handling. Did I understand the discussion correctly? |
Yes, I'm sure you did. There is another option though: We can still support |
That tickles my "go for the stricter option" leaning, as a confident author would know that Array and arguments are not the same and cannot be treated the same. A less confident author would have a chance to learn this. |
Okay, @BrandonE, I would suggest remove the arguments iterator special case and inverse the assertions in the two failing tests. This leaves us with the question of all questions: Are we going to release this as a minor or a major? |
Here’s a thought:
At this point in time, WE know why this will fail.
If we let it fail naturally, then users will either have to recognise why it fails, or investigate it.
We can save a lot of investigations for users by failing this explicitly with an explanation.
Sent from phone, apologies for brevity and auto-correct weirdness.
… On 1 Mar 2019, at 09.16, Maximilian Antoni ***@***.***> wrote:
Okay, @BrandonE, I would suggest remove the arguments iterator special case and inverse the assertions in the two failing tests.
This leaves us with the question of all questions: Are we going to release this as a minor or a major?
—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub, or mute the thread.
|
I generally agree with this idea. However, we would have to special case the "expectation is an arguments object and actual is not" situation and throw an exception. So writing an assertion that verifies that |
Here is the change I'm suggesting: diff --git a/lib/deep-equal.js b/lib/deep-equal.js
index e422472..7db27c1 100644
--- a/lib/deep-equal.js
+++ b/lib/deep-equal.js
@@ -114,12 +114,6 @@ function deepEqualCyclic(first, second, match) {
var obj1IsArguments = isArguments(obj1);
var obj2IsArguments = isArguments(obj2);
- if (obj2IsArguments) {
- keysAndSymbols2 = keysAndSymbols2.filter(function(keyOrSymbol) {
- return keyOrSymbol !== Symbol.iterator;
- });
- }
-
if (obj1IsArguments || obj2IsArguments) {
if (obj1.length !== obj2.length) {
return false;
diff --git a/lib/deep-equal.test.js b/lib/deep-equal.test.js
index 964279c..7dadf73 100644
--- a/lib/deep-equal.test.js
+++ b/lib/deep-equal.test.js
@@ -449,12 +449,12 @@ describe("deepEqual", function() {
assert.isFalse(checkDeep);
});
- it("returns true if arguments to array", function() {
+ it("returns false if arguments to array", function() {
var gather = function() {
return arguments;
};
var checkDeep = samsam.deepEqual([1, 2, {}, []], gather(1, 2, {}, []));
- assert.isTrue(checkDeep);
+ assert.isFalse(checkDeep);
});
it("returns true if array to arguments", function() {
@@ -465,13 +465,13 @@ describe("deepEqual", function() {
assert.isTrue(checkDeep);
});
- it("returns true if arguments to array like object", function() {
+ it("returns false if arguments to array like object", function() {
var gather = function() {
return arguments;
};
var arrayLike = { length: 4, "0": 1, "1": 2, "2": {}, "3": [] };
var checkDeep = samsam.deepEqual(arrayLike, gather(1, 2, {}, []));
- assert.isTrue(checkDeep);
+ assert.isFalse(checkDeep);
});
it("returns true for same error", function() { |
I am a bit slow this morning. Why would that be impossible? |
From my diff above: it("returns false if arguments to array", function() {
var gather = function() {
return arguments;
};
var checkDeep = samsam.deepEqual([1, 2, {}, []], gather(1, 2, {}, []));
- assert.isTrue(checkDeep);
+ assert.isFalse(checkDeep);
}); This case would throw an exception. |
I made the changes @mantoni suggested. I agree with @mroderick's point that having a more specific error will help the users avoid investigating this issue unnecessarily. However, I think it'd be kind of odd for this to be the only exception we throw in this module. Further, I'm worried that, because it's possible that there are other symbolic keys that we aren't handling, people will have to investigate in those instances anyway. All of this makes me think a major version would be more suitable, just in case there is some degree of backwards incompatibility. Also, in master, we already have a special handling for arguments objects. Do you know why this is necessary? |
obj1[symbol] = 42; | ||
obj2[symbol] = 43; | ||
var checkDeep = samsam.deepEqual(obj1, obj2); | ||
assert.isFalse(checkDeep); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
MINOR: Can we make the tests a bit more concise?
var obj1 = {[symbol]: 42};
var obj2 = {[symbol]: 43};
assert.isFalse(samsam.deepEqual(obj1, obj2));
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actually, I tried that first, and the linter complained about an unexpected opening bracket. Changing it to this fixed the linting error.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we should merge it like this. Thank you 👍
@BrandonE Yes, please 👍 |
…symbol concatenation. Support IE11.
@mantoni @mroderick Any other suggestions before we merge? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You may want to review the documentation https://github.com/sinonjs/samsam/blob/master/docs/index.md and see if that can be improved, considering the changes in this PR
How's this, @mroderick ? |
Lovely! I'll let @mantoni merge this, as they've spent more time thinking about it than I have 👍 |
Great! Thanks for sticking with this @BrandonE ❤️ I've manually copied As discussed, I'm going to release this as a minor and fix the |
Released in |
var expectationName = getClassName(expectationObj); | ||
var expectationSymbols = | ||
typeof Object.getOwnPropertySymbols === "function" | ||
? getOwnPropertySymbols(expectationObj) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is it possible that this line was supposed to read ? Object.getOwnPropertySymbols(expectationObj)
?
I just tried updating my project from sinon 7.2.7 to sinon 7.3.0, a change that includes this PR, and I get a lot of errors like this :
'Unhandled promise rejection', TypeError: undefined is not a constructor (evaluating 'getOwnPropertySymbols(expectationObj)')
deepEqual@http://localhost:9876/absolute/Users/angelika/myproject/node_modules/sinon/pkg/sinon.js?911775df668393e499a0995e2789094c48727cc3:4395:40
deepEqualCyclic@http://localhost:9876/absolute/Users/angelika/myproject/node_modules/sinon/pkg/sinon.js?911775df668393e499a0995e2789094c48727cc3:4493:7
...
They all go away when I change the line as I said.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Interesting. Actually, the line obove should also just refer to getOwnPropertySymbols
because we're "caching" the function at the top of the file. I would guess that your environment does not actually have symbols support, but it gets shimmed after Sinon is loaded.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That is very probable, yes. Changing the above line to refer to just getOwnPropertySymbols
without Object.
also gets rid of my errors.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would you care to send a pull request to fix it?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Here it is: #66
Purpose (TL;DR) - mandatory
Fix issue sinon#1974 by asserting that two objects are not deeply equal if the expectation object contains different symbolic keys than the actual object.
Solution - optional
Previously,
deepEqual
would just iterate over theObject.keys
of the expectation object and compare them to the actual object. This PR changes this so that the object's own property symbols are also considered. Per @mantoni's suggestion, this will assert deep equal if the actual object contains additional symbolic properties that are missing from the expectation.After making this change, this unit test failed. This is because
arguments
has a property symbol callediterator
(Mozilla Docs):To get around this, I made it so that if the expectation object is an
arguments
object, we will ignore theSymbol.iterator
key. All of the existing unit tests now pass, though I am a bit worried that there might be additional default symbolic keys that this doesn't account for.How to verify - mandatory
npm install
npm test
. This PR adds unit tests for objects with symbolic keys.Checklist for author
npm run lint
passes