diff --git a/packages/playwright/types/test.d.ts b/packages/playwright/types/test.d.ts index 73cc415a87..9d72e49c57 100644 --- a/packages/playwright/types/test.d.ts +++ b/packages/playwright/types/test.d.ts @@ -6573,6 +6573,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; @@ -6584,8 +6589,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 63928e86fb..f620f2e342 100644 --- a/tests/playwright-test/expect.spec.ts +++ b/tests/playwright-test/expect.spec.ts @@ -910,12 +910,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: () => '' }; } }); @@ -924,12 +933,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)); }); ` }); @@ -945,12 +961,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: () => '' }; } }); @@ -959,6 +984,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 }); diff --git a/utils/generate_types/overrides-test.d.ts b/utils/generate_types/overrides-test.d.ts index 90ef7fa75a..7218fd2b5b 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 {