Skip to content

Commit

Permalink
cherry-pick(#30185): chore: opt into stdio forwarding
Browse files Browse the repository at this point in the history
  • Loading branch information
pavelfeldman committed Mar 31, 2024
1 parent a01db3f commit 0889736
Show file tree
Hide file tree
Showing 5 changed files with 46 additions and 39 deletions.
22 changes: 13 additions & 9 deletions packages/playwright/src/isomorphic/testServerConnection.ts
Original file line number Diff line number Diff line change
Expand Up @@ -115,11 +115,11 @@ export class TestServerConnection implements TestServerInterface, TestServerInte
}

async ping(params: Parameters<TestServerInterface['ping']>[0]): ReturnType<TestServerInterface['ping']> {
await this._sendMessage('ping');
await this._sendMessage('ping', params);
}

async pingNoReply(params: Parameters<TestServerInterface['ping']>[0]) {
this._sendMessageNoReply('ping');
this._sendMessageNoReply('ping', params);
}

async watch(params: Parameters<TestServerInterface['watch']>[0]): ReturnType<TestServerInterface['watch']> {
Expand Down Expand Up @@ -151,19 +151,19 @@ export class TestServerConnection implements TestServerInterface, TestServerInte
}

async checkBrowsers(params: Parameters<TestServerInterface['checkBrowsers']>[0]): ReturnType<TestServerInterface['checkBrowsers']> {
return await this._sendMessage('checkBrowsers');
return await this._sendMessage('checkBrowsers', params);
}

async installBrowsers(params: Parameters<TestServerInterface['installBrowsers']>[0]): ReturnType<TestServerInterface['installBrowsers']> {
await this._sendMessage('installBrowsers');
await this._sendMessage('installBrowsers', params);
}

async runGlobalSetup(params: Parameters<TestServerInterface['runGlobalSetup']>[0]): ReturnType<TestServerInterface['runGlobalSetup']> {
return await this._sendMessage('runGlobalSetup');
return await this._sendMessage('runGlobalSetup', params);
}

async runGlobalTeardown(params: Parameters<TestServerInterface['runGlobalTeardown']>[0]): ReturnType<TestServerInterface['runGlobalTeardown']> {
return await this._sendMessage('runGlobalTeardown');
return await this._sendMessage('runGlobalTeardown', params);
}

async listFiles(params: Parameters<TestServerInterface['listFiles']>[0]): ReturnType<TestServerInterface['listFiles']> {
Expand All @@ -183,14 +183,18 @@ export class TestServerConnection implements TestServerInterface, TestServerInte
}

async stopTests(params: Parameters<TestServerInterface['stopTests']>[0]): ReturnType<TestServerInterface['stopTests']> {
await this._sendMessage('stopTests');
await this._sendMessage('stopTests', params);
}

stopTestsNoReply(params: Parameters<TestServerInterface['stopTests']>[0]) {
this._sendMessageNoReply('stopTests');
this._sendMessageNoReply('stopTests', params);
}

async setInterceptStdio(params: Parameters<TestServerInterface['setInterceptStdio']>[0]): ReturnType<TestServerInterface['setInterceptStdio']> {
await this._sendMessage('setInterceptStdio', params);
}

async closeGracefully(params: Parameters<TestServerInterface['closeGracefully']>[0]): ReturnType<TestServerInterface['closeGracefully']> {
await this._sendMessage('closeGracefully');
await this._sendMessage('closeGracefully', params);
}
}
2 changes: 2 additions & 0 deletions packages/playwright/src/isomorphic/testServerInterface.ts
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,8 @@ export interface TestServerInterface {

stopTests(params: {}): Promise<void>;

setInterceptStdio(params: { intercept: boolean }): Promise<void>;

closeGracefully(params: {}): Promise<void>;
}

Expand Down
4 changes: 4 additions & 0 deletions packages/playwright/src/program.ts
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,8 @@ async function runTests(args: string[], opts: { [key: string]: any }) {
timeout: cliOverrides.timeout,
});
await stopProfiling('runner');
if (status === 'restarted')
return;
const exitCode = status === 'interrupted' ? 130 : (status === 'passed' ? 0 : 1);
gracefullyProcessExitDoNotHang(exitCode);
return;
Expand Down Expand Up @@ -200,6 +202,8 @@ async function runTestServer(opts: { [key: string]: any }) {
const host = opts.host || 'localhost';
const port = opts.port ? +opts.port : 0;
const status = await testServer.runTestServer(opts.config, { host, port });
if (status === 'restarted')
return;
const exitCode = status === 'interrupted' ? 130 : (status === 'passed' ? 0 : 1);
gracefullyProcessExitDoNotHang(exitCode);
}
Expand Down
56 changes: 26 additions & 30 deletions packages/playwright/src/runner/testServer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,16 +39,15 @@ import type { TraceViewerRedirectOptions, TraceViewerServerOptions } from 'playw
import type { TestRunnerPluginRegistration } from '../plugins';
import { serializeError } from '../util';

