From 0937d2f7b9cb3f386965e8787da4c18b2c2f651c Mon Sep 17 00:00:00 2001 From: Dmitry Gozman Date: Mon, 9 Dec 2024 08:59:01 -0800 Subject: [PATCH] fix(types): update types for test.extend (#33784) --- packages/playwright/types/test.d.ts | 19 +++--- .../ct-react17/tests/render.spec.tsx | 1 + tests/playwright-test/types.spec.ts | 65 +++++++++++++++++++ utils/generate_types/overrides-test.d.ts | 19 +++--- 4 files changed, 84 insertions(+), 20 deletions(-) diff --git a/packages/playwright/types/test.d.ts b/packages/playwright/types/test.d.ts index fed6a1bdeb1c2..e9764b6eadba4 100644 --- a/packages/playwright/types/test.d.ts +++ b/packages/playwright/types/test.d.ts @@ -1891,7 +1891,7 @@ type ConditionBody = (args: TestArgs) => boolean; * ``` * */ -export interface TestType { +export interface TestType { /** * Declares a test. * - `test(title, body)` @@ -5632,7 +5632,7 @@ export interface TestType(fixtures: Fixtures): TestType; + extend(fixtures: Fixtures): TestType; /** * Returns information about the currently running test. This method can only be called during the test execution, * otherwise it throws. @@ -5653,19 +5653,18 @@ export interface TestType = (args: Args, use: (r: R) => Promise, testInfo: TestInfo) => any; -export type WorkerFixture = (args: Args, use: (r: R) => Promise, workerInfo: WorkerInfo) => any; -type TestFixtureValue = Exclude | TestFixture; -type WorkerFixtureValue = Exclude | WorkerFixture; -export type Fixtures = { +export type TestFixture = (args: Args, use: (r: R) => Promise, testInfo: TestInfo) => any; +export type WorkerFixture = (args: Args, use: (r: R) => Promise, workerInfo: WorkerInfo) => any; +type TestFixtureValue = Exclude | TestFixture; +type WorkerFixtureValue = Exclude | WorkerFixture; +export type Fixtures = { [K in keyof PW]?: WorkerFixtureValue | [WorkerFixtureValue, { scope: 'worker', timeout?: number | undefined, title?: string, box?: boolean }]; } & { [K in keyof PT]?: TestFixtureValue | [TestFixtureValue, { scope: 'test', timeout?: number | undefined, title?: string, box?: boolean }]; } & { - [K in keyof W]?: [WorkerFixtureValue, { scope: 'worker', auto?: boolean, option?: boolean, timeout?: number | undefined, title?: string, box?: boolean }]; + [K in Exclude]?: [WorkerFixtureValue, { scope: 'worker', auto?: boolean, option?: boolean, timeout?: number | undefined, title?: string, box?: boolean }]; } & { - [K in keyof T]?: TestFixtureValue | [TestFixtureValue, { scope?: 'test', auto?: boolean, option?: boolean, timeout?: number | undefined, title?: string, box?: boolean }]; + [K in Exclude]?: TestFixtureValue | [TestFixtureValue, { scope?: 'test', auto?: boolean, option?: boolean, timeout?: number | undefined, title?: string, box?: boolean }]; }; type BrowserName = 'chromium' | 'firefox' | 'webkit'; diff --git a/tests/components/ct-react17/tests/render.spec.tsx b/tests/components/ct-react17/tests/render.spec.tsx index ec9405c82feee..46e3ea9abf121 100644 --- a/tests/components/ct-react17/tests/render.spec.tsx +++ b/tests/components/ct-react17/tests/render.spec.tsx @@ -31,6 +31,7 @@ test('render an empty component', async ({ mount, page }) => { const testWithServer = test.extend(serverFixtures); testWithServer( 'components routing should go through context', + // @ts-ignore "serverFixtures" are imported from the impl without any types async ({ mount, context, server }) => { server.setRoute('/hello', (req: any, res: any) => { res.write('served via server'); diff --git a/tests/playwright-test/types.spec.ts b/tests/playwright-test/types.spec.ts index c34c586f7a0ad..e9fea813f066c 100644 --- a/tests/playwright-test/types.spec.ts +++ b/tests/playwright-test/types.spec.ts @@ -115,6 +115,71 @@ test('should check types of fixtures', async ({ runTSC }) => { await use(x); }, }); + + base.extend({ + page: async ({ page }) => { + type IsPage = (typeof page) extends Page ? true : never; + const isPage: IsPage = true; + }, + }); + + base.extend<{ myFixture: (arg: number) => void }>({ + page: async ({ page }) => { + type IsPage = (typeof page) extends Page ? true : never; + const isPage: IsPage = true; + }, + }); + + base.extend({ + // @ts-expect-error + myFixture: async ({ page }) => { + type IsPage = (typeof page) extends Page ? true : never; + const isPage: IsPage = true; + } + }); + + base.extend<{ myFixture: (arg: number) => void }>({ + myFixture: async ({ page }) => { + type IsPage = (typeof page) extends Page ? true : never; + const isPage: IsPage = true; + } + }); + + base.extend({ + page: async ({ page }) => { + type IsPage = (typeof page) extends Page ? true : never; + const isPage: IsPage = true; + }, + // @ts-expect-error + myFixture: async ({ page }) => { + type IsPage = (typeof page) extends Page ? true : never; + const isPage: IsPage = true; + } + }); + + base.extend<{ myFixture: (arg: number) => void }>({ + page: async ({ page }) => { + type IsPage = (typeof page) extends Page ? true : never; + const isPage: IsPage = true; + }, + myFixture: async ({ page }) => { + type IsPage = (typeof page) extends Page ? true : never; + const isPage: IsPage = true; + } + }); + + base.extend<{ myFixture: (arg: number) => void }>({ + // @ts-expect-error + myFixture: (arg: number) => {}, + }); + + base.extend<{ myFixture: (arg: number) => void }>({ + myFixture: async (_, use) => { + use((arg: number) => {}); + // @ts-expect-error + use((arg: string) => {}); + } + }); `, 'playwright.config.ts': ` import { MyOptions } from './helper'; diff --git a/utils/generate_types/overrides-test.d.ts b/utils/generate_types/overrides-test.d.ts index 5775917382e97..78df18f0147cc 100644 --- a/utils/generate_types/overrides-test.d.ts +++ b/utils/generate_types/overrides-test.d.ts @@ -78,7 +78,7 @@ export type TestDetails = { type TestBody = (args: TestArgs, testInfo: TestInfo) => Promise | void; type ConditionBody = (args: TestArgs) => boolean; -export interface TestType { +export interface TestType { (title: string, body: TestBody): void; (title: string, details: TestDetails, body: TestBody): void; @@ -164,23 +164,22 @@ export interface TestType): void; step(title: string, body: () => T | Promise, options?: { box?: boolean, location?: Location, timeout?: number }): Promise; expect: Expect<{}>; - extend(fixtures: Fixtures): TestType; + extend(fixtures: Fixtures): TestType; info(): TestInfo; } -type KeyValue = { [key: string]: any }; -export type TestFixture = (args: Args, use: (r: R) => Promise, testInfo: TestInfo) => any; -export type WorkerFixture = (args: Args, use: (r: R) => Promise, workerInfo: WorkerInfo) => any; -type TestFixtureValue = Exclude | TestFixture; -type WorkerFixtureValue = Exclude | WorkerFixture; -export type Fixtures = { +export type TestFixture = (args: Args, use: (r: R) => Promise, testInfo: TestInfo) => any; +export type WorkerFixture = (args: Args, use: (r: R) => Promise, workerInfo: WorkerInfo) => any; +type TestFixtureValue = Exclude | TestFixture; +type WorkerFixtureValue = Exclude | WorkerFixture; +export type Fixtures = { [K in keyof PW]?: WorkerFixtureValue | [WorkerFixtureValue, { scope: 'worker', timeout?: number | undefined, title?: string, box?: boolean }]; } & { [K in keyof PT]?: TestFixtureValue | [TestFixtureValue, { scope: 'test', timeout?: number | undefined, title?: string, box?: boolean }]; } & { - [K in keyof W]?: [WorkerFixtureValue, { scope: 'worker', auto?: boolean, option?: boolean, timeout?: number | undefined, title?: string, box?: boolean }]; + [K in Exclude]?: [WorkerFixtureValue, { scope: 'worker', auto?: boolean, option?: boolean, timeout?: number | undefined, title?: string, box?: boolean }]; } & { - [K in keyof T]?: TestFixtureValue | [TestFixtureValue, { scope?: 'test', auto?: boolean, option?: boolean, timeout?: number | undefined, title?: string, box?: boolean }]; + [K in Exclude]?: TestFixtureValue | [TestFixtureValue, { scope?: 'test', auto?: boolean, option?: boolean, timeout?: number | undefined, title?: string, box?: boolean }]; }; type BrowserName = 'chromium' | 'firefox' | 'webkit';