diff --git a/package-lock.json b/package-lock.json index b1b800883e..65832f4246 100644 --- a/package-lock.json +++ b/package-lock.json @@ -3483,9 +3483,9 @@ } }, "folio": { - "version": "0.4.0-alpha11", - "resolved": "https://registry.npmjs.org/folio/-/folio-0.4.0-alpha11.tgz", - "integrity": "sha512-4kXdO+Ndbn++vCbzuMbn8bGqQLQ9J/Vni/1r9UwvirE4HydfxP1PHkvx9qb7wsB2hQbXmPkU5qM0eyGWFKpmog==", + "version": "0.4.0-alpha13", + "resolved": "https://registry.npmjs.org/folio/-/folio-0.4.0-alpha13.tgz", + "integrity": "sha512-ujrTuD4bSY3jNB2QVf5B2JSZFz2PNtNR0LIIbD+o4vCNutU9IAK0vn1WAiM5uLMt47CadAuFEb7260nanrTCcw==", "dev": true, "requires": { "@babel/code-frame": "^7.12.13", diff --git a/package.json b/package.json index 5e9e1b0910..815d5de143 100644 --- a/package.json +++ b/package.json @@ -80,7 +80,7 @@ "eslint-plugin-notice": "^0.9.10", "eslint-plugin-react-hooks": "^4.2.0", "file-loader": "^6.1.0", - "folio": "=0.4.0-alpha11", + "folio": "=0.4.0-alpha13", "formidable": "^1.2.2", "html-webpack-plugin": "^4.4.1", "ncp": "^2.0.0", diff --git a/tests/browsercontext-device.spec.ts b/tests/browsercontext-device.spec.ts index 56d75b3739..610e90d80e 100644 --- a/tests/browsercontext-device.spec.ts +++ b/tests/browsercontext-device.spec.ts @@ -60,12 +60,9 @@ it.describe('device', () => { await context.close(); }); - it('should scroll twice when emulated', async ({server, contextFactory, playwright, contextOptions}) => { + it('should scroll twice when emulated', async ({server, contextFactory, playwright}) => { const device = playwright.devices['iPhone 6']; - const context = await contextFactory({ - ...contextOptions, - ...device, - }); + const context = await contextFactory(device); const page = await context.newPage(); await page.setContent(` @@ -81,14 +78,11 @@ it.describe('device', () => { await context.close(); }); - it('should reset scroll top after a navigation', async ({server, contextFactory, playwright, contextOptions, browserName}) => { + it('should reset scroll top after a navigation', async ({server, contextFactory, playwright, browserName}) => { it.skip(browserName === 'webkit'); const device = playwright.devices['iPhone 6']; - const context = await contextFactory({ - ...contextOptions, - ...device, - }); + const context = await contextFactory(device); const page = await context.newPage(); await page.goto(server.PREFIX + '/input/scrollable.html'); await page.evaluate(() => window.scroll(0, 100)); @@ -98,14 +92,11 @@ it.describe('device', () => { await context.close(); }); - it('should scroll to a precise position with mobile scale', async ({server, contextFactory, playwright, contextOptions, browserName}) => { + it('should scroll to a precise position with mobile scale', async ({server, contextFactory, playwright, browserName}) => { it.skip(browserName === 'webkit'); const device = playwright.devices['iPhone 6']; - const context = await contextFactory({ - ...contextOptions, - ...device, - }); + const context = await contextFactory(device); const page = await context.newPage(); await page.goto(server.PREFIX + '/input/scrollable.html'); expect(await page.evaluate(() => document.body.scrollHeight)).toBeGreaterThan(1000); @@ -114,12 +105,9 @@ it.describe('device', () => { await context.close(); }); - it('should emulate viewport and screen size', async ({server, contextFactory, playwright, contextOptions}) => { + it('should emulate viewport and screen size', async ({server, contextFactory, playwright}) => { const device = playwright.devices['iPhone 12']; - const context = await contextFactory({ - ...contextOptions, - ...device, - }); + const context = await contextFactory(device); const page = await context.newPage(); await page.setContent(``); @@ -136,12 +124,9 @@ it.describe('device', () => { await context.close(); }); - it('should emulate viewport without screen size', async ({server, contextFactory, playwright, contextOptions}) => { + it('should emulate viewport without screen size', async ({server, contextFactory, playwright}) => { const device = playwright.devices['iPhone 6']; - const context = await contextFactory({ - ...contextOptions, - ...device, - }); + const context = await contextFactory(device); const page = await context.newPage(); await page.setContent(``); diff --git a/tests/browsercontext-proxy.spec.ts b/tests/browsercontext-proxy.spec.ts index 6e1d5f4400..c0a8b06051 100644 --- a/tests/browsercontext-proxy.spec.ts +++ b/tests/browsercontext-proxy.spec.ts @@ -16,7 +16,7 @@ import { browserTest as it, expect } from './config/browserTest'; -it.useOptions({ launchOptions: { proxy: { server: 'per-context' } } }); +it.useOptions({ proxy: { server: 'per-context' } }); it('should throw for missing global proxy on Chromium Windows', async ({ browserName, platform, browserType, browserOptions, server }) => { it.skip(browserName !== 'chromium' || platform !== 'win32'); diff --git a/tests/chromium/oopif.spec.ts b/tests/chromium/oopif.spec.ts index b669f7cba6..4e57914867 100644 --- a/tests/chromium/oopif.spec.ts +++ b/tests/chromium/oopif.spec.ts @@ -16,7 +16,7 @@ import { contextTest as it, expect } from '../config/browserTest'; -it.useOptions({ launchOptions: { args: ['--site-per-process'] } }); +it.useOptions({ args: ['--site-per-process'] }); it('should report oopif frames', async function({page, browser, server}) { await page.goto(server.PREFIX + '/dynamic-oopif.html'); diff --git a/tests/config/android.config.ts b/tests/config/android.config.ts index 810d6783ed..24cfd2f9c6 100644 --- a/tests/config/android.config.ts +++ b/tests/config/android.config.ts @@ -42,11 +42,9 @@ class AndroidPageEnv extends AndroidEnv { } } -type AllOptions = PlaywrightEnvOptions & CommonOptions; - const outputDir = path.join(__dirname, '..', '..', 'test-results'); const testDir = path.join(__dirname, '..'); -const config: folio.Config = { +const config: folio.Config = { testDir, snapshotDir: '__snapshots__', outputDir, @@ -66,6 +64,7 @@ config.projects.push({ name: 'android', options: { loopback: '10.0.2.2', + mode: 'default', browserName: 'chromium', }, testDir: path.join(testDir, 'android'), @@ -75,6 +74,7 @@ config.projects.push({ name: 'android', options: { loopback: '10.0.2.2', + mode: 'default', browserName: 'chromium', }, testDir: path.join(testDir, 'page'), diff --git a/tests/config/baseTest.ts b/tests/config/baseTest.ts index 13cb014ae7..4b1ad8356a 100644 --- a/tests/config/baseTest.ts +++ b/tests/config/baseTest.ts @@ -26,17 +26,17 @@ import { PlaywrightClient } from '../../lib/remote/playwrightClient'; export type BrowserName = 'chromium' | 'firefox' | 'webkit'; type Mode = 'default' | 'driver' | 'service'; -type BaseWorkerArgs = { +type BaseOptions = { mode: Mode; + browserName: BrowserName; + channel?: string; + video?: boolean; + headless?: boolean; +}; +type BaseWorkerArgs = { platform: 'win32' | 'darwin' | 'linux'; - video: boolean | undefined; - headless: boolean | undefined; - playwright: typeof import('../../index'); toImpl: (rpcObject: any) => any; - browserName: BrowserName; - channel: string | undefined; - isWindows: boolean; isMac: boolean; isLinux: boolean; @@ -100,17 +100,8 @@ class DefaultMode { } } -type BaseOptions = { - mode?: Mode; - browserName?: BrowserName; - channel?: string; - video?: boolean; - headless?: boolean; -}; - class BaseEnv { private _mode: DriverMode | ServiceMode | DefaultMode; - private _browserName: BrowserName; private _options: BaseOptions; private _playwright: typeof import('../../index'); @@ -120,36 +111,30 @@ class BaseEnv { async beforeAll(options: BaseOptions, workerInfo: folio.WorkerInfo): Promise { this._options = options; - this._browserName = options.browserName || 'chromium'; this._mode = { default: new DefaultMode(), service: new ServiceMode(), driver: new DriverMode(), - }[this._options.mode || 'default']; + }[this._options.mode]; require('../../lib/utils/utils').setUnderTest(); this._playwright = await this._mode.setup(workerInfo); return { playwright: this._playwright, - browserName: this._browserName, - channel: this._options.channel, isWindows: process.platform === 'win32', isMac: process.platform === 'darwin', isLinux: process.platform === 'linux', - headless: this._options.headless, - video: this._options.video, - mode: this._options.mode || 'default', platform: process.platform as ('win32' | 'darwin' | 'linux'), toImpl: (this._playwright as any)._toImpl, }; } async beforeEach({}, testInfo: folio.TestInfo) { - testInfo.snapshotPathSegment = this._browserName; - testInfo.data = { browserName: this._browserName }; + testInfo.snapshotPathSegment = this._options.browserName; + testInfo.data = { browserName: this._options.browserName }; if (!this._options.headless) testInfo.data.headful = true; - if ((this._options.mode || 'default') !== 'default') - testInfo.data.mode = this._options.mode || 'default'; + if (this._options.mode !== 'default') + testInfo.data.mode = this._options.mode; if (this._options.video) testInfo.data.video = true; return {}; @@ -265,6 +250,6 @@ class CoverageEnv { } export type CommonOptions = BaseOptions & ServerOptions & CoverageOptions; -export type CommonArgs = BaseWorkerArgs & ServerWorkerArgs; +export type CommonArgs = CommonOptions & BaseWorkerArgs & ServerWorkerArgs; export const baseTest = folio.test.extend(new CoverageEnv()).extend(new ServerEnv()).extend(new BaseEnv()); diff --git a/tests/config/browserTest.ts b/tests/config/browserTest.ts index 9cdf457efc..6eeb29c3af 100644 --- a/tests/config/browserTest.ts +++ b/tests/config/browserTest.ts @@ -32,10 +32,8 @@ type PlaywrightTestArgs = { startRemoteServer: (options?: RemoteServerOptions) => Promise; }; -export type PlaywrightEnvOptions = { - launchOptions?: LaunchOptions; - traceDir?: string; -}; +export type PlaywrightEnvOptions = LaunchOptions; +const kLaunchOptionNames = ['args', 'channel', 'chromiumSandbox', 'devtools', 'downloadsPath', 'env', 'executablePath', 'firefoxUserPrefs', 'handleSIGHUP', 'handleSIGINT', 'handleSIGTERM', 'headless', 'ignoreDefaultArgs', 'logger', 'proxy', 'slowMo', 'timeout', 'traceDir']; type PlaywrightWorkerArgs = { browserType: BrowserType; @@ -50,18 +48,15 @@ class PlaywrightEnv { private _remoteServer: RemoteServer | undefined; hasBeforeAllOptions(options: PlaywrightEnvOptions) { - return 'launchOptions' in options || 'traceDir' in options; + return kLaunchOptionNames.some(key => key in options); } async beforeAll(args: CommonArgs & PlaywrightEnvOptions, workerInfo: folio.WorkerInfo): Promise { this._browserType = args.playwright[args.browserName]; this._browserOptions = { - traceDir: args.traceDir, - channel: args.channel, - headless: args.headless, handleSIGINT: false, - ...args.launchOptions, - } as any; + ...args, + }; return { browserType: this._browserType, browserOptions: this._browserOptions, @@ -126,9 +121,7 @@ type BrowserTestArgs = { contextFactory: (options?: BrowserContextOptions) => Promise; }; -type BrowserTestOptions = { - contextOptions?: BrowserContextOptions; -}; +type BrowserTestOptions = BrowserContextOptions; class BrowserEnv { private _browser: Browser | undefined; @@ -149,7 +142,7 @@ class BrowserEnv { const contextOptions = { recordVideo: options.video ? { dir: testInfo.outputPath('') } : undefined, _debugName: debugName, - ...options.contextOptions, + ...options, } as BrowserContextOptions; testInfo.data.browserVersion = this._browserVersion; @@ -163,8 +156,8 @@ class BrowserEnv { return { browser: this._browser, browserVersion: this._browserVersion, - contextOptions, contextFactory, + contextOptions, }; } diff --git a/tests/config/default.config.ts b/tests/config/default.config.ts index db4a2474d6..3f207ebbf1 100644 --- a/tests/config/default.config.ts +++ b/tests/config/default.config.ts @@ -30,21 +30,16 @@ const getExecutablePath = (browserName: BrowserName) => { return process.env.WKPATH; }; -type AllOptions = PlaywrightEnvOptions & CommonOptions; - class PageEnv { private _browser: Browser private _browserVersion: string; private _browserMajorVersion: number; private _context: BrowserContext | undefined; - async beforeAll(args: AllOptions & CommonArgs, workerInfo: folio.WorkerInfo) { + async beforeAll(args: PlaywrightEnvOptions & CommonArgs, workerInfo: folio.WorkerInfo) { this._browser = await args.playwright[args.browserName].launch({ - ...args.launchOptions, - traceDir: args.traceDir, - channel: args.channel, - headless: args.headless, handleSIGINT: false, + ...args, } as any); this._browserVersion = this._browser.version(); this._browserMajorVersion = Number(this._browserVersion.split('.')[0]); @@ -55,6 +50,7 @@ class PageEnv { testInfo.data.browserVersion = this._browserVersion; this._context = await this._browser.newContext({ recordVideo: args.video ? { dir: testInfo.outputPath('') } : undefined, + ...args, }); const page = await this._context.newPage(); return { @@ -78,14 +74,14 @@ class PageEnv { } } -const mode = folio.registerCLIOption('mode', 'Transport mode: default, driver or service').value as ('default' | 'driver' | 'service' | undefined); +const mode = (folio.registerCLIOption('mode', 'Transport mode: default, driver or service').value || 'default') as ('default' | 'driver' | 'service'); const headed = folio.registerCLIOption('headed', 'Run tests in headed mode (default: headless)', { type: 'boolean' }).value || !!process.env.HEADFUL; const channel = folio.registerCLIOption('channel', 'Browser channel (default: no channel)').value; const video = !!folio.registerCLIOption('video', 'Record videos for all tests', { type: 'boolean' }).value; const outputDir = path.join(__dirname, '..', '..', 'test-results'); const testDir = path.join(__dirname, '..'); -const config: folio.Config = { +const config: folio.Config = { testDir, snapshotDir: '__snapshots__', outputDir, @@ -118,10 +114,8 @@ for (const browserName of browserNames) { headless: !headed, channel, video, + executablePath, traceDir: process.env.PWTRACE ? path.join(outputDir, 'trace') : undefined, - launchOptions: { - executablePath, - }, coverageName: browserName, }, define: { test: pageTest, env: new PageEnv() }, diff --git a/tests/config/electron.config.ts b/tests/config/electron.config.ts index a9dec538d5..1450c026f2 100644 --- a/tests/config/electron.config.ts +++ b/tests/config/electron.config.ts @@ -36,11 +36,9 @@ class ElectronPageEnv extends ElectronEnv { } } -type AllOptions = PlaywrightEnvOptions & CommonOptions; - const outputDir = path.join(__dirname, '..', '..', 'test-results'); const testDir = path.join(__dirname, '..'); -const config: folio.Config = { +const config: folio.Config = { testDir, snapshotDir: '__snapshots__', outputDir, @@ -59,6 +57,7 @@ const config: folio.Config = { config.projects.push({ name: 'electron', options: { + mode: 'default', browserName: 'chromium', coverageName: 'electron', }, @@ -68,6 +67,7 @@ config.projects.push({ config.projects.push({ name: 'electron', options: { + mode: 'default', browserName: 'chromium', coverageName: 'electron', }, diff --git a/tests/config/remoteServer.ts b/tests/config/remoteServer.ts index 1ec8bde955..1523f1f6b8 100644 --- a/tests/config/remoteServer.ts +++ b/tests/config/remoteServer.ts @@ -44,8 +44,13 @@ export class RemoteServer { this._didExit = false; this._browserType = browserType; - const launchOptions = { - ...browserOptions, + // Copy options to prevent a large JSON string when launching subprocess. + // Otherwise, we get `Error: spawn ENAMETOOLONG` on Windows. + const launchOptions: LaunchOptions = { + args: browserOptions.args, + headless: browserOptions.headless, + channel: browserOptions.channel, + traceDir: browserOptions.traceDir, handleSIGINT: true, handleSIGTERM: true, handleSIGHUP: true, diff --git a/tests/inspector/inspectorTest.ts b/tests/inspector/inspectorTest.ts index 63b4ea49f5..510545132c 100644 --- a/tests/inspector/inspectorTest.ts +++ b/tests/inspector/inspectorTest.ts @@ -27,7 +27,6 @@ type CLITestArgs = { recorderPageGetter: () => Promise; openRecorder: () => Promise; runCLI: (args: string[]) => CLIMock; - executablePath: string | undefined; }; export const test = contextTest.extend({ @@ -35,7 +34,7 @@ export const test = contextTest.extend({ process.env.PWTEST_RECORDER_PORT = String(10907 + workerInfo.workerIndex); }, - async beforeEach({ page, context, toImpl, browserName, channel, headless, mode, launchOptions: { executablePath } }, testInfo: folio.TestInfo): Promise { + async beforeEach({ page, context, toImpl, browserName, channel, headless, mode, executablePath }, testInfo: folio.TestInfo): Promise { testInfo.skip(mode === 'service'); const recorderPageGetter = async () => { while (!toImpl(context).recorderAppForTest) @@ -55,7 +54,6 @@ export const test = contextTest.extend({ return new Recorder(page, await recorderPageGetter()); }, recorderPageGetter, - executablePath }; }, @@ -180,7 +178,7 @@ class CLIMock { private waitForCallback: () => void; exited: Promise; - constructor(browserName: string, channel: string | undefined, headless: boolean | undefined, args: string[], executablePath?: string) { + constructor(browserName: string, channel: string | undefined, headless: boolean | undefined, args: string[], executablePath: string | undefined) { this.data = ''; const nodeArgs = [ path.join(__dirname, '..', '..', 'lib', 'cli', 'cli.js'), diff --git a/tests/tap.spec.ts b/tests/tap.spec.ts index 5ffc97c773..e2653bb53e 100644 --- a/tests/tap.spec.ts +++ b/tests/tap.spec.ts @@ -18,7 +18,7 @@ import { contextTest as it, expect } from './config/browserTest'; import { ElementHandle } from '../index'; import type { ServerResponse } from 'http'; -it.useOptions({ contextOptions: { hasTouch: true } }); +it.useOptions({ hasTouch: true }); it('should send all of the correct events', async ({ page }) => { await page.setContent(`