From 088973633272d5cb5a7d91f4cec96b2e071f050b Mon Sep 17 00:00:00 2001 From: Pavel Feldman Date: Sat, 30 Mar 2024 18:45:10 -0700 Subject: [PATCH] cherry-pick(#30185): chore: opt into stdio forwarding --- .../src/isomorphic/testServerConnection.ts | 22 +++++--- .../src/isomorphic/testServerInterface.ts | 2 + packages/playwright/src/program.ts | 4 ++ packages/playwright/src/runner/testServer.ts | 56 +++++++++---------- packages/trace-viewer/src/ui/uiModeView.tsx | 1 + 5 files changed, 46 insertions(+), 39 deletions(-) diff --git a/packages/playwright/src/isomorphic/testServerConnection.ts b/packages/playwright/src/isomorphic/testServerConnection.ts index 8e7b7d550a9ab..af181abda68ff 100644 --- a/packages/playwright/src/isomorphic/testServerConnection.ts +++ b/packages/playwright/src/isomorphic/testServerConnection.ts @@ -115,11 +115,11 @@ export class TestServerConnection implements TestServerInterface, TestServerInte } async ping(params: Parameters[0]): ReturnType { - await this._sendMessage('ping'); + await this._sendMessage('ping', params); } async pingNoReply(params: Parameters[0]) { - this._sendMessageNoReply('ping'); + this._sendMessageNoReply('ping', params); } async watch(params: Parameters[0]): ReturnType { @@ -151,19 +151,19 @@ export class TestServerConnection implements TestServerInterface, TestServerInte } async checkBrowsers(params: Parameters[0]): ReturnType { - return await this._sendMessage('checkBrowsers'); + return await this._sendMessage('checkBrowsers', params); } async installBrowsers(params: Parameters[0]): ReturnType { - await this._sendMessage('installBrowsers'); + await this._sendMessage('installBrowsers', params); } async runGlobalSetup(params: Parameters[0]): ReturnType { - return await this._sendMessage('runGlobalSetup'); + return await this._sendMessage('runGlobalSetup', params); } async runGlobalTeardown(params: Parameters[0]): ReturnType { - return await this._sendMessage('runGlobalTeardown'); + return await this._sendMessage('runGlobalTeardown', params); } async listFiles(params: Parameters[0]): ReturnType { @@ -183,14 +183,18 @@ export class TestServerConnection implements TestServerInterface, TestServerInte } async stopTests(params: Parameters[0]): ReturnType { - await this._sendMessage('stopTests'); + await this._sendMessage('stopTests', params); } stopTestsNoReply(params: Parameters[0]) { - this._sendMessageNoReply('stopTests'); + this._sendMessageNoReply('stopTests', params); + } + + async setInterceptStdio(params: Parameters[0]): ReturnType { + await this._sendMessage('setInterceptStdio', params); } async closeGracefully(params: Parameters[0]): ReturnType { - await this._sendMessage('closeGracefully'); + await this._sendMessage('closeGracefully', params); } } diff --git a/packages/playwright/src/isomorphic/testServerInterface.ts b/packages/playwright/src/isomorphic/testServerInterface.ts index d28ec238455d2..8acfcb4543058 100644 --- a/packages/playwright/src/isomorphic/testServerInterface.ts +++ b/packages/playwright/src/isomorphic/testServerInterface.ts @@ -90,6 +90,8 @@ export interface TestServerInterface { stopTests(params: {}): Promise; + setInterceptStdio(params: { intercept: boolean }): Promise; + closeGracefully(params: {}): Promise; } diff --git a/packages/playwright/src/program.ts b/packages/playwright/src/program.ts index dbae2f693ba0a..3c271b0763160 100644 --- a/packages/playwright/src/program.ts +++ b/packages/playwright/src/program.ts @@ -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; @@ -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); } diff --git a/packages/playwright/src/runner/testServer.ts b/packages/playwright/src/runner/testServer.ts index f563e8beb6e0f..dc9c2ef49c000 100644 --- a/packages/playwright/src/runner/testServer.ts +++ b/packages/playwright/src/runner/testServer.ts @@ -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 { @@ -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 { @@ -341,6 +321,24 @@ class TestServerDispatcher implements TestServerInterface { await this._testRun?.run; } + async setInterceptStdio(params: Parameters[0]): ReturnType { + 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); } @@ -362,7 +360,7 @@ class TestServerDispatcher implements TestServerInterface { } } -export async function runUIMode(configFile: string | undefined, options: TraceViewerServerOptions & TraceViewerRedirectOptions): Promise { +export async function runUIMode(configFile: string | undefined, options: TraceViewerServerOptions & TraceViewerRedirectOptions): Promise { return await innerRunTestServer(configFile, options, async (server: HttpServer, cancelPromise: ManualPromise) => { await installRootRedirect(server, [], { ...options, webApp: 'uiMode.html' }); if (options.host !== undefined || options.port !== undefined) { @@ -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 { +export async function runTestServer(configFile: string | undefined, options: { host?: string, port?: number }): Promise { 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) => Promise): Promise { +async function innerRunTestServer(configFile: string | undefined, options: { host?: string, port?: number }, openUI: (server: HttpServer, cancelPromise: ManualPromise) => Promise): Promise { if (restartWithExperimentalTsEsm(undefined, true)) - return 'passed'; + return 'restarted'; const testServer = new TestServer(configFile); const cancelPromise = new ManualPromise(); const sigintWatcher = new SigIntWatcher(); @@ -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(); } diff --git a/packages/trace-viewer/src/ui/uiModeView.tsx b/packages/trace-viewer/src/ui/uiModeView.tsx index a4d53f11b0b64..3ff40639f0551 100644 --- a/packages/trace-viewer/src/ui/uiModeView.tsx +++ b/packages/trace-viewer/src/ui/uiModeView.tsx @@ -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')