mirror of
https://github.com/microsoft/playwright.git
synced 2024-12-04 16:44:11 +03:00
chore: send test params over the wire in ui mode (#30046)
This commit is contained in:
parent
c8e8d8f8bb
commit
ee9432b9da
@ -36,6 +36,14 @@ export type TraceViewerServerOptions = {
|
||||
};
|
||||
|
||||
export type TraceViewerRedirectOptions = {
|
||||
args?: string[];
|
||||
grep?: string;
|
||||
grepInvert?: string;
|
||||
project?: string[];
|
||||
workers?: number | string;
|
||||
headed?: boolean;
|
||||
timeout?: number;
|
||||
reporter?: string[];
|
||||
webApp?: string;
|
||||
isServer?: boolean;
|
||||
};
|
||||
@ -102,19 +110,36 @@ export async function startTraceViewerServer(options?: TraceViewerServerOptions)
|
||||
}
|
||||
|
||||
export async function installRootRedirect(server: HttpServer, traceUrls: string[], options: TraceViewerRedirectOptions) {
|
||||
const params = (traceUrls || []).map(t => `trace=${encodeURIComponent(t)}`);
|
||||
const params = new URLSearchParams();
|
||||
for (const traceUrl of traceUrls)
|
||||
params.append('trace', traceUrl);
|
||||
if (server.wsGuid())
|
||||
params.push('ws=' + server.wsGuid());
|
||||
params.append('ws', server.wsGuid()!);
|
||||
if (options?.isServer)
|
||||
params.push('isServer');
|
||||
params.append('isServer', '');
|
||||
if (isUnderTest())
|
||||
params.push('isUnderTest=true');
|
||||
const searchQuery = params.length ? '?' + params.join('&') : '';
|
||||
const urlPath = `/trace/${options.webApp || 'index.html'}${searchQuery}`;
|
||||
params.append('isUnderTest', 'true');
|
||||
for (const arg of options.args || [])
|
||||
params.append('arg', arg);
|
||||
if (options.grep)
|
||||
params.append('grep', options.grep);
|
||||
if (options.grepInvert)
|
||||
params.append('grepInvert', options.grepInvert);
|
||||
for (const project of options.project || [])
|
||||
params.append('project', project);
|
||||
if (options.workers)
|
||||
params.append('workers', String(options.workers));
|
||||
if (options.timeout)
|
||||
params.append('timeout', String(options.timeout));
|
||||
if (options.headed)
|
||||
params.append('headed', '');
|
||||
for (const reporter of options.reporter || [])
|
||||
params.append('reporter', reporter);
|
||||
|
||||
server.routePath('/', (request, response) => {
|
||||
const urlPath = `/trace/${options.webApp || 'index.html'}?${params.toString()}`;
|
||||
server.routePath('/', (_, response) => {
|
||||
response.statusCode = 302;
|
||||
response.setHeader('Location', urlPath + request.url!.substring(1));
|
||||
response.setHeader('Location', urlPath);
|
||||
response.end();
|
||||
return true;
|
||||
});
|
||||
|
@ -15,7 +15,6 @@
|
||||
*/
|
||||
|
||||
import type { TestServerInterface, TestServerInterfaceEvents } from '@testIsomorphic/testServerInterface';
|
||||
import type * as reporterTypes from 'playwright/types/testReporter';
|
||||
import * as events from './events';
|
||||
|
||||
export class TestServerConnection implements TestServerInterface, TestServerInterfaceEvents {
|
||||
@ -67,7 +66,7 @@ export class TestServerConnection implements TestServerInterface, TestServerInte
|
||||
this._connectedPromise = new Promise<void>((f, r) => {
|
||||
this._ws.addEventListener('open', () => {
|
||||
f();
|
||||
this._ws.send(JSON.stringify({ method: 'ready' }));
|
||||
this._ws.send(JSON.stringify({ id: -1, method: 'ready' }));
|
||||
});
|
||||
this._ws.addEventListener('error', r);
|
||||
});
|
||||
@ -77,6 +76,10 @@ export class TestServerConnection implements TestServerInterface, TestServerInte
|
||||
});
|
||||
}
|
||||
|
||||
connect() {
|
||||
return this._connectedPromise;
|
||||
}
|
||||
|
||||
private async _sendMessage(method: string, params?: any): Promise<any> {
|
||||
const logForTest = (globalThis as any).__logForTest;
|
||||
logForTest?.({ method, params });
|
||||
@ -103,81 +106,83 @@ export class TestServerConnection implements TestServerInterface, TestServerInte
|
||||
this._onListChangedEmitter.fire(params);
|
||||
else if (method === 'testFilesChanged')
|
||||
this._onTestFilesChangedEmitter.fire(params);
|
||||
else if (method === 'loadTraceRequested')
|
||||
this._onLoadTraceRequestedEmitter.fire(params);
|
||||
}
|
||||
|
||||
async ping(): Promise<void> {
|
||||
async ping(params: Parameters<TestServerInterface['ping']>[0]): ReturnType<TestServerInterface['ping']> {
|
||||
await this._sendMessage('ping');
|
||||
}
|
||||
|
||||
async pingNoReply() {
|
||||
async pingNoReply(params: Parameters<TestServerInterface['ping']>[0]) {
|
||||
this._sendMessageNoReply('ping');
|
||||
}
|
||||
|
||||
async watch(params: { fileNames: string[]; }): Promise<void> {
|
||||
async watch(params: Parameters<TestServerInterface['watch']>[0]): ReturnType<TestServerInterface['watch']> {
|
||||
await this._sendMessage('watch', params);
|
||||
}
|
||||
|
||||
watchNoReply(params: { fileNames: string[]; }) {
|
||||
watchNoReply(params: Parameters<TestServerInterface['watch']>[0]) {
|
||||
this._sendMessageNoReply('watch', params);
|
||||
}
|
||||
|
||||
async open(params: { location: reporterTypes.Location; }): Promise<void> {
|
||||
async open(params: Parameters<TestServerInterface['open']>[0]): ReturnType<TestServerInterface['open']> {
|
||||
await this._sendMessage('open', params);
|
||||
}
|
||||
|
||||
openNoReply(params: { location: reporterTypes.Location; }) {
|
||||
openNoReply(params: Parameters<TestServerInterface['open']>[0]) {
|
||||
this._sendMessageNoReply('open', params);
|
||||
}
|
||||
|
||||
async resizeTerminal(params: { cols: number; rows: number; }): Promise<void> {
|
||||
async resizeTerminal(params: Parameters<TestServerInterface['resizeTerminal']>[0]): ReturnType<TestServerInterface['resizeTerminal']> {
|
||||
await this._sendMessage('resizeTerminal', params);
|
||||
}
|
||||
|
||||
resizeTerminalNoReply(params: { cols: number; rows: number; }) {
|
||||
resizeTerminalNoReply(params: Parameters<TestServerInterface['resizeTerminal']>[0]) {
|
||||
this._sendMessageNoReply('resizeTerminal', params);
|
||||
}
|
||||
|
||||
async checkBrowsers(): Promise<{ hasBrowsers: boolean; }> {
|
||||
async checkBrowsers(params: Parameters<TestServerInterface['checkBrowsers']>[0]): ReturnType<TestServerInterface['checkBrowsers']> {
|
||||
return await this._sendMessage('checkBrowsers');
|
||||
}
|
||||
|
||||
async installBrowsers(): Promise<void> {
|
||||
async installBrowsers(params: Parameters<TestServerInterface['installBrowsers']>[0]): ReturnType<TestServerInterface['installBrowsers']> {
|
||||
await this._sendMessage('installBrowsers');
|
||||
}
|
||||
|
||||
async runGlobalSetup(): Promise<'passed' | 'failed' | 'timedout' | 'interrupted'> {
|
||||
async runGlobalSetup(params: Parameters<TestServerInterface['runGlobalSetup']>[0]): ReturnType<TestServerInterface['runGlobalSetup']> {
|
||||
return await this._sendMessage('runGlobalSetup');
|
||||
}
|
||||
|
||||
async runGlobalTeardown(): Promise<'passed' | 'failed' | 'timedout' | 'interrupted'> {
|
||||
async runGlobalTeardown(params: Parameters<TestServerInterface['runGlobalTeardown']>[0]): ReturnType<TestServerInterface['runGlobalTeardown']> {
|
||||
return await this._sendMessage('runGlobalTeardown');
|
||||
}
|
||||
|
||||
async listFiles(): Promise<{ projects: { name: string; testDir: string; use: { testIdAttribute?: string | undefined; }; files: string[]; }[]; cliEntryPoint?: string | undefined; error?: reporterTypes.TestError | undefined; }> {
|
||||
return await this._sendMessage('listFiles');
|
||||
async listFiles(params: Parameters<TestServerInterface['listFiles']>[0]): ReturnType<TestServerInterface['listFiles']> {
|
||||
return await this._sendMessage('listFiles', params);
|
||||
}
|
||||
|
||||
async listTests(params: { reporter?: string | undefined; fileNames?: string[] | undefined; }): Promise<{ report: any[] }> {
|
||||
async listTests(params: Parameters<TestServerInterface['listTests']>[0]): ReturnType<TestServerInterface['listTests']> {
|
||||
return await this._sendMessage('listTests', params);
|
||||
}
|
||||
|
||||
async runTests(params: { reporter?: string | undefined; locations?: string[] | undefined; grep?: string | undefined; testIds?: string[] | undefined; headed?: boolean | undefined; oneWorker?: boolean | undefined; trace?: 'off' | 'on' | undefined; projects?: string[] | undefined; reuseContext?: boolean | undefined; connectWsEndpoint?: string | undefined; }): Promise<{ status: reporterTypes.FullResult['status'] }> {
|
||||
async runTests(params: Parameters<TestServerInterface['runTests']>[0]): ReturnType<TestServerInterface['runTests']> {
|
||||
return await this._sendMessage('runTests', params);
|
||||
}
|
||||
|
||||
async findRelatedTestFiles(params: { files: string[]; }): Promise<{ testFiles: string[]; errors?: reporterTypes.TestError[] | undefined; }> {
|
||||
async findRelatedTestFiles(params: Parameters<TestServerInterface['findRelatedTestFiles']>[0]): ReturnType<TestServerInterface['findRelatedTestFiles']> {
|
||||
return await this._sendMessage('findRelatedTestFiles', params);
|
||||
}
|
||||
|
||||
async stopTests(): Promise<void> {
|
||||
async stopTests(params: Parameters<TestServerInterface['stopTests']>[0]): ReturnType<TestServerInterface['stopTests']> {
|
||||
await this._sendMessage('stopTests');
|
||||
}
|
||||
|
||||
stopTestsNoReply() {
|
||||
stopTestsNoReply(params: Parameters<TestServerInterface['stopTests']>[0]) {
|
||||
this._sendMessageNoReply('stopTests');
|
||||
}
|
||||
|
||||
async closeGracefully(): Promise<void> {
|
||||
async closeGracefully(params: Parameters<TestServerInterface['closeGracefully']>[0]): ReturnType<TestServerInterface['closeGracefully']> {
|
||||
await this._sendMessage('closeGracefully');
|
||||
}
|
||||
}
|
||||
|
@ -18,7 +18,7 @@ import type * as reporterTypes from '../../types/testReporter';
|
||||
import type { Event } from './events';
|
||||
|
||||
export interface TestServerInterface {
|
||||
ping(): Promise<void>;
|
||||
ping(params: {}): Promise<void>;
|
||||
|
||||
watch(params: {
|
||||
fileNames: string[];
|
||||
@ -28,15 +28,17 @@ export interface TestServerInterface {
|
||||
|
||||
resizeTerminal(params: { cols: number, rows: number }): Promise<void>;
|
||||
|
||||
checkBrowsers(): Promise<{ hasBrowsers: boolean }>;
|
||||
checkBrowsers(params: {}): Promise<{ hasBrowsers: boolean }>;
|
||||
|
||||
installBrowsers(): Promise<void>;
|
||||
installBrowsers(params: {}): Promise<void>;
|
||||
|
||||
runGlobalSetup(): Promise<reporterTypes.FullResult['status']>;
|
||||
runGlobalSetup(params: {}): Promise<reporterTypes.FullResult['status']>;
|
||||
|
||||
runGlobalTeardown(): Promise<reporterTypes.FullResult['status']>;
|
||||
runGlobalTeardown(params: {}): Promise<reporterTypes.FullResult['status']>;
|
||||
|
||||
listFiles(): Promise<{
|
||||
listFiles(params: {
|
||||
projects?: string[];
|
||||
}): Promise<{
|
||||
projects: {
|
||||
name: string;
|
||||
testDir: string;
|
||||
@ -51,17 +53,21 @@ export interface TestServerInterface {
|
||||
* Returns list of teleReporter events.
|
||||
*/
|
||||
listTests(params: {
|
||||
reporter?: string;
|
||||
fileNames?: string[];
|
||||
serializer?: string;
|
||||
projects?: string[];
|
||||
locations?: string[];
|
||||
}): Promise<{ report: any[] }>;
|
||||
|
||||
runTests(params: {
|
||||
reporter?: string;
|
||||
serializer?: string;
|
||||
locations?: string[];
|
||||
grep?: string;
|
||||
grepInvert?: string;
|
||||
testIds?: string[];
|
||||
headed?: boolean;
|
||||
oneWorker?: boolean;
|
||||
workers?: number | string;
|
||||
timeout?: number,
|
||||
reporters?: string[],
|
||||
trace?: 'on' | 'off';
|
||||
projects?: string[];
|
||||
reuseContext?: boolean;
|
||||
@ -72,9 +78,9 @@ export interface TestServerInterface {
|
||||
files: string[];
|
||||
}): Promise<{ testFiles: string[]; errors?: reporterTypes.TestError[]; }>;
|
||||
|
||||
stopTests(): Promise<void>;
|
||||
stopTests(params: {}): Promise<void>;
|
||||
|
||||
closeGracefully(): Promise<void>;
|
||||
closeGracefully(params: {}): Promise<void>;
|
||||
}
|
||||
|
||||
export interface TestServerInterfaceEvents {
|
||||
|
@ -34,6 +34,7 @@ export { program } from 'playwright-core/lib/cli/program';
|
||||
import type { ReporterDescription } from '../types/test';
|
||||
import { prepareErrorStack } from './reporters/base';
|
||||
import { cacheDir } from './transform/compilationCache';
|
||||
import * as testServer from './runner/testServer';
|
||||
|
||||
function addTestCommand(program: Command) {
|
||||
const command = program.command('test [test-filter...]');
|
||||
@ -151,7 +152,28 @@ Examples:
|
||||
|
||||
async function runTests(args: string[], opts: { [key: string]: any }) {
|
||||
await startProfiling();
|
||||
const config = await loadConfigFromFileRestartIfNeeded(opts.config, overridesFromOptions(opts), opts.deps === false);
|
||||
const cliOverrides = overridesFromOptions(opts);
|
||||
|
||||
if (opts.ui || opts.uiHost || opts.uiPort) {
|
||||
const status = await testServer.runUIMode(opts.config, {
|
||||
host: opts.uiHost,
|
||||
port: opts.uiPort ? +opts.uiPort : undefined,
|
||||
args,
|
||||
grep: opts.grep as string | undefined,
|
||||
grepInvert: opts.grepInvert as string | undefined,
|
||||
project: opts.project || undefined,
|
||||
headed: opts.headed,
|
||||
reporter: Array.isArray(opts.reporter) ? opts.reporter : opts.reporter ? [opts.reporter] : undefined,
|
||||
workers: cliOverrides.workers,
|
||||
timeout: cliOverrides.timeout,
|
||||
});
|
||||
await stopProfiling('runner');
|
||||
const exitCode = status === 'interrupted' ? 130 : (status === 'passed' ? 0 : 1);
|
||||
gracefullyProcessExitDoNotHang(exitCode);
|
||||
return;
|
||||
}
|
||||
|
||||
const config = await loadConfigFromFileRestartIfNeeded(opts.config, cliOverrides, opts.deps === false);
|
||||
if (!config)
|
||||
return;
|
||||
|
||||
@ -164,9 +186,7 @@ async function runTests(args: string[], opts: { [key: string]: any }) {
|
||||
|
||||
const runner = new Runner(config);
|
||||
let status: FullResult['status'];
|
||||
if (opts.ui || opts.uiHost || opts.uiPort)
|
||||
status = await runner.runUIMode({ host: opts.uiHost, port: opts.uiPort ? +opts.uiPort : undefined });
|
||||
else if (process.env.PWTEST_WATCH)
|
||||
if (process.env.PWTEST_WATCH)
|
||||
status = await runner.watchAllTests();
|
||||
else
|
||||
status = await runner.runAllTests();
|
||||
@ -176,14 +196,9 @@ async function runTests(args: string[], opts: { [key: string]: any }) {
|
||||
}
|
||||
|
||||
async function runTestServer(opts: { [key: string]: any }) {
|
||||
const config = await loadConfigFromFileRestartIfNeeded(opts.config, overridesFromOptions(opts), opts.deps === false);
|
||||
if (!config)
|
||||
return;
|
||||
config.cliPassWithNoTests = true;
|
||||
const runner = new Runner(config);
|
||||
const host = opts.host || 'localhost';
|
||||
const port = opts.port ? +opts.port : 0;
|
||||
const status = await runner.runTestServer({ host, port });
|
||||
const status = await testServer.runTestServer(opts.config, { host, port });
|
||||
const exitCode = status === 'interrupted' ? 130 : (status === 'passed' ? 0 : 1);
|
||||
gracefullyProcessExitDoNotHang(exitCode);
|
||||
}
|
||||
|
@ -51,7 +51,8 @@ type HtmlReporterOptions = {
|
||||
host?: string,
|
||||
port?: number,
|
||||
attachmentsBaseURL?: string,
|
||||
_mode?: string;
|
||||
_mode?: 'test' | 'list';
|
||||
_isTestServer?: boolean;
|
||||
};
|
||||
|
||||
class HtmlReporter extends EmptyReporter {
|
||||
@ -67,6 +68,8 @@ class HtmlReporter extends EmptyReporter {
|
||||
constructor(options: HtmlReporterOptions) {
|
||||
super();
|
||||
this._options = options;
|
||||
if (options._mode === 'test')
|
||||
process.env.PW_HTML_REPORT = '1';
|
||||
}
|
||||
|
||||
override printsToStdio() {
|
||||
@ -125,7 +128,7 @@ class HtmlReporter extends EmptyReporter {
|
||||
if (process.env.CI || !this._buildResult)
|
||||
return;
|
||||
const { ok, singleTestId } = this._buildResult;
|
||||
const shouldOpen = this._open === 'always' || (!ok && this._open === 'on-failure');
|
||||
const shouldOpen = !this._options._isTestServer && (this._open === 'always' || (!ok && this._open === 'on-failure'));
|
||||
if (shouldOpen) {
|
||||
await showHTMLReport(this._outputFolder, this._options.host, this._options.port, singleTestId);
|
||||
} else if (this._options._mode === 'test') {
|
||||
|
@ -37,7 +37,7 @@ type ReportData = {
|
||||
};
|
||||
|
||||
export async function createMergedReport(config: FullConfigInternal, dir: string, reporterDescriptions: ReporterDescription[], rootDirOverride: string | undefined) {
|
||||
const reporters = await createReporters(config, 'merge', reporterDescriptions);
|
||||
const reporters = await createReporters(config, 'merge', false, reporterDescriptions);
|
||||
const multiplexer = new Multiplexer(reporters);
|
||||
const stringPool = new StringInternPool();
|
||||
|
||||
|
@ -33,7 +33,7 @@ import { BlobReporter } from '../reporters/blob';
|
||||
import type { ReporterDescription } from '../../types/test';
|
||||
import { type ReporterV2, wrapReporterAsV2 } from '../reporters/reporterV2';
|
||||
|
||||
export async function createReporters(config: FullConfigInternal, mode: 'list' | 'test' | 'ui' | 'merge', descriptions?: ReporterDescription[]): Promise<ReporterV2[]> {
|
||||
export async function createReporters(config: FullConfigInternal, mode: 'list' | 'test' | 'merge', isTestServer: boolean, descriptions?: ReporterDescription[]): Promise<ReporterV2[]> {
|
||||
const defaultReporters: { [key in BuiltInReporter]: new(arg: any) => ReporterV2 } = {
|
||||
blob: BlobReporter,
|
||||
dot: mode === 'list' ? ListModeReporter : DotReporter,
|
||||
@ -43,14 +43,14 @@ export async function createReporters(config: FullConfigInternal, mode: 'list' |
|
||||
json: JSONReporter,
|
||||
junit: JUnitReporter,
|
||||
null: EmptyReporter,
|
||||
html: mode === 'ui' ? LineReporter : HtmlReporter,
|
||||
html: HtmlReporter,
|
||||
markdown: MarkdownReporter,
|
||||
};
|
||||
const reporters: ReporterV2[] = [];
|
||||
descriptions ??= config.config.reporter;
|
||||
if (config.configCLIOverrides.additionalReporters)
|
||||
descriptions = [...descriptions, ...config.configCLIOverrides.additionalReporters];
|
||||
const runOptions = reporterOptions(config, mode);
|
||||
const runOptions = reporterOptions(config, mode, isTestServer);
|
||||
for (const r of descriptions) {
|
||||
const [name, arg] = r;
|
||||
const options = { ...runOptions, ...arg };
|
||||
@ -78,17 +78,19 @@ export async function createReporters(config: FullConfigInternal, mode: 'list' |
|
||||
return reporters;
|
||||
}
|
||||
|
||||
export async function createReporterForTestServer(config: FullConfigInternal, file: string, mode: 'test' | 'list', messageSink: (message: any) => void): Promise<ReporterV2> {
|
||||
export async function createReporterForTestServer(config: FullConfigInternal, mode: 'list' | 'test', file: string, messageSink: (message: any) => void): Promise<ReporterV2> {
|
||||
const reporterConstructor = await loadReporter(config, file);
|
||||
const runOptions = reporterOptions(config, mode, messageSink);
|
||||
const runOptions = reporterOptions(config, mode, true, messageSink);
|
||||
const instance = new reporterConstructor(runOptions);
|
||||
return wrapReporterAsV2(instance);
|
||||
}
|
||||
|
||||
function reporterOptions(config: FullConfigInternal, mode: 'list' | 'test' | 'ui' | 'merge', send?: (message: any) => void) {
|
||||
function reporterOptions(config: FullConfigInternal, mode: 'list' | 'test' | 'merge', isTestServer: boolean, send?: (message: any) => void) {
|
||||
return {
|
||||
configDir: config.configDir,
|
||||
_send: send,
|
||||
_mode: mode,
|
||||
_isTestServer: isTestServer,
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -16,8 +16,7 @@
|
||||
*/
|
||||
|
||||
import path from 'path';
|
||||
import type { HttpServer, ManualPromise } from 'playwright-core/lib/utils';
|
||||
import { isUnderTest, monotonicTime } from 'playwright-core/lib/utils';
|
||||
import { monotonicTime } from 'playwright-core/lib/utils';
|
||||
import type { FullResult, TestError } from '../../types/testReporter';
|
||||
import { webServerPluginsForConfig } from '../plugins/webServerPlugin';
|
||||
import { collectFilesForProject, filterProjects } from './projectUtils';
|
||||
@ -26,13 +25,11 @@ import { TestRun, createTaskRunner, createTaskRunnerForList } from './tasks';
|
||||
import type { FullConfigInternal } from '../common/config';
|
||||
import { colors } from 'playwright-core/lib/utilsBundle';
|
||||
import { runWatchModeLoop } from './watchMode';
|
||||
import { runTestServer } from './testServer';
|
||||
import { InternalReporter } from '../reporters/internalReporter';
|
||||
import { Multiplexer } from '../reporters/multiplexer';
|
||||
import type { Suite } from '../common/test';
|
||||
import { wrapReporterAsV2 } from '../reporters/reporterV2';
|
||||
import { affectedTestFiles } from '../transform/compilationCache';
|
||||
import { installRootRedirect, openTraceInBrowser, openTraceViewerApp } from 'playwright-core/lib/server';
|
||||
|
||||
type ProjectConfigWithFiles = {
|
||||
name: string;
|
||||
@ -59,9 +56,9 @@ export class Runner {
|
||||
this._config = config;
|
||||
}
|
||||
|
||||
async listTestFiles(): Promise<ConfigListFilesReport> {
|
||||
async listTestFiles(projectNames?: string[]): Promise<ConfigListFilesReport> {
|
||||
const frameworkPackage = (this._config.config as any)['@playwright/test']?.['packageJSON'];
|
||||
const projects = filterProjects(this._config.projects);
|
||||
const projects = filterProjects(this._config.projects, projectNames);
|
||||
const report: ConfigListFilesReport = {
|
||||
projects: [],
|
||||
cliEntryPoint: frameworkPackage ? path.join(path.dirname(frameworkPackage), 'cli.js') : undefined,
|
||||
@ -85,7 +82,7 @@ export class Runner {
|
||||
// Legacy webServer support.
|
||||
webServerPluginsForConfig(config).forEach(p => config.plugins.push({ factory: p }));
|
||||
|
||||
const reporter = new InternalReporter(new Multiplexer(await createReporters(config, listOnly ? 'list' : 'test')));
|
||||
const reporter = new InternalReporter(new Multiplexer(await createReporters(config, listOnly ? 'list' : 'test', false)));
|
||||
const taskRunner = listOnly ? createTaskRunnerForList(config, reporter, 'in-process', { failOnLoadErrors: true })
|
||||
: createTaskRunner(config, reporter);
|
||||
|
||||
@ -148,34 +145,6 @@ export class Runner {
|
||||
return await runWatchModeLoop(config);
|
||||
}
|
||||
|
||||
async runUIMode(options: { host?: string, port?: number }): Promise<FullResult['status']> {
|
||||
const config = this._config;
|
||||
webServerPluginsForConfig(config).forEach(p => config.plugins.push({ factory: p }));
|
||||
return await runTestServer(config, options, async (server: HttpServer, cancelPromise: ManualPromise<void>) => {
|
||||
await installRootRedirect(server, [], { webApp: 'uiMode.html' });
|
||||
if (options.host !== undefined || options.port !== undefined) {
|
||||
await openTraceInBrowser(server.urlPrefix());
|
||||
} else {
|
||||
const page = await openTraceViewerApp(server.urlPrefix(), 'chromium', {
|
||||
headless: isUnderTest() && process.env.PWTEST_HEADED_FOR_TEST !== '1',
|
||||
persistentContextOptions: {
|
||||
handleSIGINT: false,
|
||||
},
|
||||
});
|
||||
page.on('close', () => cancelPromise.resolve());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
async runTestServer(options: { host?: string, port?: number }): Promise<FullResult['status']> {
|
||||
const config = this._config;
|
||||
webServerPluginsForConfig(config).forEach(p => config.plugins.push({ factory: p }));
|
||||
return await runTestServer(config, options, async server => {
|
||||
// eslint-disable-next-line no-console
|
||||
console.log('Listening on ' + server.urlPrefix().replace('http:', 'ws:') + '/' + server.wsGuid());
|
||||
});
|
||||
}
|
||||
|
||||
async findRelatedTestFiles(mode: 'in-process' | 'out-of-process', files: string[]): Promise<FindRelatedTestFilesReport> {
|
||||
const result = await this.loadAllTests(mode);
|
||||
if (result.status !== 'passed' || !result.suite)
|
||||
|
@ -16,7 +16,7 @@
|
||||
|
||||
import fs from 'fs';
|
||||
import path from 'path';
|
||||
import { registry, startTraceViewerServer } from 'playwright-core/lib/server';
|
||||
import { installRootRedirect, openTraceInBrowser, openTraceViewerApp, registry, startTraceViewerServer } from 'playwright-core/lib/server';
|
||||
import { ManualPromise, gracefullyProcessExitDoNotHang, isUnderTest } from 'playwright-core/lib/utils';
|
||||
import type { Transport, HttpServer } from 'playwright-core/lib/utils';
|
||||
import type * as reporterTypes from '../../types/testReporter';
|
||||
@ -34,33 +34,26 @@ import type { TestServerInterface, TestServerInterfaceEventEmitters } from '../i
|
||||
import { Runner } from './runner';
|
||||
import { serializeError } from '../util';
|
||||
import { prepareErrorStack } from '../reporters/base';
|
||||
import type { ConfigCLIOverrides } from '../common/ipc';
|
||||
import { loadConfig, resolveConfigFile, restartWithExperimentalTsEsm } from '../common/configLoader';
|
||||
import { webServerPluginsForConfig } from '../plugins/webServerPlugin';
|
||||
import type { TraceViewerRedirectOptions, TraceViewerServerOptions } from 'playwright-core/lib/server/trace/viewer/traceViewer';
|
||||
import type { TestRunnerPluginRegistration } from '../plugins';
|
||||
|
||||
class TestServer {
|
||||
private _config: FullConfigInternal;
|
||||
private _configFile: string | undefined;
|
||||
private _dispatcher: TestServerDispatcher | undefined;
|
||||
private _originalStdoutWrite: NodeJS.WriteStream['write'];
|
||||
private _originalStderrWrite: NodeJS.WriteStream['write'];
|
||||
|
||||
constructor(config: FullConfigInternal) {
|
||||
this._config = config;
|
||||
process.env.PW_LIVE_TRACE_STACKS = '1';
|
||||
config.cliListOnly = false;
|
||||
config.cliPassWithNoTests = true;
|
||||
config.config.preserveOutput = 'always';
|
||||
|
||||
for (const p of config.projects) {
|
||||
p.project.retries = 0;
|
||||
p.project.repeatEach = 1;
|
||||
}
|
||||
config.configCLIOverrides.use = config.configCLIOverrides.use || {};
|
||||
config.configCLIOverrides.use.trace = { mode: 'on', sources: false, _live: true };
|
||||
|
||||
constructor(configFile: string | undefined) {
|
||||
this._configFile = configFile;
|
||||
this._originalStdoutWrite = process.stdout.write;
|
||||
this._originalStderrWrite = process.stderr.write;
|
||||
}
|
||||
|
||||
async start(options: { host?: string, port?: number }): Promise<HttpServer> {
|
||||
this._dispatcher = new TestServerDispatcher(this._config);
|
||||
this._dispatcher = new TestServerDispatcher(this._configFile);
|
||||
return await startTraceViewerServer({ ...options, transport: this._dispatcher.transport });
|
||||
}
|
||||
|
||||
@ -90,7 +83,7 @@ class TestServer {
|
||||
}
|
||||
|
||||
class TestServerDispatcher implements TestServerInterface {
|
||||
private _config: FullConfigInternal;
|
||||
private _configFile: string | undefined;
|
||||
private _globalWatcher: Watcher;
|
||||
private _testWatcher: Watcher;
|
||||
private _testRun: { run: Promise<reporterTypes.FullResult['status']>, stop: ManualPromise<void> } | undefined;
|
||||
@ -98,9 +91,10 @@ class TestServerDispatcher implements TestServerInterface {
|
||||
private _queue = Promise.resolve();
|
||||
private _globalCleanup: (() => Promise<reporterTypes.FullResult['status']>) | undefined;
|
||||
readonly _dispatchEvent: TestServerInterfaceEventEmitters['dispatchEvent'];
|
||||
private _plugins: TestRunnerPluginRegistration[] | undefined;
|
||||
|
||||
constructor(config: FullConfigInternal) {
|
||||
this._config = config;
|
||||
constructor(configFile: string | undefined) {
|
||||
this._configFile = configFile;
|
||||
this.transport = {
|
||||
dispatch: (method, params) => (this as any)[method](params),
|
||||
onclose: () => {},
|
||||
@ -114,16 +108,18 @@ class TestServerDispatcher implements TestServerInterface {
|
||||
this._dispatchEvent = (method, params) => this.transport.sendEvent?.(method, params);
|
||||
}
|
||||
|
||||
async ready() {}
|
||||
|
||||
async ping() {}
|
||||
|
||||
async open(params: { location: reporterTypes.Location }) {
|
||||
async open(params: Parameters<TestServerInterface['open']>[0]): ReturnType<TestServerInterface['open']> {
|
||||
if (isUnderTest())
|
||||
return;
|
||||
// eslint-disable-next-line no-console
|
||||
open('vscode://file/' + params.location.file + ':' + params.location.line).catch(e => console.error(e));
|
||||
}
|
||||
|
||||
async resizeTerminal(params: { cols: number; rows: number; }) {
|
||||
async resizeTerminal(params: Parameters<TestServerInterface['resizeTerminal']>[0]): ReturnType<TestServerInterface['resizeTerminal']> {
|
||||
process.stdout.columns = params.cols;
|
||||
process.stdout.rows = params.rows;
|
||||
process.stderr.columns = params.cols;
|
||||
@ -141,10 +137,13 @@ class TestServerDispatcher implements TestServerInterface {
|
||||
async runGlobalSetup(): Promise<reporterTypes.FullResult['status']> {
|
||||
await this.runGlobalTeardown();
|
||||
|
||||
const config = await this._loadConfig(this._configFile);
|
||||
webServerPluginsForConfig(config).forEach(p => config.plugins.push({ factory: p }));
|
||||
|
||||
const reporter = new InternalReporter(new ListReporter());
|
||||
const taskRunner = createTaskRunnerForWatchSetup(this._config, reporter);
|
||||
reporter.onConfigure(this._config.config);
|
||||
const testRun = new TestRun(this._config, reporter);
|
||||
const taskRunner = createTaskRunnerForWatchSetup(config, reporter);
|
||||
reporter.onConfigure(config.config);
|
||||
const testRun = new TestRun(config, reporter);
|
||||
const { status, cleanup: globalCleanup } = await taskRunner.runDeferCleanup(testRun, 0);
|
||||
await reporter.onEnd({ status });
|
||||
await reporter.onExit();
|
||||
@ -162,10 +161,11 @@ class TestServerDispatcher implements TestServerInterface {
|
||||
return result;
|
||||
}
|
||||
|
||||
async listFiles() {
|
||||
async listFiles(params: Parameters<TestServerInterface['listFiles']>[0]): ReturnType<TestServerInterface['listFiles']> {
|
||||
try {
|
||||
const runner = new Runner(this._config);
|
||||
return runner.listTestFiles();
|
||||
const config = await this._loadConfig(this._configFile);
|
||||
const runner = new Runner(config);
|
||||
return runner.listTestFiles(params.projects);
|
||||
} catch (e) {
|
||||
const error: reporterTypes.TestError = serializeError(e);
|
||||
error.location = prepareErrorStack(e.stack).location;
|
||||
@ -173,82 +173,109 @@ class TestServerDispatcher implements TestServerInterface {
|
||||
}
|
||||
}
|
||||
|
||||
async listTests(params: { reporter?: string; fileNames: string[]; }) {
|
||||
let report: any[] = [];
|
||||
async listTests(params: Parameters<TestServerInterface['listTests']>[0]): ReturnType<TestServerInterface['listTests']> {
|
||||
let result: Awaited<ReturnType<TestServerInterface['listTests']>>;
|
||||
this._queue = this._queue.then(async () => {
|
||||
report = await this._innerListTests(params);
|
||||
result = await this._innerListTests(params);
|
||||
}).catch(printInternalError);
|
||||
await this._queue;
|
||||
return { report };
|
||||
return result!;
|
||||
}
|
||||
|
||||
private async _innerListTests(params: { reporter?: string; fileNames?: string[]; }) {
|
||||
private async _innerListTests(params: Parameters<TestServerInterface['listTests']>[0]): ReturnType<TestServerInterface['listTests']> {
|
||||
const overrides: ConfigCLIOverrides = {
|
||||
repeatEach: 1,
|
||||
retries: 0,
|
||||
};
|
||||
const config = await this._loadConfig(this._configFile, overrides);
|
||||
config.cliArgs = params.locations || [];
|
||||
config.cliProjectFilter = params.projects?.length ? params.projects : undefined;
|
||||
config.cliListOnly = true;
|
||||
|
||||
const wireReporter = await createReporterForTestServer(config, 'list', params.serializer || require.resolve('./uiModeReporter'), e => report.push(e));
|
||||
const report: any[] = [];
|
||||
const wireReporter = await createReporterForTestServer(this._config, params.reporter || require.resolve('./uiModeReporter'), 'list', e => report.push(e));
|
||||
const reporter = new InternalReporter(wireReporter);
|
||||
this._config.cliArgs = params.fileNames || [];
|
||||
this._config.cliListOnly = true;
|
||||
this._config.testIdMatcher = undefined;
|
||||
const taskRunner = createTaskRunnerForList(this._config, reporter, 'out-of-process', { failOnLoadErrors: false });
|
||||
const testRun = new TestRun(this._config, reporter);
|
||||
reporter.onConfigure(this._config.config);
|
||||
|
||||
const taskRunner = createTaskRunnerForList(config, reporter, 'out-of-process', { failOnLoadErrors: false });
|
||||
const testRun = new TestRun(config, reporter);
|
||||
reporter.onConfigure(config.config);
|
||||
const status = await taskRunner.run(testRun, 0);
|
||||
await reporter.onEnd({ status });
|
||||
await reporter.onExit();
|
||||
|
||||
const projectDirs = new Set<string>();
|
||||
const projectOutputs = new Set<string>();
|
||||
for (const p of this._config.projects) {
|
||||
for (const p of config.projects) {
|
||||
projectDirs.add(p.project.testDir);
|
||||
projectOutputs.add(p.project.outputDir);
|
||||
}
|
||||
|
||||
const result = await resolveCtDirs(this._config);
|
||||
const result = await resolveCtDirs(config);
|
||||
if (result) {
|
||||
projectDirs.add(result.templateDir);
|
||||
projectOutputs.add(result.outDir);
|
||||
}
|
||||
|
||||
this._globalWatcher.update([...projectDirs], [...projectOutputs], false);
|
||||
return report;
|
||||
return { report };
|
||||
}
|
||||
|
||||
async runTests(params: { reporter?: string; locations?: string[] | undefined; grep?: string | undefined; testIds?: string[] | undefined; headed?: boolean | undefined; oneWorker?: boolean | undefined; trace?: 'off' | 'on' | undefined; projects?: string[] | undefined; reuseContext?: boolean | undefined; connectWsEndpoint?: string | undefined; }) {
|
||||
let status: reporterTypes.FullResult['status'];
|
||||
async runTests(params: Parameters<TestServerInterface['runTests']>[0]): ReturnType<TestServerInterface['runTests']> {
|
||||
let result: Awaited<ReturnType<TestServerInterface['runTests']>>;
|
||||
this._queue = this._queue.then(async () => {
|
||||
status = await this._innerRunTests(params).catch(printInternalError) || 'failed';
|
||||
result = await this._innerRunTests(params).catch(printInternalError) || { status: 'failed' };
|
||||
});
|
||||
await this._queue;
|
||||
return { status: status! };
|
||||
return result!;
|
||||
}
|
||||
|
||||
private async _innerRunTests(params: { reporter?: string; locations?: string[] | undefined; grep?: string | undefined; testIds?: string[] | undefined; headed?: boolean | undefined; oneWorker?: boolean | undefined; trace?: 'off' | 'on' | undefined; projects?: string[] | undefined; reuseContext?: boolean | undefined; connectWsEndpoint?: string | undefined; }): Promise<reporterTypes.FullResult['status']> {
|
||||
private async _innerRunTests(params: Parameters<TestServerInterface['runTests']>[0]): ReturnType<TestServerInterface['runTests']> {
|
||||
await this.stopTests();
|
||||
const { testIds, projects, locations, grep } = params;
|
||||
const overrides: ConfigCLIOverrides = {
|
||||
repeatEach: 1,
|
||||
retries: 0,
|
||||
preserveOutputDir: true,
|
||||
timeout: params.timeout,
|
||||
reporter: params.reporters ? params.reporters.map(r => [r]) : undefined,
|
||||
use: {
|
||||
trace: params.trace === 'on' ? { mode: 'on', sources: false, _live: true } : undefined,
|
||||
headless: params.headed ? false : undefined,
|
||||
_optionContextReuseMode: params.reuseContext ? 'when-possible' : undefined,
|
||||
_optionConnectOptions: params.connectWsEndpoint ? { wsEndpoint: params.connectWsEndpoint } : undefined,
|
||||
},
|
||||
workers: params.workers,
|
||||
};
|
||||
if (params.trace === 'on')
|
||||
process.env.PW_LIVE_TRACE_STACKS = '1';
|
||||
else
|
||||
process.env.PW_LIVE_TRACE_STACKS = undefined;
|
||||
|
||||
const testIdSet = testIds ? new Set<string>(testIds) : null;
|
||||
this._config.cliArgs = locations ? locations : [];
|
||||
this._config.cliGrep = grep;
|
||||
this._config.cliListOnly = false;
|
||||
this._config.cliProjectFilter = projects?.length ? projects : undefined;
|
||||
this._config.testIdMatcher = id => !testIdSet || testIdSet.has(id);
|
||||
const testIdSet = params.testIds ? new Set<string>(params.testIds) : null;
|
||||
const config = await this._loadConfig(this._configFile, overrides);
|
||||
config.cliListOnly = false;
|
||||
config.cliPassWithNoTests = true;
|
||||
config.cliArgs = params.locations || [];
|
||||
config.cliGrep = params.grep;
|
||||
config.cliGrepInvert = params.grepInvert;
|
||||
config.cliProjectFilter = params.projects?.length ? params.projects : undefined;
|
||||
config.testIdMatcher = testIdSet ? id => testIdSet.has(id) : undefined;
|
||||
|
||||
const reporters = await createReporters(this._config, 'ui');
|
||||
reporters.push(await createReporterForTestServer(this._config, params.reporter || require.resolve('./uiModeReporter'), 'list', e => this._dispatchEvent('report', e)));
|
||||
const reporters = await createReporters(config, 'test', true);
|
||||
reporters.push(await createReporterForTestServer(config, 'test', params.serializer || require.resolve('./uiModeReporter'), e => this._dispatchEvent('report', e)));
|
||||
const reporter = new InternalReporter(new Multiplexer(reporters));
|
||||
const taskRunner = createTaskRunnerForWatch(this._config, reporter);
|
||||
const testRun = new TestRun(this._config, reporter);
|
||||
reporter.onConfigure(this._config.config);
|
||||
const taskRunner = createTaskRunnerForWatch(config, reporter);
|
||||
const testRun = new TestRun(config, reporter);
|
||||
reporter.onConfigure(config.config);
|
||||
const stop = new ManualPromise();
|
||||
const run = taskRunner.run(testRun, 0, stop).then(async status => {
|
||||
await reporter.onEnd({ status });
|
||||
await reporter.onExit();
|
||||
this._testRun = undefined;
|
||||
this._config.testIdMatcher = undefined;
|
||||
return status;
|
||||
});
|
||||
this._testRun = { run, stop };
|
||||
return await run;
|
||||
const status = await run;
|
||||
return { status };
|
||||
}
|
||||
|
||||
async watch(params: { fileNames: string[]; }) {
|
||||
@ -260,8 +287,9 @@ class TestServerDispatcher implements TestServerInterface {
|
||||
this._testWatcher.update([...files], [], true);
|
||||
}
|
||||
|
||||
findRelatedTestFiles(params: { files: string[]; }): Promise<{ testFiles: string[]; errors?: reporterTypes.TestError[] | undefined; }> {
|
||||
const runner = new Runner(this._config);
|
||||
async findRelatedTestFiles(params: Parameters<TestServerInterface['findRelatedTestFiles']>[0]): ReturnType<TestServerInterface['findRelatedTestFiles']> {
|
||||
const config = await this._loadConfig(this._configFile);
|
||||
const runner = new Runner(config);
|
||||
return runner.findRelatedTestFiles('out-of-process', params.files);
|
||||
}
|
||||
|
||||
@ -273,10 +301,49 @@ class TestServerDispatcher implements TestServerInterface {
|
||||
async closeGracefully() {
|
||||
gracefullyProcessExitDoNotHang(0);
|
||||
}
|
||||
|
||||
private async _loadConfig(configFile: string | undefined, overrides?: ConfigCLIOverrides): Promise<FullConfigInternal> {
|
||||
const configFileOrDirectory = configFile ? path.resolve(process.cwd(), configFile) : process.cwd();
|
||||
const resolvedConfigFile = resolveConfigFile(configFileOrDirectory);
|
||||
const config = await loadConfig({ resolvedConfigFile, configDir: resolvedConfigFile === configFileOrDirectory ? path.dirname(resolvedConfigFile) : configFileOrDirectory }, overrides);
|
||||
|
||||
// Preserve plugin instances between setup and build.
|
||||
if (!this._plugins)
|
||||
this._plugins = config.plugins || [];
|
||||
else
|
||||
config.plugins.splice(0, config.plugins.length, ...this._plugins);
|
||||
return config;
|
||||
}
|
||||
}
|
||||
|
||||
export async function runTestServer(config: FullConfigInternal, options: { host?: string, port?: number }, openUI: (server: HttpServer, cancelPromise: ManualPromise<void>) => Promise<void>): Promise<reporterTypes.FullResult['status']> {
|
||||
const testServer = new TestServer(config);
|
||||
export async function runUIMode(configFile: string | undefined, options: TraceViewerServerOptions & TraceViewerRedirectOptions): Promise<reporterTypes.FullResult['status']> {
|
||||
return await innerRunTestServer(configFile, options, async (server: HttpServer, cancelPromise: ManualPromise<void>) => {
|
||||
await installRootRedirect(server, [], { ...options, webApp: 'uiMode.html' });
|
||||
if (options.host !== undefined || options.port !== undefined) {
|
||||
await openTraceInBrowser(server.urlPrefix());
|
||||
} else {
|
||||
const page = await openTraceViewerApp(server.urlPrefix(), 'chromium', {
|
||||
headless: isUnderTest() && process.env.PWTEST_HEADED_FOR_TEST !== '1',
|
||||
persistentContextOptions: {
|
||||
handleSIGINT: false,
|
||||
},
|
||||
});
|
||||
page.on('close', () => cancelPromise.resolve());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
export async function runTestServer(configFile: string | undefined, options: { host?: string, port?: number }): Promise<reporterTypes.FullResult['status']> {
|
||||
return await innerRunTestServer(configFile, options, async server => {
|
||||
// eslint-disable-next-line no-console
|
||||
console.log('Listening on ' + server.urlPrefix().replace('http:', 'ws:') + '/' + server.wsGuid());
|
||||
});
|
||||
}
|
||||
|
||||
async function innerRunTestServer(configFile: string | undefined, options: { host?: string, port?: number }, openUI: (server: HttpServer, cancelPromise: ManualPromise<void>) => Promise<void>): Promise<reporterTypes.FullResult['status']> {
|
||||
if (restartWithExperimentalTsEsm(undefined, true))
|
||||
return 'passed';
|
||||
const testServer = new TestServer(configFile);
|
||||
const cancelPromise = new ManualPromise<void>();
|
||||
const sigintWatcher = new SigIntWatcher();
|
||||
void sigintWatcher.promise().then(() => cancelPromise.resolve());
|
||||
|
@ -48,6 +48,21 @@ const xtermDataSource: XtermDataSource = {
|
||||
resize: () => {},
|
||||
};
|
||||
|
||||
const searchParams = new URLSearchParams(window.location.search);
|
||||
const guid = searchParams.get('ws');
|
||||
const wsURL = new URL(`../${guid}`, window.location.toString());
|
||||
wsURL.protocol = (window.location.protocol === 'https:' ? 'wss:' : 'ws:');
|
||||
const queryParams = {
|
||||
args: searchParams.getAll('arg'),
|
||||
grep: searchParams.get('grep') || undefined,
|
||||
grepInvert: searchParams.get('grepInvert') || undefined,
|
||||
projects: searchParams.getAll('project'),
|
||||
workers: searchParams.get('workers') || undefined,
|
||||
timeout: searchParams.has('timeout') ? +searchParams.get('timeout')! : undefined,
|
||||
headed: searchParams.has('headed'),
|
||||
reporters: searchParams.has('reporter') ? searchParams.getAll('reporter') : undefined,
|
||||
};
|
||||
|
||||
export const UIModeView: React.FC<{}> = ({
|
||||
}) => {
|
||||
const [filterText, setFilterText] = React.useState<string>('');
|
||||
@ -76,9 +91,6 @@ export const UIModeView: React.FC<{}> = ({
|
||||
const inputRef = React.useRef<HTMLInputElement>(null);
|
||||
|
||||
const reloadTests = React.useCallback(() => {
|
||||
const guid = new URLSearchParams(window.location.search).get('ws');
|
||||
const wsURL = new URL(`../${guid}`, window.location.toString());
|
||||
wsURL.protocol = (window.location.protocol === 'https:' ? 'wss:' : 'ws:');
|
||||
setTestServerConnection(new TestServerConnection(wsURL.toString()));
|
||||
}, []);
|
||||
|
||||
@ -143,7 +155,7 @@ export const UIModeView: React.FC<{}> = ({
|
||||
commandQueue.current = commandQueue.current.then(async () => {
|
||||
setIsLoading(true);
|
||||
try {
|
||||
const result = await testServerConnection.listTests({});
|
||||
const result = await testServerConnection.listTests({ projects: queryParams.projects, locations: queryParams.args });
|
||||
teleSuiteUpdater.processListReport(result.report);
|
||||
} catch (e) {
|
||||
// eslint-disable-next-line no-console
|
||||
@ -158,10 +170,10 @@ export const UIModeView: React.FC<{}> = ({
|
||||
setIsLoading(true);
|
||||
setWatchedTreeIds({ value: new Set() });
|
||||
(async () => {
|
||||
const status = await testServerConnection.runGlobalSetup();
|
||||
const status = await testServerConnection.runGlobalSetup({});
|
||||
if (status !== 'passed')
|
||||
return;
|
||||
const result = await testServerConnection.listTests({});
|
||||
const result = await testServerConnection.listTests({ projects: queryParams.projects, locations: queryParams.args });
|
||||
teleSuiteUpdater.processListReport(result.report);
|
||||
|
||||
testServerConnection.onListChanged(updateList);
|
||||
@ -170,7 +182,7 @@ export const UIModeView: React.FC<{}> = ({
|
||||
});
|
||||
setIsLoading(false);
|
||||
|
||||
const { hasBrowsers } = await testServerConnection.checkBrowsers();
|
||||
const { hasBrowsers } = await testServerConnection.checkBrowsers({});
|
||||
setHasBrowsers(hasBrowsers);
|
||||
})();
|
||||
return () => {
|
||||
@ -251,7 +263,18 @@ export const UIModeView: React.FC<{}> = ({
|
||||
setProgress({ total: 0, passed: 0, failed: 0, skipped: 0 });
|
||||
setRunningState({ testIds });
|
||||
|
||||
await testServerConnection.runTests({ testIds: [...testIds], projects: [...projectFilters].filter(([_, v]) => v).map(([p]) => p) });
|
||||
await testServerConnection.runTests({
|
||||
locations: queryParams.args,
|
||||
grep: queryParams.grep,
|
||||
grepInvert: queryParams.grepInvert,
|
||||
testIds: [...testIds],
|
||||
projects: [...projectFilters].filter(([_, v]) => v).map(([p]) => p),
|
||||
workers: queryParams.workers,
|
||||
timeout: queryParams.timeout,
|
||||
headed: queryParams.headed,
|
||||
reporters: queryParams.reporters,
|
||||
trace: 'on',
|
||||
});
|
||||
// Clear pending tests in case of interrupt.
|
||||
for (const test of testModel.rootSuite?.allTests() || []) {
|
||||
if (test.results[0]?.duration === -1)
|
||||
@ -298,7 +321,7 @@ export const UIModeView: React.FC<{}> = ({
|
||||
const onShortcutEvent = (e: KeyboardEvent) => {
|
||||
if (e.code === 'F6') {
|
||||
e.preventDefault();
|
||||
testServerConnection?.stopTestsNoReply();
|
||||
testServerConnection?.stopTestsNoReply({});
|
||||
} else if (e.code === 'F5') {
|
||||
e.preventDefault();
|
||||
reloadTests();
|
||||
@ -325,9 +348,9 @@ export const UIModeView: React.FC<{}> = ({
|
||||
const installBrowsers = React.useCallback((e: React.MouseEvent) => {
|
||||
closeInstallDialog(e);
|
||||
setIsShowingOutput(true);
|
||||
testServerConnection?.installBrowsers().then(async () => {
|
||||
testServerConnection?.installBrowsers({}).then(async () => {
|
||||
setIsShowingOutput(false);
|
||||
const { hasBrowsers } = await testServerConnection?.checkBrowsers();
|
||||
const { hasBrowsers } = await testServerConnection?.checkBrowsers({});
|
||||
setHasBrowsers(hasBrowsers);
|
||||
});
|
||||
}, [closeInstallDialog, testServerConnection]);
|
||||
@ -390,7 +413,7 @@ export const UIModeView: React.FC<{}> = ({
|
||||
<div>Running {progress.passed}/{runningState.testIds.size} passed ({(progress.passed / runningState.testIds.size) * 100 | 0}%)</div>
|
||||
</div>}
|
||||
<ToolbarButton icon='play' title='Run all' onClick={() => runTests('bounce-if-busy', visibleTestIds)} disabled={isRunningTest || isLoading}></ToolbarButton>
|
||||
<ToolbarButton icon='debug-stop' title='Stop' onClick={() => testServerConnection?.stopTests()} disabled={!isRunningTest || isLoading}></ToolbarButton>
|
||||
<ToolbarButton icon='debug-stop' title='Stop' onClick={() => testServerConnection?.stopTests({})} disabled={!isRunningTest || isLoading}></ToolbarButton>
|
||||
<ToolbarButton icon='eye' title='Watch all' toggled={watchAll} onClick={() => {
|
||||
setWatchedTreeIds({ value: new Set() });
|
||||
setWatchAll(!watchAll);
|
||||
|
Loading…
Reference in New Issue
Block a user