mirror of
https://github.com/microsoft/playwright.git
synced 2025-01-05 19:04:43 +03:00
chore: an iteration on test server (#29589)
This commit is contained in:
parent
7c9ca0c7ef
commit
ba72f7e429
@ -83,6 +83,14 @@ export function createTaskRunnerForWatch(config: FullConfigInternal, reporter: R
|
||||
return taskRunner;
|
||||
}
|
||||
|
||||
export function createTaskRunnerForTestServer(config: FullConfigInternal, reporter: ReporterV2): TaskRunner<TestRun> {
|
||||
const taskRunner = new TaskRunner<TestRun>(reporter, 0);
|
||||
addGlobalSetupTasks(taskRunner, config);
|
||||
taskRunner.addTask('load tests', createLoadTask('out-of-process', { filterOnly: true, failOnLoadErrors: false, doNotRunTestsOutsideProjectFilter: true }));
|
||||
addRunTasks(taskRunner, config);
|
||||
return taskRunner;
|
||||
}
|
||||
|
||||
function addGlobalSetupTasks(taskRunner: TaskRunner<TestRun>, config: FullConfigInternal) {
|
||||
if (!config.configCLIOverrides.preserveOutputDir && !process.env.PW_TEST_NO_REMOVE_OUTPUT_DIRS)
|
||||
taskRunner.addTask('clear output', createRemoveOutputDirsTask());
|
||||
|
@ -24,7 +24,7 @@ import { loadConfigFromFile } from '../common/configLoader';
|
||||
import { InternalReporter } from '../reporters/internalReporter';
|
||||
import { Multiplexer } from '../reporters/multiplexer';
|
||||
import { createReporters } from './reporters';
|
||||
import { TestRun, createTaskRunnerForList, createTaskRunnerForWatch } from './tasks';
|
||||
import { TestRun, createTaskRunnerForList, createTaskRunnerForTestServer } from './tasks';
|
||||
|
||||
type PlaywrightTestOptions = {
|
||||
headed?: boolean,
|
||||
@ -44,13 +44,18 @@ export async function runTestServer(configFile: string) {
|
||||
if (!config)
|
||||
return;
|
||||
|
||||
const dispatcher = new Dispatcher(config);
|
||||
const wss = new WSServer({
|
||||
onConnection(request: http.IncomingMessage, url: URL, ws: WebSocket, id: string) {
|
||||
const dispatcher = new Dispatcher(config, ws);
|
||||
ws.on('message', async message => {
|
||||
const { id, method, params } = JSON.parse(message.toString());
|
||||
const result = await (dispatcher as any)[method](params);
|
||||
ws.send(JSON.stringify({ id, result }));
|
||||
try {
|
||||
const result = await (dispatcher as any)[method](params);
|
||||
ws.send(JSON.stringify({ id, result }));
|
||||
} catch (e) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.error(e);
|
||||
}
|
||||
});
|
||||
return {
|
||||
async close() {}
|
||||
@ -64,62 +69,111 @@ export async function runTestServer(configFile: string) {
|
||||
|
||||
class Dispatcher {
|
||||
private _config: FullConfigInternal;
|
||||
private _testRun: { run: Promise<FullResult['status']>, stop: ManualPromise<void> } | undefined;
|
||||
private _ws: WebSocket;
|
||||
|
||||
constructor(config: FullConfigInternal) {
|
||||
constructor(config: FullConfigInternal, ws: WebSocket) {
|
||||
this._config = config;
|
||||
this._ws = ws;
|
||||
|
||||
process.stdout.write = ((chunk: string | Buffer, cb?: Buffer | Function, cb2?: Function) => {
|
||||
this._dispatchEvent('stdio', chunkToPayload('stdout', chunk));
|
||||
if (typeof cb === 'function')
|
||||
(cb as any)();
|
||||
if (typeof cb2 === 'function')
|
||||
(cb2 as any)();
|
||||
return true;
|
||||
}) as any;
|
||||
process.stderr.write = ((chunk: string | Buffer, cb?: Buffer | Function, cb2?: Function) => {
|
||||
this._dispatchEvent('stdio', chunkToPayload('stderr', chunk));
|
||||
if (typeof cb === 'function')
|
||||
(cb as any)();
|
||||
if (typeof cb2 === 'function')
|
||||
(cb2 as any)();
|
||||
return true;
|
||||
}) as any;
|
||||
}
|
||||
|
||||
async test(params: { mode: 'list' | 'run', locations: string[], options: PlaywrightTestOptions, reporter: string, env: NodeJS.ProcessEnv }) {
|
||||
async list(params: { locations: string[], reporter: string, env: NodeJS.ProcessEnv }) {
|
||||
for (const name in params.env)
|
||||
process.env[name] = params.env[name];
|
||||
if (params.mode === 'list')
|
||||
await listTests(this._config, params.reporter, params.locations);
|
||||
if (params.mode === 'run')
|
||||
await runTests(this._config, params.reporter, params.locations, params.options);
|
||||
await this._listTests(params.reporter, params.locations);
|
||||
}
|
||||
|
||||
async test(params: { locations: string[], options: PlaywrightTestOptions, reporter: string, env: NodeJS.ProcessEnv }) {
|
||||
for (const name in params.env)
|
||||
process.env[name] = params.env[name];
|
||||
await this._runTests(params.reporter, params.locations, params.options);
|
||||
}
|
||||
|
||||
async stop() {
|
||||
await this._stopTests();
|
||||
}
|
||||
|
||||
private async _listTests(reporterPath: string, locations: string[] | undefined) {
|
||||
this._config.cliArgs = [...(locations || []), '--reporter=null'];
|
||||
const reporter = new InternalReporter(new Multiplexer(await createReporters(this._config, 'list', [[reporterPath]])));
|
||||
const taskRunner = createTaskRunnerForList(this._config, reporter, 'out-of-process', { failOnLoadErrors: true });
|
||||
const testRun = new TestRun(this._config, reporter);
|
||||
reporter.onConfigure(this._config.config);
|
||||
|
||||
const taskStatus = await taskRunner.run(testRun, 0);
|
||||
let status: FullResult['status'] = testRun.failureTracker.result();
|
||||
if (status === 'passed' && taskStatus !== 'passed')
|
||||
status = taskStatus;
|
||||
const modifiedResult = await reporter.onEnd({ status });
|
||||
if (modifiedResult && modifiedResult.status)
|
||||
status = modifiedResult.status;
|
||||
await reporter.onExit();
|
||||
}
|
||||
|
||||
private async _runTests(reporterPath: string, locations: string[] | undefined, options: PlaywrightTestOptions) {
|
||||
await this._stopTests();
|
||||
this._config.cliListOnly = false;
|
||||
this._config.cliArgs = locations || [];
|
||||
this._config.cliGrep = options.grep;
|
||||
this._config.cliProjectFilter = options.projects?.length ? options.projects : undefined;
|
||||
this._config.configCLIOverrides.reporter = [[reporterPath]];
|
||||
this._config.configCLIOverrides.repeatEach = 1;
|
||||
this._config.configCLIOverrides.retries = 0;
|
||||
this._config.configCLIOverrides.preserveOutputDir = true;
|
||||
this._config.configCLIOverrides.use = {
|
||||
trace: options.trace,
|
||||
headless: options.headed ? false : undefined,
|
||||
_optionContextReuseMode: options.reuseContext ? 'when-possible' : undefined,
|
||||
_optionConnectOptions: options.connectWsEndpoint ? { wsEndpoint: options.connectWsEndpoint } : undefined,
|
||||
};
|
||||
// Too late to adjust via overrides for this one.
|
||||
if (options.oneWorker)
|
||||
this._config.config.workers = 1;
|
||||
|
||||
const reporter = new InternalReporter(new Multiplexer(await createReporters(this._config, 'run')));
|
||||
const taskRunner = createTaskRunnerForTestServer(this._config, reporter);
|
||||
const testRun = new TestRun(this._config, reporter);
|
||||
reporter.onConfigure(this._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;
|
||||
return status;
|
||||
});
|
||||
this._testRun = { run, stop };
|
||||
await run;
|
||||
}
|
||||
|
||||
private async _stopTests() {
|
||||
this._testRun?.stop?.resolve();
|
||||
await this._testRun?.run;
|
||||
}
|
||||
|
||||
private _dispatchEvent(method: string, params: any) {
|
||||
this._ws.send(JSON.stringify({ method, params }));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
async function listTests(config: FullConfigInternal, reporterPath: string, locations: string[] | undefined) {
|
||||
config.cliArgs = [...(locations || []), '--reporter=null'];
|
||||
const reporter = new InternalReporter(new Multiplexer(await createReporters(config, 'list', [[reporterPath]])));
|
||||
const taskRunner = createTaskRunnerForList(config, reporter, 'out-of-process', { failOnLoadErrors: false });
|
||||
const testRun = new TestRun(config, reporter);
|
||||
reporter.onConfigure(config.config);
|
||||
|
||||
const taskStatus = await taskRunner.run(testRun, 0);
|
||||
let status: FullResult['status'] = testRun.failureTracker.result();
|
||||
if (status === 'passed' && taskStatus !== 'passed')
|
||||
status = taskStatus;
|
||||
const modifiedResult = await reporter.onEnd({ status });
|
||||
if (modifiedResult && modifiedResult.status)
|
||||
status = modifiedResult.status;
|
||||
await reporter.onExit();
|
||||
}
|
||||
|
||||
async function runTests(config: FullConfigInternal, reporterPath: string, locations: string[] | undefined, options: PlaywrightTestOptions) {
|
||||
config.cliArgs = locations || [];
|
||||
config.cliGrep = options.grep;
|
||||
config.cliProjectFilter = options.projects;
|
||||
|
||||
config.configCLIOverrides.reporter = [[reporterPath]];
|
||||
config.configCLIOverrides.repeatEach = 1;
|
||||
config.configCLIOverrides.retries = 0;
|
||||
config.configCLIOverrides.workers = options.oneWorker ? 1 : undefined;
|
||||
config.configCLIOverrides.preserveOutputDir = true;
|
||||
config.configCLIOverrides.use = {
|
||||
trace: options.trace,
|
||||
headless: options.headed ? false : undefined,
|
||||
_optionContextReuseMode: options.reuseContext ? 'when-possible' : undefined,
|
||||
_optionConnectOptions: options.connectWsEndpoint ? { wsEndpoint: options.connectWsEndpoint } : undefined,
|
||||
};
|
||||
|
||||
const reporter = new InternalReporter(new Multiplexer(await createReporters(config, 'run')));
|
||||
const taskRunner = createTaskRunnerForWatch(config, reporter);
|
||||
const testRun = new TestRun(config, reporter);
|
||||
reporter.onConfigure(config.config);
|
||||
const stop = new ManualPromise();
|
||||
const status = await taskRunner.run(testRun, 0, stop);
|
||||
await reporter.onEnd({ status });
|
||||
await reporter.onExit();
|
||||
function chunkToPayload(type: 'stdout' | 'stderr', chunk: Buffer | string) {
|
||||
if (chunk instanceof Buffer)
|
||||
return { type, buffer: chunk.toString('base64') };
|
||||
return { type, text: chunk };
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user