diff --git a/packages/playwright-core/src/server/browserContext.ts b/packages/playwright-core/src/server/browserContext.ts index c8d54471ee..d952c9aa94 100644 --- a/packages/playwright-core/src/server/browserContext.ts +++ b/packages/playwright-core/src/server/browserContext.ts @@ -588,7 +588,7 @@ export function assertBrowserContextIsNotOwned(context: BrowserContext) { export function validateBrowserContextOptions(options: channels.BrowserNewContextParams, browserOptions: BrowserOptions) { if (options.noDefaultViewport && options.deviceScaleFactor !== undefined) throw new Error(`"deviceScaleFactor" option is not supported with null "viewport"`); - if (options.noDefaultViewport && options.isMobile !== undefined) + if (options.noDefaultViewport && !!options.isMobile) throw new Error(`"isMobile" option is not supported with null "viewport"`); if (options.acceptDownloads === undefined) options.acceptDownloads = true; diff --git a/packages/playwright-test/src/index.ts b/packages/playwright-test/src/index.ts index b76917c425..5685cd0272 100644 --- a/packages/playwright-test/src/index.ts +++ b/packages/playwright-test/src/index.ts @@ -127,18 +127,18 @@ const playwrightFixtures: Fixtures = ({ }, { scope: 'worker', timeout: 0 }], acceptDownloads: [({ contextOptions }, use) => use(contextOptions.acceptDownloads ?? true), { option: true }], - bypassCSP: [({ contextOptions }, use) => use(contextOptions.bypassCSP), { option: true }], - colorScheme: [({ contextOptions }, use) => use(contextOptions.colorScheme), { option: true }], + bypassCSP: [({ contextOptions }, use) => use(contextOptions.bypassCSP ?? false), { option: true }], + colorScheme: [({ contextOptions }, use) => use(contextOptions.colorScheme === undefined ? 'light' : contextOptions.colorScheme), { option: true }], deviceScaleFactor: [({ contextOptions }, use) => use(contextOptions.deviceScaleFactor), { option: true }], extraHTTPHeaders: [({ contextOptions }, use) => use(contextOptions.extraHTTPHeaders), { option: true }], geolocation: [({ contextOptions }, use) => use(contextOptions.geolocation), { option: true }], - hasTouch: [({ contextOptions }, use) => use(contextOptions.hasTouch), { option: true }], + hasTouch: [({ contextOptions }, use) => use(contextOptions.hasTouch ?? false), { option: true }], httpCredentials: [({ contextOptions }, use) => use(contextOptions.httpCredentials), { option: true }], - ignoreHTTPSErrors: [({ contextOptions }, use) => use(contextOptions.ignoreHTTPSErrors), { option: true }], - isMobile: [({ contextOptions }, use) => use(contextOptions.isMobile), { option: true }], + ignoreHTTPSErrors: [({ contextOptions }, use) => use(contextOptions.ignoreHTTPSErrors ?? false), { option: true }], + isMobile: [({ contextOptions }, use) => use(contextOptions.isMobile ?? false), { option: true }], javaScriptEnabled: [({ contextOptions }, use) => use(contextOptions.javaScriptEnabled ?? true), { option: true }], locale: [({ contextOptions }, use) => use(contextOptions.locale ?? 'en-US'), { option: true }], - offline: [({ contextOptions }, use) => use(contextOptions.offline), { option: true }], + offline: [({ contextOptions }, use) => use(contextOptions.offline ?? false), { option: true }], permissions: [({ contextOptions }, use) => use(contextOptions.permissions), { option: true }], proxy: [({ contextOptions }, use) => use(contextOptions.proxy), { option: true }], storageState: [({ contextOptions }, use) => use(contextOptions.storageState), { option: true }], diff --git a/packages/playwright-test/types/test.d.ts b/packages/playwright-test/types/test.d.ts index 6afe389f07..650b8439c4 100644 --- a/packages/playwright-test/types/test.d.ts +++ b/packages/playwright-test/types/test.d.ts @@ -3362,7 +3362,7 @@ export interface PlaywrightWorkerOptions { * [Firefox](https://developer.mozilla.org/en-US/docs/Mozilla/Firefox/Headless_mode). Defaults to `true` unless the * `devtools` option is `true`. */ - headless: boolean | undefined; + headless: boolean; /** * Browser distribution channel. Supported values are "chrome", "chrome-beta", "chrome-dev", "chrome-canary", * "msedge", "msedge-beta", "msedge-dev", "msedge-canary". Read more about using @@ -3468,17 +3468,17 @@ export interface PlaywrightTestOptions { /** * Whether to automatically download all the attachments. Defaults to `true` where all the downloads are accepted. */ - acceptDownloads: boolean | undefined; + acceptDownloads: boolean; /** * Toggles bypassing page's Content-Security-Policy. */ - bypassCSP: boolean | undefined; + bypassCSP: boolean; /** * Emulates `'prefers-colors-scheme'` media feature, supported values are `'light'`, `'dark'`, `'no-preference'`. See * [page.emulateMedia([options])](https://playwright.dev/docs/api/class-page#page-emulate-media) for more details. * Passing `null` resets emulation to system defaults. Defaults to `'light'`. */ - colorScheme: ColorScheme | undefined; + colorScheme: ColorScheme; /** * Specify device scale factor (can be thought of as dpr). Defaults to `1`. */ @@ -3491,7 +3491,7 @@ export interface PlaywrightTestOptions { /** * Specifies if viewport supports touch events. Defaults to false. */ - hasTouch: boolean | undefined; + hasTouch: boolean; /** * Credentials for [HTTP authentication](https://developer.mozilla.org/en-US/docs/Web/HTTP/Authentication). */ @@ -3499,16 +3499,16 @@ export interface PlaywrightTestOptions { /** * Whether to ignore HTTPS errors when sending network requests. Defaults to `false`. */ - ignoreHTTPSErrors: boolean | undefined; + ignoreHTTPSErrors: boolean; /** * Whether the `meta viewport` tag is taken into account and touch events are enabled. Defaults to `false`. Not * supported in Firefox. */ - isMobile: boolean | undefined; + isMobile: boolean; /** * Whether or not to enable JavaScript in the context. Defaults to `true`. */ - javaScriptEnabled: boolean | undefined; + javaScriptEnabled: boolean; /** * Specify user locale, for example `en-GB`, `de-DE`, etc. Locale will affect `navigator.language` value, * `Accept-Language` request header value as well as number and date formatting rules. @@ -3517,7 +3517,7 @@ export interface PlaywrightTestOptions { /** * Whether to emulate network being offline. Defaults to `false`. */ - offline: boolean | undefined; + offline: boolean; /** * A list of permissions to grant to all pages in this context. See * [browserContext.grantPermissions(permissions[, options])](https://playwright.dev/docs/api/class-browsercontext#browser-context-grant-permissions) @@ -3552,7 +3552,7 @@ export interface PlaywrightTestOptions { * **NOTE** The `null` value opts out from the default presets, makes viewport depend on the host window size defined * by the operating system. It makes the execution of the tests non-deterministic. */ - viewport: ViewportSize | null | undefined; + viewport: ViewportSize | null; /** * When using [page.goto(url[, options])](https://playwright.dev/docs/api/class-page#page-goto), * [page.route(url, handler[, options])](https://playwright.dev/docs/api/class-page#page-route), @@ -3585,7 +3585,7 @@ export interface PlaywrightTestOptions { * * Learn more about [various timeouts](https://playwright.dev/docs/test-timeouts). */ - actionTimeout: number | undefined; + actionTimeout: number; /** * Timeout for each navigation action in milliseconds. Defaults to 0 (no timeout). * @@ -3594,20 +3594,20 @@ export interface PlaywrightTestOptions { * * Learn more about [various timeouts](https://playwright.dev/docs/test-timeouts). */ - navigationTimeout: number | undefined; + navigationTimeout: number; /** * Whether to allow sites to register Service workers. Defaults to `'allow'`. * - `'allow'`: [Service Workers](https://developer.mozilla.org/en-US/docs/Web/API/Service_Worker_API) can be * registered. * - `'block'`: Playwright will block all registration of Service Workers. */ - serviceWorkers: ServiceWorkerPolicy | undefined; + serviceWorkers: ServiceWorkerPolicy; /** * Custom attribute to be used in * [page.getByTestId(testId)](https://playwright.dev/docs/api/class-page#page-get-by-test-id). `data-testid` is used * by default. */ - testIdAttribute: string | undefined; + testIdAttribute: string; } diff --git a/tests/playwright-test/playwright.spec.ts b/tests/playwright-test/playwright.spec.ts index 026af19372..cf42ae1613 100644 --- a/tests/playwright-test/playwright.spec.ts +++ b/tests/playwright-test/playwright.spec.ts @@ -686,3 +686,65 @@ test('should not throw with many fixtures set to undefined', async ({ runInlineT expect(result.exitCode).toBe(0); expect(result.passed).toBe(1); }); + +test('should have strict types for options but allow use(undefined)', async ({ runTSC }) => { + const result = await runTSC({ + 'a.spec.ts': ` + const { test } = pwt; + test.use({ + headless: undefined, + acceptDownloads: undefined, + bypassCSP: undefined, + hasTouch: undefined, + ignoreHTTPSErrors: undefined, + isMobile: undefined, + javaScriptEnabled: undefined, + offline: undefined, + actionTimeout: undefined, + navigationTimeout: undefined, + testIdAttribute: undefined, + }); + test('my test', async ({ + headless, acceptDownloads, bypassCSP, + hasTouch, ignoreHTTPSErrors, isMobile, javaScriptEnabled, offline, + actionTimeout, navigationTimeout, testIdAttribute }) => { + test.skip(headless, 'boolean'); + test.skip(acceptDownloads, 'boolean'); + test.skip(bypassCSP, 'boolean'); + test.skip(hasTouch, 'boolean'); + test.skip(ignoreHTTPSErrors, 'boolean'); + test.skip(isMobile, 'boolean'); + test.skip(javaScriptEnabled, 'boolean'); + test.skip(offline, 'boolean'); + test.skip(actionTimeout > 0, 'number'); + test.skip(navigationTimeout > 0, 'number'); + test.skip(testIdAttribute.length > 0, 'string'); + }); + `, + }); + expect(result.exitCode).toBe(0); +}); + +test('should skip on mobile', async ({ runInlineTest }) => { + const result = await runInlineTest({ + 'a.spec.ts': ` + const { test } = pwt; + + test.describe(() => { + test.use({ isMobile: true }); + test('test 1', async ({ isMobile }) => { + test.skip(isMobile, 'desktop only!'); + }); + }); + + test.describe(() => { + test('test 2', async ({ isMobile }) => { + test.skip(isMobile, 'desktop only!'); + }); + }); + `, + }); + expect(result.exitCode).toBe(0); + expect(result.skipped).toBe(1); + expect(result.passed).toBe(1); +}); diff --git a/utils/generate_types/overrides-test.d.ts b/utils/generate_types/overrides-test.d.ts index ef765b698b..8635cc6a11 100644 --- a/utils/generate_types/overrides-test.d.ts +++ b/utils/generate_types/overrides-test.d.ts @@ -200,7 +200,7 @@ type ConnectOptions = { export interface PlaywrightWorkerOptions { browserName: BrowserName; defaultBrowserType: BrowserName; - headless: boolean | undefined; + headless: boolean; channel: BrowserChannel | undefined; launchOptions: LaunchOptions; connectOptions: ConnectOptions | undefined; @@ -214,31 +214,31 @@ export type TraceMode = 'off' | 'on' | 'retain-on-failure' | 'on-first-retry'; export type VideoMode = 'off' | 'on' | 'retain-on-failure' | 'on-first-retry'; export interface PlaywrightTestOptions { - acceptDownloads: boolean | undefined; - bypassCSP: boolean | undefined; - colorScheme: ColorScheme | undefined; + acceptDownloads: boolean; + bypassCSP: boolean; + colorScheme: ColorScheme; deviceScaleFactor: number | undefined; extraHTTPHeaders: ExtraHTTPHeaders | undefined; geolocation: Geolocation | undefined; - hasTouch: boolean | undefined; + hasTouch: boolean; httpCredentials: HTTPCredentials | undefined; - ignoreHTTPSErrors: boolean | undefined; - isMobile: boolean | undefined; - javaScriptEnabled: boolean | undefined; + ignoreHTTPSErrors: boolean; + isMobile: boolean; + javaScriptEnabled: boolean; locale: string | undefined; - offline: boolean | undefined; + offline: boolean; permissions: string[] | undefined; proxy: Proxy | undefined; storageState: StorageState | undefined; timezoneId: string | undefined; userAgent: string | undefined; - viewport: ViewportSize | null | undefined; + viewport: ViewportSize | null; baseURL: string | undefined; contextOptions: BrowserContextOptions; - actionTimeout: number | undefined; - navigationTimeout: number | undefined; - serviceWorkers: ServiceWorkerPolicy | undefined; - testIdAttribute: string | undefined; + actionTimeout: number; + navigationTimeout: number; + serviceWorkers: ServiceWorkerPolicy; + testIdAttribute: string; }