diff --git a/CHANGELOG.md b/CHANGELOG.md index 130cfdad2da8..1deaa173c2f5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,7 @@ ### Fixes +- `[expect]` [**BREAKING**] Revise `expect.not.objectContaining()` to be the inverse of `expect.objectContaining()`, as documented. ([#10708](https://github.com/facebook/jest/pull/10708)) - `[jest-resolve]` Replace read-pkg-up with escalade package ([#10781](https://github.com/facebook/jest/pull/10781)) - `[jest-runtime]` [**BREAKING**] Do not inject `global` variable into module wrapper ([#10644](https://github.com/facebook/jest/pull/10644)) - `[jest-runtime]` [**BREAKING**] remove long-deprecated `jest.addMatchers`, `jest.resetModuleRegistry`, and `jest.runTimersToTime` ([#9853](https://github.com/facebook/jest/pull/9853)) diff --git a/packages/expect/src/__tests__/asymmetricMatchers.test.ts b/packages/expect/src/__tests__/asymmetricMatchers.test.ts index 6e1886b65409..ec48bc73a624 100644 --- a/packages/expect/src/__tests__/asymmetricMatchers.test.ts +++ b/packages/expect/src/__tests__/asymmetricMatchers.test.ts @@ -225,10 +225,21 @@ test('ObjectContaining does not mutate the sample', () => { test('ObjectNotContaining matches', () => { [ - objectNotContaining({}).asymmetricMatch('jest'), objectNotContaining({foo: 'foo'}).asymmetricMatch({bar: 'bar'}), objectNotContaining({foo: 'foo'}).asymmetricMatch({foo: 'foox'}), objectNotContaining({foo: undefined}).asymmetricMatch({}), + objectNotContaining({ + first: objectNotContaining({second: {}}), + }).asymmetricMatch({first: {second: {}}}), + objectNotContaining({first: {second: {}, third: {}}}).asymmetricMatch({ + first: {second: {}}, + }), + objectNotContaining({first: {second: {}}}).asymmetricMatch({ + first: {second: {}, third: {}}, + }), + objectNotContaining({foo: 'foo', jest: 'jest'}).asymmetricMatch({ + foo: 'foo', + }), ].forEach(test => { jestExpect(test).toEqual(true); }); @@ -236,19 +247,43 @@ test('ObjectNotContaining matches', () => { test('ObjectNotContaining does not match', () => { [ + objectNotContaining({}).asymmetricMatch('jest'), objectNotContaining({foo: 'foo'}).asymmetricMatch({ foo: 'foo', jest: 'jest', }), objectNotContaining({foo: undefined}).asymmetricMatch({foo: undefined}), + objectNotContaining({first: {second: {}}}).asymmetricMatch({ + first: {second: {}}, + }), objectNotContaining({ - first: objectNotContaining({second: {}}), + first: objectContaining({second: {}}), }).asymmetricMatch({first: {second: {}}}), + objectNotContaining({}).asymmetricMatch(null), + objectNotContaining({}).asymmetricMatch({}), ].forEach(test => { jestExpect(test).toEqual(false); }); }); +test('ObjectNotContaining inverts ObjectContaining', () => { + [ + [{}, null], + [{foo: 'foo'}, {foo: 'foo', jest: 'jest'}], + [{foo: 'foo', jest: 'jest'}, {foo: 'foo'}], + [{foo: undefined}, {foo: undefined}], + [{foo: undefined}, {}], + [{first: {second: {}}}, {first: {second: {}}}], + [{first: objectContaining({second: {}})}, {first: {second: {}}}], + [{first: objectNotContaining({second: {}})}, {first: {second: {}}}], + [{}, {foo: undefined}], + ].forEach(([sample, received]) => { + jestExpect(objectNotContaining(sample).asymmetricMatch(received)).toEqual( + !objectContaining(sample).asymmetricMatch(received), + ); + }); +}); + test('ObjectNotContaining throws for non-objects', () => { jestExpect(() => objectNotContaining(1337).asymmetricMatch()).toThrow(); }); diff --git a/packages/expect/src/asymmetricMatchers.ts b/packages/expect/src/asymmetricMatchers.ts index b20f3052ca97..c906f247e742 100644 --- a/packages/expect/src/asymmetricMatchers.ts +++ b/packages/expect/src/asymmetricMatchers.ts @@ -7,7 +7,6 @@ */ import {equals, fnNameFor, hasProperty, isA, isUndefined} from './jasmineUtils'; -import {emptyObject} from './utils'; export class AsymmetricMatcher { protected sample: T; @@ -162,31 +161,19 @@ class ObjectContaining extends AsymmetricMatcher> { ); } - if (this.inverse) { - for (const property in this.sample) { - if ( - hasProperty(other, property) && - equals(this.sample[property], other[property]) && - !emptyObject(this.sample[property]) && - !emptyObject(other[property]) - ) { - return false; - } - } + let result = true; - return true; - } else { - for (const property in this.sample) { - if ( - !hasProperty(other, property) || - !equals(this.sample[property], other[property]) - ) { - return false; - } + for (const property in this.sample) { + if ( + !hasProperty(other, property) || + !equals(this.sample[property], other[property]) + ) { + result = false; + break; } - - return true; } + + return this.inverse ? !result : result; } toString() {