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 path from 'path';
|
||||
import type { Config } from './types';
|
||||
import { Runner, builtInReporters, BuiltInReporter } from './runner';
|
||||
import { Runner, builtInReporters, BuiltInReporter, kDefaultConfigFiles } from './runner';
|
||||
import { stopProfiling, startProfiling } from './profiler';
|
||||
import { FilePatternFilter } from './util';
|
||||
import { Loader } from './loader';
|
||||
import { showHTMLReport } from './reporters/html';
|
||||
import { GridServer } from 'playwright-core/lib/grid/gridServer';
|
||||
import dockerFactory from 'playwright-core/lib/grid/dockerGridFactory';
|
||||
@ -31,17 +30,6 @@ import { createGuid } from 'playwright-core/lib/utils/utils';
|
||||
|
||||
const defaultTimeout = 30000;
|
||||
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) {
|
||||
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('--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('-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('-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`);
|
||||
@ -98,7 +86,18 @@ Examples:
|
||||
$ 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) {
|
||||
const browserOpt = opts.browser.toLowerCase();
|
||||
if (!['all', 'chromium', 'firefox', 'webkit'].includes(browserOpt))
|
||||
@ -122,55 +121,14 @@ async function createLoader(opts: { [key: string]: any }): Promise<Loader> {
|
||||
process.env.PWDEBUG = '1';
|
||||
}
|
||||
|
||||
const loader = new Loader(defaultConfig, overrides);
|
||||
const runner = new Runner(overrides, defaultConfig);
|
||||
|
||||
async function loadConfig(configFile: string) {
|
||||
if (fs.existsSync(configFile)) {
|
||||
if (process.stdout.isTTY)
|
||||
console.log(`Using config at ` + configFile);
|
||||
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.`);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
// When no --config option is passed, let's look for the config file in the current directory.
|
||||
const configFile = opts.config ? path.resolve(process.cwd(), opts.config) : process.cwd();
|
||||
const config = await runner.loadConfigFromFile(configFile);
|
||||
if (('projects' in config) && opts.browser)
|
||||
throw new Error(`Cannot use --browser option when configuration file defines projects. Specify browserName in the projects instead.`);
|
||||
|
||||
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 match = /^(.*):(\d+)$/.exec(arg);
|
||||
return {
|
||||
@ -179,7 +137,6 @@ async function runTests(args: string[], opts: { [key: string]: any }) {
|
||||
};
|
||||
});
|
||||
|
||||
const runner = new Runner(loader);
|
||||
if (process.env.PLAYWRIGHT_DOCKER)
|
||||
runner.addInternalGlobalSetup(launchDockerContainer);
|
||||
const result = await runner.run(!!opts.list, filePatternFilters, opts.project || undefined);
|
||||
|
||||
@ -68,9 +68,10 @@ export class Loader {
|
||||
return rawConfig;
|
||||
}
|
||||
|
||||
loadEmptyConfig(rootDir: string) {
|
||||
loadEmptyConfig(rootDir: string): Config {
|
||||
this._config = {};
|
||||
this._processConfigObject(rootDir);
|
||||
return {};
|
||||
}
|
||||
|
||||
private _processConfigObject(rootDir: string) {
|
||||
|
||||
@ -35,13 +35,14 @@ import EmptyReporter from './reporters/empty';
|
||||
import HtmlReporter from './reporters/html';
|
||||
import { ProjectImpl } from './project';
|
||||
import { Minimatch } from 'minimatch';
|
||||
import { FullConfig } from './types';
|
||||
import { Config, FullConfig } from './types';
|
||||
import { WebServer } from './webServer';
|
||||
import { raceAgainstDeadline } from 'playwright-core/lib/utils/async';
|
||||
|
||||
const removeFolderAsync = promisify(rimraf);
|
||||
const readDirAsync = promisify(fs.readdir);
|
||||
const readFileAsync = promisify(fs.readFile);
|
||||
export const kDefaultConfigFiles = ['playwright.config.ts', 'playwright.config.js', 'playwright.config.mjs'];
|
||||
|
||||
type InternalGlobalSetupFunction = () => Promise<() => Promise<void>>;
|
||||
|
||||
@ -51,8 +52,42 @@ export class Runner {
|
||||
private _didBegin = false;
|
||||
private _internalGlobalSetups: Array<InternalGlobalSetupFunction> = [];
|
||||
|
||||
constructor(loader: Loader) {
|
||||
this._loader = loader;
|
||||
constructor(configOverrides: Config, defaultConfig: Config = {}) {
|
||||
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) {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user