mirror of
https://github.com/microsoft/playwright.git
synced 2025-06-26 21:40:17 +00:00
chore(test runner): move config loading to runner (#11186)
In preparation for the TestRunner api.
This commit is contained in:
parent
576a9c1ae3
commit
3839917eb6
@ -20,10 +20,9 @@ import { Command } from 'commander';
|
|||||||
import fs from 'fs';
|
import fs from 'fs';
|
||||||
import path from 'path';
|
import path from 'path';
|
||||||
import type { Config } from './types';
|
import type { Config } from './types';
|
||||||
import { Runner, builtInReporters, BuiltInReporter } from './runner';
|
import { Runner, builtInReporters, BuiltInReporter, kDefaultConfigFiles } from './runner';
|
||||||
import { stopProfiling, startProfiling } from './profiler';
|
import { stopProfiling, startProfiling } from './profiler';
|
||||||
import { FilePatternFilter } from './util';
|
import { FilePatternFilter } from './util';
|
||||||
import { Loader } from './loader';
|
|
||||||
import { showHTMLReport } from './reporters/html';
|
import { showHTMLReport } from './reporters/html';
|
||||||
import { GridServer } from 'playwright-core/lib/grid/gridServer';
|
import { GridServer } from 'playwright-core/lib/grid/gridServer';
|
||||||
import dockerFactory from 'playwright-core/lib/grid/dockerGridFactory';
|
import dockerFactory from 'playwright-core/lib/grid/dockerGridFactory';
|
||||||
@ -31,17 +30,6 @@ import { createGuid } from 'playwright-core/lib/utils/utils';
|
|||||||
|
|
||||||
const defaultTimeout = 30000;
|
const defaultTimeout = 30000;
|
||||||
const defaultReporter: BuiltInReporter = process.env.CI ? 'dot' : 'list';
|
const defaultReporter: BuiltInReporter = process.env.CI ? 'dot' : 'list';
|
||||||
const tsConfig = 'playwright.config.ts';
|
|
||||||
const jsConfig = 'playwright.config.js';
|
|
||||||
const mjsConfig = 'playwright.config.mjs';
|
|
||||||
const defaultConfig: Config = {
|
|
||||||
preserveOutput: 'always',
|
|
||||||
reporter: [ [defaultReporter] ],
|
|
||||||
reportSlowTests: { max: 5, threshold: 15000 },
|
|
||||||
timeout: defaultTimeout,
|
|
||||||
updateSnapshots: 'missing',
|
|
||||||
workers: Math.ceil(require('os').cpus().length / 2),
|
|
||||||
};
|
|
||||||
|
|
||||||
export function addTestCommand(program: Command) {
|
export function addTestCommand(program: Command) {
|
||||||
const command = program.command('test [test-filter...]');
|
const command = program.command('test [test-filter...]');
|
||||||
@ -49,7 +37,7 @@ export function addTestCommand(program: Command) {
|
|||||||
command.option('--browser <browser>', `Browser to use for tests, one of "all", "chromium", "firefox" or "webkit" (default: "chromium")`);
|
command.option('--browser <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('--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.option('--debug', `Run tests with Playwright Inspector. Shortcut for "PWDEBUG=1" environment variable and "--timeout=0 --maxFailures=1 --headed --workers=1" options`);
|
||||||
command.option('-c, --config <file>', `Configuration file, or a test directory with optional "${tsConfig}"/"${jsConfig}"`);
|
command.option('-c, --config <file>', `Configuration file, or a test directory with optional ${kDefaultConfigFiles.map(file => `"${file}"`).join('/')}`);
|
||||||
command.option('--forbid-only', `Fail if test.only is called (default: false)`);
|
command.option('--forbid-only', `Fail if test.only is called (default: false)`);
|
||||||
command.option('-g, --grep <grep>', `Only run tests matching this regular expression (default: ".*")`);
|
command.option('-g, --grep <grep>', `Only run tests matching this regular expression (default: ".*")`);
|
||||||
command.option('-gv, --grep-invert <grep>', `Only run tests that do not match this regular expression`);
|
command.option('-gv, --grep-invert <grep>', `Only run tests that do not match this regular expression`);
|
||||||
@ -98,7 +86,18 @@ Examples:
|
|||||||
$ npx playwright show-report playwright-report`);
|
$ npx playwright show-report playwright-report`);
|
||||||
}
|
}
|
||||||
|
|
||||||
async function createLoader(opts: { [key: string]: any }): Promise<Loader> {
|
async function runTests(args: string[], opts: { [key: string]: any }) {
|
||||||
|
await startProfiling();
|
||||||
|
|
||||||
|
const defaultConfig: Config = {
|
||||||
|
preserveOutput: 'always',
|
||||||
|
reporter: [ [defaultReporter] ],
|
||||||
|
reportSlowTests: { max: 5, threshold: 15000 },
|
||||||
|
timeout: defaultTimeout,
|
||||||
|
updateSnapshots: 'missing',
|
||||||
|
workers: Math.ceil(require('os').cpus().length / 2),
|
||||||
|
};
|
||||||
|
|
||||||
if (opts.browser) {
|
if (opts.browser) {
|
||||||
const browserOpt = opts.browser.toLowerCase();
|
const browserOpt = opts.browser.toLowerCase();
|
||||||
if (!['all', 'chromium', 'firefox', 'webkit'].includes(browserOpt))
|
if (!['all', 'chromium', 'firefox', 'webkit'].includes(browserOpt))
|
||||||
@ -122,55 +121,14 @@ async function createLoader(opts: { [key: string]: any }): Promise<Loader> {
|
|||||||
process.env.PWDEBUG = '1';
|
process.env.PWDEBUG = '1';
|
||||||
}
|
}
|
||||||
|
|
||||||
const loader = new Loader(defaultConfig, overrides);
|
const runner = new Runner(overrides, defaultConfig);
|
||||||
|
|
||||||
async function loadConfig(configFile: string) {
|
// When no --config option is passed, let's look for the config file in the current directory.
|
||||||
if (fs.existsSync(configFile)) {
|
const configFile = opts.config ? path.resolve(process.cwd(), opts.config) : process.cwd();
|
||||||
if (process.stdout.isTTY)
|
const config = await runner.loadConfigFromFile(configFile);
|
||||||
console.log(`Using config at ` + configFile);
|
if (('projects' in config) && opts.browser)
|
||||||
const loadedConfig = await loader.loadConfigFile(configFile);
|
|
||||||
if (('projects' in loadedConfig) && opts.browser)
|
|
||||||
throw new Error(`Cannot use --browser option when configuration file defines projects. Specify browserName in the projects instead.`);
|
throw new Error(`Cannot use --browser option when configuration file defines projects. Specify browserName in the projects instead.`);
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
async function loadConfigFromDirectory(directory: string) {
|
|
||||||
const configNames = [tsConfig, jsConfig, mjsConfig];
|
|
||||||
for (const configName of configNames) {
|
|
||||||
if (await loadConfig(path.resolve(directory, configName)))
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (opts.config) {
|
|
||||||
const configFile = path.resolve(process.cwd(), opts.config);
|
|
||||||
if (!fs.existsSync(configFile))
|
|
||||||
throw new Error(`${opts.config} does not exist`);
|
|
||||||
if (fs.statSync(configFile).isDirectory()) {
|
|
||||||
// When passed a directory, look for a config file inside.
|
|
||||||
if (!await loadConfigFromDirectory(configFile)) {
|
|
||||||
// If there is no config, assume this as a root testing directory.
|
|
||||||
loader.loadEmptyConfig(configFile);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// When passed a file, it must be a config file.
|
|
||||||
await loadConfig(configFile);
|
|
||||||
}
|
|
||||||
} else if (!await loadConfigFromDirectory(process.cwd())) {
|
|
||||||
// No --config option, let's look for the config file in the current directory.
|
|
||||||
// If not, scan the world.
|
|
||||||
loader.loadEmptyConfig(process.cwd());
|
|
||||||
}
|
|
||||||
return loader;
|
|
||||||
}
|
|
||||||
|
|
||||||
async function runTests(args: string[], opts: { [key: string]: any }) {
|
|
||||||
await startProfiling();
|
|
||||||
|
|
||||||
const loader = await createLoader(opts);
|
|
||||||
const filePatternFilters: FilePatternFilter[] = args.map(arg => {
|
const filePatternFilters: FilePatternFilter[] = args.map(arg => {
|
||||||
const match = /^(.*):(\d+)$/.exec(arg);
|
const match = /^(.*):(\d+)$/.exec(arg);
|
||||||
return {
|
return {
|
||||||
@ -179,7 +137,6 @@ async function runTests(args: string[], opts: { [key: string]: any }) {
|
|||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
const runner = new Runner(loader);
|
|
||||||
if (process.env.PLAYWRIGHT_DOCKER)
|
if (process.env.PLAYWRIGHT_DOCKER)
|
||||||
runner.addInternalGlobalSetup(launchDockerContainer);
|
runner.addInternalGlobalSetup(launchDockerContainer);
|
||||||
const result = await runner.run(!!opts.list, filePatternFilters, opts.project || undefined);
|
const result = await runner.run(!!opts.list, filePatternFilters, opts.project || undefined);
|
||||||
|
|||||||
@ -68,9 +68,10 @@ export class Loader {
|
|||||||
return rawConfig;
|
return rawConfig;
|
||||||
}
|
}
|
||||||
|
|
||||||
loadEmptyConfig(rootDir: string) {
|
loadEmptyConfig(rootDir: string): Config {
|
||||||
this._config = {};
|
this._config = {};
|
||||||
this._processConfigObject(rootDir);
|
this._processConfigObject(rootDir);
|
||||||
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
private _processConfigObject(rootDir: string) {
|
private _processConfigObject(rootDir: string) {
|
||||||
|
|||||||
@ -35,13 +35,14 @@ import EmptyReporter from './reporters/empty';
|
|||||||
import HtmlReporter from './reporters/html';
|
import HtmlReporter from './reporters/html';
|
||||||
import { ProjectImpl } from './project';
|
import { ProjectImpl } from './project';
|
||||||
import { Minimatch } from 'minimatch';
|
import { Minimatch } from 'minimatch';
|
||||||
import { FullConfig } from './types';
|
import { Config, FullConfig } from './types';
|
||||||
import { WebServer } from './webServer';
|
import { WebServer } from './webServer';
|
||||||
import { raceAgainstDeadline } from 'playwright-core/lib/utils/async';
|
import { raceAgainstDeadline } from 'playwright-core/lib/utils/async';
|
||||||
|
|
||||||
const removeFolderAsync = promisify(rimraf);
|
const removeFolderAsync = promisify(rimraf);
|
||||||
const readDirAsync = promisify(fs.readdir);
|
const readDirAsync = promisify(fs.readdir);
|
||||||
const readFileAsync = promisify(fs.readFile);
|
const readFileAsync = promisify(fs.readFile);
|
||||||
|
export const kDefaultConfigFiles = ['playwright.config.ts', 'playwright.config.js', 'playwright.config.mjs'];
|
||||||
|
|
||||||
type InternalGlobalSetupFunction = () => Promise<() => Promise<void>>;
|
type InternalGlobalSetupFunction = () => Promise<() => Promise<void>>;
|
||||||
|
|
||||||
@ -51,8 +52,42 @@ export class Runner {
|
|||||||
private _didBegin = false;
|
private _didBegin = false;
|
||||||
private _internalGlobalSetups: Array<InternalGlobalSetupFunction> = [];
|
private _internalGlobalSetups: Array<InternalGlobalSetupFunction> = [];
|
||||||
|
|
||||||
constructor(loader: Loader) {
|
constructor(configOverrides: Config, defaultConfig: Config = {}) {
|
||||||
this._loader = loader;
|
this._loader = new Loader(defaultConfig, configOverrides);
|
||||||
|
}
|
||||||
|
|
||||||
|
async loadConfigFromFile(configFileOrDirectory: string): Promise<Config> {
|
||||||
|
const loadConfig = async (configFile: string) => {
|
||||||
|
if (fs.existsSync(configFile)) {
|
||||||
|
if (process.stdout.isTTY)
|
||||||
|
console.log(`Using config at ` + configFile);
|
||||||
|
const config = await this._loader.loadConfigFile(configFile);
|
||||||
|
return config;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const loadConfigFromDirectory = async (directory: string) => {
|
||||||
|
for (const configName of kDefaultConfigFiles) {
|
||||||
|
const config = await loadConfig(path.resolve(directory, configName));
|
||||||
|
if (config)
|
||||||
|
return config;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if (!fs.existsSync(configFileOrDirectory))
|
||||||
|
throw new Error(`${configFileOrDirectory} does not exist`);
|
||||||
|
if (fs.statSync(configFileOrDirectory).isDirectory()) {
|
||||||
|
// When passed a directory, look for a config file inside.
|
||||||
|
const config = await loadConfigFromDirectory(configFileOrDirectory);
|
||||||
|
if (config)
|
||||||
|
return config;
|
||||||
|
// If there is no config, assume this as a root testing directory.
|
||||||
|
return this._loader.loadEmptyConfig(configFileOrDirectory);
|
||||||
|
} else {
|
||||||
|
// When passed a file, it must be a config file.
|
||||||
|
const config = await loadConfig(configFileOrDirectory);
|
||||||
|
return config!;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async _createReporter(list: boolean) {
|
private async _createReporter(list: boolean) {
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user