Skip to content

Commit 6d7e796

Browse files
authored
Fix calledWith(objectContaining) when there are multiple calls (#15508)
1 parent 9753fdc commit 6d7e796

File tree

6 files changed

+158
-10
lines changed

6 files changed

+158
-10
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
### Features
44

55
- `[babel-jest]` Add option `excludeJestPreset` to allow opting out of `babel-preset-jest` ([#15164](https://github.com/jestjs/jest/pull/15164))
6+
- `[expect]` Revert [#15038](https://github.com/jestjs/jest/pull/15038) to fix `expect(fn).toHaveBeenCalledWith(expect.objectContaining(...))` when there are multiple calls ([#15508](https://github.com/jestjs/jest/pull/15508))
67
- `[jest-circus, jest-cli, jest-config]` Add `waitNextEventLoopTurnForUnhandledRejectionEvents` flag to minimise performance impact of correct detection of unhandled promise rejections introduced in [#14315](https://github.com/jestjs/jest/pull/14315) ([#14681](https://github.com/jestjs/jest/pull/14681))
78
- `[jest-circus]` Add a `waitBeforeRetry` option to `jest.retryTimes` ([#14738](https://github.com/jestjs/jest/pull/14738))
89
- `[jest-circus]` Add a `retryImmediately` option to `jest.retryTimes` ([#14696](https://github.com/jestjs/jest/pull/14696))

packages/expect/src/__tests__/__snapshots__/matchers.test.js.snap

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2176,13 +2176,13 @@ exports[`.toEqual() {pass: false} expect({"a": 1, "b": 2}).toEqual(ObjectContain
21762176
<d>expect(</><r>received</><d>).</>toEqual<d>(</><g>expected</><d>) // deep equality</>
21772177

21782178
<g>- Expected - 2</>
2179-
<r>+ Received + 2</>
2179+
<r>+ Received + 3</>
21802180

21812181
<g>- ObjectContaining {</>
21822182
<g>- "a": 2,</>
21832183
<r>+ Object {</>
21842184
<r>+ "a": 1,</>
2185-
<d> "b": 2,</>
2185+
<r>+ "b": 2,</>
21862186
<d> }</>
21872187
`;
21882188

packages/expect/src/__tests__/__snapshots__/spyMatchers.test.ts.snap

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -361,6 +361,38 @@ Received
361361
Number of calls: <r>3</>
362362
`;
363363

364+
exports[`toHaveBeenCalledWith works with objectContaining 1`] = `
365+
<d>expect(</><r>jest.fn()</><d>).</>toHaveBeenCalledWith<d>(</><g>...expected</><d>)</>
366+
367+
Expected: <g>ObjectContaining {"b": 3}</>
368+
Received
369+
1: <r>{"a": 1, "b": 2, "c": 4}</>
370+
2: <r>{"a": 3, "b": 7, "c": 4}</>
371+
372+
Number of calls: <r>2</>
373+
`;
374+
375+
exports[`toHaveBeenCalledWith works with objectContaining 2`] = `
376+
<d>expect(</><r>jest.fn()</><d>).</>not<d>.</>toHaveBeenCalledWith<d>(</><g>...expected</><d>)</>
377+
378+
Expected: not <g>ObjectContaining {"b": 7}</>
379+
Received
380+
2: <d>{"a": 3, "b": 7, "c": 4}</>
381+
382+
Number of calls: <r>2</>
383+
`;
384+
385+
exports[`toHaveBeenCalledWith works with objectContaining 3`] = `
386+
<d>expect(</><r>jest.fn()</><d>).</>toHaveBeenCalledWith<d>(</><g>...expected</><d>)</>
387+
388+
Expected: <g>ObjectNotContaining {"c": 4}</>
389+
Received
390+
1: <r>{"a": 1, "b": 2, "c": 4}</>
391+
2: <r>{"a": 3, "b": 7, "c": 4}</>
392+
393+
Number of calls: <r>2</>
394+
`;
395+
364396
exports[`toHaveBeenCalledWith works with trailing undefined arguments 1`] = `
365397
<d>expect(</><r>jest.fn()</><d>).</>toHaveBeenCalledWith<d>(</><g>...expected</><d>)</>
366398

@@ -552,6 +584,28 @@ Received
552584
Number of calls: <r>3</>
553585
`;
554586

587+
exports[`toHaveBeenLastCalledWith works with objectContaining 1`] = `
588+
<d>expect(</><r>jest.fn()</><d>).</>toHaveBeenLastCalledWith<d>(</><g>...expected</><d>)</>
589+
590+
Expected: <g>ObjectContaining {"b": 3}</>
591+
Received
592+
1: <r>{"a": 1, "b": 2, "c": 4}</>
593+
-> 2: <r>{"a": 3, "b": 7, "c": 4}</>
594+
595+
Number of calls: <r>2</>
596+
`;
597+
598+
exports[`toHaveBeenLastCalledWith works with objectContaining 2`] = `
599+
<d>expect(</><r>jest.fn()</><d>).</>not<d>.</>toHaveBeenLastCalledWith<d>(</><g>...expected</><d>)</>
600+
601+
Expected: not <g>ObjectContaining {"b": 7}</>
602+
Received
603+
1: <r>{"a": 1, "b": 2, "c": 4}</>
604+
-> 2: <d>{"a": 3, "b": 7, "c": 4}</>
605+
606+
Number of calls: <r>2</>
607+
`;
608+
555609
exports[`toHaveBeenLastCalledWith works with trailing undefined arguments 1`] = `
556610
<d>expect(</><r>jest.fn()</><d>).</>toHaveBeenLastCalledWith<d>(</><g>...expected</><d>)</>
557611

@@ -762,6 +816,42 @@ Received: <r>0</>, <r>["foo", "bar"]</>
762816
Number of calls: <r>1</>
763817
`;
764818

819+
exports[`toHaveBeenNthCalledWith works with objectContaining 1`] = `
820+
<d>expect(</><r>jest.fn()</><d>).</>toHaveBeenNthCalledWith<d>(</>n<d>, </><g>...expected</><d>)</>
821+
822+
n: 1
823+
Expected: <g>ObjectContaining {"b": 7}</>
824+
Received
825+
-> 1: <r>{"a": 1, "b": 2, "c": 4}</>
826+
2: <d>{"a": 3, "b": 7, "c": 4}</>
827+
828+
Number of calls: <r>2</>
829+
`;
830+
831+
exports[`toHaveBeenNthCalledWith works with objectContaining 2`] = `
832+
<d>expect(</><r>jest.fn()</><d>).</>not<d>.</>toHaveBeenNthCalledWith<d>(</>n<d>, </><g>...expected</><d>)</>
833+
834+
n: 1
835+
Expected: not <g>ObjectContaining {"b": 2}</>
836+
Received
837+
-> 1: <d>{"a": 1, "b": 2, "c": 4}</>
838+
2: <r>{"a": 3, "b": 7, "c": 4}</>
839+
840+
Number of calls: <r>2</>
841+
`;
842+
843+
exports[`toHaveBeenNthCalledWith works with objectContaining 3`] = `
844+
<d>expect(</><r>jest.fn()</><d>).</>toHaveBeenNthCalledWith<d>(</>n<d>, </><g>...expected</><d>)</>
845+
846+
n: 1
847+
Expected: <g>ObjectNotContaining {"b": 2}</>
848+
Received
849+
-> 1: <r>{"a": 1, "b": 2, "c": 4}</>
850+
2: <d>{"a": 3, "b": 7, "c": 4}</>
851+
852+
Number of calls: <r>2</>
853+
`;
854+
765855
exports[`toHaveBeenNthCalledWith works with three calls 1`] = `
766856
<d>expect(</><r>jest.fn()</><d>).</>not<d>.</>toHaveBeenNthCalledWith<d>(</>n<d>, </><g>...expected</><d>)</>
767857

packages/expect/src/__tests__/matchers.test.js

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1009,6 +1009,20 @@ describe('.toEqual()', () => {
10091009
expect(actual).toEqual({x: 3});
10101010
});
10111011

1012+
test('objectContaining sample can be used multiple times', () => {
1013+
// This mimics what happens when there are multiple calls to a function:
1014+
// expect(mockFn).toHaveBeenCalledWith(expect.objectContaining(...))
1015+
const expected = expect.objectContaining({b: 7});
1016+
expect({a: 1, b: 2}).not.toEqual(expected);
1017+
expect({a: 3, b: 7}).toEqual(expected);
1018+
});
1019+
1020+
test('inverse objectContaining sample can be used multiple times', () => {
1021+
const expected = expect.not.objectContaining({b: 7});
1022+
expect({a: 1, b: 2}).toEqual(expected);
1023+
expect({a: 3, b: 7}).not.toEqual(expected);
1024+
});
1025+
10121026
describe('cyclic object equality', () => {
10131027
test('properties with the same circularity are equal', () => {
10141028
const a = {};

packages/expect/src/__tests__/spyMatchers.test.ts

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -673,6 +673,57 @@ describe.each([
673673
).toThrowErrorMatchingSnapshot();
674674
}
675675
});
676+
677+
test('works with objectContaining', () => {
678+
const fn = jest.fn();
679+
// Call the function twice with different objects and verify that the
680+
// correct comparison sample is still used (original sample isn't mutated)
681+
fn({a: 1, b: 2, c: 4});
682+
fn({a: 3, b: 7, c: 4});
683+
684+
if (isToHaveNth(calledWith)) {
685+
jestExpect(fn)[calledWith](1, jestExpect.objectContaining({b: 2}));
686+
jestExpect(fn)[calledWith](2, jestExpect.objectContaining({b: 7}));
687+
jestExpect(fn)[calledWith](2, jestExpect.not.objectContaining({b: 2}));
688+
689+
expect(() =>
690+
jestExpect(fn)[calledWith](1, jestExpect.objectContaining({b: 7})),
691+
).toThrowErrorMatchingSnapshot();
692+
693+
expect(() =>
694+
jestExpect(fn).not[calledWith](1, jestExpect.objectContaining({b: 2})),
695+
).toThrowErrorMatchingSnapshot();
696+
697+
expect(() =>
698+
jestExpect(fn)[calledWith](1, jestExpect.not.objectContaining({b: 2})),
699+
).toThrowErrorMatchingSnapshot();
700+
} else {
701+
jestExpect(fn)[calledWith](jestExpect.objectContaining({b: 7}));
702+
jestExpect(fn)[calledWith](jestExpect.not.objectContaining({b: 3}));
703+
704+
// The function was never called with this value.
705+
// Only {"b": 3} should be shown as the expected value in the snapshot
706+
// (no extra properties in the expected value).
707+
expect(() =>
708+
jestExpect(fn)[calledWith](jestExpect.objectContaining({b: 3})),
709+
).toThrowErrorMatchingSnapshot();
710+
711+
// Only {"b": 7} should be shown in the snapshot.
712+
expect(() =>
713+
jestExpect(fn).not[calledWith](jestExpect.objectContaining({b: 7})),
714+
).toThrowErrorMatchingSnapshot();
715+
}
716+
717+
if (calledWith === 'toHaveBeenCalledWith') {
718+
// The first call had {b: 2}, so this passes.
719+
jestExpect(fn)[calledWith](jestExpect.not.objectContaining({b: 7}));
720+
721+
// Only {"c": 4} should be shown in the snapshot.
722+
expect(() =>
723+
jestExpect(fn)[calledWith](jestExpect.not.objectContaining({c: 4})),
724+
).toThrowErrorMatchingSnapshot();
725+
}
726+
});
676727
});
677728

678729
describe('toHaveReturned', () => {

packages/expect/src/asymmetricMatchers.ts

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -254,19 +254,11 @@ class ObjectContaining extends AsymmetricMatcher<
254254
const matcherContext = this.getMatcherContext();
255255
const objectKeys = getObjectKeys(this.sample);
256256

257-
const otherKeys = other ? getObjectKeys(other) : [];
258-
259257
for (const key of objectKeys) {
260258
if (
261259
!hasProperty(other, key) ||
262260
!equals(this.sample[key], other[key], matcherContext.customTesters)
263261
) {
264-
// Result has already been determined, mutation only affects diff output
265-
for (const key of otherKeys) {
266-
if (!hasProperty(this.sample, key)) {
267-
this.sample[key] = other[key];
268-
}
269-
}
270262
result = false;
271263
break;
272264
}

0 commit comments

Comments
 (0)