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

[ES6] should.equal fails for Symbols #669

Closed
meeber opened this issue Apr 7, 2016 · 6 comments
Closed

[ES6] should.equal fails for Symbols #669

meeber opened this issue Apr 7, 2016 · 6 comments

Comments

@meeber
Copy link
Contributor

meeber commented Apr 7, 2016

Hallo :D

I ran into this small issue while testing Symbol equality using the should syntax:

let prince = Symbol("Love");

expect(prince).to.equal(prince);  // Passes
prince.should.equal(prince);  // AssertionError: expected {} to equal {}

Adding || this instanceof Symbol to the shouldGetter resolved the issue. I'm not sure if a PR is desired since it's ES6-specific?

Thanks!

@keithamus
Copy link
Member

Thanks for the issue @meeber!

Where do you propose this change should go? Which line? We should make sure everything expect supports should also supports, so this is a valid bug.

@meeber
Copy link
Contributor Author

meeber commented Apr 7, 2016

Looks like this can be resolved in...

function shouldGetter() {
if (this instanceof String || this instanceof Number || this instanceof Boolean ) {
return new Assertion(this.valueOf(), null, shouldGetter);
}
return new Assertion(this, null, shouldGetter);
}

...by changing...

    function shouldGetter() {
      if (this instanceof String || this instanceof Number || this instanceof Boolean ) {
        return new Assertion(this.valueOf(), null, shouldGetter);
      }
      return new Assertion(this, null, shouldGetter);
    }

...to...

    function shouldGetter() {
      if (this instanceof String || this instanceof Number || this instanceof Boolean || this instanceof Symbol) {
        return new Assertion(this.valueOf(), null, shouldGetter);
      }
      return new Assertion(this, null, shouldGetter);
    }

Although that would throw a ReferenceError in pre-ES6 Node/browsers without Symbol support. And it's a really long line. So maybe:

    function shouldGetter() {
      if (this instanceof String
          || this instanceof Number
          || this instanceof Boolean
          || 'function' === typeof Symbol && this instanceof Symbol) {
        return new Assertion(this.valueOf(), null, shouldGetter);
      }
      return new Assertion(this, null, shouldGetter);
    }

@keithamus
Copy link
Member

I wonder if we should just ducktype that line instead?

function shouldGetter() {
  if (typeof this.valueOf === 'function') {
    return new Assertion(this.valueOf(), null, shouldGetter);
  }
  return new Assertion(this, null, shouldGetter);
}

Might be an interesting solution to explore.

@meeber
Copy link
Contributor Author

meeber commented Apr 7, 2016

Love that approach. Object.prototype.valueOf will just end up returning this for most non-primitives anyway unless the user shadowed valueOf or used Object.create(null).

I guess the main question is: If a user does shadow an object's valueOf with a function, is there any potential for an assertion to behave differently than before since this.valueOf() would now be passed in instead of this?

A stylistic alternative to this approach:

function shouldGetter() {
  var val = typeof this.valueOf === 'function' ? this.valueOf() : this;
  return new Assertion(val, null, shouldGetter);
}

Regardless, probably good to add a pair of Symbol tests to

chai/test/should.js

Lines 309 to 320 in 816d526

it('equal(val)', function(){
'test'.should.equal('test');
(1).should.equal(1);
err(function(){
(4).should.equal(3, 'blah');
}, 'blah: expected 4 to equal 3');
err(function(){
'4'.should.equal(4, 'blah');
}, "blah: expected '4' to equal 4");
});

@keithamus
Copy link
Member

I guess the main question is: If a user does shadow an object's valueOf with a function, is there any potential for an assertion to behave differently than before since this.valueOf() would now be passed in instead of this?

Yeah... you raise a good point here. This could be potentially dangerous for that exact reason actually. Probably best just to add the extra Symbol code there, as per your original comment. In fact we could handle it a bit easier by doing typeof this === 'symbol' - seeing as every browser that supports Symbols also supports typeof Symbol() === 'symbol'.

Regardless, probably good to add a pair of Symbol tests...

Absolutely.

I'll mark this as PR wanted - based on the following criteria:

  • The PR just adds the extra check for Symbols in the if - as described above
  • The PR has tests to back up the correct behaviour for should
  • Those tests should be conditional, so the tests still pass in browsers which don't support Symbols.

@meeber
Copy link
Contributor Author

meeber commented Apr 7, 2016

Sounds good :D

I'll submit a PR after work today if it's still open then.

Edit: Working on this now.

meeber added a commit to meeber/chai that referenced this issue Apr 8, 2016
- Fix bug when testing Symbol equality with should syntax (chaijs#669)
- Fix bug when testing Symbol keys in Map and Set
- Add Symbol tests for assert, expect, and should
meeber added a commit to meeber/chai that referenced this issue Apr 8, 2016
- Fix bug when testing Symbol equality with should syntax (chaijs#669)
- Fix bug when testing Symbol keys in Map and Set
- Add Symbol tests for assert, expect, and should
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

3 participants