feat(testrunner): expose test and runner config to fixtures (#3580)

This commit is contained in:
Pavel Feldman 2020-08-22 16:44:56 -07:00 committed by GitHub
parent f4e8f34c96
commit 4025f9f1ef
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 196 additions and 156 deletions

View File

@ -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();

View File

@ -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<Config> {
pool: FixturePool<Config>;
name: string;
scope: any;
fn: any;
deps: any;
usages: Set<unknown>;
scope: string;
fn: Function;
deps: string[];
usages: Set<string>;
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<void>;
_setup = false;
_teardown = false;
constructor(pool: FixturePool<Config>, 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<any, any>;
export class FixturePool<Config> {
instances: Map<string, Fixture<Config>>;
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.<anonymous> \((.*)\)/, '$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<T extends keyof TestState>(name: T, fn: (params: FixtureParameters & WorkerState & TestState, test: (arg: TestState[T]) => Promise<TestResult>) => Promise<void>) {
export function registerFixture<Config, T extends keyof TestState>(name: T, fn: (params: FixtureParameters & WorkerState & TestState, runTest: (arg: TestState[T]) => Promise<void>, config: Config, test: Test) => Promise<void>) {
innerRegisterFixture(name, 'test', fn, registerFixture);
};
export function registerWorkerFixture<T extends keyof (WorkerState & FixtureParameters)>(name: T, fn: (params: FixtureParameters & WorkerState, test: (arg: (WorkerState & FixtureParameters)[T]) => Promise<void>) => Promise<void>) {
export function registerWorkerFixture<Config, T extends keyof (WorkerState & FixtureParameters)>(name: T, fn: (params: FixtureParameters & WorkerState, runTest: (arg: (WorkerState & FixtureParameters)[T]) => Promise<void>, config: Config) => Promise<void>) {
innerRegisterFixture(name, 'worker', fn, registerWorkerFixture);
};

View File

@ -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<T extends keyof TestState>(name: T, fn: (params: FixtureParameters & WorkerState & TestState, runTest: (arg: TestState[T]) => Promise<void>, config: RunnerConfig, test: Test) => Promise<void>) {
registerFixtureT<RunnerConfig, T>(name, fn);
};
export function registerWorkerFixture<T extends keyof (WorkerState & FixtureParameters)>(name: T, fn: (params: FixtureParameters & WorkerState, runTest: (arg: (WorkerState & FixtureParameters)[T]) => Promise<void>, config: RunnerConfig) => Promise<void>) {
registerWorkerFixtureT<RunnerConfig, T>(name, fn);
};

View File

@ -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
};

View File

@ -18,27 +18,12 @@ 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<Worker>();
private _freeWorkers: Worker[] = [];
private _workerClaimers: (() => void)[] = [];
@ -48,10 +33,13 @@ export class Runner extends EventEmitter {
private _testsByConfiguredFile = new Map<any, any>();
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<RunnerConfig>;
constructor(runner: Runner) {
super(runner);
this.fixturePool = require('./testRunner').fixturePool;
this.fixturePool = require('./testRunner').fixturePool as FixturePool<RunnerConfig>;
}
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();

View File

@ -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;
};

View File

@ -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)[] = [];

View File

@ -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<Test>) {
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));

View File

@ -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<RunnerConfig>();
export type TestRunnerEntry = {
file: string;
@ -40,30 +41,23 @@ export class TestRunner extends EventEmitter {
private _remaining: Set<number>;
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) {

View File

@ -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();

View File

@ -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]);
}

View File

@ -20,9 +20,6 @@ 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 () => {
@ -30,9 +27,3 @@ before(async () => {
after(async () => {
});
function valueFromEnv(name, defaultValue) {
if (!(name in process.env))
return defaultValue;
return JSON.parse(process.env[name]);
}