diff --git a/packages/playwright-core/src/protocol/channels.ts b/packages/playwright-core/src/protocol/channels.ts index 307216e2fe..4cbd5a1c80 100644 --- a/packages/playwright-core/src/protocol/channels.ts +++ b/packages/playwright-core/src/protocol/channels.ts @@ -997,17 +997,17 @@ export type BrowserContextNewPageResult = { page: PageChannel, }; export type BrowserContextSetDefaultNavigationTimeoutNoReplyParams = { - timeout: number, + timeout?: number, }; export type BrowserContextSetDefaultNavigationTimeoutNoReplyOptions = { - + timeout?: number, }; export type BrowserContextSetDefaultNavigationTimeoutNoReplyResult = void; export type BrowserContextSetDefaultTimeoutNoReplyParams = { - timeout: number, + timeout?: number, }; export type BrowserContextSetDefaultTimeoutNoReplyOptions = { - + timeout?: number, }; export type BrowserContextSetDefaultTimeoutNoReplyResult = void; export type BrowserContextSetExtraHTTPHeadersParams = { @@ -1253,17 +1253,17 @@ export type PageWorkerEvent = { worker: WorkerChannel, }; export type PageSetDefaultNavigationTimeoutNoReplyParams = { - timeout: number, + timeout?: number, }; export type PageSetDefaultNavigationTimeoutNoReplyOptions = { - + timeout?: number, }; export type PageSetDefaultNavigationTimeoutNoReplyResult = void; export type PageSetDefaultTimeoutNoReplyParams = { - timeout: number, + timeout?: number, }; export type PageSetDefaultTimeoutNoReplyOptions = { - + timeout?: number, }; export type PageSetDefaultTimeoutNoReplyResult = void; export type PageSetFileChooserInterceptedNoReplyParams = { diff --git a/packages/playwright-core/src/protocol/protocol.yml b/packages/playwright-core/src/protocol/protocol.yml index 082417ac63..1e607e8a06 100644 --- a/packages/playwright-core/src/protocol/protocol.yml +++ b/packages/playwright-core/src/protocol/protocol.yml @@ -733,11 +733,11 @@ BrowserContext: setDefaultNavigationTimeoutNoReply: parameters: - timeout: number + timeout: number? setDefaultTimeoutNoReply: parameters: - timeout: number + timeout: number? setExtraHTTPHeaders: parameters: @@ -897,11 +897,11 @@ Page: setDefaultNavigationTimeoutNoReply: parameters: - timeout: number + timeout: number? setDefaultTimeoutNoReply: parameters: - timeout: number + timeout: number? setFileChooserInterceptedNoReply: parameters: diff --git a/packages/playwright-core/src/protocol/validator.ts b/packages/playwright-core/src/protocol/validator.ts index 4a7b278998..57af3d49e1 100644 --- a/packages/playwright-core/src/protocol/validator.ts +++ b/packages/playwright-core/src/protocol/validator.ts @@ -455,10 +455,10 @@ export function createScheme(tChannel: (name: string) => Validator): Scheme { }); scheme.BrowserContextNewPageParams = tOptional(tObject({})); scheme.BrowserContextSetDefaultNavigationTimeoutNoReplyParams = tObject({ - timeout: tNumber, + timeout: tOptional(tNumber), }); scheme.BrowserContextSetDefaultTimeoutNoReplyParams = tObject({ - timeout: tNumber, + timeout: tOptional(tNumber), }); scheme.BrowserContextSetExtraHTTPHeadersParams = tObject({ headers: tArray(tType('NameValue')), @@ -511,10 +511,10 @@ export function createScheme(tChannel: (name: string) => Validator): Scheme { scheme.BrowserContextTracingStopParams = tOptional(tObject({})); scheme.BrowserContextHarExportParams = tOptional(tObject({})); scheme.PageSetDefaultNavigationTimeoutNoReplyParams = tObject({ - timeout: tNumber, + timeout: tOptional(tNumber), }); scheme.PageSetDefaultTimeoutNoReplyParams = tObject({ - timeout: tNumber, + timeout: tOptional(tNumber), }); scheme.PageSetFileChooserInterceptedNoReplyParams = tObject({ intercepted: tBoolean, diff --git a/packages/playwright-core/src/server/browserContext.ts b/packages/playwright-core/src/server/browserContext.ts index ff2729a9da..ae30ecb0dc 100644 --- a/packages/playwright-core/src/server/browserContext.ts +++ b/packages/playwright-core/src/server/browserContext.ts @@ -204,11 +204,11 @@ export abstract class BrowserContext extends SdkObject { await this._doClearPermissions(); } - setDefaultNavigationTimeout(timeout: number) { + setDefaultNavigationTimeout(timeout: number | undefined) { this._timeoutSettings.setDefaultNavigationTimeout(timeout); } - setDefaultTimeout(timeout: number) { + setDefaultTimeout(timeout: number | undefined) { this._timeoutSettings.setDefaultTimeout(timeout); } diff --git a/packages/playwright-core/src/server/page.ts b/packages/playwright-core/src/server/page.ts index 30ceb6f86f..effc29c53d 100644 --- a/packages/playwright-core/src/server/page.ts +++ b/packages/playwright-core/src/server/page.ts @@ -260,11 +260,11 @@ export class Page extends SdkObject { return this._frameManager.frames(); } - setDefaultNavigationTimeout(timeout: number) { + setDefaultNavigationTimeout(timeout: number | undefined) { this._timeoutSettings.setDefaultNavigationTimeout(timeout); } - setDefaultTimeout(timeout: number) { + setDefaultTimeout(timeout: number | undefined) { this._timeoutSettings.setDefaultTimeout(timeout); } diff --git a/packages/playwright-core/src/server/trace/recorder/tracing.ts b/packages/playwright-core/src/server/trace/recorder/tracing.ts index 6771032b72..c02216243d 100644 --- a/packages/playwright-core/src/server/trace/recorder/tracing.ts +++ b/packages/playwright-core/src/server/trace/recorder/tracing.ts @@ -218,7 +218,7 @@ export class Tracing implements InstrumentationListener, SnapshotterDelegate, Ha const zipArtifact = skipCompress ? null : await this._exportZip(entries, state).catch(() => null); return { artifact: zipArtifact, entries }; - }); + }) || { artifact: null, entries: [] }; } private async _exportZip(entries: NameValue[], state: RecordingState): Promise { @@ -360,11 +360,13 @@ export class Tracing implements InstrumentationListener, SnapshotterDelegate, Ha }); } - private async _appendTraceOperation(cb: () => Promise): Promise { + private async _appendTraceOperation(cb: () => Promise): Promise { // This method serializes all writes to the trace. let error: Error | undefined; let result: T | undefined; this._writeChain = this._writeChain.then(async () => { + if (!this._context._browser.isConnected()) + return; try { result = await cb(); } catch (e) { @@ -374,7 +376,7 @@ export class Tracing implements InstrumentationListener, SnapshotterDelegate, Ha await this._writeChain; if (error) throw error; - return result!; + return result; } } diff --git a/packages/playwright-core/src/utils/timeoutSettings.ts b/packages/playwright-core/src/utils/timeoutSettings.ts index 46b8fd4cea..822aa20baa 100644 --- a/packages/playwright-core/src/utils/timeoutSettings.ts +++ b/packages/playwright-core/src/utils/timeoutSettings.ts @@ -22,27 +22,27 @@ const TIMEOUT = debugMode() ? 0 : DEFAULT_TIMEOUT; export class TimeoutSettings { private _parent: TimeoutSettings | undefined; - private _defaultTimeout: number | null = null; - private _defaultNavigationTimeout: number | null = null; + private _defaultTimeout: number | undefined; + private _defaultNavigationTimeout: number | undefined; constructor(parent?: TimeoutSettings) { this._parent = parent; } - setDefaultTimeout(timeout: number) { + setDefaultTimeout(timeout: number | undefined) { this._defaultTimeout = timeout; } - setDefaultNavigationTimeout(timeout: number) { + setDefaultNavigationTimeout(timeout: number | undefined) { this._defaultNavigationTimeout = timeout; } navigationTimeout(options: { timeout?: number }): number { if (typeof options.timeout === 'number') return options.timeout; - if (this._defaultNavigationTimeout !== null) + if (this._defaultNavigationTimeout !== undefined) return this._defaultNavigationTimeout; - if (this._defaultTimeout !== null) + if (this._defaultTimeout !== undefined) return this._defaultTimeout; if (this._parent) return this._parent.navigationTimeout(options); @@ -52,7 +52,7 @@ export class TimeoutSettings { timeout(options: { timeout?: number }): number { if (typeof options.timeout === 'number') return options.timeout; - if (this._defaultTimeout !== null) + if (this._defaultTimeout !== undefined) return this._defaultTimeout; if (this._parent) return this._parent.timeout(options); diff --git a/packages/playwright-test/src/cli.ts b/packages/playwright-test/src/cli.ts index 68f4cb5001..66bbd221f3 100644 --- a/packages/playwright-test/src/cli.ts +++ b/packages/playwright-test/src/cli.ts @@ -16,7 +16,7 @@ /* eslint-disable no-console */ -import { Command, Option } from 'commander'; +import { Command } from 'commander'; import fs from 'fs'; import path from 'path'; import type { Config } from './types'; @@ -49,7 +49,6 @@ export function addTestCommand(program: Command) { command.option('--browser ', `Browser to use for tests, one of "all", "chromium", "firefox" or "webkit" (default: "chromium")`); command.option('--headed', `Run tests in headed browsers (default: headless)`); command.option('--debug', `Run tests with Playwright Inspector. Shortcut for "PWDEBUG=1" environment variable and "--timeout=0 --maxFailures=1 --headed --workers=1" options`); - command.addOption(new Option('--reuse-context').hideHelp()); command.option('-c, --config ', `Configuration file, or a test directory with optional "${tsConfig}"/"${jsConfig}"`); command.option('--forbid-only', `Fail if test.only is called (default: false)`); command.option('-g, --grep ', `Only run tests matching this regular expression (default: ".*")`); @@ -114,17 +113,15 @@ async function createLoader(opts: { [key: string]: any }): Promise { } const overrides = overridesFromOptions(opts); - if (opts.headed || opts.debug || opts.reuseContext) + if (opts.headed || opts.debug) overrides.use = { headless: false }; - if (opts.debug || opts.reuseContext) { + if (opts.debug) { overrides.maxFailures = 1; overrides.timeout = 0; overrides.workers = 1; } if (opts.debug) process.env.PWDEBUG = '1'; - if (opts.reuseContext) - process.env.PWTEST_REUSE_CONTEXT = '1'; const loader = new Loader(defaultConfig, overrides); diff --git a/packages/playwright-test/src/index.ts b/packages/playwright-test/src/index.ts index bd5c20295a..5df6b9cb2a 100644 --- a/packages/playwright-test/src/index.ts +++ b/packages/playwright-test/src/index.ts @@ -16,10 +16,10 @@ import * as fs from 'fs'; import * as path from 'path'; -import type { LaunchOptions, BrowserContextOptions, Page, BrowserContext, BrowserType } from 'playwright-core'; +import type { LaunchOptions, BrowserContextOptions, Page, BrowserContext, BrowserType, Video } from 'playwright-core'; import type { TestType, PlaywrightTestArgs, PlaywrightTestOptions, PlaywrightWorkerArgs, PlaywrightWorkerOptions, TestInfo } from '../types/test'; import { rootTestType } from './testType'; -import { assert, createGuid, removeFolders } from 'playwright-core/lib/utils/utils'; +import { createGuid, removeFolders } from 'playwright-core/lib/utils/utils'; import { GridClient } from 'playwright-core/lib/grid/gridClient'; import { Browser } from 'playwright-core'; export { expect } from './expect'; @@ -28,97 +28,15 @@ export const _baseTest: TestType<{}, {}> = rootTestType.test; type TestFixtures = PlaywrightTestArgs & PlaywrightTestOptions & { _combinedContextOptions: BrowserContextOptions, _setupContextOptionsAndArtifacts: void; + _contextFactory: (options?: BrowserContextOptions) => Promise; }; type WorkerAndFileFixtures = PlaywrightWorkerArgs & PlaywrightWorkerOptions & { _browserType: BrowserType; _browserOptions: LaunchOptions; - _artifactsDir: () => string, - _reuseBrowserContext: ReuseBrowserContextStorage, + _artifactsDir: () => string; + _snapshotSuffix: string; }; -export class ReuseBrowserContextStorage { - private _browserContext?: BrowserContext; - private _uniqueOrigins = new Set(); - private _options?: BrowserContextOptions; - private _pauseNavigationEventCollection = false; - - isEnabled(): boolean { - return !!process.env.PWTEST_REUSE_CONTEXT; - } - - async obtainContext(browser: Browser, newContextOptions: BrowserContextOptions): Promise { - if (!this._browserContext) - return await this._createNewContext(browser); - return await this._refurbishExistingContext(newContextOptions); - } - - private async _createNewContext(browser: Browser): Promise { - this._browserContext = await browser.newContext(); - this._options = (this._browserContext as any)._options; - this._browserContext.on('page', page => { - page.on('framenavigated', frame => { - if (this._pauseNavigationEventCollection) - return; - const origin = new URL(frame.url()).origin; - if (origin !== 'null') // 'chrome-error://chromewebdata/' - this._uniqueOrigins.add(origin); - }); - page.on('crash', () => { - this._browserContext?.close().then(() => {}); - this._browserContext = undefined; - }); - }); - return this._browserContext; - } - - async _refurbishExistingContext(newContextOptions: BrowserContextOptions): Promise { - assert(this._browserContext); - const page = this._browserContext.pages().length > 0 ? this._browserContext.pages()[0] : await this._browserContext.newPage(); - this._pauseNavigationEventCollection = true; - try { - const initialOrigin = new URL(page.url()).origin; - await page.route('**/*', route => route.fulfill({ body: ``, contentType: 'text/html' })); - while (this._uniqueOrigins.size > 0) { - const nextOrigin = this._uniqueOrigins.has(initialOrigin) ? initialOrigin : this._uniqueOrigins.values().next().value; - this._uniqueOrigins.delete(nextOrigin); - await page.goto(nextOrigin); - await page.evaluate(() => window.localStorage.clear()); - await page.evaluate(() => window.sessionStorage.clear()); - } - await page.unroute('**/*'); - await page.goto('about:blank'); - await Promise.all(this._browserContext.pages().slice(1).map(page => page.close())); - await this._browserContext.clearCookies(); - await this._applyNewContextOptions(page, newContextOptions); - } finally { - this._pauseNavigationEventCollection = false; - } - return this._browserContext; - } - - private async _applyNewContextOptions(page: Page, newOptions: BrowserContextOptions) { - assert(this._options); - const currentViewport = page.viewportSize(); - const newViewport = newOptions.viewport === undefined ? { width: 1280, height: 720 } : newOptions.viewport; - if ( - ( - currentViewport?.width !== newViewport?.width || - currentViewport?.height !== newViewport?.height - ) && - (newViewport?.height && newViewport?.width) - ) - await page.setViewportSize(newViewport); - this._options = newOptions; - } - - async obtainPage(): Promise { - assert(this._browserContext); - if (this._browserContext.pages().length === 0) - return await this._browserContext.newPage(); - return this._browserContext.pages()[0]; - } -} - export const test = _baseTest.extend({ defaultBrowserType: [ 'chromium', { scope: 'worker' } ], browserName: [ ({ defaultBrowserType }, use) => use(defaultBrowserType), { scope: 'worker' } ], @@ -251,8 +169,10 @@ export const test = _baseTest.extend({ }); }, - _setupContextOptionsAndArtifacts: [async ({ _browserType, _combinedContextOptions, _artifactsDir, trace, screenshot, actionTimeout, navigationTimeout }, use, testInfo) => { - testInfo.snapshotSuffix = process.platform; + _snapshotSuffix: [process.platform, { scope: 'worker' }], + + _setupContextOptionsAndArtifacts: [async ({ _snapshotSuffix, _browserType, _combinedContextOptions, _artifactsDir, trace, screenshot, actionTimeout, navigationTimeout }, use, testInfo) => { + testInfo.snapshotSuffix = _snapshotSuffix; if (process.env.PWDEBUG) testInfo.setTimeout(0); @@ -381,39 +301,55 @@ export const test = _baseTest.extend({ })); }, { auto: true }], - _reuseBrowserContext: [new ReuseBrowserContextStorage(), { scope: 'worker' }], - - context: async ({ browser, video, _artifactsDir, _reuseBrowserContext, _combinedContextOptions }, use, testInfo) => { - const hook = hookType(testInfo); - if (hook) - throw new Error(`"context" and "page" fixtures are not supported in ${hook}. Use browser.newContext() instead.`); - if (_reuseBrowserContext.isEnabled()) { - const context = await _reuseBrowserContext.obtainContext(browser, _combinedContextOptions); - await use(context); - return; - } - + _contextFactory: async ({ browser, video, _artifactsDir }, use, testInfo) => { let videoMode = typeof video === 'string' ? video : video.mode; if (videoMode === 'retry-with-video') videoMode = 'on-first-retry'; const captureVideo = (videoMode === 'on' || videoMode === 'retain-on-failure' || (videoMode === 'on-first-retry' && testInfo.retry === 1)); - const videoOptions: BrowserContextOptions = captureVideo ? { - recordVideo: { - dir: _artifactsDir(), - size: typeof video === 'string' ? undefined : video.size, - } - } : {}; - const context = await browser.newContext(videoOptions); + const contexts = new Map(); - const allPages: Page[] = []; - context.on('page', page => allPages.push(page)); - - await use(context); + await use(async options => { + const hook = hookType(testInfo); + if (hook) + throw new Error(`"context" and "page" fixtures are not supported in ${hook}. Use browser.newContext() instead.`); + const videoOptions: BrowserContextOptions = captureVideo ? { + recordVideo: { + dir: _artifactsDir(), + size: typeof video === 'string' ? undefined : video.size, + } + } : {}; + const context = await browser.newContext({ ...videoOptions, ...options }); + const contextData: { pages: Page[] } = { pages: [] }; + contexts.set(context, contextData); + context.on('page', page => contextData.pages.push(page)); + return context; + }); const prependToError = testInfo.status === 'timedOut' ? - formatPendingCalls((context as any)._connection.pendingProtocolCalls()) : ''; - await context.close(); + formatPendingCalls((browser as any)._connection.pendingProtocolCalls()) : ''; + + await Promise.all([...contexts.keys()].map(async context => { + await context.close(); + + const testFailed = testInfo.status !== testInfo.expectedStatus; + const preserveVideo = captureVideo && (videoMode === 'on' || (testFailed && videoMode === 'retain-on-failure') || (videoMode === 'on-first-retry' && testInfo.retry === 1)); + if (preserveVideo) { + const { pages } = contexts.get(context)!; + const videos = pages.map(p => p.video()).filter(Boolean) as Video[]; + await Promise.all(videos.map(async v => { + try { + const videoPath = await v.path(); + const savedPath = testInfo.outputPath(path.basename(videoPath)); + await v.saveAs(savedPath); + testInfo.attachments.push({ name: 'video', path: savedPath, contentType: 'video/webm' }); + } catch (e) { + // Silent catch empty videos. + } + })); + } + })); + if (prependToError) { if (!testInfo.error) { testInfo.error = { value: prependToError }; @@ -423,31 +359,13 @@ export const test = _baseTest.extend({ testInfo.error.stack = prependToError + testInfo.error.stack; } } - - const testFailed = testInfo.status !== testInfo.expectedStatus; - const preserveVideo = captureVideo && (videoMode === 'on' || (testFailed && videoMode === 'retain-on-failure') || (videoMode === 'on-first-retry' && testInfo.retry === 1)); - if (preserveVideo) { - await Promise.all(allPages.map(async page => { - const v = page.video(); - if (!v) - return; - try { - const videoPath = await v.path(); - const savedPath = testInfo.outputPath(path.basename(videoPath)); - await v.saveAs(savedPath); - testInfo.attachments.push({ name: 'video', path: savedPath, contentType: 'video/webm' }); - } catch (e) { - // Silent catch empty videos. - } - })); - } }, - page: async ({ context, _reuseBrowserContext }, use) => { - if (_reuseBrowserContext.isEnabled()) { - await use(await _reuseBrowserContext.obtainPage()); - return; - } + context: async ({ _contextFactory }, use) => { + await use(await _contextFactory()); + }, + + page: async ({ context }, use) => { await use(await context.newPage()); }, diff --git a/tests/android/androidTest.ts b/tests/android/androidTest.ts index 15bce22c28..802219c2c2 100644 --- a/tests/android/androidTest.ts +++ b/tests/android/androidTest.ts @@ -14,28 +14,27 @@ * limitations under the License. */ +import { baseTest } from '../config/baseTest'; +import { PageTestFixtures, PageWorkerFixtures } from '../page/pageTestApi'; import type { AndroidDevice, BrowserContext } from 'playwright-core'; -import type { Fixtures, PlaywrightWorkerOptions } from '@playwright/test'; -import { PageTestFixtures } from '../page/pageTest'; -import { TestModeWorkerFixtures } from '../config/testModeFixtures'; -import { browserTest } from '../config/browserTest'; export { expect } from '@playwright/test'; -type AndroidWorkerFixtures = { +type AndroidWorkerFixtures = PageWorkerFixtures & { androidDevice: AndroidDevice; + androidContext: BrowserContext; }; -export const androidFixtures: Fixtures = { - androidDevice: [ async ({ playwright }, run) => { +export const androidTest = baseTest.extend({ + androidDevice: [async ({ playwright }, run) => { const device = (await playwright._android.devices())[0]; await device.shell('am force-stop org.chromium.webview_shell'); await device.shell('am force-stop com.android.chrome'); device.setDefaultTimeout(90000); await run(device); await device.close(); - }, { scope: 'worker' } ], + }, { scope: 'worker' }], - browserVersion: async ({ androidDevice }, run) => { + browserVersion: [async ({ androidDevice }, run) => { const browserVersion = (await androidDevice.shell('dumpsys package com.android.chrome')) .toString('utf8') .split('\n') @@ -43,21 +42,21 @@ export const androidFixtures: Fixtures { + browserMajorVersion: [async ({ browserVersion }, run) => { await run(Number(browserVersion.split('.')[0])); - }, + }, { scope: 'worker' }], - isAndroid: true, - isElectron: false, + isAndroid: [true, { scope: 'worker' }], + isElectron: [false, { scope: 'worker' }], - androidContext: [ async ({ androidDevice }, run) => { + androidContext: [async ({ androidDevice }, run) => { const context = await androidDevice.launchBrowser(); - const [ page ] = context.pages(); + const [page] = context.pages(); await page.goto('data:text/html,Default page'); await run(context); - }, { scope: 'worker' } ], + }, { scope: 'worker' }], page: async ({ androidContext }, run) => { // Retain default page, otherwise Clank will re-create it. @@ -66,6 +65,4 @@ export const androidFixtures: Fixtures(androidFixtures as any); +}); diff --git a/tests/browsercontext-basic.spec.ts b/tests/browsercontext-basic.spec.ts index 7975dafd76..9d82b38c8c 100644 --- a/tests/browsercontext-basic.spec.ts +++ b/tests/browsercontext-basic.spec.ts @@ -134,10 +134,7 @@ it('close() should abort waitForEvent', async ({ browser }) => { it('close() should be callable twice', async ({ browser }) => { const context = await browser.newContext(); - await Promise.all([ - context.close(), - context.close(), - ]); + await context.close(); await context.close(); }); diff --git a/tests/browsercontext-device.spec.ts b/tests/browsercontext-device.spec.ts index 9804db41ca..2689e25cbd 100644 --- a/tests/browsercontext-device.spec.ts +++ b/tests/browsercontext-device.spec.ts @@ -42,9 +42,8 @@ it.describe('device', () => { await context.close(); }); - it('should scroll to click', async ({ browser, server, contextOptions }) => { + it('should scroll to click', async ({ browser, server }) => { const context = await browser.newContext({ - ...contextOptions, viewport: { width: 400, height: 400, @@ -60,7 +59,7 @@ it.describe('device', () => { await context.close(); }); - it('should scroll twice when emulated', async ({ server, contextFactory, playwright }) => { + it('should scroll twice when emulated', async ({ contextFactory, playwright }) => { const device = playwright.devices['iPhone 6']; const context = await contextFactory(device); const page = await context.newPage(); @@ -105,7 +104,7 @@ it.describe('device', () => { await context.close(); }); - it('should emulate viewport and screen size', async ({ server, contextFactory, playwright }) => { + it('should emulate viewport and screen size', async ({ contextFactory, playwright }) => { const device = playwright.devices['iPhone 12']; const context = await contextFactory(device); const page = await context.newPage(); @@ -124,7 +123,7 @@ it.describe('device', () => { await context.close(); }); - it('should emulate viewport without screen size', async ({ server, contextFactory, playwright }) => { + it('should emulate viewport without screen size', async ({ contextFactory, playwright }) => { const device = playwright.devices['iPhone 6']; const context = await contextFactory(device); const page = await context.newPage(); diff --git a/tests/browsercontext-proxy.spec.ts b/tests/browsercontext-proxy.spec.ts index b22ffecaee..dc06d3eee4 100644 --- a/tests/browsercontext-proxy.spec.ts +++ b/tests/browsercontext-proxy.spec.ts @@ -47,7 +47,7 @@ it('should throw for missing global proxy on Chromium Windows', async ({ browser } }); -it('should work when passing the proxy only on the context level', async ({ browserName, platform, browserType, contextOptions, server, proxyServer }) => { +it('should work when passing the proxy only on the context level', async ({ browserName, platform, browserType, server, proxyServer }) => { // Currently an upstream bug in the network stack of Chromium which leads that // the wrong proxy gets used in the BrowserContext. it.fixme(browserName === 'chromium' && platform === 'win32'); @@ -59,7 +59,6 @@ it('should work when passing the proxy only on the context level', async ({ brow proxy: undefined, }); const context = await browser.newContext({ - ...contextOptions, proxy: { server: `localhost:${proxyServer.PORT}` } }); diff --git a/tests/config/android.config.ts b/tests/config/android.config.ts index c690b3531a..0f693a0eba 100644 --- a/tests/config/android.config.ts +++ b/tests/config/android.config.ts @@ -16,10 +16,9 @@ import type { Config, PlaywrightTestOptions, PlaywrightWorkerOptions } from '@playwright/test'; import * as path from 'path'; -import { test as pageTest } from '../page/pageTest'; -import { androidFixtures } from '../android/androidTest'; import { ServerWorkerOptions } from './serverFixtures'; -import { playwrightFixtures } from './browserTest'; + +process.env.PWPAGE_IMPL = 'android'; const outputDir = path.join(__dirname, '..', '..', 'test-results'); const testDir = path.join(__dirname, '..'); @@ -65,7 +64,6 @@ config.projects.push({ browserName: 'chromium', }, testDir: path.join(testDir, 'page'), - define: { test: pageTest, fixtures: { ...playwrightFixtures, ...androidFixtures } }, metadata, }); diff --git a/tests/config/baseTest.ts b/tests/config/baseTest.ts index 321fc01c65..0df963aca2 100644 --- a/tests/config/baseTest.ts +++ b/tests/config/baseTest.ts @@ -14,16 +14,24 @@ * limitations under the License. */ -import { _baseTest } from '@playwright/test'; +import { test } from '@playwright/test'; import { commonFixtures, CommonFixtures } from './commonFixtures'; import { serverFixtures, ServerFixtures, ServerWorkerOptions } from './serverFixtures'; import { coverageFixtures, CoverageWorkerOptions } from './coverageFixtures'; import { platformFixtures, PlatformWorkerFixtures } from './platformFixtures'; import { testModeFixtures, TestModeWorkerFixtures } from './testModeFixtures'; -export const baseTest = _baseTest - .extend<{}, CoverageWorkerOptions>(coverageFixtures) + +export type BaseTestWorkerFixtures = { + _snapshotSuffix: string; +}; + +export const baseTest = test + .extend<{}, CoverageWorkerOptions>(coverageFixtures as any) .extend<{}, PlatformWorkerFixtures>(platformFixtures) - .extend<{}, TestModeWorkerFixtures>(testModeFixtures) + .extend<{}, TestModeWorkerFixtures>(testModeFixtures as any) .extend(commonFixtures) - .extend(serverFixtures as any); + .extend(serverFixtures as any) + .extend<{}, BaseTestWorkerFixtures>({ + _snapshotSuffix: ['', { scope: 'worker' }], + }); diff --git a/tests/config/browserTest.ts b/tests/config/browserTest.ts index cb0800c6d7..8c2ce3090c 100644 --- a/tests/config/browserTest.ts +++ b/tests/config/browserTest.ts @@ -14,70 +14,49 @@ * limitations under the License. */ -import type { Fixtures, PlaywrightTestOptions, PlaywrightWorkerOptions } from '@playwright/test'; -import type { Browser, BrowserContext, BrowserContextOptions, BrowserType, LaunchOptions, Page } from 'playwright-core'; -import { removeFolders } from 'playwright-core/lib/utils/utils'; -import { browserOptionsWorkerFixture, browserTypeWorkerFixture, browserWorkerFixture, ReuseBrowserContextStorage } from '../../packages/playwright-test/lib/index'; -import * as path from 'path'; import * as fs from 'fs'; import * as os from 'os'; -import { RemoteServer, RemoteServerOptions } from './remoteServer'; +import { PageTestFixtures, PageWorkerFixtures } from '../page/pageTestApi'; +import * as path from 'path'; +import type { BrowserContext, BrowserContextOptions, BrowserType, Page } from 'playwright-core'; +import { removeFolders } from 'playwright-core/lib/utils/utils'; import { baseTest } from './baseTest'; -import { CommonFixtures } from './commonFixtures'; -import type { ParsedStackTrace } from 'playwright-core/lib/utils/stackTrace'; -import { DefaultTestMode, DriverTestMode, ServiceTestMode } from './testMode'; -import { TestModeWorkerFixtures } from './testModeFixtures'; +import { RemoteServer, RemoteServerOptions } from './remoteServer'; -export type PlaywrightWorkerFixtures = { - playwright: typeof import('playwright-core'); - _browserType: BrowserType; - _browserOptions: LaunchOptions; - browserType: BrowserType; - browser: Browser; +export type BrowserTestWorkerFixtures = PageWorkerFixtures & { browserVersion: string; - _reuseBrowserContext: ReuseBrowserContextStorage; - toImpl: (rpcObject: any) => any; + browserMajorVersion: number; + browserType: BrowserType; + isAndroid: boolean; + isElectron: boolean; }; -type PlaywrightTestFixtures = { +type BrowserTestTestFixtures = PageTestFixtures & { createUserDataDir: () => Promise; launchPersistent: (options?: Parameters[1]) => Promise<{ context: BrowserContext, page: Page }>; startRemoteServer: (options?: RemoteServerOptions) => Promise; - contextOptions: BrowserContextOptions; contextFactory: (options?: BrowserContextOptions) => Promise; - context: BrowserContext; - page: Page; }; -export const playwrightFixtures: Fixtures = { - hasTouch: undefined, - - playwright: [ async ({ mode }, run) => { - const testMode = { - default: new DefaultTestMode(), - service: new ServiceTestMode(), - driver: new DriverTestMode(), - }[mode]; - require('playwright-core/lib/utils/utils').setUnderTest(); - const playwright = await testMode.setup(); - await run(playwright); - await testMode.teardown(); - }, { scope: 'worker' } ], - - toImpl: [ async ({ playwright }, run) => run((playwright as any)._toImpl), { scope: 'worker' } ], - - _browserType: [browserTypeWorkerFixture, { scope: 'worker' } ], - _browserOptions: [browserOptionsWorkerFixture, { scope: 'worker' } ], - - launchOptions: [ {}, { scope: 'worker' } ], - browserType: [async ({ _browserType }, use) => use(_browserType), { scope: 'worker' } ], - browser: [browserWorkerFixture, { scope: 'worker' } ], - +const test = baseTest.extend({ browserVersion: [async ({ browser }, run) => { await run(browser.version()); }, { scope: 'worker' } ], - _reuseBrowserContext: [new ReuseBrowserContextStorage(), { scope: 'worker' }], + browserType: [async ({ _browserType }: any, run) => { + await run(_browserType); + }, { scope: 'worker' } ], + + browserMajorVersion: [async ({ browserVersion }, run) => { + await run(Number(browserVersion.split('.')[0])); + }, { scope: 'worker' } ], + + isAndroid: [false, { scope: 'worker' } ], + isElectron: [false, { scope: 'worker' } ], + + contextFactory: async ({ _contextFactory }: any, run) => { + await run(_contextFactory); + }, createUserDataDir: async ({}, run) => { const dirs: string[] = []; @@ -99,7 +78,7 @@ export const playwrightFixtures: Fixtures { if (persistentContext) - throw new Error('can only launch one persitent context'); + throw new Error('can only launch one persistent context'); const userDataDir = await createUserDataDir(); persistentContext = await browserType.launchPersistentContext(userDataDir, { ...options }); const page = persistentContext.pages()[0]; @@ -121,90 +100,8 @@ export const playwrightFixtures: Fixtures { - const debugName = path.relative(testInfo.project.outputDir, testInfo.outputDir).replace(/[\/\\]/g, '-'); - const contextOptions = { - recordVideo: video === 'on' ? { dir: testInfo.outputPath('') } : undefined, - _debugName: debugName, - hasTouch, - } as BrowserContextOptions; - await run(contextOptions); - }, - - contextFactory: async ({ browser, contextOptions, trace }, run, testInfo) => { - const contexts = new Map(); - await run(async options => { - const context = await browser.newContext({ ...contextOptions, ...options }); - contexts.set(context, { closed: false }); - context.on('close', () => contexts.get(context).closed = true); - if (trace === 'on') - await context.tracing.start({ screenshots: true, snapshots: true, sources: true } as any); - (context as any)._instrumentation.addListener({ - onApiCallBegin: (apiCall: string, stackTrace: ParsedStackTrace | null, userData: any) => { - if (apiCall.startsWith('expect.')) - return { userObject: null }; - const testInfoImpl = testInfo as any; - const step = testInfoImpl._addStep({ - location: stackTrace?.frames[0], - category: 'pw:api', - title: apiCall, - canHaveChildren: false, - forceNoParent: false - }); - userData.userObject = step; - }, - onApiCallEnd: (userData: any, error?: Error) => { - const step = userData.userObject; - step?.complete(error); - }, - }); - return context; - }); - await Promise.all([...contexts.keys()].map(async context => { - const videos = context.pages().map(p => p.video()).filter(Boolean); - if (trace === 'on' && !contexts.get(context)!.closed) { - const tracePath = testInfo.outputPath('trace.zip'); - await context.tracing.stop({ path: tracePath }); - testInfo.attachments.push({ name: 'trace', path: tracePath, contentType: 'application/zip' }); - } - await context.close(); - for (const v of videos) { - const videoPath = await v.path().catch(() => null); - if (!videoPath) - continue; - const savedPath = testInfo.outputPath(path.basename(videoPath)); - await v.saveAs(savedPath); - testInfo.attachments.push({ name: 'video', path: savedPath, contentType: 'video/webm' }); - } - })); - }, - - context: async ({ contextFactory, browser, _reuseBrowserContext, contextOptions }, run) => { - if (_reuseBrowserContext.isEnabled()) { - const context = await _reuseBrowserContext.obtainContext(browser, contextOptions); - await run(context); - return; - } - await run(await contextFactory()); - }, - - page: async ({ context, _reuseBrowserContext }, run) => { - if (_reuseBrowserContext.isEnabled()) { - await run(await _reuseBrowserContext.obtainPage()); - return; - } - await run(await context.newPage()); - }, - - browserName: [ 'chromium' , { scope: 'worker' } ], - headless: [ undefined, { scope: 'worker' } ], - channel: [ undefined, { scope: 'worker' } ], - video: [ 'off', { scope: 'worker' } ], - trace: [ 'off', { scope: 'worker' } ], -}; - -const test = baseTest.extend(playwrightFixtures); export const playwrightTest = test; export const browserTest = test; export const contextTest = test; diff --git a/tests/config/default.config.ts b/tests/config/default.config.ts index 566642e958..1c92b5b5eb 100644 --- a/tests/config/default.config.ts +++ b/tests/config/default.config.ts @@ -16,8 +16,6 @@ import type { Config, PlaywrightTestOptions, PlaywrightWorkerOptions } from '@playwright/test'; import * as path from 'path'; -import { playwrightFixtures } from './browserTest'; -import { test as pageTest } from '../page/pageTest'; import { TestModeWorkerFixtures } from './testModeFixtures'; import { CoverageWorkerOptions } from './coverageFixtures'; @@ -32,15 +30,6 @@ const getExecutablePath = (browserName: BrowserName) => { return process.env.WKPATH; }; -const pageFixtures = { - ...playwrightFixtures, - browserMajorVersion: async ({ browserVersion }, run) => { - await run(Number(browserVersion.split('.')[0])); - }, - isAndroid: false, - isElectron: false, -}; - const mode = (process.env.PWTEST_MODE || 'default') as ('default' | 'driver' | 'service'); const headed = !!process.env.HEADFUL; const channel = process.env.PWTEST_CHANNEL as any; @@ -95,7 +84,6 @@ for (const browserName of browserNames) { trace: trace ? 'on' : undefined, coverageName: browserName, }, - define: { test: pageTest, fixtures: pageFixtures }, metadata: { platform: process.platform, docker: !!process.env.INSIDE_DOCKER, diff --git a/tests/config/electron.config.ts b/tests/config/electron.config.ts index 4eac391295..a10450fe85 100644 --- a/tests/config/electron.config.ts +++ b/tests/config/electron.config.ts @@ -16,11 +16,10 @@ import type { Config, PlaywrightTestOptions, PlaywrightWorkerOptions } from '@playwright/test'; import * as path from 'path'; -import { electronFixtures } from '../electron/electronTest'; -import { test as pageTest } from '../page/pageTest'; -import { playwrightFixtures } from './browserTest'; import { CoverageWorkerOptions } from './coverageFixtures'; +process.env.PWPAGE_IMPL = 'electron'; + const outputDir = path.join(__dirname, '..', '..', 'test-results'); const testDir = path.join(__dirname, '..'); const config: Config = { @@ -65,7 +64,6 @@ config.projects.push({ coverageName: 'electron', }, testDir: path.join(testDir, 'page'), - define: { test: pageTest, fixtures: { ...playwrightFixtures, ...electronFixtures } }, metadata, }); diff --git a/tests/electron/electronTest.ts b/tests/electron/electronTest.ts index 0d755b7a4f..c5a66cd2d1 100644 --- a/tests/electron/electronTest.ts +++ b/tests/electron/electronTest.ts @@ -14,12 +14,10 @@ * limitations under the License. */ -import { ElectronApplication, Page } from 'playwright-core'; -import type { Fixtures, PlaywrightWorkerOptions } from '@playwright/test'; +import { baseTest } from '../config/baseTest'; import * as path from 'path'; -import { PageTestFixtures } from '../page/pageTest'; -import { TestModeWorkerFixtures } from '../config/testModeFixtures'; -import { browserTest } from '../config/browserTest'; +import { ElectronApplication, Page } from 'playwright-core'; +import { PageTestFixtures, PageWorkerFixtures } from '../page/pageTestApi'; export { expect } from '@playwright/test'; type ElectronTestFixtures = PageTestFixtures & { @@ -28,11 +26,12 @@ type ElectronTestFixtures = PageTestFixtures & { }; const electronVersion = require('electron/package.json').version; -export const electronFixtures: Fixtures = { - browserVersion: electronVersion, - browserMajorVersion: Number(electronVersion.split('.')[0]), - isAndroid: false, - isElectron: true, + +export const electronTest = baseTest.extend({ + browserVersion: [electronVersion, { scope: 'worker' }], + browserMajorVersion: [Number(electronVersion.split('.')[0]), { scope: 'worker' }], + isAndroid: [false, { scope: 'worker' }], + isElectron: [true, { scope: 'worker' }], electronApp: async ({ playwright }, run) => { // This env prevents 'Electron Security Policy' console message. @@ -70,6 +69,4 @@ export const electronFixtures: Fixtures { await run(await newWindow()); }, -}; - -export const electronTest = browserTest.extend(electronFixtures as any); +}); diff --git a/tests/global-fetch.spec.ts b/tests/global-fetch.spec.ts index 57561c9e7f..193818ee4c 100644 --- a/tests/global-fetch.spec.ts +++ b/tests/global-fetch.spec.ts @@ -168,8 +168,8 @@ it('should set playwright as user-agent', async ({ playwright, server }) => { expect(serverRequest.headers['user-agent']).toBe('Playwright/' + getPlaywrightVersion()); }); -it('should be able to construct with context options', async ({ playwright, server, contextOptions }) => { - const request = await playwright.request.newContext(contextOptions); +it('should be able to construct with context options', async ({ playwright, browserType, server }) => { + const request = await playwright.request.newContext((browserType as any)._defaultContextOptions); const response = await request.get(server.EMPTY_PAGE); expect(response.ok()).toBeTruthy(); }); diff --git a/tests/page/page-goto.spec.ts b/tests/page/page-goto.spec.ts index 5433779cce..bf29f61dc9 100644 --- a/tests/page/page-goto.spec.ts +++ b/tests/page/page-goto.spec.ts @@ -244,7 +244,8 @@ it('should work when page calls history API in beforeunload', async ({ page, ser expect(response.status()).toBe(200); }); -it('should fail when navigating to bad url', async ({ page, browserName }) => { +it('should fail when navigating to bad url', async ({ mode, page, browserName }) => { + it.fixme(mode === 'service', 'baseURL is inherited from webServer in config'); let error = null; await page.goto('asdfasdf').catch(e => error = e); if (browserName === 'chromium' || browserName === 'webkit') @@ -347,6 +348,8 @@ it('should fail when exceeding default maximum timeout', async ({ page, server, // Hang for request to the empty.html server.setRoute('/empty.html', (req, res) => { }); let error = null; + // Undo what harness did. + page.context().setDefaultNavigationTimeout(undefined); page.context().setDefaultTimeout(2); page.setDefaultTimeout(1); await page.goto(server.PREFIX + '/empty.html').catch(e => error = e); @@ -361,6 +364,8 @@ it('should fail when exceeding browser context timeout', async ({ page, server, // Hang for request to the empty.html server.setRoute('/empty.html', (req, res) => { }); let error = null; + // Undo what harness did. + page.context().setDefaultNavigationTimeout(undefined); page.context().setDefaultTimeout(2); await page.goto(server.PREFIX + '/empty.html').catch(e => error = e); expect(error.message).toContain('page.goto: Timeout 2ms exceeded.'); diff --git a/tests/page/pageTest.ts b/tests/page/pageTest.ts index c06aefd18e..3028c7051d 100644 --- a/tests/page/pageTest.ts +++ b/tests/page/pageTest.ts @@ -14,26 +14,21 @@ * limitations under the License. */ -import { baseTest } from '../config/baseTest'; -import type { Page, ViewportSize } from 'playwright-core'; -import { VideoMode } from '@playwright/test'; +import { TestType } from '@playwright/test'; +import { PlatformWorkerFixtures } from '../config/platformFixtures'; +import { TestModeWorkerFixtures } from '../config/testModeFixtures'; +import { androidTest } from '../android/androidTest'; +import { browserTest } from '../config/browserTest'; +import { electronTest } from '../electron/electronTest'; +import { PageTestFixtures, PageWorkerFixtures } from './pageTestApi'; +import { ServerFixtures, ServerWorkerOptions } from '../config/serverFixtures'; export { expect } from '@playwright/test'; -// Page test does not guarantee an isolated context, just a new page (because Android). -export type PageTestFixtures = { - browserVersion: string; - browserMajorVersion: number; - page: Page; - isAndroid: boolean; - isElectron: boolean; -}; +let impl: TestType = browserTest; -export type PageWorkerFixtures = { - headless: boolean, - channel: string, - trace: 'off' | 'on' | 'retain-on-failure' | 'on-first-retry' | /** deprecated */ 'retry-with-trace'; - video: VideoMode | { mode: VideoMode, size: ViewportSize }; - browserName: 'chromium' | 'firefox' | 'webkit', -}; +if (process.env.PWPAGE_IMPL === 'android') + impl = androidTest; +if (process.env.PWPAGE_IMPL === 'electron') + impl = electronTest; -export const test = baseTest.declare(); +export const test = impl; diff --git a/tests/page/pageTestApi.ts b/tests/page/pageTestApi.ts new file mode 100644 index 0000000000..e432a49b4f --- /dev/null +++ b/tests/page/pageTestApi.ts @@ -0,0 +1,36 @@ +/** + * Copyright (c) Microsoft Corporation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import type { Page, ViewportSize } from 'playwright-core'; +import { VideoMode } from '@playwright/test'; +export { expect } from '@playwright/test'; + +// Page test does not guarantee an isolated context, just a new page (because Android). +export type PageTestFixtures = { + page: Page; +}; + +export type PageWorkerFixtures = { + headless: boolean; + channel: string; + trace: 'off' | 'on' | 'retain-on-failure' | 'on-first-retry' | /** deprecated */ 'retry-with-trace'; + video: VideoMode | { mode: VideoMode, size: ViewportSize }; + browserName: 'chromium' | 'firefox' | 'webkit'; + browserVersion: string; + browserMajorVersion: number; + isAndroid: boolean; + isElectron: boolean; +}; diff --git a/tests/playwright-test/playwright.spec.ts b/tests/playwright-test/playwright.spec.ts index da41d7d81c..119662a161 100644 --- a/tests/playwright-test/playwright.spec.ts +++ b/tests/playwright-test/playwright.spec.ts @@ -456,83 +456,3 @@ test('should work with video size', async ({ runInlineTest }, testInfo) => { expect(videoPlayer.videoWidth).toBe(220); expect(videoPlayer.videoHeight).toBe(110); }); - -test('should be able to re-use the context when debug mode is used', async ({ runInlineTest }, testInfo) => { - const result = await runInlineTest({ - 'a.test.ts': ` - const { test } = pwt; - - test.use({ - colorScheme: 'light', - viewport: { - width: 1920, - height: 1080, - }, - }) - - const host1 = 'http://host1.com/foobar'; - - test.beforeEach(async({page, context}) => { - context.route(host1, route => route.fulfill({body: '', contentType: 'text/html'}, {times: 1})); - console.log(page._guid + '|'); - console.log(context._guid + '|'); - }) - - test('initial setup', async ({ page }) => { - await page.goto(host1); - expect(await page.evaluate(() => window.localStorage.getItem('foobar'))).toBe(null); - await page.evaluate(() => window.localStorage.setItem('foobar', 'bar')); - expect(page.viewportSize()).toStrictEqual({ - width: 1920, - height: 1080, - }); - }); - - test('second run after persistent data has changed', async ({ page }) => { - await page.goto(host1); - expect(await page.evaluate(() => window.localStorage.getItem('foobar'))).toBe(null); - await page.evaluate(() => window.localStorage.setItem('foobar', 'bar')); - expect(page.viewportSize()).toStrictEqual({ - width: 1920, - height: 1080, - }); - }); - - test.describe('inside a describe block', () => { - test.use({ - colorScheme: 'dark', - viewport: { - width: 1000, - height: 500, - }, - }); - test('using different options', async ({ page }) => { - await page.goto(host1); - expect(await page.evaluate(() => window.localStorage.getItem('foobar'))).toBe(null); - expect(page.viewportSize()).toStrictEqual({ - width: 1000, - height: 500, - }); - }); - }); - - test('after the describe block', async ({ page }) => { - await page.goto(host1); - expect(await page.evaluate(() => window.localStorage.getItem('foobar'))).toBe(null); - expect(page.viewportSize()).toStrictEqual({ - width: 1920, - height: 1080, - }); - }); - ` - }, { '--reuse-context': true }); - expect(result.exitCode).toBe(0); - expect(result.passed).toBe(4); - const pageIds = result.output.match(/page@(.*)\|/g); - const browserContextIds = result.output.match(/browser-context@(.*)\|/g); - expect(pageIds.length).toBe(4); - expect(new Set(pageIds).size).toBe(1); - expect(browserContextIds.length).toBe(4); - expect(new Set(browserContextIds).size).toBe(1); -}); - diff --git a/tests/video.spec.ts b/tests/video.spec.ts index d6f34d6b7f..ab9fc6bda1 100644 --- a/tests/video.spec.ts +++ b/tests/video.spec.ts @@ -530,12 +530,11 @@ it.describe('screencast', () => { } }); - it('should emulate an iphone', async ({ contextFactory, playwright, contextOptions, browserName }, testInfo) => { + it('should emulate an iphone', async ({ contextFactory, playwright, browserName }, testInfo) => { it.skip(browserName === 'firefox', 'isMobile is not supported in Firefox'); const device = playwright.devices['iPhone 6']; const context = await contextFactory({ - ...contextOptions, ...device, recordVideo: { dir: testInfo.outputPath(''), @@ -552,11 +551,10 @@ it.describe('screencast', () => { expect(videoPlayer.videoHeight).toBe(666); }); - it('should throw on browser close', async ({ browserType, contextOptions }, testInfo) => { + it('should throw on browser close', async ({ browserType }, testInfo) => { const size = { width: 320, height: 240 }; const browser = await browserType.launch(); const context = await browser.newContext({ - ...contextOptions, recordVideo: { dir: testInfo.outputPath(''), size, @@ -573,12 +571,11 @@ it.describe('screencast', () => { expect(saveResult.message).toContain('browser has been closed'); }); - it('should throw if browser dies', async ({ browserType, contextOptions }, testInfo) => { + it('should throw if browser dies', async ({ browserType }, testInfo) => { const size = { width: 320, height: 240 }; const browser = await browserType.launch(); const context = await browser.newContext({ - ...contextOptions, recordVideo: { dir: testInfo.outputPath(''), size, @@ -595,13 +592,12 @@ it.describe('screencast', () => { expect(saveResult.message).toContain('rowser has been closed'); }); - it('should wait for video to finish if page was closed', async ({ browserType, contextOptions }, testInfo) => { + it('should wait for video to finish if page was closed', async ({ browserType }, testInfo) => { const size = { width: 320, height: 240 }; const browser = await browserType.launch(); const videoDir = testInfo.outputPath(''); const context = await browser.newContext({ - ...contextOptions, recordVideo: { dir: videoDir, size, @@ -622,7 +618,7 @@ it.describe('screencast', () => { expect(videoPlayer.videoHeight).toBe(240); }); - it('should not create video for internal pages', async ({ browser, browserName, contextOptions, server }, testInfo) => { + it('should not create video for internal pages', async ({ browser, server }, testInfo) => { it.fixme(true, 'https://github.com/microsoft/playwright/issues/6743'); server.setRoute('/empty.html', (req, res) => { res.setHeader('Set-Cookie', 'name=value'); @@ -631,7 +627,6 @@ it.describe('screencast', () => { const videoDir = testInfo.outputPath(''); const context = await browser.newContext({ - ...contextOptions, recordVideo: { dir: videoDir }