-
-
Notifications
You must be signed in to change notification settings - Fork 6.5k
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
toStrictEqual
fails to distinguish 0 from 5e-324
#7941
Comments
The native const assert = require('assert');
assert.notStrictEqual(0, 5e-324); |
Good catch. Here is my confirmation and quick analysis: describe('MIN_VALUE', () => {
const min = Number.MIN_VALUE;
test('Object.is for toBe', () => {
expect(Object.is(0, min)).toBe(false);
});
describe('egal for isEqual or isStrictEqual', () => {
const egal = (a, b) => a != +a ? b != +b : a === 0 ? 1 / a == 1 / b : a == +b;
test('min received', () => {
expect(egal(min, 0)).toBe(false);
});
test('min expected', () => {
expect(egal(0, min)).toBe(true); // incorrect and inconsistent
});
});
}); @dubzzz Can you research the egal comparison at: https://github.com/facebook/jest/blob/master/packages/expect/src/jasmineUtils.ts#L108-L111
Regardless of the reason for the problem, we can replace it with |
My personal feeling is that In other languages, such as Java we can read the following,
@pedrottimark If you are OK with it, I will try to issue a new PR to fix the lines https://github.com/facebook/jest/blob/master/packages/expect/src/jasmineUtils.ts#L108-L111 |
Here is a quick investigation I just did: function compare(a, b) {
// a and b are such that:
// - Object.prototype.toString.call(a) -> [object Number]
// - Object.prototype.toString.call(b) -> [object Number]
if (a != +a) // if a is Number.NaN
return b != +b; // return b is Number.NaN
if (a === 0) // if a is 0 (or -0)
return 1 / a == 1 / b; // return (a and b are -0) or (a and b are 0)
return a == +b; // return a == +b
} The code above is the one at https://github.com/facebook/jest/blob/master/packages/expect/src/jasmineUtils.ts#L108-L111 with ternary operators changed into classical if-else. Problem in the second if: 1 / 0 === Infinity
1 / Number.MIN_VALUE === Infinity |
Good point about |
We should maybe just get rid off the |
Notice that case '[object String]':
case '[object Number]':
case '[object Boolean]':
return Object.is(a, b); EDIT: |
I'm fine with people needing to polyfill |
Indeed, I think it would be a good thing to factorize those cases in a single one. I believe we could also add If we do the |
Be careful about Date objects because they needs value comparison instead of object identity. While we are looking at it, can you take a careful look at symbols, because I don’t see a case for them? Future readers will thank us, if we can bring together logic for the primitive types. |
@pedrottimark In reality it seems that moving to // Today:
a = new String('5'); b = '5'; a == String(b); // -> is true
a = new Number(0); b = 0; a != +a ? b != +b : a === 0 && b === 0 ? 1 / a == 1 / b : a == +b; // -> is true
a = new Boolean(true); b = true; +a == +b; +a == +b; // -> is true
// With Object.is:
a = new String('5'); b = '5'; Object.is(a, b); // -> is false
a = new Number(0); b = 0; Object.is(a, b); // -> is false
a = new Boolean(true); b = true; +a == +b; Object.is(a, b); // -> is false EDIT: tested on Firefox 66.0b9 (64 bits) |
Oh yeah, good point, I messed up and tested But we can use That raises a different question in my mind is |
Good point for the string case ^^ I just tried to check and it seems ok (no failing assert below): const assert = require('assert');
const cmp = (a, b) => a == String(b);
assert.ok(cmp('', ''));
assert.ok(cmp('', String('')));
assert.ok(cmp('', new String('')));
assert.ok(cmp(String(''), ''));
assert.ok(cmp(String(''), String('')));
assert.ok(cmp(String(''), new String('')));
assert.ok(cmp(new String(''), ''));
assert.ok(cmp(new String(''), String('')));
assert.ok(cmp(new String(''), new String(''))); Another problem I just spotted is the |
Thank you for walking through this together. Working on Jest is a deep dive into corners of ECMAScript. Here is a step back to candidate minimal solution taking instances into account: case '[object Number]':
return Object.is(Number(a), Number(b)); |
Review updated accordingly. |
Thank you. I have enjoyed the interaction. I wrote a comment in PR about CI and 2 suggested tests. |
Thanks a lot for your help on this issue |
This issue has been automatically locked since there has not been any recent activity after it was closed. Please open a new issue for related bugs. |
🐛 Bug Report
While trying to automate the detection of bugs and regressions in Jest (see my previous PR #7938), I found a very strange failing case in
toStrictEqual
:To Reproduce
Steps to reproduce the behavior:
expect(0).not.toStrictEqual(5e-324)
Expected behavior
I would have expect Jest to succeed to distinguish 0 from 5e-324.
Run
npx envinfo --preset jest
Paste the results here:
The text was updated successfully, but these errors were encountered: