feat(connect): print debug log when remote connection failed (#23069)
This commit is contained in:
parent
3cd21cb6d4
commit
fec5059fee
|
|
@ -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<api.BrowserServer>;
|
||||
|
|
@ -200,10 +199,6 @@ export class BrowserType extends ChannelOwner<channels.BrowserTypeChannel> 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);
|
||||
|
|
|
|||
|
|
@ -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 => {
|
||||
|
|
|
|||
|
|
@ -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<WebSocketTransport> {
|
||||
static async connect(progress: (Progress|undefined), url: string, headers?: { [key: string]: string; }, followRedirects?: boolean, debugLogHeader?: string): Promise<WebSocketTransport> {
|
||||
const logUrl = stripQueryParams(url);
|
||||
progress?.log(`<ws connecting> ${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.
|
||||
|
|
|
|||
|
|
@ -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('<div>FAIL</div>');
|
||||
});
|
||||
`,
|
||||
});
|
||||
expect(result.exitCode).toBe(1);
|
||||
expect(result.failed).toBe(1);
|
||||
expect(result.output).toContain('b-debug-log-string');
|
||||
expect(result.results[0].attachments).toEqual([]);
|
||||
});
|
||||
|
|
|
|||
Loading…
Reference in a new issue