const originalStdoutWrite = process.stdout.write;
const originalStderrWrite = process.stderr.write;

class TestServer {
private _configFile: string | undefined;
private _dispatcher: TestServerDispatcher | undefined;
private _originalStdoutWrite: NodeJS.WriteStream['write'];
private _originalStderrWrite: NodeJS.WriteStream['write'];

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> {
Expand All @@ -57,28 +56,9 @@ class TestServer {
}

async stop() {
await this._dispatcher?.setInterceptStdio({ intercept: false });
await this._dispatcher?.runGlobalTeardown();
}

wireStdIO() {
if (!process.env.PWTEST_DEBUG) {
process.stdout.write = (chunk: string | Buffer) => {
this._dispatcher?._dispatchEvent('stdio', chunkToPayload('stdout', chunk));
return true;
};
process.stderr.write = (chunk: string | Buffer) => {
this._dispatcher?._dispatchEvent('stdio', chunkToPayload('stderr', chunk));
return true;
};
}
}

unwireStdIO() {
if (!process.env.PWTEST_DEBUG) {
process.stdout.write = this._originalStdoutWrite;
process.stderr.write = this._originalStderrWrite;
}
}
}

class TestServerDispatcher implements TestServerInterface {
Expand Down Expand Up @@ -341,6 +321,24 @@ class TestServerDispatcher implements TestServerInterface {
await this._testRun?.run;
}

async setInterceptStdio(params: Parameters<TestServerInterface['setInterceptStdio']>[0]): ReturnType<TestServerInterface['setInterceptStdio']> {
if (process.env.PWTEST_DEBUG)
return;
if (params.intercept) {
process.stdout.write = (chunk: string | Buffer) => {
this._dispatchEvent('stdio', chunkToPayload('stdout', chunk));
return true;
};
process.stderr.write = (chunk: string | Buffer) => {
this._dispatchEvent('stdio', chunkToPayload('stderr', chunk));
return true;
};
} else {
process.stdout.write = originalStdoutWrite;
process.stderr.write = originalStderrWrite;
}
}

async closeGracefully() {
gracefullyProcessExitDoNotHang(0);
}
Expand All @@ -362,7 +360,7 @@ class TestServerDispatcher implements TestServerInterface {
}
}

export async function runUIMode(configFile: string | undefined, options: TraceViewerServerOptions & TraceViewerRedirectOptions): Promise<reporterTypes.FullResult['status']> {
export async function runUIMode(configFile: string | undefined, options: TraceViewerServerOptions & TraceViewerRedirectOptions): Promise<reporterTypes.FullResult['status'] | 'restarted'> {
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) {
Expand All @@ -379,16 +377,16 @@ export async function runUIMode(configFile: string | undefined, options: TraceVi
});
}

export async function runTestServer(configFile: string | undefined, options: { host?: string, port?: number }): Promise<reporterTypes.FullResult['status']> {
export async function runTestServer(configFile: string | undefined, options: { host?: string, port?: number }): Promise<reporterTypes.FullResult['status'] | 'restarted'> {
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']> {
async function innerRunTestServer(configFile: string | undefined, options: { host?: string, port?: number }, openUI: (server: HttpServer, cancelPromise: ManualPromise<void>) => Promise<void>): Promise<reporterTypes.FullResult['status'] | 'restarted'> {
if (restartWithExperimentalTsEsm(undefined, true))
return 'passed';
return 'restarted';
const testServer = new TestServer(configFile);
const cancelPromise = new ManualPromise<void>();
const sigintWatcher = new SigIntWatcher();
Expand All @@ -397,10 +395,8 @@ async function innerRunTestServer(configFile: string | undefined, options: { hos
try {
const server = await testServer.start(options);
await openUI(server, cancelPromise);
testServer.wireStdIO();
await cancelPromise;
} finally {
testServer.unwireStdIO();
await testServer.stop();
sigintWatcher.disarm();
}
Expand Down
1 change: 1 addition & 0 deletions packages/trace-viewer/src/ui/uiModeView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,7 @@ export const UIModeView: React.FC<{}> = ({
setIsLoading(true);
setWatchedTreeIds({ value: new Set() });
(async () => {
await testServerConnection.setInterceptStdio({ intercept: true });
await testServerConnection.watchTestDir({});
const { status } = await testServerConnection.runGlobalSetup({});
if (status !== 'passed')
Expand Down

0 comments on commit 0889736

Please sign in to comment.