From 3467338370646e00c95abe62765c04ea362576e3 Mon Sep 17 00:00:00 2001 From: souldzin Date: Thu, 20 Aug 2020 22:49:27 -0500 Subject: [PATCH] Recurse proto chain in hasPropertyInObject This way we can catch the actual props of the object without catching those in Object.prototype --- .../__snapshots__/matchers.test.js.snap | 19 +++++++++++++++++++ .../expect/src/__tests__/matchers.test.js | 2 ++ packages/expect/src/utils.ts | 15 +++++++++++++-- 3 files changed, 34 insertions(+), 2 deletions(-) diff --git a/packages/expect/src/__tests__/__snapshots__/matchers.test.js.snap b/packages/expect/src/__tests__/__snapshots__/matchers.test.js.snap index 96ff8dbb4fbe..829838a271b1 100644 --- a/packages/expect/src/__tests__/__snapshots__/matchers.test.js.snap +++ b/packages/expect/src/__tests__/__snapshots__/matchers.test.js.snap @@ -3988,6 +3988,18 @@ exports[`toMatchObject() {pass: false} expect({"a": "b"}).toMatchObject({"c": "d } `; +exports[`toMatchObject() {pass: false} expect({"a": "b"}).toMatchObject({"toString": Any}) 1`] = ` +expect(received).toMatchObject(expected) + +- Expected - 0 ++ Received + 1 + + Object { ++ "a": "b", + "toString": Any, + } +`; + exports[`toMatchObject() {pass: false} expect({"a": [{"a": "a", "b": "b"}]}).toMatchObject({"a": [{"a": "c"}]}) 1`] = ` expect(received).toMatchObject(expected) @@ -4246,6 +4258,13 @@ Expected: not {"t": {"x": {"r": "r"}}} Received: {"a": "b", "t": {"x": {"r": "r"}, "z": "z"}} `; +exports[`toMatchObject() {pass: true} expect({"a": "b", "toString": [Function toString]}).toMatchObject({"toString": Any}) 1`] = ` +expect(received).not.toMatchObject(expected) + +Expected: not {"toString": Any} +Received: {"a": "b", "toString": [Function toString]} +`; + exports[`toMatchObject() {pass: true} expect({"a": "b"}).toMatchObject({"a": "b"}) 1`] = ` expect(received).not.toMatchObject(expected) diff --git a/packages/expect/src/__tests__/matchers.test.js b/packages/expect/src/__tests__/matchers.test.js index 0cf0b703f34b..232ca8e06ab7 100644 --- a/packages/expect/src/__tests__/matchers.test.js +++ b/packages/expect/src/__tests__/matchers.test.js @@ -2103,6 +2103,7 @@ describe('toMatchObject()', () => { // these keys a little smarter before reporting accurately. [new Sub(), {a: undefined, b: 'b', c: 'c'}], [withDefineProperty(new Sub(), 'd', 4), {d: 4}], + [{a: 'b', toString() {}}, {toString: jestExpect.any(Function)}], ]); testToMatchSnapshots([ @@ -2150,6 +2151,7 @@ describe('toMatchObject()', () => { {a: 'b', c: 'd', [Symbol.for('jest')]: 'jest'}, {a: 'c', [Symbol.for('jest')]: expect.any(String)}, ], + [{a: 'b'}, {toString: jestExpect.any(Function)}], ]); [ diff --git a/packages/expect/src/utils.ts b/packages/expect/src/utils.ts index 26570c0ae29a..ba24819c6cda 100644 --- a/packages/expect/src/utils.ts +++ b/packages/expect/src/utils.ts @@ -21,8 +21,19 @@ type GetPath = { value?: unknown; }; -const hasPropertyInObject = (object: object, key: string) => - typeof object === 'object' && key in object; +const hasPropertyInObject = (object: object, key: string) => { + const shouldTerminate = + !object || typeof object !== 'object' || object === Object.prototype; + + if (shouldTerminate) { + return false; + } + + return ( + Object.prototype.hasOwnProperty.call(object, key) || + hasPropertyInObject(Object.getPrototypeOf(object), key) + ); +}; // Return whether object instance inherits getter from its class. const hasGetterFromConstructor = (object: object, key: string) => {