diff --git a/packages/playwright-core/src/client/browserType.ts b/packages/playwright-core/src/client/browserType.ts index 1a8f02c879..d002fb5197 100644 --- a/packages/playwright-core/src/client/browserType.ts +++ b/packages/playwright-core/src/client/browserType.ts @@ -28,7 +28,6 @@ import type * as api from '../../types/types'; import { kBrowserClosedError } from '../common/errors'; import { raceAgainstTimeout } from '../utils/timeoutRunner'; import type { Playwright } from './playwright'; -import { debugLogger } from '../common/debugLogger'; export interface BrowserServerLauncher { launchServer(options?: LaunchServerOptions): Promise; @@ -200,10 +199,6 @@ export class BrowserType extends ChannelOwner imple this._didLaunchBrowser(browser, {}, logger); browser._shouldCloseConnectionOnClose = true; browser._connectHeaders = connectHeaders; - for (const header of connectHeaders) { - if (header.name === 'x-playwright-debug-log') - debugLogger.log('browser', header.value); - } browser.on(Events.Browser.Disconnected, closePipe); return browser; }, deadline ? deadline - monotonicTime() : 0); diff --git a/packages/playwright-core/src/server/dispatchers/localUtilsDispatcher.ts b/packages/playwright-core/src/server/dispatchers/localUtilsDispatcher.ts index 191f31636c..25ba643015 100644 --- a/packages/playwright-core/src/server/dispatchers/localUtilsDispatcher.ts +++ b/packages/playwright-core/src/server/dispatchers/localUtilsDispatcher.ts @@ -202,7 +202,7 @@ export class LocalUtilsDispatcher extends Dispatcher<{ guid: string }, channels. }; const wsEndpoint = await urlToWSEndpoint(progress, params.wsEndpoint); - const transport = await WebSocketTransport.connect(progress, wsEndpoint, wsHeaders, true); + const transport = await WebSocketTransport.connect(progress, wsEndpoint, wsHeaders, true, 'x-playwright-debug-log'); const socksInterceptor = new SocksInterceptor(transport, params.exposeNetwork, params.socksProxyRedirectPortForTest); const pipe = new JsonPipeDispatcher(this); transport.onmessage = json => { diff --git a/packages/playwright-core/src/server/transport.ts b/packages/playwright-core/src/server/transport.ts index c9b9e96d3d..a7dbb7deff 100644 --- a/packages/playwright-core/src/server/transport.ts +++ b/packages/playwright-core/src/server/transport.ts @@ -58,10 +58,10 @@ export class WebSocketTransport implements ConnectionTransport { readonly wsEndpoint: string; readonly headers: HeadersArray = []; - static async connect(progress: (Progress|undefined), url: string, headers?: { [key: string]: string; }, followRedirects?: boolean): Promise { + static async connect(progress: (Progress|undefined), url: string, headers?: { [key: string]: string; }, followRedirects?: boolean, debugLogHeader?: string): Promise { const logUrl = stripQueryParams(url); progress?.log(` ${logUrl}`); - const transport = new WebSocketTransport(progress, url, logUrl, headers, followRedirects); + const transport = new WebSocketTransport(progress, url, logUrl, headers, followRedirects, debugLogHeader); let success = false; progress?.cleanupWhenAborted(async () => { if (!success) @@ -78,6 +78,10 @@ export class WebSocketTransport implements ConnectionTransport { transport._ws.close(); }); transport._ws.on('unexpected-response', (request: ClientRequest, response: IncomingMessage) => { + for (let i = 0; i < response.rawHeaders.length; i += 2) { + if (debugLogHeader && response.rawHeaders[i] === debugLogHeader) + progress?.log(response.rawHeaders[i + 1]); + } const chunks: Buffer[] = []; const errorPrefix = `${logUrl} ${response.statusCode} ${response.statusMessage}`; response.on('data', chunk => chunks.push(chunk)); @@ -93,7 +97,7 @@ export class WebSocketTransport implements ConnectionTransport { return transport; } - constructor(progress: Progress|undefined, url: string, logUrl: string, headers?: { [key: string]: string; }, followRedirects?: boolean) { + constructor(progress: Progress|undefined, url: string, logUrl: string, headers?: { [key: string]: string; }, followRedirects?: boolean, debugLogHeader?: string) { this.wsEndpoint = url; this._logUrl = logUrl; this._ws = new ws(url, [], { @@ -105,9 +109,12 @@ export class WebSocketTransport implements ConnectionTransport { followRedirects, agent: (/^(https|wss):\/\//.test(url)) ? httpsHappyEyeballsAgent : httpHappyEyeballsAgent }); - this._ws.on('upgrade', request => { - for (let i = 0; i < request.rawHeaders.length; i += 2) - this.headers.push({ name: request.rawHeaders[i], value: request.rawHeaders[i + 1] }); + this._ws.on('upgrade', response => { + for (let i = 0; i < response.rawHeaders.length; i += 2) { + this.headers.push({ name: response.rawHeaders[i], value: response.rawHeaders[i + 1] }); + if (debugLogHeader && response.rawHeaders[i] === debugLogHeader) + progress?.log(response.rawHeaders[i + 1]); + } }); this._progress = progress; // The 'ws' module in node sometimes sends us multiple messages in a single task. diff --git a/tests/playwright-test/playwright.connect.spec.ts b/tests/playwright-test/playwright.connect.spec.ts index 35b803e514..d73344ab9c 100644 --- a/tests/playwright-test/playwright.connect.spec.ts +++ b/tests/playwright-test/playwright.connect.spec.ts @@ -117,3 +117,44 @@ test('should respect connectOptions.timeout', async ({ runInlineTest }) => { expect(result.passed).toBe(0); expect(result.output).toContain('browserType.launch: Timeout 1ms exceeded.'); }); + +test('should print debug log when failed to connect', async ({ runInlineTest }) => { + const result = await runInlineTest({ + 'playwright.config.js': ` + module.exports = { + globalSetup: './global-setup', + use: { + connectOptions: { + wsEndpoint: process.env.CONNECT_WS_ENDPOINT, + }, + }, + }; + `, + 'global-setup.ts': ` + import { chromium } from '@playwright/test'; + import ws from 'ws'; + import http from 'http'; + module.exports = async () => { + const server = http.createServer(() => {}); + server.on('upgrade', async (request, socket, head) => { + socket.write('HTTP/1.1 401 Unauthorized\\r\\nx-playwright-debug-log: b-debug-log-string\\r\\n\\r\\nUnauthorized body'); + socket.destroy(); + }); + server.listen(0); + await new Promise(f => server.once('listening', f)); + process.env.CONNECT_WS_ENDPOINT = 'ws://localhost:' + server.address().port; + return () => new Promise(f => server.close(f)); + }; + `, + 'a.test.ts': ` + import { test, expect } from '@playwright/test'; + test('fail', async ({ page }) => { + await page.setContent('
FAIL
'); + }); + `, + }); + expect(result.exitCode).toBe(1); + expect(result.failed).toBe(1); + expect(result.output).toContain('b-debug-log-string'); + expect(result.results[0].attachments).toEqual([]); +});