Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(inspector): render api names from metainfo #5530

Merged
merged 1 commit into from
Feb 20, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/client/android.ts
Original file line number Diff line number Diff line change
Expand Up @@ -239,7 +239,7 @@ export class AndroidDevice extends ChannelOwner<channels.AndroidDeviceChannel, c
async waitForEvent(event: string, optionsOrPredicate: types.WaitForEventOptions = {}): Promise<any> {
const timeout = this._timeoutSettings.timeout(typeof optionsOrPredicate === 'function' ? {} : optionsOrPredicate);
const predicate = typeof optionsOrPredicate === 'function' ? optionsOrPredicate : optionsOrPredicate.predicate;
const waiter = Waiter.createForEvent(this, event);
const waiter = Waiter.createForEvent(this, 'androidDevice', event);
waiter.rejectOnTimeout(timeout, `Timeout while waiting for event "${event}"`);
if (event !== Events.AndroidDevice.Close)
waiter.rejectOnEvent(this, Events.AndroidDevice.Close, new Error('Device closed'));
Expand Down
8 changes: 1 addition & 7 deletions src/client/browserContext.ts
Original file line number Diff line number Diff line change
Expand Up @@ -217,7 +217,7 @@ export class BrowserContext extends ChannelOwner<channels.BrowserContextChannel,
async waitForEvent(event: string, optionsOrPredicate: WaitForEventOptions = {}): Promise<any> {
const timeout = this._timeoutSettings.timeout(typeof optionsOrPredicate === 'function' ? {} : optionsOrPredicate);
const predicate = typeof optionsOrPredicate === 'function' ? optionsOrPredicate : optionsOrPredicate.predicate;
const waiter = Waiter.createForEvent(this, event);
const waiter = Waiter.createForEvent(this, 'browserContext', event);
waiter.rejectOnTimeout(timeout, `Timeout while waiting for event "${event}"`);
if (event !== Events.BrowserContext.Close)
waiter.rejectOnEvent(this, Events.BrowserContext.Close, new Error('Context closed'));
Expand Down Expand Up @@ -256,12 +256,6 @@ export class BrowserContext extends ChannelOwner<channels.BrowserContextChannel,
}
}

async _pause() {
return this._wrapApiCall('browserContext.pause', async (channel: channels.BrowserContextChannel) => {
await channel.pause();
});
}

async _enableRecorder(params: {
language: string,
launchOptions?: LaunchOptions,
Expand Down
43 changes: 24 additions & 19 deletions src/client/channelOwner.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,20 +48,7 @@ export abstract class ChannelOwner<T extends channels.Channel = channels.Channel
this._logger = this._parent._logger;
}

const base = new EventEmitter();
this._channel = new Proxy(base, {
get: (obj: any, prop) => {
if (prop === 'debugScopeState')
return (params: any) => this._connection.sendMessageToServer(guid, prop, params);
if (typeof prop === 'string') {
const validator = scheme[paramsName(type, prop)];
if (validator)
return (params: any) => this._connection.sendMessageToServer(guid, prop, validator(params, ''));
}
return obj[prop];
},
});
(this._channel as any)._object = this;
this._channel = this._createChannel(new EventEmitter(), '');
this._initializer = initializer;
}

Expand All @@ -84,11 +71,29 @@ export abstract class ChannelOwner<T extends channels.Channel = channels.Channel
};
}

