From 4d2f49332ec8bbd89afc6e6147b6669618d5b217 Mon Sep 17 00:00:00 2001 From: SixTfour Date: Fri, 6 Sep 2019 12:09:20 -0500 Subject: [PATCH 1/6] fix: fixed toHaveProperty returning false positives --- CHANGELOG.md | 1 + .../__tests__/__snapshots__/matchers.test.js.snap | 13 +++++++++++++ packages/expect/src/__tests__/matchers.test.js | 2 +- packages/expect/src/__tests__/utils.test.js | 8 ++++++++ packages/expect/src/matchers.ts | 9 +++++---- packages/expect/src/utils.ts | 5 +++-- 6 files changed, 31 insertions(+), 7 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 512742a01103..281580fc6640 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,7 @@ ### Fixes +- `[expect]` Fix false positives when looking for undefined prop ([#8786](https://github.com/facebook/jest/issues/8786)) - `[expect]` Fix circular references in iterable equality ([#8160](https://github.com/facebook/jest/pull/8160)) - `[jest-changed-files]` Change method of obtaining git root ([#8052](https://github.com/facebook/jest/pull/8052)) - `[jest-each]` Fix test function type ([#8145](https://github.com/facebook/jest/pull/8145)) diff --git a/packages/expect/src/__tests__/__snapshots__/matchers.test.js.snap b/packages/expect/src/__tests__/__snapshots__/matchers.test.js.snap index 34be35ea0c08..bd71fba37293 100644 --- a/packages/expect/src/__tests__/__snapshots__/matchers.test.js.snap +++ b/packages/expect/src/__tests__/__snapshots__/matchers.test.js.snap @@ -3027,6 +3027,19 @@ Difference: Comparing two different types of values. Expected undefined but received number." `; +exports[`.toHaveProperty() {pass: false} expect({"a": {}}).toHaveProperty('a.b', undefined) 1`] = ` +"expect(object).toHaveProperty(path, value) + +Expected the object: + {\\"a\\": {}} +To have a nested property: + \\"a.b\\" +With a value of: + undefined +Received: + object.a: {}" +`; + exports[`.toHaveProperty() {pass: false} expect({"a": 1}).toHaveProperty('a.b.c.d') 1`] = ` "expect(object).toHaveProperty(path) diff --git a/packages/expect/src/__tests__/matchers.test.js b/packages/expect/src/__tests__/matchers.test.js index 953de2f2fcaf..a88e61548c79 100644 --- a/packages/expect/src/__tests__/matchers.test.js +++ b/packages/expect/src/__tests__/matchers.test.js @@ -1377,7 +1377,7 @@ describe('.toHaveProperty()', () => { [{a: {b: {c: 5}}}, 'a.b', {c: 4}], [new Foo(), 'a', 'a'], [new Foo(), 'b', undefined], - // [{a: {}}, 'a.b', undefined], // wait until Jest 25 + [{a: {}}, 'a.b', undefined], // wait until Jest 25 ].forEach(([obj, keyPath, value]) => { test(`{pass: false} expect(${stringify( obj, diff --git a/packages/expect/src/__tests__/utils.test.js b/packages/expect/src/__tests__/utils.test.js index 7ff609a8ab87..b51df1045205 100644 --- a/packages/expect/src/__tests__/utils.test.js +++ b/packages/expect/src/__tests__/utils.test.js @@ -21,6 +21,7 @@ const { describe('getPath()', () => { test('property exists', () => { expect(getPath({a: {b: {c: 5}}}, 'a.b.c')).toEqual({ + endPropIsDefined: true, hasEndProp: true, lastTraversedObject: {c: 5}, traversedPath: ['a', 'b', 'c'], @@ -28,6 +29,7 @@ describe('getPath()', () => { }); expect(getPath({a: {b: {c: {d: 1}}}}, 'a.b.c.d')).toEqual({ + endPropIsDefined: true, hasEndProp: true, lastTraversedObject: {d: 1}, traversedPath: ['a', 'b', 'c', 'd'], @@ -37,6 +39,7 @@ describe('getPath()', () => { test('property doesnt exist', () => { expect(getPath({a: {b: {}}}, 'a.b.c')).toEqual({ + endPropIsDefined: false, hasEndProp: false, lastTraversedObject: {}, traversedPath: ['a', 'b'], @@ -46,6 +49,7 @@ describe('getPath()', () => { test('property exist but undefined', () => { expect(getPath({a: {b: {c: undefined}}}, 'a.b.c')).toEqual({ + endPropIsDefined: true, hasEndProp: true, lastTraversedObject: {c: undefined}, traversedPath: ['a', 'b', 'c'], @@ -64,12 +68,14 @@ describe('getPath()', () => { } expect(getPath(new A(), 'a')).toEqual({ + endPropIsDefined: true, hasEndProp: true, lastTraversedObject: new A(), traversedPath: ['a'], value: 'a', }); expect(getPath(new A(), 'b.c')).toEqual({ + endPropIsDefined: true, hasEndProp: true, lastTraversedObject: {c: 'c'}, traversedPath: ['b', 'c'], @@ -82,6 +88,7 @@ describe('getPath()', () => { A.prototype.a = 'a'; expect(getPath(new A(), 'a')).toEqual({ + endPropIsDefined: true, hasEndProp: true, lastTraversedObject: new A(), traversedPath: ['a'], @@ -100,6 +107,7 @@ describe('getPath()', () => { test('empty object at the end', () => { expect(getPath({a: {b: {c: {}}}}, 'a.b.c.d')).toEqual({ + endPropIsDefined: false, hasEndProp: false, lastTraversedObject: {}, traversedPath: ['a', 'b', 'c'], diff --git a/packages/expect/src/matchers.ts b/packages/expect/src/matchers.ts index 60c53d9ec436..e5c5553d7e7c 100644 --- a/packages/expect/src/matchers.ts +++ b/packages/expect/src/matchers.ts @@ -648,11 +648,12 @@ const matchers: MatchersObject = { } const result = getPath(object, keyPath); - const {lastTraversedObject, hasEndProp} = result; + const {lastTraversedObject, endPropIsDefined, hasEndProp} = result; - const pass = valuePassed - ? equals(result.value, value, [iterableEquality]) - : hasEndProp; + const pass = + valuePassed && endPropIsDefined + ? equals(result.value, value, [iterableEquality]) + : hasEndProp; const traversedPath = result.traversedPath.join('.'); diff --git a/packages/expect/src/utils.ts b/packages/expect/src/utils.ts index 2f3f97ffe2ea..8e52f6ef7ead 100644 --- a/packages/expect/src/utils.ts +++ b/packages/expect/src/utils.ts @@ -16,6 +16,7 @@ import { type GetPath = { hasEndProp?: boolean; + endPropIsDefined?: boolean; lastTraversedObject: unknown; traversedPath: Array; value?: unknown; @@ -84,8 +85,8 @@ export const getPath = ( // Does object have the property with an undefined value? // Although primitive values support bracket notation (above) // they would throw TypeError for in operator (below). - result.hasEndProp = - newObject !== undefined || (!isPrimitive(object) && prop in object); + result.endPropIsDefined = !isPrimitive(object) && prop in object; + result.hasEndProp = newObject !== undefined || result.endPropIsDefined; if (!result.hasEndProp) { result.traversedPath.shift(); From 613e627ca689300982cf109fbc70caf6bdfea2f1 Mon Sep 17 00:00:00 2001 From: sixtfour Date: Fri, 6 Sep 2019 14:41:56 -0500 Subject: [PATCH 2/6] fix: fixed merge conflicts --- .../__snapshots__/matchers.test.js.snap | 27 +++++-------------- .../expect/src/__tests__/matchers.test.js | 1 - packages/expect/src/matchers.ts | 11 ++++---- 3 files changed, 11 insertions(+), 28 deletions(-) diff --git a/packages/expect/src/__tests__/__snapshots__/matchers.test.js.snap b/packages/expect/src/__tests__/__snapshots__/matchers.test.js.snap index 2dd219441565..725a42051761 100644 --- a/packages/expect/src/__tests__/__snapshots__/matchers.test.js.snap +++ b/packages/expect/src/__tests__/__snapshots__/matchers.test.js.snap @@ -3084,16 +3084,13 @@ Received value: 3" `; exports[`.toHaveProperty() {pass: false} expect({"a": {}}).toHaveProperty('a.b', undefined) 1`] = ` -"expect(object).toHaveProperty(path, value) +"expect(received).toHaveProperty(path, value) + +Expected path: \\"a.b\\" +Received path: \\"a\\" -Expected the object: - {\\"a\\": {}} -To have a nested property: - \\"a.b\\" -With a value of: - undefined -Received: - object.a: {}" +Expected value: undefined +Received value: {}" `; exports[`.toHaveProperty() {pass: false} expect({"a": 1}).toHaveProperty('a.b.c.d') 1`] = ` @@ -3352,18 +3349,6 @@ Expected path: \\"a.b\\" Expected value: not undefined" `; -exports[`.toHaveProperty() {pass: true} expect({"a": {}}).toHaveProperty('a.b', undefined) 1`] = ` -"expect(received).not.toHaveProperty(path, value) - -Expected path: \\"a.b\\" -Received path: \\"a\\" - -Expected value: not undefined -Received value: {} - -Because a positive assertion passes for expected value undefined if the property does not exist, this negative assertion fails unless the property does exist and has a defined value" -`; - exports[`.toHaveProperty() {pass: true} expect({"a": 0}).toHaveProperty('a') 1`] = ` "expect(received).not.toHaveProperty(path) diff --git a/packages/expect/src/__tests__/matchers.test.js b/packages/expect/src/__tests__/matchers.test.js index 0e089519bee4..7a556528cb05 100644 --- a/packages/expect/src/__tests__/matchers.test.js +++ b/packages/expect/src/__tests__/matchers.test.js @@ -1659,7 +1659,6 @@ describe('.toHaveProperty()', () => { [{a: {b: [1, 2, 3]}}, ['a', 'b', 1], expect.any(Number)], [{a: 0}, 'a', 0], [{a: {b: undefined}}, 'a.b', undefined], - [{a: {}}, 'a.b', undefined], // delete for breaking change in future major [{a: {b: {c: 5}}}, 'a.b', {c: 5}], [Object.assign(Object.create(null), {property: 1}), 'property', 1], [new Foo(), 'a', undefined], diff --git a/packages/expect/src/matchers.ts b/packages/expect/src/matchers.ts index 036410b66d17..0b1ddca4c186 100644 --- a/packages/expect/src/matchers.ts +++ b/packages/expect/src/matchers.ts @@ -728,18 +728,17 @@ const matchers: MatchersObject = { ), ); } - - const result = getPath(object, expectedPath); - const {lastTraversedObject, endPropIsDefined, hasEndProp} = result; + const result = getPath(received, expectedPath); + const {lastTraversedObject, endPropIsDefined, hasEndProp, value} = result; const receivedPath = result.traversedPath; const hasCompletePath = receivedPath.length === expectedPathLength; const receivedValue = hasCompletePath ? result.value : lastTraversedObject; const pass = - valuePassed && endPropIsDefined - ? equals(result.value, value, [iterableEquality]) - : hasEndProp; + hasValue && endPropIsDefined + ? equals(value, expectedValue, [iterableEquality]) + : Boolean(hasEndProp); const message = pass ? () => From ae5ff366a59400d965383355a25df95fc32e9dc5 Mon Sep 17 00:00:00 2001 From: sixtfour Date: Fri, 6 Sep 2019 15:11:25 -0500 Subject: [PATCH 3/6] fix: fixed extra white space --- packages/expect/src/matchers.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/expect/src/matchers.ts b/packages/expect/src/matchers.ts index 0b1ddca4c186..a130a0e8856d 100644 --- a/packages/expect/src/matchers.ts +++ b/packages/expect/src/matchers.ts @@ -728,7 +728,7 @@ const matchers: MatchersObject = { ), ); } - + const result = getPath(received, expectedPath); const {lastTraversedObject, endPropIsDefined, hasEndProp, value} = result; const receivedPath = result.traversedPath; From c72053143efd0267c0a94d19123804a00ee95e51 Mon Sep 17 00:00:00 2001 From: sixtfour Date: Mon, 9 Sep 2019 09:22:14 -0500 Subject: [PATCH 4/6] chore: removed comment from matchers.test.js. Moved comment from changelog to master --- CHANGELOG.md | 2 +- packages/expect/src/__tests__/matchers.test.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d893d8c3795a..a663ba09bc55 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -20,6 +20,7 @@ ### Fixes +- `[expect]` Fix false positives when looking for undefined prop ([#8786](https://github.com/facebook/jest/issues/8786)) - `[expect]` Display expectedDiff more carefully in toBeCloseTo ([#8389](https://github.com/facebook/jest/pull/8389)) - `[jest-diff]` Do not inverse format if line consists of one change ([#8903](https://github.com/facebook/jest/pull/8903)) - `[jest-fake-timers]` `getTimerCount` will not include cancelled immediates ([#8764](https://github.com/facebook/jest/pull/8764)) @@ -197,7 +198,6 @@ ### Fixes -- `[expect]` Fix false positives when looking for undefined prop ([#8786](https://github.com/facebook/jest/issues/8786)) - `[jest-circus]` Fix test retries with beforeAll/beforeEach failures ([#8227](https://github.com/facebook/jest/pull/8227)) - `[expect]` Fix circular references in iterable equality ([#8160](https://github.com/facebook/jest/pull/8160)) - `[jest-changed-files]` Change method of obtaining git root ([#8052](https://github.com/facebook/jest/pull/8052)) diff --git a/packages/expect/src/__tests__/matchers.test.js b/packages/expect/src/__tests__/matchers.test.js index 7a556528cb05..ad258c47f494 100644 --- a/packages/expect/src/__tests__/matchers.test.js +++ b/packages/expect/src/__tests__/matchers.test.js @@ -1698,7 +1698,7 @@ describe('.toHaveProperty()', () => { [{a: {b: {c: 5}}}, 'a.b', {c: 4}], [new Foo(), 'a', 'a'], [new Foo(), 'b', undefined], - [{a: {}}, 'a.b', undefined], // wait until Jest 25 + [{a: {}}, 'a.b', undefined], ].forEach(([obj, keyPath, value]) => { test(`{pass: false} expect(${stringify( obj, From b18316a4ae969f7ead86d11f5c2304addd692e7f Mon Sep 17 00:00:00 2001 From: sixtfour Date: Mon, 9 Sep 2019 10:07:55 -0500 Subject: [PATCH 5/6] fix: add breaking tag to changelog --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a663ba09bc55..76e878cc0e49 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -20,7 +20,7 @@ ### Fixes -- `[expect]` Fix false positives when looking for undefined prop ([#8786](https://github.com/facebook/jest/issues/8786)) +- `[expect]` [**BREAKING**] Fix false positives when looking for undefined prop ([#8786](https://github.com/facebook/jest/issues/8786)) - `[expect]` Display expectedDiff more carefully in toBeCloseTo ([#8389](https://github.com/facebook/jest/pull/8389)) - `[jest-diff]` Do not inverse format if line consists of one change ([#8903](https://github.com/facebook/jest/pull/8903)) - `[jest-fake-timers]` `getTimerCount` will not include cancelled immediates ([#8764](https://github.com/facebook/jest/pull/8764)) From 9340f97f8f961c19d6a8841218e23efaab3e8ea5 Mon Sep 17 00:00:00 2001 From: Simen Bekkhus Date: Wed, 23 Feb 2022 15:59:19 +0100 Subject: [PATCH 6/6] update changelog for renamed package --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3f9ee703a98f..52e76be06f7e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,7 +4,6 @@ - `[babel-jest]` Export `createTransformer` function ([#12399](https://github.com/facebook/jest/pull/12399)) - `[expect]` Expose `AsymmetricMatchers`, `MatcherFunction` and `MatcherFunctionWithState` interfaces ([#12363](https://github.com/facebook/jest/pull/12363), [#12376](https://github.com/facebook/jest/pull/12376)) -- `[expect]` [**BREAKING**] Fix false positives when looking for `undefined` prop ([#8923](https://github.com/facebook/jest/pull/8923)) - `[jest-cli, jest-config]` [**BREAKING**] Remove `testURL` config, use `testEnvironmentOptions.url` instead ([#10797](https://github.com/facebook/jest/pull/10797)) - `[jest-config]` [**BREAKING**] Stop shipping `jest-environment-jsdom` by default ([#12354](https://github.com/facebook/jest/pull/12354)) - `[jest-config]` [**BREAKING**] Stop shipping `jest-jasmine2` by default ([#12355](https://github.com/facebook/jest/pull/12355)) @@ -36,6 +35,7 @@ - `[jest-config]` Pass `moduleTypes` to `ts-node` to enforce CJS when transpiling ([#12397](https://github.com/facebook/jest/pull/12397)) - `[jest-config, jest-haste-map]` Allow searching for tests in `node_modules` by exposing `retainAllFiles` ([#11084](https://github.com/facebook/jest/pull/11084)) - `[jest-environment-jsdom]` Make `jsdom` accessible to extending environments again ([#12232](https://github.com/facebook/jest/pull/12232)) +- `[@jest/expect-utils]` [**BREAKING**] Fix false positives when looking for `undefined` prop ([#8923](https://github.com/facebook/jest/pull/8923)) - `[jest-haste-map]` Don't use partial results if file crawl errors ([#12420](https://github.com/facebook/jest/pull/12420)) - `[jest-jasmine2, jest-types]` [**BREAKING**] Move all `jasmine` specific types from `@jest/types` to its own package ([#12125](https://github.com/facebook/jest/pull/12125)) - `[jest-matcher-utils]` Pass maxWidth to `pretty-format` to avoid printing every element in arrays by default ([#12402](https://github.com/facebook/jest/pull/12402))