diff --git a/test-runner/src/cli.ts b/test-runner/src/cli.ts index 18d429d754..d4470e204e 100644 --- a/test-runner/src/cli.ts +++ b/test-runner/src/cli.ts @@ -17,6 +17,7 @@ import * as fs from 'fs'; import * as path from 'path'; import program from 'commander'; +import { reporters } from './reporters'; import { installTransform } from './transform'; import { Runner } from './runner'; import { TestCollector } from './testCollector'; @@ -90,13 +91,15 @@ program grep: command.grep, jobs, outputDir: command.output, - reporter: command.reporter, snapshotDir: path.join(testDir, '__snapshots__'), testDir, timeout: command.timeout, trialRun: command.trialRun, updateSnapshots: command.updateSnapshots }); + const reporterFactory = reporters[command.reporter || 'dot']; + new reporterFactory(runner); + try { if (beforeFunction) await beforeFunction(); diff --git a/test-runner/src/fixtures.ts b/test-runner/src/fixtures.ts index ddde08ae6d..edbcd2bfdc 100644 --- a/test-runner/src/fixtures.ts +++ b/test-runner/src/fixtures.ts @@ -39,32 +39,21 @@ export function setParameters(params: any) { registerWorkerFixture(name as keyof WorkerState, async ({}, test) => await test(parameters[name] as never)); } -type TestConfig = { - outputDir: string; - testDir: string; -}; - -type TestResult = { - success: boolean; - test: Test; - config: TestConfig; - error?: Error; -}; - -class Fixture { - pool: FixturePool; +class Fixture { + pool: FixturePool; name: string; - scope: any; - fn: any; - deps: any; - usages: Set; + scope: string; + fn: Function; + deps: string[]; + usages: Set; hasGeneratorValue: boolean; value: any; _teardownFenceCallback: (value?: unknown) => void; - _tearDownComplete: any; - _setup: boolean; - _teardown: any; - constructor(pool: FixturePool, name: string, scope: any, fn: any) { + _tearDownComplete: Promise; + _setup = false; + _teardown = false; + + constructor(pool: FixturePool, name: string, scope: string, fn: any) { this.pool = pool; this.name = name; this.scope = scope; @@ -75,11 +64,11 @@ class Fixture { this.value = this.hasGeneratorValue ? parameters[name] : null; } - async setup() { + async setup(config: Config, test?: Test) { if (this.hasGeneratorValue) return; for (const name of this.deps) { - await this.pool.setupFixture(name); + await this.pool.setupFixture(name, config, test); this.pool.instances.get(name).usages.add(this.name); } @@ -95,12 +84,12 @@ class Fixture { this.value = value; setupFenceFulfill(); return await teardownFence; - }).catch((e: any) => setupFenceReject(e)); + }, config, test).catch((e: any) => setupFenceReject(e)); await setupFence; this._setup = true; } - async teardown(testResult: TestResult) { + async teardown() { if (this.hasGeneratorValue) return; if (this._teardown) @@ -110,24 +99,24 @@ class Fixture { const fixture = this.pool.instances.get(name); if (!fixture) continue; - await fixture.teardown(testResult); + await fixture.teardown(); } if (this._setup) { debug('pw:test:hook')(`teardown "${this.name}"`); - this._teardownFenceCallback(testResult); + this._teardownFenceCallback(); } await this._tearDownComplete; this.pool.instances.delete(this.name); } } -export class FixturePool { - instances: Map; +export class FixturePool { + instances: Map>; constructor() { this.instances = new Map(); } - async setupFixture(name: string) { + async setupFixture(name: string, config: Config, test?: Test) { let fixture = this.instances.get(name); if (fixture) return fixture; @@ -137,21 +126,21 @@ export class FixturePool { const { scope, fn } = registrations.get(name); fixture = new Fixture(this, name, scope, fn); this.instances.set(name, fixture); - await fixture.setup(); + await fixture.setup(config, test); return fixture; } - async teardownScope(scope: string, testResult?: TestResult) { + async teardownScope(scope: string) { for (const [name, fixture] of this.instances) { if (fixture.scope === scope) - await fixture.teardown(testResult); + await fixture.teardown(); } } - async resolveParametersAndRun(fn: (arg0: {}) => any, timeout: number) { + async resolveParametersAndRun(fn: (arg0: {}) => any, timeout: number, config: Config, test?: Test) { const names = fixtureParameterNames(fn); for (const name of names) - await this.setupFixture(name); + await this.setupFixture(name, config, test); const params = {}; for (const n of names) params[n] = this.instances.get(n).value; @@ -167,19 +156,14 @@ export class FixturePool { ]); } - wrapTestCallback(callback: any, timeout: number, test: Test, config: TestConfig) { + wrapTestCallback(callback: any, timeout: number, config: Config, test: Test) { if (!callback) return callback; - const testResult: TestResult = { success: true, test, config }; return async() => { try { - await this.resolveParametersAndRun(callback, timeout); - } catch (e) { - testResult.success = false; - testResult.error = e; - throw e; + await this.resolveParametersAndRun(callback, timeout, config, test); } finally { - await this.teardownScope('test', testResult); + await this.teardownScope('test'); } }; } @@ -205,7 +189,7 @@ export function fixturesForCallback(callback: any): string[] { return result; } -function fixtureParameterNames(fn: { toString: () => any; }) { +function fixtureParameterNames(fn: { toString: () => any; }): string[] { const text = fn.toString(); const match = text.match(/async(?:\s+function)?\s*\(\s*{\s*([^}]*)\s*}/); if (!match || !match[1].trim()) @@ -214,10 +198,10 @@ function fixtureParameterNames(fn: { toString: () => any; }) { return signature.split(',').map((t: string) => t.trim()); } -function innerRegisterFixture(name: any, scope: string, fn: any, caller: Function) { +function innerRegisterFixture(name: string, scope: string, fn: Function, caller: Function) { const obj = {stack: ''}; Error.captureStackTrace(obj, caller); - const stackFrame = obj.stack.split('\n')[1]; + const stackFrame = obj.stack.split('\n')[2]; const location = stackFrame.replace(/.*at Object. \((.*)\)/, '$1'); const file = location.replace(/^(.+):\d+:\d+$/, '$1'); const registration = { name, scope, fn, file, location }; @@ -227,11 +211,11 @@ function innerRegisterFixture(name: any, scope: string, fn: any, caller: Functio registrationsByFile.get(file).push(registration); }; -export function registerFixture(name: T, fn: (params: FixtureParameters & WorkerState & TestState, test: (arg: TestState[T]) => Promise) => Promise) { +export function registerFixture(name: T, fn: (params: FixtureParameters & WorkerState & TestState, runTest: (arg: TestState[T]) => Promise, config: Config, test: Test) => Promise) { innerRegisterFixture(name, 'test', fn, registerFixture); }; -export function registerWorkerFixture(name: T, fn: (params: FixtureParameters & WorkerState, test: (arg: (WorkerState & FixtureParameters)[T]) => Promise) => Promise) { +export function registerWorkerFixture(name: T, fn: (params: FixtureParameters & WorkerState, runTest: (arg: (WorkerState & FixtureParameters)[T]) => Promise, config: Config) => Promise) { innerRegisterFixture(name, 'worker', fn, registerWorkerFixture); }; diff --git a/test-runner/src/index.ts b/test-runner/src/index.ts index fa54310d8d..9a53864a7b 100644 --- a/test-runner/src/index.ts +++ b/test-runner/src/index.ts @@ -17,4 +17,15 @@ import './builtin.fixtures'; import './expect'; -export {registerFixture, registerWorkerFixture, registerParameter, parameters} from './fixtures'; +import { registerFixture as registerFixtureT, registerWorkerFixture as registerWorkerFixtureT } from './fixtures'; +import { RunnerConfig } from './runnerConfig'; +import { Test } from './test'; +export { parameters, registerParameter } from './fixtures'; + +export function registerFixture(name: T, fn: (params: FixtureParameters & WorkerState & TestState, runTest: (arg: TestState[T]) => Promise, config: RunnerConfig, test: Test) => Promise) { + registerFixtureT(name, fn); +}; + +export function registerWorkerFixture(name: T, fn: (params: FixtureParameters & WorkerState, runTest: (arg: (WorkerState & FixtureParameters)[T]) => Promise, config: RunnerConfig) => Promise) { + registerWorkerFixtureT(name, fn); +}; diff --git a/test-runner/src/reporters.ts b/test-runner/src/reporters.ts index 5bf256701d..a520386791 100644 --- a/test-runner/src/reporters.ts +++ b/test-runner/src/reporters.ts @@ -22,8 +22,9 @@ import fs from 'fs'; import os from 'os'; import terminalLink from 'terminal-link'; import StackUtils from 'stack-utils'; -import { Test } from './test'; +import { Test, Suite } from './test'; import { EventEmitter } from 'ws'; +import { RunnerConfig } from './runnerConfig'; const stackUtils = new StackUtils(); @@ -33,6 +34,8 @@ class BaseReporter { failures: Test[] = []; duration = 0; startTime: number; + config: RunnerConfig; + suite: Suite; constructor(runner: EventEmitter) { process.on('SIGINT', async () => { @@ -52,8 +55,10 @@ class BaseReporter { this.failures.push(test); }); - runner.once('begin', () => { + runner.once('begin', (options: { config: RunnerConfig, suite: Suite }) => { this.startTime = Date.now(); + this.config = options.config; + this.suite = options.suite; }); runner.once('end', () => { @@ -167,6 +172,44 @@ export class ListReporter extends BaseReporter { } } +export class JSONReporter extends BaseReporter { + constructor(runner: EventEmitter) { + super(runner); + + runner.once('end', () => { + const result = { + config: this.config, + tests: this.suite.tests.map(test => this._serializeTest(test)), + suites: this.suite.suites.map(suite => this._serializeSuite(suite)) + }; + console.log(JSON.stringify(result, undefined, 2)); + }); + } + + private _serializeSuite(suite: Suite): any { + return { + title: suite.title, + file: suite.file, + configuration: suite.configuration, + tests: suite.tests.map(test => this._serializeTest(test)), + suites: suite.suites.map(suite => this._serializeSuite(suite)) + }; + } + + private _serializeTest(test: Test): any { + return { + title: test.title, + file: test.file, + only: test.only, + pending: test.pending, + slow: test.slow, + duration: test.duration, + timeout: test.timeout, + error: test.error + }; + } +} + function indent(lines: string, tab: string) { return lines.replace(/^/gm, tab); } @@ -181,3 +224,9 @@ function positionInFile(stack: string, file: string): { column: number; line: nu } return null; } + +export const reporters = { + 'dot': DotReporter, + 'list': ListReporter, + 'json': JSONReporter +}; diff --git a/test-runner/src/runner.ts b/test-runner/src/runner.ts index e38ea1bbe2..85a294651f 100644 --- a/test-runner/src/runner.ts +++ b/test-runner/src/runner.ts @@ -18,28 +18,13 @@ import child_process from 'child_process'; import crypto from 'crypto'; import path from 'path'; import { EventEmitter } from 'events'; -import { DotReporter, ListReporter} from './reporters'; import { lookupRegistrations, FixturePool } from './fixtures'; import { Suite } from './test'; import { TestRunnerEntry } from './testRunner'; - -type RunnerOptions = { - jobs: number; - reporter: any; - outputDir: string; - snapshotDir: string; - testDir: string; - timeout: number; - debug?: boolean; - quiet?: boolean; - grep?: string; - trialRun?: boolean; - updateSnapshots?: boolean; -}; +import { RunnerConfig } from './runnerConfig'; export class Runner extends EventEmitter { - readonly _options: RunnerOptions; - private _workers =new Set(); + private _workers = new Set(); private _freeWorkers: Worker[] = []; private _workerClaimers: (() => void)[] = []; stats: { duration: number; failures: number; passes: number; pending: number; tests: number; }; @@ -48,10 +33,13 @@ export class Runner extends EventEmitter { private _testsByConfiguredFile = new Map(); private _queue: TestRunnerEntry[] = []; private _stopCallback: () => void; + readonly _config: RunnerConfig; + private _suite: Suite; - constructor(suite: Suite, total: number, options: RunnerOptions) { + constructor(suite: Suite, total: number, config: RunnerConfig) { super(); - this._options = options; + + this._config = config; this.stats = { duration: 0, failures: 0, @@ -59,13 +47,11 @@ export class Runner extends EventEmitter { pending: 0, tests: 0, }; - const reporterFactory = options.reporter === 'list' ? ListReporter : DotReporter; - new reporterFactory(this); this._testById = new Map(); this._testsByConfiguredFile = new Map(); - - suite.eachTest(test => { + this._suite = suite; + this._suite.eachTest(test => { const configuredFile = `${test.file}::[${test._configurationString}]`; if (!this._testsByConfiguredFile.has(configuredFile)) { this._testsByConfiguredFile.set(configuredFile, { @@ -83,7 +69,7 @@ export class Runner extends EventEmitter { if (process.stdout.isTTY) { console.log(); - const jobs = Math.min(options.jobs, this._testsByConfiguredFile.size); + const jobs = Math.min(config.jobs, this._testsByConfiguredFile.size); console.log(`Running ${total} test${ total > 1 ? 's' : '' } using ${jobs} worker${ jobs > 1 ? 's' : ''}`); } } @@ -97,7 +83,7 @@ export class Runner extends EventEmitter { } async run() { - this.emit('begin', {}); + this.emit('begin', { config: this._config, suite: this._suite }); this._queue = this._filesSortedByWorkerHash(); // Loop in case job schedules more jobs while (this._queue.length) @@ -144,7 +130,7 @@ export class Runner extends EventEmitter { if (this._freeWorkers.length) return this._freeWorkers.pop(); // If we can create worker, create it. - if (this._workers.size < this._options.jobs) + if (this._workers.size < this._config.jobs) this._createWorker(); // Wait for the next available worker. await new Promise(f => this._workerClaimers.push(f)); @@ -160,7 +146,7 @@ export class Runner extends EventEmitter { } _createWorker() { - const worker = this._options.debug ? new InProcessWorker(this) : new OopWorker(this); + const worker = this._config.debug ? new InProcessWorker(this) : new OopWorker(this); worker.on('test', params => { ++this.stats.tests; this.emit('test', this._updateTest(params.test)); @@ -216,7 +202,7 @@ export class Runner extends EventEmitter { let lastWorkerId = 0; class Worker extends EventEmitter { - runner: any; + runner: Runner; hash: string; constructor(runner) { @@ -254,26 +240,26 @@ class OopWorker extends Worker { this.stderr = []; this.on('stdout', params => { const chunk = chunkFromParams(params); - if (!runner._options.quiet) + if (!runner._config.quiet) process.stdout.write(chunk); this.stdout.push(chunk); }); this.on('stderr', params => { const chunk = chunkFromParams(params); - if (!runner._options.quiet) + if (!runner._config.quiet) process.stderr.write(chunk); this.stderr.push(chunk); }); } async init() { - this.process.send({ method: 'init', params: { workerId: lastWorkerId++, ...this.runner._options } }); + this.process.send({ method: 'init', params: { workerId: lastWorkerId++, ...this.runner._config } }); await new Promise(f => this.process.once('message', f)); // Ready ack } run(entry) { this.hash = entry.hash; - this.process.send({ method: 'run', params: { entry, options: this.runner._options } }); + this.process.send({ method: 'run', params: { entry, config: this.runner._config } }); } stop() { @@ -294,22 +280,22 @@ class OopWorker extends Worker { } class InProcessWorker extends Worker { - fixturePool: FixturePool; + fixturePool: FixturePool; constructor(runner: Runner) { super(runner); - this.fixturePool = require('./testRunner').fixturePool; + this.fixturePool = require('./testRunner').fixturePool as FixturePool; } async init() { const { initializeImageMatcher } = require('./expect'); - initializeImageMatcher(this.runner._options); + initializeImageMatcher(this.runner._config); } async run(entry) { delete require.cache[entry.file]; const { TestRunner } = require('./testRunner'); - const testRunner = new TestRunner(entry, this.runner._options, 0); + const testRunner = new TestRunner(entry, this.runner._config, 0); for (const event of ['test', 'pending', 'pass', 'fail', 'done']) testRunner.on(event, this.emit.bind(this, event)); testRunner.run(); diff --git a/test-runner/src/runnerConfig.ts b/test-runner/src/runnerConfig.ts new file mode 100644 index 0000000000..8f6cf5511d --- /dev/null +++ b/test-runner/src/runnerConfig.ts @@ -0,0 +1,28 @@ +/** + * Copyright Microsoft Corporation. All rights reserved. + * + * 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. + */ + +export type RunnerConfig = { + jobs: number; + outputDir: string; + snapshotDir: string; + testDir: string; + timeout: number; + debug?: boolean; + quiet?: boolean; + grep?: string; + trialRun?: boolean; + updateSnapshots?: boolean; +}; diff --git a/test-runner/src/test.ts b/test-runner/src/test.ts index 9999724053..12ff299e17 100644 --- a/test-runner/src/test.ts +++ b/test-runner/src/test.ts @@ -22,6 +22,7 @@ export class Test { file: string; only = false; pending = false; + slow = false; duration = 0; timeout = 0; fn: Function; @@ -56,10 +57,6 @@ export class Test { fullTitle(): string { return this.titlePath().join(' '); } - - slow(): number { - return 10000; - } } export class Suite { @@ -70,6 +67,7 @@ export class Suite { only = false; pending = false; file: string; + configuration: Configuration; _hooks: { type: string, fn: Function } [] = []; _entries: (Suite | Test)[] = []; diff --git a/test-runner/src/testCollector.ts b/test-runner/src/testCollector.ts index e5583745ef..a06f0d2346 100644 --- a/test-runner/src/testCollector.ts +++ b/test-runner/src/testCollector.ts @@ -15,7 +15,7 @@ */ import path from 'path'; -import { fixturesForCallback, registerWorkerFixture } from './fixtures'; +import { fixturesForCallback } from './fixtures'; import { Configuration, Test, Suite } from './test'; import { fixturesUI } from './fixturesUI'; @@ -29,9 +29,6 @@ export class TestCollector { constructor(files: string[], matrix: { [key: string] : string }, options) { this._matrix = matrix; - for (const name of Object.keys(matrix)) - //@ts-ignore - registerWorkerFixture(name, async ({}, test) => test()); this._options = options; this.suite = new Suite(''); if (options.grep) { @@ -106,6 +103,7 @@ export class TestCollector { _cloneSuite(suite: Suite, configurationObject: Configuration, configurationString: string, tests: Set) { const copy = suite.clone(); copy.only = suite.only; + copy.configuration = configurationObject; for (const entry of suite._entries) { if (entry instanceof Suite) { copy.addSuite(this._cloneSuite(entry, configurationObject, configurationString, tests)); diff --git a/test-runner/src/testRunner.ts b/test-runner/src/testRunner.ts index 1dd21bdd7e..8d4af6c1ea 100644 --- a/test-runner/src/testRunner.ts +++ b/test-runner/src/testRunner.ts @@ -15,13 +15,14 @@ */ import path from 'path'; -import { FixturePool, registerWorkerFixture, rerunRegistrations, setParameters } from './fixtures'; +import { FixturePool, rerunRegistrations, setParameters } from './fixtures'; import { EventEmitter } from 'events'; import { setCurrentTestFile } from './expect'; import { Test, Suite } from './test'; import { fixturesUI } from './fixturesUI'; +import { RunnerConfig } from './runnerConfig'; -export const fixturePool = new FixturePool(); +export const fixturePool = new FixturePool(); export type TestRunnerEntry = { file: string; @@ -40,30 +41,23 @@ export class TestRunner extends EventEmitter { private _remaining: Set; private _trialRun: any; private _configuredFile: any; - private _configurationObject: any; private _parsedGeneratorConfiguration: any = {}; - private _outDir: string; + private _config: RunnerConfig; private _timeout: number; - private _testDir: string; - constructor(entry: TestRunnerEntry, options, workerId) { + constructor(entry: TestRunnerEntry, config: RunnerConfig, workerId: number) { super(); this._file = entry.file; this._ordinals = new Set(entry.ordinals); this._remaining = new Set(entry.ordinals); - this._trialRun = options.trialRun; - this._timeout = options.timeout; - this._testDir = options.testDir; - this._outDir = options.outputDir; + this._trialRun = config.trialRun; + this._timeout = config.timeout; + this._config = config; this._configuredFile = entry.configuredFile; - this._configurationObject = entry.configurationObject; - for (const {name, value} of this._configurationObject) { + for (const {name, value} of entry.configurationObject) this._parsedGeneratorConfiguration[name] = value; - // @ts-ignore - registerWorkerFixture(name, async ({}, test) => await test(value)); - } this._parsedGeneratorConfiguration['parallelIndex'] = workerId; - setCurrentTestFile(path.relative(options.testDir, this._file)); + setCurrentTestFile(path.relative(config.testDir, this._file)); } stop() { @@ -146,7 +140,7 @@ export class TestRunner extends EventEmitter { if (dir === 'before') all.reverse(); for (const hook of all) - await fixturePool.resolveParametersAndRun(hook, 0); + await fixturePool.resolveParametersAndRun(hook, 0, this._config); } private _reportDone() { @@ -159,10 +153,7 @@ export class TestRunner extends EventEmitter { private _testWrapper(test: Test) { const timeout = test.slow ? this._timeout * 3 : this._timeout; - return fixturePool.wrapTestCallback(test.fn, timeout, test, { - outputDir: this._outDir, - testDir: this._testDir, - }); + return fixturePool.wrapTestCallback(test.fn, timeout, { ...this._config }, test); } private _serializeTest(test) { diff --git a/test-runner/src/worker.js b/test-runner/src/worker.ts similarity index 91% rename from test-runner/src/worker.js rename to test-runner/src/worker.ts index 1ef9e6e2c9..a1573e6228 100644 --- a/test-runner/src/worker.js +++ b/test-runner/src/worker.ts @@ -14,10 +14,9 @@ * limitations under the License. */ -const { initializeImageMatcher } = require('./expect'); -const { TestRunner, fixturePool } = require('./testRunner'); - -const util = require('util'); +import { initializeImageMatcher } from './expect'; +import { TestRunner, fixturePool } from './testRunner'; +import * as util from 'util'; let closed = false; @@ -45,8 +44,8 @@ process.on('disconnect', gracefullyCloseAndExit); process.on('SIGINT',() => {}); process.on('SIGTERM',() => {}); -let workerId; -let testRunner; +let workerId: number; +let testRunner: TestRunner; process.on('message', async message => { if (message.method === 'init') { @@ -59,7 +58,7 @@ process.on('message', async message => { return; } if (message.method === 'run') { - testRunner = new TestRunner(message.params.entry, message.params.options, workerId); + testRunner = new TestRunner(message.params.entry, message.params.config, workerId); for (const event of ['test', 'pending', 'pass', 'fail', 'done']) testRunner.on(event, sendMessageToParent.bind(null, event)); await testRunner.run(); diff --git a/test/playwright.fixtures.ts b/test/playwright.fixtures.ts index 47e482033f..2b5dbb72b6 100644 --- a/test/playwright.fixtures.ts +++ b/test/playwright.fixtures.ts @@ -25,12 +25,16 @@ import { Transport } from '../lib/protocol/transport'; import { setUnderTest } from '../lib/utils/utils'; import { installCoverageHooks } from './coverage'; import { parameters, registerFixture, registerWorkerFixture } from '../test-runner'; - import {mkdtempAsync, removeFolderAsync} from './utils'; -setUnderTest(); // Note: we must call setUnderTest before requiring Playwright - -const platform = os.platform(); +export const options = { + CHROMIUM: parameters.browserName === 'chromium', + FIREFOX: parameters.browserName === 'firefox', + WEBKIT: parameters.browserName === 'webkit', + HEADLESS : !!valueFromEnv('HEADLESS', true), + WIRE: !!process.env.PWWIRE, + SLOW_MO: valueFromEnv('SLOW_MO', 0), +} declare global { interface WorkerState { @@ -52,9 +56,6 @@ declare global { } interface FixtureParameters { browserName: string; - headless: boolean; - wire: boolean; - slowMo: number; } } @@ -63,6 +64,7 @@ declare global { const LINUX: boolean; const WIN: boolean; } +const platform = os.platform(); global['MAC'] = platform === 'darwin'; global['LINUX'] = platform === 'linux'; global['WIN'] = platform === 'win32'; @@ -96,22 +98,24 @@ const getExecutablePath = (browserName) => { return process.env.WKPATH; } -registerWorkerFixture('defaultBrowserOptions', async({browserName, headless, slowMo}, test) => { +registerWorkerFixture('defaultBrowserOptions', async({browserName}, test) => { let executablePath = getExecutablePath(browserName); if (executablePath) console.error(`Using executable at ${executablePath}`); await test({ handleSIGINT: false, - slowMo, - headless, + slowMo: options.SLOW_MO, + headless: options.HEADLESS, executablePath }); }); -registerWorkerFixture('playwright', async({browserName, wire}, test) => { +registerWorkerFixture('playwright', async({browserName}, test) => { + setUnderTest(); // Note: we must call setUnderTest before requiring Playwright + const {coverage, uninstall} = installCoverageHooks(browserName); - if (wire) { + if (options.WIRE) { const connection = new Connection(); const spawnedProcess = childProcess.fork(path.join(__dirname, '..', 'lib', 'rpc', 'server'), [], { stdio: 'pipe', @@ -184,10 +188,10 @@ registerFixture('context', async ({browser}, test) => { await context.close(); }); -registerFixture('page', async ({context}, runTest) => { +registerFixture('page', async ({context}, runTest, config, test) => { const page = await context.newPage(); - const { success, test, config } = await runTest(page); - if (!success) { + await runTest(page); + if (test.error) { const relativePath = path.relative(config.testDir, test.file).replace(/\.spec\.[jt]s/, ''); const sanitizedTitle = test.title.replace(/[^\w\d]+/g, '_'); const assetPath = path.join(config.outputDir, relativePath, sanitizedTitle) + '-failed.png'; @@ -211,10 +215,8 @@ registerFixture('tmpDir', async ({}, test) => { await removeFolderAsync(tmpDir).catch(e => {}); }); -export const options = { - CHROMIUM: parameters.browserName === 'chromium', - FIREFOX: parameters.browserName === 'firefox', - WEBKIT: parameters.browserName === 'webkit', - HEADLESS : parameters.headless, - WIRE: parameters.wire, +function valueFromEnv(name, defaultValue) { + if (!(name in process.env)) + return defaultValue; + return JSON.parse(process.env[name]); } diff --git a/test/setup.ts b/test/setup.ts index 5517ea390a..0300734ace 100644 --- a/test/setup.ts +++ b/test/setup.ts @@ -20,19 +20,10 @@ declare const matrix: (m: any) => void; matrix({ 'browserName': process.env.BROWSER ? [process.env.BROWSER] : ['chromium', 'webkit', 'firefox'], - 'headless': [!!valueFromEnv('HEADLESS', true)], - 'wire': [!!process.env.PWWIRE], - 'slowMo': [valueFromEnv('SLOW_MO', 0)] }); before(async () => { }); -after(async () => { +after(async () => { }); - -function valueFromEnv(name, defaultValue) { - if (!(name in process.env)) - return defaultValue; - return JSON.parse(process.env[name]); -}