diff --git a/packages/playwright/types/test.d.ts b/packages/playwright/types/test.d.ts index f158baac36..bfbda09724 100644 --- a/packages/playwright/types/test.d.ts +++ b/packages/playwright/types/test.d.ts @@ -6571,6 +6571,11 @@ type PollMatchers = { not: PollMatchers; } & BaseMatchers & ToUserMatcherObject; +type ExtendedAsymmetricMatchers = { + [K in keyof M]: M[K] extends (this: ExpectMatcherState, received: any, ...args: infer P) => any ? (...args: P) => AsymmetricMatcher + : never +} + export type Expect = { (actual: T, messageOrOptions?: string | { message?: string }): MakeMatchers; soft: (actual: T, messageOrOptions?: string | { message?: string }) => MakeMatchers; @@ -6582,8 +6587,8 @@ export type Expect = { soft?: boolean, }) => Expect; getState(): unknown; - not: Omit; -} & AsymmetricMatchers; + not: Omit, 'any' | 'anything'>; +} & AsymmetricMatchers & ExtendedAsymmetricMatchers; // --- BEGINGLOBAL --- declare global { diff --git a/tests/playwright-test/expect.spec.ts b/tests/playwright-test/expect.spec.ts index a541ad9c96..a14b2f41bf 100644 --- a/tests/playwright-test/expect.spec.ts +++ b/tests/playwright-test/expect.spec.ts @@ -875,12 +875,21 @@ test('should support mergeExpects (TSC)', async ({ runTSC }) => { const expect1 = baseExpect.extend({ async toBeAGoodPage(page: Page, x: number) { return { pass: true, message: () => '' }; + }, + matchFoo(received: unknown, expected: string) { + return { pass: true, message: () => '' }; + }, + toBeFoo(received: unknown) { + return { pass: true, message: () => '' }; } }); const expect2 = baseExpect.extend({ async toBeABadPage(page: Page, y: string) { return { pass: true, message: () => '' }; + }, + matchBar(received: unknown, expected: string) { + return { pass: true, message: () => '' }; } }); @@ -889,12 +898,19 @@ test('should support mergeExpects (TSC)', async ({ runTSC }) => { test('custom matchers', async ({ page }) => { await expect(page).toBeAGoodPage(123); await expect(page).toBeABadPage('123'); + await expect('foo').toEqual(expect.matchFoo('123')); + await expect('bar').not.toEqual(expect.not.matchBar('123')); + await expect({ name: 'Foo' }).toMatchObject({ name: expect.toBeFoo() }); // @ts-expect-error await expect(page).toBeAMediocrePage(); // @ts-expect-error await expect(page).toBeABadPage(123); // @ts-expect-error await expect(page).toBeAGoodPage('123'); + // @ts-expect-error + expect('foo').toEqual(expect.matchFoo(123)); + // @ts-expect-error + expect('bar').not.toEqual(expect.not.matchBar(123)); }); ` }); @@ -910,12 +926,21 @@ test('should support mergeExpects', async ({ runInlineTest }) => { const expect1 = baseExpect.extend({ async toBeAGoodPage(page: Page, x: number) { return { pass: true, message: () => '' }; + }, + matchFoo(received: unknown, expected: string) { + return { pass: true, message: () => '' }; + }, + toBeFoo(received: unknown) { + return { pass: true, message: () => '' }; } }); const expect2 = baseExpect.extend({ async toBeABadPage(page: Page, y: string) { return { pass: true, message: () => '' }; + }, + matchBar(received: unknown, expected: string) { + return { pass: true, message: () => '' }; } }); @@ -924,6 +949,9 @@ test('should support mergeExpects', async ({ runInlineTest }) => { test('custom matchers', async ({ page }) => { await expect(page).toBeAGoodPage(123); await expect(page).toBeABadPage('123'); + await expect('foo').toEqual(expect.matchFoo('123')); + await expect('bar').not.toEqual(expect.not.matchBar('123')); + await expect({ name: 'Foo' }).toMatchObject({ name: expect.toBeFoo() }); }); ` }, { workers: 1 }); @@ -1068,4 +1096,4 @@ test('expect.extend should be immutable', async ({ runInlineTest }) => { 'foo', 'bar', ]); -}); \ No newline at end of file +}); diff --git a/utils/generate_types/overrides-test.d.ts b/utils/generate_types/overrides-test.d.ts index be1fa7ee37..6049399da1 100644 --- a/utils/generate_types/overrides-test.d.ts +++ b/utils/generate_types/overrides-test.d.ts @@ -411,6 +411,11 @@ type PollMatchers = { not: PollMatchers; } & BaseMatchers & ToUserMatcherObject; +type ExtendedAsymmetricMatchers = { + [K in keyof M]: M[K] extends (this: ExpectMatcherState, received: any, ...args: infer P) => any ? (...args: P) => AsymmetricMatcher + : never +} + export type Expect = { (actual: T, messageOrOptions?: string | { message?: string }): MakeMatchers; soft: (actual: T, messageOrOptions?: string | { message?: string }) => MakeMatchers; @@ -422,8 +427,8 @@ export type Expect = { soft?: boolean, }) => Expect; getState(): unknown; - not: Omit; -} & AsymmetricMatchers; + not: Omit, 'any' | 'anything'>; +} & AsymmetricMatchers & ExtendedAsymmetricMatchers; // --- BEGINGLOBAL --- declare global {