mirror of
https://github.com/microsoft/playwright.git
synced 2025-06-26 21:40:17 +00:00
chore(tests): log protocol messages when a test fails on the bots (#963)
Similarly to how we handle stdio, this captures all protocol messages and logs them when a test fails. Great for when debugging tests in parallel or for bot failures! Currently I made `DEBUGP=true` turn this on locally, and turned it on always for the CI. Open to suggestions for a better environment variable. I would turn it on by default always, like the stdio logging, but it adds an enormous amount of noise to our error messages.
This commit is contained in:
parent
8abf35c8e4
commit
d367a2ed3a
@ -24,6 +24,7 @@ export interface Browser extends platform.EventEmitterType {
|
|||||||
newPage(options?: BrowserContextOptions): Promise<Page>;
|
newPage(options?: BrowserContextOptions): Promise<Page>;
|
||||||
isConnected(): boolean;
|
isConnected(): boolean;
|
||||||
close(): Promise<void>;
|
close(): Promise<void>;
|
||||||
|
_setDebugFunction(debugFunction: (message: string) => void): void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export type ConnectOptions = {
|
export type ConnectOptions = {
|
||||||
|
|||||||
@ -304,4 +304,8 @@ export class CRBrowser extends platform.EventEmitter implements Browser {
|
|||||||
isConnected(): boolean {
|
isConnected(): boolean {
|
||||||
return !this._connection._closed;
|
return !this._connection._closed;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_setDebugFunction(debugFunction: (message: string) => void) {
|
||||||
|
this._connection._debugProtocol = debugFunction;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -20,8 +20,6 @@ import { ConnectionTransport } from '../transport';
|
|||||||
import { assert } from '../helper';
|
import { assert } from '../helper';
|
||||||
import { Protocol } from './protocol';
|
import { Protocol } from './protocol';
|
||||||
|
|
||||||
const debugProtocol = platform.debug('pw:protocol');
|
|
||||||
|
|
||||||
export const ConnectionEvents = {
|
export const ConnectionEvents = {
|
||||||
Disconnected: Symbol('ConnectionEvents.Disconnected')
|
Disconnected: Symbol('ConnectionEvents.Disconnected')
|
||||||
};
|
};
|
||||||
@ -36,6 +34,7 @@ export class CRConnection extends platform.EventEmitter {
|
|||||||
private _sessions = new Map<string, CRSession>();
|
private _sessions = new Map<string, CRSession>();
|
||||||
readonly rootSession: CRSession;
|
readonly rootSession: CRSession;
|
||||||
_closed = false;
|
_closed = false;
|
||||||
|
_debugProtocol: (message: string) => void;
|
||||||
|
|
||||||
constructor(transport: ConnectionTransport) {
|
constructor(transport: ConnectionTransport) {
|
||||||
super();
|
super();
|
||||||
@ -44,6 +43,7 @@ export class CRConnection extends platform.EventEmitter {
|
|||||||
this._transport.onclose = this._onClose.bind(this);
|
this._transport.onclose = this._onClose.bind(this);
|
||||||
this.rootSession = new CRSession(this, 'browser', '');
|
this.rootSession = new CRSession(this, 'browser', '');
|
||||||
this._sessions.set('', this.rootSession);
|
this._sessions.set('', this.rootSession);
|
||||||
|
this._debugProtocol = platform.debug('pw:protocol');
|
||||||
}
|
}
|
||||||
|
|
||||||
static fromSession(session: CRSession): CRConnection {
|
static fromSession(session: CRSession): CRConnection {
|
||||||
@ -60,13 +60,13 @@ export class CRConnection extends platform.EventEmitter {
|
|||||||
if (sessionId)
|
if (sessionId)
|
||||||
message.sessionId = sessionId;
|
message.sessionId = sessionId;
|
||||||
const data = JSON.stringify(message);
|
const data = JSON.stringify(message);
|
||||||
debugProtocol('SEND ► ' + data);
|
this._debugProtocol('SEND ► ' + data);
|
||||||
this._transport.send(data);
|
this._transport.send(data);
|
||||||
return id;
|
return id;
|
||||||
}
|
}
|
||||||
|
|
||||||
async _onMessage(message: string) {
|
async _onMessage(message: string) {
|
||||||
debugProtocol('◀ RECV ' + message);
|
this._debugProtocol('◀ RECV ' + message);
|
||||||
const object = JSON.parse(message);
|
const object = JSON.parse(message);
|
||||||
if (object.id === kBrowserCloseMessageId)
|
if (object.id === kBrowserCloseMessageId)
|
||||||
return;
|
return;
|
||||||
|
|||||||
@ -242,6 +242,10 @@ export class FFBrowser extends platform.EventEmitter implements Browser {
|
|||||||
}, options);
|
}, options);
|
||||||
return context;
|
return context;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_setDebugFunction(debugFunction: (message: string) => void) {
|
||||||
|
this._connection._debugProtocol = debugFunction;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class Target {
|
class Target {
|
||||||
|
|||||||
@ -20,8 +20,6 @@ import * as platform from '../platform';
|
|||||||
import { ConnectionTransport } from '../transport';
|
import { ConnectionTransport } from '../transport';
|
||||||
import { Protocol } from './protocol';
|
import { Protocol } from './protocol';
|
||||||
|
|
||||||
const debugProtocol = platform.debug('pw:protocol');
|
|
||||||
|
|
||||||
export const ConnectionEvents = {
|
export const ConnectionEvents = {
|
||||||
Disconnected: Symbol('Disconnected'),
|
Disconnected: Symbol('Disconnected'),
|
||||||
};
|
};
|
||||||
@ -35,6 +33,7 @@ export class FFConnection extends platform.EventEmitter {
|
|||||||
private _callbacks: Map<number, {resolve: Function, reject: Function, error: Error, method: string}>;
|
private _callbacks: Map<number, {resolve: Function, reject: Function, error: Error, method: string}>;
|
||||||
private _transport: ConnectionTransport;
|
private _transport: ConnectionTransport;
|
||||||
private _sessions: Map<string, FFSession>;
|
private _sessions: Map<string, FFSession>;
|
||||||
|
_debugProtocol: (message: string) => void = platform.debug('pw:protocol');
|
||||||
_closed: boolean;
|
_closed: boolean;
|
||||||
|
|
||||||
on: <T extends keyof Protocol.Events | symbol>(event: T, listener: (payload: T extends symbol ? any : Protocol.Events[T extends keyof Protocol.Events ? T : never]) => void) => this;
|
on: <T extends keyof Protocol.Events | symbol>(event: T, listener: (payload: T extends symbol ? any : Protocol.Events[T extends keyof Protocol.Events ? T : never]) => void) => this;
|
||||||
@ -86,12 +85,12 @@ export class FFConnection extends platform.EventEmitter {
|
|||||||
|
|
||||||
_rawSend(message: any) {
|
_rawSend(message: any) {
|
||||||
message = JSON.stringify(message);
|
message = JSON.stringify(message);
|
||||||
debugProtocol('SEND ► ' + message);
|
this._debugProtocol('SEND ► ' + message);
|
||||||
this._transport.send(message);
|
this._transport.send(message);
|
||||||
}
|
}
|
||||||
|
|
||||||
async _onMessage(message: string) {
|
async _onMessage(message: string) {
|
||||||
debugProtocol('◀ RECV ' + message);
|
this._debugProtocol('◀ RECV ' + message);
|
||||||
const object = JSON.parse(message);
|
const object = JSON.parse(message);
|
||||||
if (object.id === kBrowserCloseMessageId)
|
if (object.id === kBrowserCloseMessageId)
|
||||||
return;
|
return;
|
||||||
|
|||||||
@ -239,4 +239,8 @@ export class WKBrowser extends platform.EventEmitter implements Browser {
|
|||||||
}, options);
|
}, options);
|
||||||
return context;
|
return context;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_setDebugFunction(debugFunction: (message: string) => void) {
|
||||||
|
this._connection._debugFunction = debugFunction;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -20,8 +20,6 @@ import * as platform from '../platform';
|
|||||||
import { ConnectionTransport } from '../transport';
|
import { ConnectionTransport } from '../transport';
|
||||||
import { Protocol } from './protocol';
|
import { Protocol } from './protocol';
|
||||||
|
|
||||||
const debugProtocol = platform.debug('pw:protocol');
|
|
||||||
|
|
||||||
// WKPlaywright uses this special id to issue Browser.close command which we
|
// WKPlaywright uses this special id to issue Browser.close command which we
|
||||||
// should ignore.
|
// should ignore.
|
||||||
export const kBrowserCloseMessageId = -9999;
|
export const kBrowserCloseMessageId = -9999;
|
||||||
@ -36,6 +34,7 @@ export class WKConnection {
|
|||||||
private readonly _transport: ConnectionTransport;
|
private readonly _transport: ConnectionTransport;
|
||||||
private _closed = false;
|
private _closed = false;
|
||||||
private _onDisconnect: () => void;
|
private _onDisconnect: () => void;
|
||||||
|
_debugFunction: (message: string) => void = platform.debug('pw:protocol');
|
||||||
|
|
||||||
readonly browserSession: WKSession;
|
readonly browserSession: WKSession;
|
||||||
|
|
||||||
@ -55,12 +54,12 @@ export class WKConnection {
|
|||||||
|
|
||||||
rawSend(message: any) {
|
rawSend(message: any) {
|
||||||
message = JSON.stringify(message);
|
message = JSON.stringify(message);
|
||||||
debugProtocol('SEND ► ' + message);
|
this._debugFunction('SEND ► ' + message);
|
||||||
this._transport.send(message);
|
this._transport.send(message);
|
||||||
}
|
}
|
||||||
|
|
||||||
private _dispatchMessage(message: string) {
|
private _dispatchMessage(message: string) {
|
||||||
debugProtocol('◀ RECV ' + message);
|
this._debugFunction('◀ RECV ' + message);
|
||||||
const object = JSON.parse(message);
|
const object = JSON.parse(message);
|
||||||
if (object.id === kBrowserCloseMessageId)
|
if (object.id === kBrowserCloseMessageId)
|
||||||
return;
|
return;
|
||||||
|
|||||||
@ -42,9 +42,16 @@ module.exports.describe = ({testRunner, product, playwrightPath}) => {
|
|||||||
const playwrightModule = require(playwrightPath);
|
const playwrightModule = require(playwrightPath);
|
||||||
const playwright = playwrightModule[product.toLowerCase()];
|
const playwright = playwrightModule[product.toLowerCase()];
|
||||||
|
|
||||||
const headless = (process.env.HEADLESS || 'true').trim().toLowerCase() === 'true';
|
const headless = !!valueFromEnv('HEADLESS', true);
|
||||||
const slowMo = parseInt((process.env.SLOW_MO || '0').trim(), 10);
|
const slowMo = valueFromEnv('SLOW_MO', 0);
|
||||||
|
const CI = valueFromEnv('CI', false);
|
||||||
|
const dumpProtocolOnFailure = CI || valueFromEnv('DEBUGP', true);
|
||||||
|
|
||||||
|
function valueFromEnv(name, defaultValue) {
|
||||||
|
if (!(name in process.env))
|
||||||
|
return defaultValue;
|
||||||
|
return JSON.parse(process.env[name]);
|
||||||
|
}
|
||||||
const executablePath = {
|
const executablePath = {
|
||||||
'Chromium': process.env.CRPATH,
|
'Chromium': process.env.CRPATH,
|
||||||
'Firefox': process.env.FFPATH,
|
'Firefox': process.env.FFPATH,
|
||||||
@ -108,6 +115,8 @@ module.exports.describe = ({testRunner, product, playwrightPath}) => {
|
|||||||
beforeEach(async(state, test) => {
|
beforeEach(async(state, test) => {
|
||||||
const contexts = [];
|
const contexts = [];
|
||||||
const onLine = (line) => test.output += line + '\n';
|
const onLine = (line) => test.output += line + '\n';
|
||||||
|
if (dumpProtocolOnFailure)
|
||||||
|
state.browser._setDebugFunction(onLine);
|
||||||
|
|
||||||
let rl;
|
let rl;
|
||||||
if (state.browserServer.process().stderr) {
|
if (state.browserServer.process().stderr) {
|
||||||
@ -123,6 +132,8 @@ module.exports.describe = ({testRunner, product, playwrightPath}) => {
|
|||||||
rl.removeListener('line', onLine);
|
rl.removeListener('line', onLine);
|
||||||
rl.close();
|
rl.close();
|
||||||
}
|
}
|
||||||
|
if (dumpProtocolOnFailure)
|
||||||
|
state.browser._setDebugFunction(() => void 0);
|
||||||
};
|
};
|
||||||
|
|
||||||
state.newContext = async (options) => {
|
state.newContext = async (options) => {
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user