131 lines
4.6 KiB
TypeScript
Raw Normal View History

/**
* Copyright 2019 Google Inc. All rights reserved.
* Modifications copyright (c) Microsoft Corporation.
*
* 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.
*/
import { monotonicTime } from 'playwright-core/lib/utils';
import type { FullResult } from '../../types/testReporter';
import { webServerPluginsForConfig } from '../plugins/webServerPlugin';
2023-01-30 14:34:48 -08:00
import { collectFilesForProject, filterProjects } from './projectUtils';
import { createReporter } from './reporters';
import { TestRun, createTaskRunner, createTaskRunnerForList } from './tasks';
import type { FullConfigInternal } from '../common/config';
2023-01-30 14:34:48 -08:00
import { colors } from 'playwright-core/lib/utilsBundle';
import { runWatchModeLoop } from './watchMode';
2023-03-01 15:27:23 -08:00
import { runUIMode } from './uiMode';
export class Runner {
private _config: FullConfigInternal;
constructor(config: FullConfigInternal) {
this._config = config;
}
async listTestFiles(projectNames: string[] | undefined): Promise<any> {
const projects = filterProjects(this._config.projects, projectNames);
const report: any = {
projects: []
};
2023-01-30 14:34:48 -08:00
for (const project of projects) {
report.projects.push({
...sanitizeConfigForJSON(project, new Set()),
2023-01-31 15:59:13 -08:00
files: await collectFilesForProject(project)
});
}
return report;
}
async runAllTests(): Promise<FullResult['status']> {
const config = this._config;
const listOnly = config.cliListOnly;
const deadline = config.config.globalTimeout ? monotonicTime() + config.config.globalTimeout : 0;
// Legacy webServer support.
webServerPluginsForConfig(config).forEach(p => config.plugins.push({ factory: p }));
const reporter = await createReporter(config, listOnly ? 'list' : 'run');
const taskRunner = listOnly ? createTaskRunnerForList(config, reporter, 'in-process')
: createTaskRunner(config, reporter);
const testRun = new TestRun(config, reporter);
reporter.onConfigure(config);
2023-01-30 14:34:48 -08:00
if (!listOnly && config.ignoreSnapshots) {
2023-01-30 14:34:48 -08:00
reporter.onStdOut(colors.dim([
'NOTE: running with "ignoreSnapshots" option. All of the following asserts are silently ignored:',
'- expect().toMatchSnapshot()',
'- expect().toHaveScreenshot()',
'',
].join('\n')));
}
const taskStatus = await taskRunner.run(testRun, deadline);
let status: FullResult['status'] = 'passed';
if (testRun.phases.find(p => p.dispatcher.hasWorkerErrors()) || testRun.rootSuite?.allTests().some(test => !test.ok()))
status = 'failed';
if (status === 'passed' && taskStatus !== 'passed')
status = taskStatus;
await reporter.onExit({ status });
// Calling process.exit() might truncate large stdout/stderr output.
// See https://github.com/nodejs/node/issues/6456.
// See https://github.com/nodejs/node/issues/12921
await new Promise<void>(resolve => process.stdout.write('', () => resolve()));
await new Promise<void>(resolve => process.stderr.write('', () => resolve()));
return status;
2022-11-01 23:44:30 -07:00
}
async watchAllTests(): Promise<FullResult['status']> {
const config = this._config;
webServerPluginsForConfig(config).forEach(p => config.plugins.push({ factory: p }));
return await runWatchModeLoop(config);
}
2023-03-01 15:27:23 -08:00
async uiAllTests(): Promise<FullResult['status']> {
const config = this._config;
webServerPluginsForConfig(config).forEach(p => config.plugins.push({ factory: p }));
2023-03-01 15:27:23 -08:00
return await runUIMode(config);
}
}
function sanitizeConfigForJSON(object: any, visited: Set<any>): any {
const type = typeof object;
if (type === 'function' || type === 'symbol')
return undefined;
if (!object || type !== 'object')
return object;
if (object instanceof RegExp)
return String(object);
if (object instanceof Date)
return object.toISOString();
if (visited.has(object))
return undefined;
visited.add(object);
if (Array.isArray(object))
return object.map(a => sanitizeConfigForJSON(a, visited));
const result: any = {};
const keys = Object.keys(object).slice(0, 100);
for (const key of keys) {
if (key.startsWith('_'))
continue;
result[key] = sanitizeConfigForJSON(object[key], visited);
}
return result;
}