From 8705e6b275a53ce313928459c4a9959b9ce0254b Mon Sep 17 00:00:00 2001 From: Trivikram Kamat <16024985+trivikr@users.noreply.github.com> Date: Sat, 11 Feb 2023 06:13:52 -0800 Subject: [PATCH] fix(expect): accept array index as number in toHaveProperty (#2808) * test(expect): add toHaveProperty array index string * test(expect): add toHaveProperty array index number * fix: coerce key to string primitive in expect.toHaveProperty * fix: accept string or number array in toHaveProperty property * test: remove ts-expect-error from toHaveProperty test for array index number * test: expect.toHaveProperty with object key '0' * fix: snapshot for complex object in jest-expect.test.ts * docs: expect.toHaveProperty deep referencing using an array containing the keyPath --- docs/api/expect.md | 6 +++++- packages/expect/src/jest-expect.ts | 4 ++-- packages/vitest/src/types/global.ts | 2 +- test/core/test/jest-expect.test.ts | 13 ++++++++++++- 4 files changed, 20 insertions(+), 5 deletions(-) diff --git a/docs/api/expect.md b/docs/api/expect.md index 5bcb3c3a8fa6..c89f9c0d14af 100644 --- a/docs/api/expect.md +++ b/docs/api/expect.md @@ -474,6 +474,10 @@ type Awaitable = T | PromiseLike expect(invoice).toHaveProperty('items[0].type', 'apples') expect(invoice).toHaveProperty('items.0.type', 'apples') // dot notation also works + // Deep referencing using an array containing the keyPath + expect(invoice).toHaveProperty(['items', 0, 'type'], 'apples') + expect(invoice).toHaveProperty(['items', '0', 'type'], 'apples') // string notation also works + // Wrap your key in an array to avoid the key from being parsed as a deep reference expect(invoice).toHaveProperty(['P.O'], '12345') }) @@ -1270,4 +1274,4 @@ type Awaitable = T | PromiseLike :::tip If you want to know more, checkout [guide on extending matchers](/guide/extending-matchers). - ::: \ No newline at end of file + ::: diff --git a/packages/expect/src/jest-expect.ts b/packages/expect/src/jest-expect.ts index 3528aa157f3a..6e03309c4237 100644 --- a/packages/expect/src/jest-expect.ts +++ b/packages/expect/src/jest-expect.ts @@ -278,9 +278,9 @@ export const JestChaiExpect: ChaiPlugin = (chai, utils) => { return this.have.length(length) }) // destructuring, because it checks `arguments` inside, and value is passing as `undefined` - def('toHaveProperty', function (...args: [property: string | string[], value?: any]) { + def('toHaveProperty', function (...args: [property: string | (string | number)[], value?: any]) { if (Array.isArray(args[0])) - args[0] = args[0].map(key => key.replace(/([.[\]])/g, '\\$1')).join('.') + args[0] = args[0].map(key => String(key).replace(/([.[\]])/g, '\\$1')).join('.') const actual = this._obj const [propertyName, expected] = args diff --git a/packages/vitest/src/types/global.ts b/packages/vitest/src/types/global.ts index bfcd580dfef2..3e7c3936bfab 100644 --- a/packages/vitest/src/types/global.ts +++ b/packages/vitest/src/types/global.ts @@ -97,7 +97,7 @@ declare global { toBeInstanceOf(expected: E): void toBeCalledTimes(times: number): void toHaveLength(length: number): void - toHaveProperty(property: string | string[], value?: E): void + toHaveProperty(property: string | (string | number)[], value?: E): void toBeCloseTo(number: number, numDigits?: number): void toHaveBeenCalledTimes(times: number): void toHaveBeenCalledOnce(): void diff --git a/test/core/test/jest-expect.test.ts b/test/core/test/jest-expect.test.ts index ae44403dc0e2..cfb35980ca30 100644 --- a/test/core/test/jest-expect.test.ts +++ b/test/core/test/jest-expect.test.ts @@ -205,6 +205,7 @@ describe('jest-expect', () => { const foo = {} const complex = { + '0': 'zero', 'foo': 1, 'foo.bar[0]': 'baz', 'a-b': true, @@ -227,12 +228,22 @@ describe('jest-expect', () => { expect(complex).toHaveProperty('a-b') expect(complex).toHaveProperty('a-b-1.0.0') + expect(complex).toHaveProperty('0') + expect(complex).toHaveProperty('0', 'zero') + expect(complex).toHaveProperty(['0']) + expect(complex).toHaveProperty(['0'], 'zero') + expect(complex).toHaveProperty([0]) + expect(complex).toHaveProperty([0], 'zero') expect(complex).toHaveProperty('foo') expect(complex).toHaveProperty('foo', 1) expect(complex).toHaveProperty('bar.foo', 'foo') expect(complex).toHaveProperty('bar.arr[0]') expect(complex).toHaveProperty('bar.arr[1].zoo', 'monkey') expect(complex).toHaveProperty('bar.arr.0') + expect(complex).toHaveProperty(['bar', 'arr', '0']) + expect(complex).toHaveProperty(['bar', 'arr', '0'], 'first') + expect(complex).toHaveProperty(['bar', 'arr', 0]) + expect(complex).toHaveProperty(['bar', 'arr', 0], 'first') expect(complex).toHaveProperty('bar.arr.1.zoo', 'monkey') expect(complex).toHaveProperty(['bar', 'arr', '1', 'zoo'], 'monkey') expect(complex).toHaveProperty(['foo.bar[0]'], 'baz') @@ -248,7 +259,7 @@ describe('jest-expect', () => { expect(() => { expect(complex).toHaveProperty('a-b', false) - }).toThrowErrorMatchingInlineSnapshot('"expected { foo: 1, \'foo.bar[0]\': \'baz\', …(3) } to have property \\"a-b\\" with value false"') + }).toThrowErrorMatchingInlineSnapshot('"expected { \'0\': \'zero\', foo: 1, …(4) } to have property \\"a-b\\" with value false"') }) it('assertions', () => {