async _wrapApiCall<T, K extends channels.Channel>(apiName: string, func: (channel: K) => Promise<T>, logger?: Logger): Promise<T> {
_createChannel(base: Object, apiName: string): T {
const channel = new Proxy(base, {
get: (obj: any, prop) => {
if (prop === 'debugScopeState')
return (params: any) => this._connection.sendMessageToServer(this._guid, prop, params, apiName);
if (typeof prop === 'string') {
const validator = scheme[paramsName(this._type, prop)];
if (validator)
return (params: any) => this._connection.sendMessageToServer(this._guid, prop, validator(params, ''), apiName);
}
return obj[prop];
},
});
(channel as any)._object = this;
return channel;
}

async _wrapApiCall<R, C extends channels.Channel>(apiName: string, func: (channel: C) => Promise<R>, logger?: Logger): Promise<R> {
logger = logger || this._logger;
try {
logApiCall(logger, `=> ${apiName} started`);
const result = await func(this._channel as any);
const channel = this._createChannel({}, apiName);
const result = await func(channel as any);
logApiCall(logger, `<= ${apiName} succeeded`);
return result;
} catch (e) {
Expand All @@ -99,15 +104,15 @@ export abstract class ChannelOwner<T extends channels.Channel = channels.Channel
}

_waitForEventInfoBefore(waitId: string, name: string, stack: StackFrame[]) {
this._connection.sendMessageToServer(this._guid, 'waitForEventInfo', { info: { name, waitId, phase: 'before', stack } }).catch(() => {});
this._connection.sendMessageToServer(this._guid, 'waitForEventInfo', { info: { name, waitId, phase: 'before', stack } }, undefined).catch(() => {});
}

_waitForEventInfoAfter(waitId: string, error?: string) {
this._connection.sendMessageToServer(this._guid, 'waitForEventInfo', { info: { waitId, phase: 'after', error } }).catch(() => {});
this._connection.sendMessageToServer(this._guid, 'waitForEventInfo', { info: { waitId, phase: 'after', error } }, undefined).catch(() => {});
}

_waitForEventInfoLog(waitId: string, message: string) {
this._connection.sendMessageToServer(this._guid, 'waitForEventInfo', { info: { waitId, phase: 'log', message } }).catch(() => {});
this._connection.sendMessageToServer(this._guid, 'waitForEventInfo', { info: { waitId, phase: 'log', message } }, undefined).catch(() => {});
}

private toJSON() {
Expand Down
4 changes: 2 additions & 2 deletions src/client/connection.ts
Original file line number Diff line number Diff line change
Expand Up @@ -71,13 +71,13 @@ export class Connection {
return this._objects.get(guid)!;
}

async sendMessageToServer(guid: string, method: string, params: any): Promise<any> {
async sendMessageToServer(guid: string, method: string, params: any, apiName: string | undefined): Promise<any> {
const { stack, frames } = captureStackTrace();
const id = ++this._lastId;
const converted = { id, guid, method, params };
// Do not include metadata in debug logs to avoid noise.
debugLogger.log('channel:command', converted);
this.onmessage({ ...converted, metadata: { stack: frames } });
this.onmessage({ ...converted, metadata: { stack: frames, apiName } });
try {
return await new Promise((resolve, reject) => this._callbacks.set(id, { resolve, reject }));
} catch (e) {
Expand Down
2 changes: 1 addition & 1 deletion src/client/electron.ts
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@ export class ElectronApplication extends ChannelOwner<channels.ElectronApplicati
async waitForEvent(event: string, optionsOrPredicate: WaitForEventOptions = {}): Promise<any> {
const timeout = this._timeoutSettings.timeout(typeof optionsOrPredicate === 'function' ? {} : optionsOrPredicate);
const predicate = typeof optionsOrPredicate === 'function' ? optionsOrPredicate : optionsOrPredicate.predicate;
const waiter = Waiter.createForEvent(this, event);
const waiter = Waiter.createForEvent(this, 'electronApplication', event);
waiter.rejectOnTimeout(timeout, `Timeout while waiting for event "${event}"`);
if (event !== Events.ElectronApplication.Close)
waiter.rejectOnEvent(this, Events.ElectronApplication.Close, new Error('Electron application closed'));
Expand Down
4 changes: 2 additions & 2 deletions src/client/frame.ts
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,7 @@ export class Frame extends ChannelOwner<channels.FrameChannel, channels.FrameIni
async waitForNavigation(options: WaitForNavigationOptions = {}): Promise<network.Response | null> {
return this._wrapApiCall(this._apiName('waitForNavigation'), async (channel: channels.FrameChannel) => {
const waitUntil = verifyLoadState('waitUntil', options.waitUntil === undefined ? 'load' : options.waitUntil);
const waiter = this._setupNavigationWaiter('waitForNavigation', options);
const waiter = this._setupNavigationWaiter(this._apiName('waitForNavigation'), options);

const toUrl = typeof options.url === 'string' ? ` to "${options.url}"` : '';
waiter.log(`waiting for navigation${toUrl} until "${waitUntil}"`);
Expand Down Expand Up @@ -150,7 +150,7 @@ export class Frame extends ChannelOwner<channels.FrameChannel, channels.FrameIni
if (this._loadStates.has(state))
return;
return this._wrapApiCall(this._apiName('waitForLoadState'), async (channel: channels.FrameChannel) => {
const waiter = this._setupNavigationWaiter('waitForLoadState', options);
const waiter = this._setupNavigationWaiter(this._apiName('waitForLoadState'), options);
await waiter.waitForEvent<LifecycleEvent>(this._eventEmitter, 'loadstate', s => {
waiter.log(` "${s}" event fired`);
return s === state;
Expand Down
2 changes: 1 addition & 1 deletion src/client/network.ts
Original file line number Diff line number Diff line change
Expand Up @@ -365,7 +365,7 @@ export class WebSocket extends ChannelOwner<channels.WebSocketChannel, channels.
async waitForEvent(event: string, optionsOrPredicate: WaitForEventOptions = {}): Promise<any> {
const timeout = this._page._timeoutSettings.timeout(typeof optionsOrPredicate === 'function' ? {} : optionsOrPredicate);
const predicate = typeof optionsOrPredicate === 'function' ? optionsOrPredicate : optionsOrPredicate.predicate;
const waiter = Waiter.createForEvent(this, event);
const waiter = Waiter.createForEvent(this, 'webSocket', event);
waiter.rejectOnTimeout(timeout, `Timeout while waiting for event "${event}"`);
if (event !== Events.WebSocket.Error)
waiter.rejectOnEvent(this, Events.WebSocket.Error, new Error('Socket error'));
Expand Down
6 changes: 4 additions & 2 deletions src/client/page.ts
Original file line number Diff line number Diff line change
Expand Up @@ -397,7 +397,7 @@ export class Page extends ChannelOwner<channels.PageChannel, channels.PageInitia
private async _waitForEvent(event: string, optionsOrPredicate: WaitForEventOptions, logLine?: string): Promise<any> {
const timeout = this._timeoutSettings.timeout(typeof optionsOrPredicate === 'function' ? {} : optionsOrPredicate);
const predicate = typeof optionsOrPredicate === 'function' ? optionsOrPredicate : optionsOrPredicate.predicate;
const waiter = Waiter.createForEvent(this, event);
const waiter = Waiter.createForEvent(this, 'page', event);
if (logLine)
waiter.log(logLine);
waiter.rejectOnTimeout(timeout, `Timeout while waiting for event "${event}"`);
Expand Down Expand Up @@ -644,7 +644,9 @@ export class Page extends ChannelOwner<channels.PageChannel, channels.PageInitia
}

async pause() {
await this.context()._pause();
return this.context()._wrapApiCall('page.pause', async (channel: channels.BrowserContextChannel) => {
await channel.pause();
});
}

async _pdf(options: PDFOptions = {}): Promise<Buffer> {
Expand Down
4 changes: 2 additions & 2 deletions src/client/waiter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,8 @@ export class Waiter {
];
}

static createForEvent(channelOwner: ChannelOwner, event: string) {
return new Waiter(channelOwner, `waitForEvent(${event})`);
static createForEvent(channelOwner: ChannelOwner, target: string, event: string) {
return new Waiter(channelOwner, `${target}.waitForEvent(${event})`);
}

async waitForEvent<T = void>(emitter: EventEmitter, event: string, predicate?: (arg: T) => boolean): Promise<T> {
Expand Down
1 change: 1 addition & 0 deletions src/protocol/channels.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ export type StackFrame = {

export type Metadata = {
stack?: StackFrame[],
apiName?: string,
};

export type WaitForEventInfo = {
Expand Down
1 change: 1 addition & 0 deletions src/protocol/protocol.yml
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ Metadata:
stack:
type: array?
items: StackFrame
apiName: string?


WaitForEventInfo:
Expand Down
1 change: 1 addition & 0 deletions src/protocol/validator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ export function createScheme(tChannel: (name: string) => Validator): Scheme {
});
scheme.Metadata = tObject({
stack: tOptional(tArray(tType('StackFrame'))),
apiName: tOptional(tString),
});
scheme.WaitForEventInfo = tObject({
waitId: tString,
Expand Down
1 change: 1 addition & 0 deletions src/server/instrumentation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ export type CallMetadata = {
type: string;
method: string;
params: any;
apiName?: string;
stack?: StackFrame[];
log: string[];
error?: string;
Expand Down
5 changes: 3 additions & 2 deletions src/server/supplements/recorderSupplement.ts
Original file line number Diff line number Diff line change
Expand Up @@ -434,7 +434,7 @@ export class RecorderSupplement {
for (const metadata of metadatas) {
if (!metadata.method)
continue;
const title = metadata.method;
const title = metadata.apiName || metadata.method;
let status: 'done' | 'in-progress' | 'paused' | 'error' = 'done';
if (this._currentCallsMetadata.has(metadata))
status = 'in-progress';
Expand All @@ -452,7 +452,8 @@ export class RecorderSupplement {
logs.push({
id: metadata.id,
messages: metadata.log,
title, status,
title,
status,
error: metadata.error,
params,
duration
Expand Down
18 changes: 9 additions & 9 deletions test/pause.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -155,9 +155,9 @@ describe('pause', (suite, { mode }) => {
await recorderPage.click('[title="Resume"]');
await recorderPage.waitForSelector('.source-line-paused:has-text("page.pause(); // 2")');
expect(await sanitizeLog(recorderPage)).toEqual([
'pause- XXms',
'click(button)- XXms',
'pause',
'page.pause- XXms',
'page.click(button)- XXms',
'page.pause',
]);
await recorderPage.click('[title="Resume"]');
await scriptPromise;
Expand All @@ -177,10 +177,10 @@ describe('pause', (suite, { mode }) => {
await recorderPage.click('[title="Resume"]');
await recorderPage.waitForSelector('.source-line-paused:has-text("page.pause(); // 2")');
expect(await sanitizeLog(recorderPage)).toEqual([
'pause- XXms',
'waitForEvent(console)- XXms',
'click(button)- XXms',
'pause',
'page.pause- XXms',
'page.waitForEvent(console)- XXms',
'page.click(button)- XXms',
'page.pause',
]);
await recorderPage.click('[title="Resume"]');
await scriptPromise;
Expand All @@ -196,8 +196,8 @@ describe('pause', (suite, { mode }) => {
await recorderPage.click('[title="Resume"]');
await recorderPage.waitForSelector('.source-line-error');
expect(await sanitizeLog(recorderPage)).toEqual([
'pause- XXms',
'isChecked(button)- XXms',
'page.pause- XXms',
'page.isChecked(button)- XXms',
'checking \"checked\" state of \"button\"',
'selector resolved to <button onclick=\"console.log(1)\">Submit</button>',
'Not a checkbox or radio button',
Expand Down