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

property-based test for Node deepStrictEqual equivalence #9167

Merged
merged 3 commits into from
Nov 13, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,8 @@

- `[expect]` Display `expectedDiff` more carefully in `toBeCloseTo` ([#8389](https://github.com/facebook/jest/pull/8389))
- `[expect]` Avoid incorrect difference for subset when `toMatchObject` fails ([#9005](https://github.com/facebook/jest/pull/9005))
- `[expect]` Consider all RegExp flags for equality ([#9167](https://github.com/facebook/jest/pull/9167))
- `[expect]` [**BREAKING**] Consider primitives different from wrappers instantiated with `new` ([#9167](https://github.com/facebook/jest/pull/9167))
- `[jest-config]` Use half of the available cores when `watchAll` mode is enabled ([#9117](https://github.com/facebook/jest/pull/9117))
- `[jest-console]` Add missing `console.group` calls to `NullConsole` ([#9024](https://github.com/facebook/jest/pull/9024))
- `[jest-core]` Don't include unref'd timers in --detectOpenHandles results ([#8941](https://github.com/facebook/jest/pull/8941))
Expand Down Expand Up @@ -63,6 +65,7 @@
- `[docs]` Add alias and optional boolean value to `coverage` CLI Reference ([#8996](https://github.com/facebook/jest/pull/8996))
- `[docs]` Fix broken link pointing to legacy JS file in "Snapshot Testing".
- `[docs]` Add `setupFilesAfterEnv` and `jest.setTimeout` example ([#8971](https://github.com/facebook/jest/pull/8971))
- `[expect]` Test that `toStrictEqual` is equivalent to Node's `assert.deepStrictEqual` ([#9167](https://github.com/facebook/jest/pull/9167))
- `[jest]` [**BREAKING**] Use ESM exports ([#8874](https://github.com/facebook/jest/pull/8874))
- `[jest-cli]` [**BREAKING**] Use ESM exports ([#8874](https://github.com/facebook/jest/pull/8874))
- `[jest-cli]` [**BREAKING**] Remove re-exports from `@jest/core` ([#8874](https://github.com/facebook/jest/pull/8874))
Expand Down
79 changes: 64 additions & 15 deletions packages/expect/src/__tests__/__snapshots__/matchers.test.js.snap
Original file line number Diff line number Diff line change
Expand Up @@ -1986,6 +1986,13 @@ Expected: <g>{"asymmetricMatch": [Function asymmetricMatch]}</>
Received: <r>"Eve"</>
`;

exports[`.toEqual() {pass: false} expect("abc").toEqual({"0": "a", "1": "b", "2": "c"}) 1`] = `
<d>expect(</><r>received</><d>).</>toEqual<d>(</><g>expected</><d>) // deep equality</>

Expected: <g>{"0": "a", "1": "b", "2": "c"}</>
Received: <r>"abc"</>
`;

exports[`.toEqual() {pass: false} expect("abd").toEqual(StringContaining "bc") 1`] = `
<d>expect(</><r>received</><d>).</>toEqual<d>(</><g>expected</><d>) // deep equality</>

Expand Down Expand Up @@ -2021,6 +2028,13 @@ exports[`.toEqual() {pass: false} expect("type TypeName<T> = T extends Function
<r>+ type TypeName<T> = T extends Function<i> </i>? "function"<i> </i>: "object";</>
`;

exports[`.toEqual() {pass: false} expect(/abc/gy).toEqual(/abc/g) 1`] = `
<d>expect(</><r>received</><d>).</>toEqual<d>(</><g>expected</><d>) // deep equality</>

Expected: <g>/abc/g</>
Received: <r>/abc/gy</>
`;

exports[`.toEqual() {pass: false} expect([1, 2]).toEqual([2, 1]) 1`] = `
<d>expect(</><r>received</><d>).</>toEqual<d>(</><g>expected</><d>) // deep equality</>

Expand Down Expand Up @@ -2053,6 +2067,13 @@ exports[`.toEqual() {pass: false} expect([1]).toEqual([2]) 1`] = `
<d> ]</>
`;

exports[`.toEqual() {pass: false} expect({"0": "a", "1": "b", "2": "c"}).toEqual("abc") 1`] = `
<d>expect(</><r>received</><d>).</>toEqual<d>(</><g>expected</><d>) // deep equality</>

Expected: <g>"abc"</>
Received: <r>{"0": "a", "1": "b", "2": "c"}</>
`;

exports[`.toEqual() {pass: false} expect({"a": 1, "b": 2}).toEqual(ObjectContaining {"a": 2}) 1`] = `
<d>expect(</><r>received</><d>).</>toEqual<d>(</><g>expected</><d>) // deep equality</>

Expand Down Expand Up @@ -2112,6 +2133,27 @@ exports[`.toEqual() {pass: false} expect({"target": {"nodeType": 1, "value": "a"
<d> }</>
`;

exports[`.toEqual() {pass: false} expect({}).toEqual({}) 1`] = `
<d>expect(</><r>received</><d>).</>toEqual<d>(</><g>expected</><d>) // deep equality</>

Expected: <g>{}</>
Received: serializes to the same string
`;

exports[`.toEqual() {pass: false} expect({}).toEqual(0) 1`] = `
<d>expect(</><r>received</><d>).</>toEqual<d>(</><g>expected</><d>) // deep equality</>

Expected: <g>0</>
Received: <r>{}</>
`;

exports[`.toEqual() {pass: false} expect(0).toEqual({}) 1`] = `
<d>expect(</><r>received</><d>).</>toEqual<d>(</><g>expected</><d>) // deep equality</>

Expected: <g>{}</>
Received: <r>0</>
`;

exports[`.toEqual() {pass: false} expect(0).toEqual(-0) 1`] = `
<d>expect(</><r>received</><d>).</>toEqual<d>(</><g>expected</><d>) // deep equality</>

Expand Down Expand Up @@ -2467,11 +2509,18 @@ Expected: not <g>"abc"</>

`;

exports[`.toEqual() {pass: true} expect("abc").not.toEqual({"0": "a", "1": "b", "2": "c"}) 1`] = `
exports[`.toEqual() {pass: true} expect("abc").not.toEqual("abc") 2`] = `
<d>expect(</><r>received</><d>).</>not<d>.</>toEqual<d>(</><g>expected</><d>) // deep equality</>

Expected: not <g>"abc"</>

`;

exports[`.toEqual() {pass: true} expect("abc").not.toEqual("abc") 3`] = `
<d>expect(</><r>received</><d>).</>not<d>.</>toEqual<d>(</><g>expected</><d>) // deep equality</>

Expected: not <g>{"0": "a", "1": "b", "2": "c"}</>
Received: <r>"abc"</>
Expected: not <g>"abc"</>

`;

exports[`.toEqual() {pass: true} expect("abcd").not.toEqual(StringContaining "bc") 1`] = `
Expand Down Expand Up @@ -2516,13 +2565,6 @@ Expected: not <g>Any<Function></>
Received: <r>[Function anonymous]</>
`;

exports[`.toEqual() {pass: true} expect({"0": "a", "1": "b", "2": "c"}).not.toEqual("abc") 1`] = `
<d>expect(</><r>received</><d>).</>not<d>.</>toEqual<d>(</><g>expected</><d>) // deep equality</>

Expected: not <g>"abc"</>
Received: <r>{"0": "a", "1": "b", "2": "c"}</>
`;

exports[`.toEqual() {pass: true} expect({"a": 1, "b": [Function b], "c": true}).not.toEqual({"a": 1, "b": Any<Function>, "c": Anything}) 1`] = `
<d>expect(</><r>received</><d>).</>not<d>.</>toEqual<d>(</><g>expected</><d>) // deep equality</>

Expand Down Expand Up @@ -2558,18 +2600,25 @@ Expected: not <g>{}</>

`;

exports[`.toEqual() {pass: true} expect({}).not.toEqual(0) 1`] = `
exports[`.toEqual() {pass: true} expect({}).not.toEqual({}) 2`] = `
<d>expect(</><r>received</><d>).</>not<d>.</>toEqual<d>(</><g>expected</><d>) // deep equality</>

Expected: not <g>{}</>

`;

exports[`.toEqual() {pass: true} expect(0).not.toEqual(0) 1`] = `
<d>expect(</><r>received</><d>).</>not<d>.</>toEqual<d>(</><g>expected</><d>) // deep equality</>

Expected: not <g>0</>
Received: <r>{}</>

`;

exports[`.toEqual() {pass: true} expect(0).not.toEqual({}) 1`] = `
exports[`.toEqual() {pass: true} expect(0).not.toEqual(0) 2`] = `
<d>expect(</><r>received</><d>).</>not<d>.</>toEqual<d>(</><g>expected</><d>) // deep equality</>

Expected: not <g>{}</>
Received: <r>0</>
Expected: not <g>0</>

`;

exports[`.toEqual() {pass: true} expect(1).not.toEqual(1) 1`] = `
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import {
anythingSettings,
assertSettings,
} from './__arbitraries__/sharedSettings';
import expect from '..';
jeysal marked this conversation as resolved.
Show resolved Hide resolved

describe('toContain', () => {
it('should always find the value when inside the array', () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import {
anythingSettings,
assertSettings,
} from './__arbitraries__/sharedSettings';
import expect from '..';

describe('toContainEqual', () => {
it('should always find the value when inside the array', () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import {
anythingSettings,
assertSettings,
} from './__arbitraries__/sharedSettings';
import expect from '..';

describe('toEqual', () => {
it('should be reflexive', () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,32 @@
*
*/

import assert from 'assert';
import {onNodeVersions} from '@jest/test-utils';
import fc from 'fast-check';
import {
anythingSettings,
assertSettings,
} from './__arbitraries__/sharedSettings';
import expect from '..';

describe('toStrictEqual', () => {
const safeExpectStrictEqual = (a, b) => {
try {
expect(a).toStrictEqual(b);
return true;
} catch (err) {
return false;
}
};
const safeAssertDeepStrictEqual = (a, b) => {
try {
assert.deepStrictEqual(a, b);
return true;
} catch (err) {
return false;
}
};
it('should be reflexive', () => {
fc.assert(
fc.property(fc.dedup(fc.anything(anythingSettings), 2), ([a, b]) => {
Expand All @@ -24,14 +43,6 @@ describe('toStrictEqual', () => {
});

it('should be symmetric', () => {
const safeExpectStrictEqual = (a, b) => {
try {
expect(a).toStrictEqual(b);
return true;
} catch (err) {
return false;
}
};
fc.assert(
fc.property(
fc.anything(anythingSettings),
Expand All @@ -46,4 +57,21 @@ describe('toStrictEqual', () => {
assertSettings,
);
});

onNodeVersions('>=9', () => {
it('should be equivalent to Node deepStrictEqual', () => {
fc.assert(
fc.property(
fc.anything(anythingSettings),
fc.anything(anythingSettings),
(a, b) => {
expect(safeExpectStrictEqual(a, b)).toBe(
safeAssertDeepStrictEqual(a, b),
);
},
),
assertSettings,
);
});
});
});
21 changes: 13 additions & 8 deletions packages/expect/src/__tests__/matchers.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -419,12 +419,19 @@ describe('.toStrictEqual()', () => {
});

describe('.toEqual()', () => {
/* eslint-disable no-new-wrappers */
[
[true, false],
[1, 2],
[0, -0],
[0, Number.MIN_VALUE], // issues/7941
[Number.MIN_VALUE, 0],
[0, new Number(0)],
[new Number(0), 0],
[new Number(0), new Number(1)],
['abc', new String('abc')],
[new String('abc'), 'abc'],
[/abc/gsy, /abc/g],
[{a: 1}, {a: 2}],
[{a: 5}, {b: 6}],
['banana', 'apple'],
Expand Down Expand Up @@ -548,15 +555,12 @@ describe('.toEqual()', () => {
[true, true],
[1, 1],
[NaN, NaN],
// eslint-disable-next-line no-new-wrappers
[0, new Number(0)],
// eslint-disable-next-line no-new-wrappers
[new Number(0), 0],
[0, Number(0)],
[Number(0), 0],
[new Number(0), new Number(0)],
['abc', 'abc'],
// eslint-disable-next-line no-new-wrappers
[new String('abc'), 'abc'],
// eslint-disable-next-line no-new-wrappers
['abc', new String('abc')],
[String('abc'), 'abc'],
['abc', String('abc')],
[[1], [1]],
[
[1, 2],
Expand Down Expand Up @@ -856,6 +860,7 @@ describe('.toEqual()', () => {
expect(d).not.toEqual(c);
});
});
/* eslint-enable */
});

describe('.toBeInstanceOf()', () => {
Expand Down
28 changes: 14 additions & 14 deletions packages/expect/src/jasmineUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -100,29 +100,29 @@ function eq(
return false;
}
switch (className) {
// Strings, numbers, dates, and booleans are compared by value.
case '[object Boolean]':
case '[object String]':
// Primitives and their corresponding object wrappers are equivalent; thus, `"5"` is
// equivalent to `new String("5")`.
return a == String(b);
case '[object Number]':
return Object.is(Number(a), Number(b));
if (typeof a !== typeof b) {
// One is a primitive, one a `new Primitive()`
return false;
} else if (typeof a !== 'object' && typeof b !== 'object') {
// both are proper primitives
return Object.is(a, b);
} else {
// both are `new Primitive()`s
return Object.is(a.valueOf(), b.valueOf());
}
case '[object Date]':
case '[object Boolean]':
// Coerce dates and booleans to numeric primitive values. Dates are compared by their
// Coerce dates to numeric primitive values. Dates are compared by their
// millisecond representations. Note that invalid dates with millisecond representations
// of `NaN` are not equivalent.
return +a == +b;
// RegExps are compared by their source patterns and flags.
case '[object RegExp]':
return (
a.source == b.source &&
a.global == b.global &&
a.multiline == b.multiline &&
a.ignoreCase == b.ignoreCase
);
return a.source === b.source && a.flags === b.flags;
}
if (typeof a != 'object' || typeof b != 'object') {
if (typeof a !== 'object' || typeof b !== 'object') {
return false;
}

Expand Down