131 lines
4.3 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 type { TestRunnerPlugin } from '../plugins';
import { setRunnerToAddPluginsTo } from '../plugins';
import { dockerPlugin } from '../plugins/dockerPlugin';
import { webServerPluginsForConfig } from '../plugins/webServerPlugin';
import { collectFilesForProjects, collectProjects } from './projectUtils';
import { createReporter } from './reporters';
import { createTaskRunner } from './tasks';
import type { TaskRunnerState } from './tasks';
import type { FullConfigInternal } from '../common/types';
import type { Matcher, TestFileFilter } from '../util';
export type RunOptions = {
listOnly: boolean;
testFileFilters: TestFileFilter[];
testTitleMatcher: Matcher;
projectFilter?: string[];
passWithNoTests?: boolean;
};
export class Runner {
private _config: FullConfigInternal;
2022-05-03 13:25:56 -08:00
private _plugins: TestRunnerPlugin[] = [];
constructor(config: FullConfigInternal) {
this._config = config;
2022-05-03 13:25:56 -08:00
setRunnerToAddPluginsTo(this);
}
addPlugin(plugin: TestRunnerPlugin) {
this._plugins.push(plugin);
}
async listTestFiles(projectNames: string[] | undefined): Promise<any> {
const projects = collectProjects(this._config, projectNames);
const filesByProject = await collectFilesForProjects(projects, []);
const report: any = {
projects: []
};
for (const [project, files] of filesByProject) {
report.projects.push({
...sanitizeConfigForJSON(project, new Set()),
files
});
}
return report;
}
async runAllTests(options: RunOptions): Promise<FullResult['status']> {
const config = this._config;
const deadline = config.globalTimeout ? monotonicTime() + config.globalTimeout : 0;
// Legacy webServer support.
this._plugins.push(...webServerPluginsForConfig(config));
// Docker support.
this._plugins.push(dockerPlugin);
const reporter = await createReporter(config, options.listOnly);
const taskRunner = createTaskRunner(config, reporter, this._plugins, options);
const context: TaskRunnerState = {
config,
options,
reporter,
};
reporter.onConfigure(config);
const taskStatus = await taskRunner.run(context, deadline);
let status: FullResult['status'] = 'passed';
if (context.dispatcher?.hasWorkerErrors() || context.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
}
}
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;
}