chore: annotate protocol methods with internal & titles (#35979)

This commit is contained in:
Pavel Feldman 2025-05-16 14:01:09 -07:00 committed by GitHub
parent 313de29fab
commit 0e9cf0a3ad
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
16 changed files with 613 additions and 284 deletions

View File

@ -51,9 +51,7 @@ export class Android extends ChannelOwner<channels.AndroidChannel> implements ap
setDefaultTimeout(timeout: number) {
this._timeoutSettings.setDefaultTimeout(timeout);
this._wrapApiCall(async () => {
await this._channel.setDefaultTimeoutNoReply({ timeout });
}, true).catch(() => {});
this._channel.setDefaultTimeoutNoReply({ timeout }).catch(() => {});
}
async devices(options: { port?: number } = {}): Promise<AndroidDevice[]> {
@ -135,9 +133,7 @@ export class AndroidDevice extends ChannelOwner<channels.AndroidDeviceChannel> i
setDefaultTimeout(timeout: number) {
this._timeoutSettings.setDefaultTimeout(timeout);
this._wrapApiCall(async () => {
await this._channel.setDefaultTimeoutNoReply({ timeout });
}, true).catch(() => {});
this._channel.setDefaultTimeoutNoReply({ timeout }).catch(() => {});
}
serial(): string {

View File

@ -71,9 +71,7 @@ export class Browser extends ChannelOwner<channels.BrowserChannel> implements ap
}
async _stopPendingOperations(reason: string) {
return await this._wrapApiCall(async () => {
await this._channel.stopPendingOperations({ reason });
}, true);
await this._channel.stopPendingOperations({ reason });
}
async _innerNewContext(options: BrowserContextOptions = {}, forReuse: boolean): Promise<BrowserContext> {

View File

@ -219,7 +219,7 @@ export class BrowserContext extends ChannelOwner<channels.BrowserContextChannel>
this._routes.splice(index, 1);
const handled = await routeHandler.handle(route);
if (!this._routes.length)
this._wrapApiCall(() => this._updateInterceptionPatterns(), true).catch(() => {});
this._updateInterceptionPatterns().catch(() => {});
if (handled)
return;
}
@ -245,16 +245,12 @@ export class BrowserContext extends ChannelOwner<channels.BrowserContextChannel>
setDefaultNavigationTimeout(timeout: number | undefined) {
this._timeoutSettings.setDefaultNavigationTimeout(timeout);
this._wrapApiCall(async () => {
await this._channel.setDefaultNavigationTimeoutNoReply({ timeout });
}, true).catch(() => {});
this._channel.setDefaultNavigationTimeoutNoReply({ timeout }).catch(() => {});
}
setDefaultTimeout(timeout: number | undefined) {
this._timeoutSettings.setDefaultTimeout(timeout);
this._wrapApiCall(async () => {
await this._channel.setDefaultTimeoutNoReply({ timeout });
}, true).catch(() => {});
this._channel.setDefaultTimeoutNoReply({ timeout }).catch(() => {});
}
browser(): Browser | null {
@ -476,9 +472,7 @@ export class BrowserContext extends ChannelOwner<channels.BrowserContextChannel>
return;
this._closeReason = options.reason;
this._closeWasCalled = true;
await this._wrapApiCall(async () => {
await this.request.dispose(options);
}, true);
await this.request.dispose(options);
await this._wrapApiCall(async () => {
await this._browserType?._willCloseContext(this);
for (const [harId, harParams] of this._harRecorders) {

View File

@ -16,6 +16,7 @@
import { EventEmitter } from './eventEmitter';
import { ValidationError, maybeFindValidator } from '../protocol/validator';
import { methodMetainfo } from '../protocol/debug';
import { captureLibraryStackTrace } from './clientStackTrace';
import { stringifyStackFrames } from '../utils/isomorphic/stackTrace';
@ -40,7 +41,6 @@ export abstract class ChannelOwner<T extends channels.Channel = channels.Channel
_logger: Logger | undefined;
readonly _instrumentation: ClientInstrumentation;
private _eventToSubscriptionMapping: Map<string, string> = new Map();
private _isInternalType = false;
_wasCollected: boolean = false;
constructor(parent: ChannelOwner | Connection, type: string, guid: string, initializer: channels.InitializerTraits<T>) {
@ -63,21 +63,14 @@ export abstract class ChannelOwner<T extends channels.Channel = channels.Channel
this._initializer = initializer;
}
protected markAsInternalType() {
this._isInternalType = true;
}
_setEventToSubscriptionMapping(mapping: Map<string, string>) {
this._eventToSubscriptionMapping = mapping;
}
private _updateSubscription(event: string | symbol, enabled: boolean) {
const protocolEvent = this._eventToSubscriptionMapping.get(String(event));
if (protocolEvent) {
this._wrapApiCall(async () => {
await (this._channel as any).updateSubscription({ event: protocolEvent, enabled });
}, true).catch(() => {});
}
if (protocolEvent)
(this._channel as any).updateSubscription({ event: protocolEvent, enabled }).catch(() => {});
}
override on(event: string | symbol, listener: Listener): this {
@ -154,6 +147,7 @@ export abstract class ChannelOwner<T extends channels.Channel = channels.Channel
get: (obj: any, prop: string | symbol) => {
if (typeof prop === 'string') {
const validator = maybeFindValidator(this._type, prop, 'Params');
const { internal } = methodMetainfo.get(this._type + '.' + prop) || {};
if (validator) {
return async (params: any) => {
return await this._wrapApiCall(async apiZone => {
@ -169,7 +163,7 @@ export abstract class ChannelOwner<T extends channels.Channel = channels.Channel
// Since this api call is either internal, or has already been reported/traced once,
// passing undefined apiName will avoid an extra unneeded tracing entry.
return await this._connection.sendMessageToServer(this, prop, validatedParams, undefined, [], undefined);
});
}, internal);
};
}
}
@ -180,14 +174,12 @@ export abstract class ChannelOwner<T extends channels.Channel = channels.Channel
return channel;
}
async _wrapApiCall<R>(func: (apiZone: ApiZone) => Promise<R>, isInternal?: boolean): Promise<R> {
async _wrapApiCall<R>(func: (apiZone: ApiZone) => Promise<R>, isInternal: boolean = false): Promise<R> {
const logger = this._logger;
const existingApiZone = this._platform.zones.current().data<ApiZone>();
if (existingApiZone)
return await func(existingApiZone);
if (isInternal === undefined)
isInternal = this._isInternalType;
const stackTrace = captureLibraryStackTrace(this._platform);
const apiZone: ApiZone = { apiName: stackTrace.apiName, frames: stackTrace.frames, isInternal, reported: false, userData: undefined, stepId: undefined };

View File

@ -34,7 +34,6 @@ export class LocalUtils extends ChannelOwner<channels.LocalUtilsChannel> {
constructor(parent: ChannelOwner, type: string, guid: string, initializer: channels.LocalUtilsInitializer) {
super(parent, type, guid, initializer);
this.markAsInternalType();
this.devices = {};
for (const { name, descriptor } of initializer.deviceDescriptors)
this.devices[name] = descriptor;

View File

@ -102,7 +102,6 @@ export class Request extends ChannelOwner<channels.RequestChannel> implements ap
constructor(parent: ChannelOwner, type: string, guid: string, initializer: channels.RequestInitializer) {
super(parent, type, guid, initializer);
this.markAsInternalType();
this._redirectedFrom = Request.fromNullable(initializer.redirectedFrom);
if (this._redirectedFrom)
this._redirectedFrom._redirectedTo = this;
@ -177,7 +176,7 @@ export class Request extends ChannelOwner<channels.RequestChannel> implements ap
if (!this._actualHeadersPromise) {
this._actualHeadersPromise = this._wrapApiCall(async () => {
return new RawHeaders((await this._channel.rawRequestHeaders()).headers);
});
}, true);
}
return await this._actualHeadersPromise;
}
@ -199,9 +198,7 @@ export class Request extends ChannelOwner<channels.RequestChannel> implements ap
}
async _internalResponse(): Promise<Response | null> {
return await this._wrapApiCall(async () => {
return Response.fromNullable((await this._channel.response()).response);
}, true);
return Response.fromNullable((await this._channel.response()).response);
}
frame(): Frame {
@ -305,7 +302,6 @@ export class Route extends ChannelOwner<channels.RouteChannel> implements api.Ro
constructor(parent: ChannelOwner, type: string, guid: string, initializer: channels.RouteInitializer) {
super(parent, type, guid, initializer);
this.markAsInternalType();
}
request(): Request {
@ -352,7 +348,7 @@ export class Route extends ChannelOwner<channels.RouteChannel> implements api.Ro
await this._handleRoute(async () => {
await this._wrapApiCall(async () => {
await this._innerFulfill(options);
});
}, true);
});
}
@ -468,7 +464,6 @@ export class WebSocketRoute extends ChannelOwner<channels.WebSocketRouteChannel>
constructor(parent: ChannelOwner, type: string, guid: string, initializer: channels.WebSocketRouteInitializer) {
super(parent, type, guid, initializer);
this.markAsInternalType();
this._server = {
onMessage: (handler: (message: string | Buffer) => any) => {
@ -651,7 +646,6 @@ export class Response extends ChannelOwner<channels.ResponseChannel> implements
constructor(parent: ChannelOwner, type: string, guid: string, initializer: channels.ResponseInitializer) {
super(parent, type, guid, initializer);
this.markAsInternalType();
this._provisionalHeaders = new RawHeaders(initializer.headers);
this._request = Request.from(this._initializer.request);
Object.assign(this._request._timing, this._initializer.timing);

View File

@ -846,7 +846,6 @@ export class BindingCall extends ChannelOwner<channels.BindingCallChannel> {
constructor(parent: ChannelOwner, type: string, guid: string, initializer: channels.BindingCallInitializer) {
super(parent, type, guid, initializer);
this.markAsInternalType();
}
async call(func: (source: structs.BindingSource, ...args: any[]) => any) {

View File

@ -32,7 +32,6 @@ export class Tracing extends ChannelOwner<channels.TracingChannel> implements ap
constructor(parent: ChannelOwner, type: string, guid: string, initializer: channels.TracingInitializer) {
super(parent, type, guid, initializer);
this.markAsInternalType();
}
async start(options: { name?: string, title?: string, snapshots?: boolean, screenshots?: boolean, sources?: boolean, _live?: boolean } = {}) {
@ -53,15 +52,11 @@ export class Tracing extends ChannelOwner<channels.TracingChannel> implements ap
}
async group(name: string, options: { location?: { file: string, line?: number, column?: number } } = {}) {
await this._wrapApiCall(async () => {
await this._channel.tracingGroup({ name, location: options.location });
}, false);
await this._channel.tracingGroup({ name, location: options.location });
}
async groupEnd() {
await this._wrapApiCall(async () => {
await this._channel.tracingGroupEnd();
}, false);
await this._channel.tracingGroupEnd();
}
private async _startCollectingStacks(traceName: string) {

View File

@ -62,17 +62,13 @@ class JsonPipeTransport implements Transport {
}
async connect(params: channels.LocalUtilsConnectParams) {
const { pipe, headers: connectHeaders } = await this._owner._wrapApiCall(async () => {
return await this._owner._channel.connect(params);
}, /* isInternal */ true);
const { pipe, headers: connectHeaders } = await this._owner._channel.connect(params);
this._pipe = pipe;
return connectHeaders;
}
async send(message: object) {
await this._owner._wrapApiCall(async () => {
await this._pipe!.send({ message });
}, /* isInternal */ true);
await this._pipe!.send({ message });
}
onMessage(callback: (message: object) => void) {
@ -84,9 +80,7 @@ class JsonPipeTransport implements Transport {
}
async close() {
await this._owner._wrapApiCall(async () => {
await this._pipe!.close().catch(() => {});
}, /* isInternal */ true);
await this._pipe!.close().catch(() => {});
}
}

View File

@ -16,186 +16,291 @@
// This file is generated by generate_channels.js, do not edit manually.
export const slowMoActions = new Set([
'Page.goBack',
'Page.goForward',
'Page.reload',
'Page.keyboardDown',
'Page.keyboardUp',
'Page.keyboardInsertText',
'Page.keyboardType',
'Page.keyboardPress',
'Page.mouseMove',
'Page.mouseDown',
'Page.mouseUp',
'Page.mouseClick',
'Page.mouseWheel',
'Page.touchscreenTap',
'Frame.blur',
'Frame.check',
'Frame.click',
'Frame.dragAndDrop',
'Frame.dblclick',
'Frame.dispatchEvent',
'Frame.fill',
'Frame.focus',
'Frame.goto',
'Frame.hover',
'Frame.press',
'Frame.selectOption',
'Frame.setInputFiles',
'Frame.tap',
'Frame.type',
'Frame.uncheck',
'ElementHandle.check',
'ElementHandle.click',
'ElementHandle.dblclick',
'ElementHandle.dispatchEvent',
'ElementHandle.fill',
'ElementHandle.focus',
'ElementHandle.hover',
'ElementHandle.press',
'ElementHandle.scrollIntoViewIfNeeded',
'ElementHandle.selectOption',
'ElementHandle.selectText',
'ElementHandle.setInputFiles',
'ElementHandle.tap',
'ElementHandle.type',
'ElementHandle.uncheck'
]);
export const commandsWithTracingSnapshots = new Set([
'EventTarget.waitForEventInfo',
'BrowserContext.waitForEventInfo',
'Page.waitForEventInfo',
'WebSocket.waitForEventInfo',
'ElectronApplication.waitForEventInfo',
'AndroidDevice.waitForEventInfo',
'Page.emulateMedia',
'Page.goBack',
'Page.goForward',
'Page.reload',
'Page.expectScreenshot',
'Page.screenshot',
'Page.setViewportSize',
'Page.keyboardDown',
'Page.keyboardUp',
'Page.keyboardInsertText',
'Page.keyboardType',
'Page.keyboardPress',
'Page.mouseMove',
'Page.mouseDown',
'Page.mouseUp',
'Page.mouseClick',
'Page.mouseWheel',
'Page.touchscreenTap',
'Page.accessibilitySnapshot',
'Page.snapshotForAI',
'Frame.evalOnSelector',
'Frame.evalOnSelectorAll',
'Frame.addScriptTag',
'Frame.addStyleTag',
'Frame.ariaSnapshot',
'Frame.blur',
'Frame.check',
'Frame.click',
'Frame.content',
'Frame.dragAndDrop',
'Frame.dblclick',
'Frame.dispatchEvent',
'Frame.evaluateExpression',
'Frame.evaluateExpressionHandle',
'Frame.fill',
'Frame.focus',
'Frame.getAttribute',
'Frame.goto',
'Frame.hover',
'Frame.innerHTML',
'Frame.innerText',
'Frame.inputValue',
'Frame.isChecked',
'Frame.isDisabled',
'Frame.isEnabled',
'Frame.isHidden',
'Frame.isVisible',
'Frame.isEditable',
'Frame.press',
'Frame.querySelector',
'Frame.querySelectorAll',
'Frame.queryCount',
'Frame.selectOption',
'Frame.setContent',
'Frame.setInputFiles',
'Frame.tap',
'Frame.textContent',
'Frame.type',
'Frame.uncheck',
'Frame.waitForTimeout',
'Frame.waitForFunction',
'Frame.waitForSelector',
'Frame.expect',
'JSHandle.evaluateExpression',
'ElementHandle.evaluateExpression',
'JSHandle.evaluateExpressionHandle',
'ElementHandle.evaluateExpressionHandle',
'ElementHandle.evalOnSelector',
'ElementHandle.evalOnSelectorAll',
'ElementHandle.boundingBox',
'ElementHandle.check',
'ElementHandle.click',
'ElementHandle.contentFrame',
'ElementHandle.dblclick',
'ElementHandle.dispatchEvent',
'ElementHandle.fill',
'ElementHandle.focus',
'ElementHandle.hover',
'ElementHandle.innerHTML',
'ElementHandle.innerText',
'ElementHandle.inputValue',
'ElementHandle.isChecked',
'ElementHandle.isDisabled',
'ElementHandle.isEditable',
'ElementHandle.isEnabled',
'ElementHandle.isHidden',
'ElementHandle.isVisible',
'ElementHandle.press',
'ElementHandle.querySelector',
'ElementHandle.querySelectorAll',
'ElementHandle.screenshot',
'ElementHandle.scrollIntoViewIfNeeded',
'ElementHandle.selectOption',
'ElementHandle.selectText',
'ElementHandle.setInputFiles',
'ElementHandle.tap',
'ElementHandle.textContent',
'ElementHandle.type',
'ElementHandle.uncheck',
'ElementHandle.waitForElementState',
'ElementHandle.waitForSelector'
]);
export const pausesBeforeInputActions = new Set([
'Frame.check',
'Frame.click',
'Frame.dragAndDrop',
'Frame.dblclick',
'Frame.fill',
'Frame.hover',
'Frame.press',
'Frame.selectOption',
'Frame.setInputFiles',
'Frame.tap',
'Frame.type',
'Frame.uncheck',
'ElementHandle.check',
'ElementHandle.click',
'ElementHandle.dblclick',
'ElementHandle.fill',
'ElementHandle.hover',
'ElementHandle.press',
'ElementHandle.selectOption',
'ElementHandle.setInputFiles',
'ElementHandle.tap',
'ElementHandle.type',
'ElementHandle.uncheck'
export const methodMetainfo = new Map<string, { internal?: boolean, title?: string, slowMo?: boolean, snapshot?: boolean, pausesBeforeInput?: boolean }>([
['APIRequestContext.fetch', { title: 'Fetch', }],
['APIRequestContext.fetchResponseBody', { internal: true, }],
['APIRequestContext.fetchLog', { internal: true, }],
['APIRequestContext.storageState', { internal: true, }],
['APIRequestContext.disposeAPIResponse', { internal: true, }],
['APIRequestContext.dispose', { internal: true, }],
['LocalUtils.zip', { internal: true, }],
['LocalUtils.harOpen', { internal: true, }],
['LocalUtils.harLookup', { internal: true, }],
['LocalUtils.harClose', { internal: true, }],
['LocalUtils.harUnzip', { internal: true, }],
['LocalUtils.connect', { internal: true, }],
['LocalUtils.tracingStarted', { internal: true, }],
['LocalUtils.addStackToTracingNoReply', { internal: true, }],
['LocalUtils.traceDiscarded', { internal: true, }],
['LocalUtils.globToRegex', { internal: true, }],
['Root.initialize', { internal: true, }],
['Playwright.newRequest', { title: 'Create request context', }],
['DebugController.initialize', { internal: true, }],
['DebugController.setReportStateChanged', { internal: true, }],
['DebugController.resetForReuse', { internal: true, }],
['DebugController.navigate', { internal: true, }],
['DebugController.setRecorderMode', { internal: true, }],
['DebugController.highlight', { internal: true, }],
['DebugController.hideHighlight', { internal: true, }],
['DebugController.resume', { internal: true, }],
['DebugController.kill', { internal: true, }],
['DebugController.closeAllBrowsers', { internal: true, }],
['SocksSupport.socksConnected', { internal: true, }],
['SocksSupport.socksFailed', { internal: true, }],
['SocksSupport.socksData', { internal: true, }],
['SocksSupport.socksError', { internal: true, }],
['SocksSupport.socksEnd', { internal: true, }],
['Selectors.register', { internal: true, }],
['Selectors.setTestIdAttributeName', { internal: true, }],
['BrowserType.launch', { title: 'Launch browser', }],
['BrowserType.launchPersistentContext', { title: 'Launch persistent context', }],
['BrowserType.connectOverCDP', { title: 'Connect over CDP', }],
['Browser.close', { title: 'Close browser', }],
['Browser.killForTests', { internal: true, }],
['Browser.defaultUserAgentForTest', { internal: true, }],
['Browser.newContext', { title: 'Create new context', }],
['Browser.newContextForReuse', { internal: true, }],
['Browser.stopPendingOperations', { internal: true, title: 'Stop pending operations', }],
['Browser.newBrowserCDPSession', { internal: true, title: 'Create CDP session', }],
['Browser.startTracing', { internal: true, }],
['Browser.stopTracing', { internal: true, }],
['EventTarget.waitForEventInfo', { internal: true, snapshot: true, }],
['BrowserContext.addCookies', { title: 'Add cookies', }],
['BrowserContext.addInitScript', { title: 'Add init script', }],
['BrowserContext.clearCookies', { title: 'Clear cookies', }],
['BrowserContext.clearPermissions', { title: 'Clear permissions', }],
['BrowserContext.close', { title: 'Close context', }],
['BrowserContext.cookies', { title: 'Get cookies', }],
['BrowserContext.exposeBinding', { title: 'Expose binding', }],
['BrowserContext.grantPermissions', { title: 'Grant permissions', }],
['BrowserContext.newPage', { title: 'Create new page', }],
['BrowserContext.setDefaultNavigationTimeoutNoReply', { internal: true, }],
['BrowserContext.setDefaultTimeoutNoReply', { internal: true, }],
['BrowserContext.setExtraHTTPHeaders', { title: 'Set extra HTTP headers', }],
['BrowserContext.setGeolocation', { title: 'Set geolocation', }],
['BrowserContext.setHTTPCredentials', { title: 'Set HTTP credentials', }],
['BrowserContext.setNetworkInterceptionPatterns', { }],
['BrowserContext.setWebSocketInterceptionPatterns', { title: 'Route web sockets', }],
['BrowserContext.setOffline', { title: 'Set offline mode', }],
['BrowserContext.storageState', { title: 'Get storage state', }],
['BrowserContext.pause', { title: 'Pause', }],
['BrowserContext.enableRecorder', { internal: true, }],
['BrowserContext.newCDPSession', { internal: true, }],
['BrowserContext.harStart', { internal: true, }],
['BrowserContext.harExport', { internal: true, }],
['BrowserContext.createTempFiles', { internal: true, }],
['BrowserContext.updateSubscription', { internal: true, }],
['BrowserContext.clockFastForward', { title: 'Fast forward clock', }],
['BrowserContext.clockInstall', { title: 'Install clock', }],
['BrowserContext.clockPauseAt', { title: 'Pause clock', }],
['BrowserContext.clockResume', { title: 'Resume clock', }],
['BrowserContext.clockRunFor', { title: 'Run clock', }],
['BrowserContext.clockSetFixedTime', { title: 'Set fixed time', }],
['BrowserContext.clockSetSystemTime', { title: 'Set system time', }],
['Page.setDefaultNavigationTimeoutNoReply', { internal: true, }],
['Page.setDefaultTimeoutNoReply', { internal: true, }],
['Page.addInitScript', { }],
['Page.close', { }],
['Page.emulateMedia', { title: 'Emulate media', snapshot: true, }],
['Page.exposeBinding', { title: 'Expose binding', }],
['Page.goBack', { title: 'Go back', slowMo: true, snapshot: true, }],
['Page.goForward', { title: 'Go forward', slowMo: true, snapshot: true, }],
['Page.requestGC', { title: 'Request garbage collection', }],
['Page.registerLocatorHandler', { title: 'Register locator handler', }],
['Page.resolveLocatorHandlerNoReply', { internal: true, }],
['Page.unregisterLocatorHandler', { title: 'Unregister locator handler', }],
['Page.reload', { title: 'Reload', slowMo: true, snapshot: true, }],
['Page.expectScreenshot', { title: 'Expect screenshot', snapshot: true, }],
['Page.screenshot', { title: 'Screenshot', snapshot: true, }],
['Page.setExtraHTTPHeaders', { title: 'Set extra HTTP headers', }],
['Page.setNetworkInterceptionPatterns', { title: 'Route', }],
['Page.setWebSocketInterceptionPatterns', { title: 'Route web sockets', }],
['Page.setViewportSize', { title: 'Set viewport size', snapshot: true, }],
['Page.keyboardDown', { title: 'Key down', slowMo: true, snapshot: true, }],
['Page.keyboardUp', { title: 'Key up', slowMo: true, snapshot: true, }],
['Page.keyboardInsertText', { title: 'Insert text', slowMo: true, snapshot: true, }],
['Page.keyboardType', { title: 'Type', slowMo: true, snapshot: true, }],
['Page.keyboardPress', { title: 'Press', slowMo: true, snapshot: true, }],
['Page.mouseMove', { title: 'Move mouse', slowMo: true, snapshot: true, }],
['Page.mouseDown', { title: 'Mouse down', slowMo: true, snapshot: true, }],
['Page.mouseUp', { title: 'Mouse up', slowMo: true, snapshot: true, }],
['Page.mouseClick', { title: 'Click', slowMo: true, snapshot: true, }],
['Page.mouseWheel', { title: 'Mouse wheel', slowMo: true, snapshot: true, }],
['Page.touchscreenTap', { title: 'Tap', slowMo: true, snapshot: true, }],
['Page.accessibilitySnapshot', { internal: true, snapshot: true, }],
['Page.pdf', { title: 'PDF', }],
['Page.snapshotForAI', { internal: true, snapshot: true, }],
['Page.startJSCoverage', { internal: true, }],
['Page.stopJSCoverage', { internal: true, }],
['Page.startCSSCoverage', { internal: true, }],
['Page.stopCSSCoverage', { internal: true, }],
['Page.bringToFront', { title: 'Bring to front', }],
['Page.updateSubscription', { internal: true, }],
['Frame.evalOnSelector', { title: 'Evaluate', snapshot: true, }],
['Frame.evalOnSelectorAll', { title: 'Evaluate', snapshot: true, }],
['Frame.addScriptTag', { title: 'Add script tag', snapshot: true, }],
['Frame.addStyleTag', { title: 'Add style tag', snapshot: true, }],
['Frame.ariaSnapshot', { title: 'Aria snapshot', snapshot: true, }],
['Frame.blur', { title: 'Blur', slowMo: true, snapshot: true, }],
['Frame.check', { title: 'Check', slowMo: true, snapshot: true, pausesBeforeInput: true, }],
['Frame.click', { title: 'Click', slowMo: true, snapshot: true, pausesBeforeInput: true, }],
['Frame.content', { title: 'Get content', snapshot: true, }],
['Frame.dragAndDrop', { title: 'Drag and drop', slowMo: true, snapshot: true, pausesBeforeInput: true, }],
['Frame.dblclick', { title: 'Double click', slowMo: true, snapshot: true, pausesBeforeInput: true, }],
['Frame.dispatchEvent', { title: 'Dispatch event', slowMo: true, snapshot: true, }],
['Frame.evaluateExpression', { title: 'Evaluate', snapshot: true, }],
['Frame.evaluateExpressionHandle', { title: 'Evaluate', snapshot: true, }],
['Frame.fill', { title: 'Fill', slowMo: true, snapshot: true, pausesBeforeInput: true, }],
['Frame.focus', { title: 'Focus', slowMo: true, snapshot: true, }],
['Frame.frameElement', { internal: true, }],
['Frame.highlight', { internal: true, }],
['Frame.getAttribute', { internal: true, snapshot: true, }],
['Frame.goto', { title: 'Navigate', slowMo: true, snapshot: true, }],
['Frame.hover', { title: 'Hover', slowMo: true, snapshot: true, pausesBeforeInput: true, }],
['Frame.innerHTML', { title: 'Get HTML', snapshot: true, }],
['Frame.innerText', { title: 'Get inner text', snapshot: true, }],
['Frame.inputValue', { title: 'Get input value', snapshot: true, }],
['Frame.isChecked', { title: 'Is checked', snapshot: true, }],
['Frame.isDisabled', { title: 'Is disabled', snapshot: true, }],
['Frame.isEnabled', { title: 'Is enabled', snapshot: true, }],
['Frame.isHidden', { title: 'Is hidden', snapshot: true, }],
['Frame.isVisible', { title: 'Is visible', snapshot: true, }],
['Frame.isEditable', { title: 'Is editable', snapshot: true, }],
['Frame.press', { title: 'Press', slowMo: true, snapshot: true, pausesBeforeInput: true, }],
['Frame.querySelector', { title: 'Query selector', snapshot: true, }],
['Frame.querySelectorAll', { title: 'Query selector all', snapshot: true, }],
['Frame.queryCount', { title: 'Query count', snapshot: true, }],
['Frame.selectOption', { title: 'Select option', slowMo: true, snapshot: true, pausesBeforeInput: true, }],
['Frame.setContent', { title: 'Set content', snapshot: true, }],
['Frame.setInputFiles', { title: 'Set input files', slowMo: true, snapshot: true, pausesBeforeInput: true, }],
['Frame.tap', { title: 'Tap', slowMo: true, snapshot: true, pausesBeforeInput: true, }],
['Frame.textContent', { title: 'Get text content', snapshot: true, }],
['Frame.title', { internal: true, }],
['Frame.type', { title: 'Type', slowMo: true, snapshot: true, pausesBeforeInput: true, }],
['Frame.uncheck', { title: 'Uncheck', slowMo: true, snapshot: true, pausesBeforeInput: true, }],
['Frame.waitForTimeout', { title: 'Wait for timeout', snapshot: true, }],
['Frame.waitForFunction', { title: 'Wait for function', snapshot: true, }],
['Frame.waitForSelector', { title: 'Wait for selector', snapshot: true, }],
['Frame.expect', { title: 'Expect', snapshot: true, }],
['Worker.evaluateExpression', { title: 'Evaluate', }],
['Worker.evaluateExpressionHandle', { title: 'Evaluate', }],
['JSHandle.dispose', { }],
['JSHandle.evaluateExpression', { title: 'Evaluate', snapshot: true, }],
['JSHandle.evaluateExpressionHandle', { title: 'Evaluate', snapshot: true, }],
['JSHandle.getPropertyList', { internal: true, }],
['JSHandle.getProperty', { internal: true, }],
['JSHandle.jsonValue', { internal: true, }],
['ElementHandle.evalOnSelector', { title: 'Evaluate', snapshot: true, }],
['ElementHandle.evalOnSelectorAll', { title: 'Evaluate', snapshot: true, }],
['ElementHandle.boundingBox', { title: 'Get bounding box', snapshot: true, }],
['ElementHandle.check', { title: 'Check', slowMo: true, snapshot: true, pausesBeforeInput: true, }],
['ElementHandle.click', { title: 'Click', slowMo: true, snapshot: true, pausesBeforeInput: true, }],
['ElementHandle.contentFrame', { internal: true, snapshot: true, }],
['ElementHandle.dblclick', { title: 'Double click', slowMo: true, snapshot: true, pausesBeforeInput: true, }],
['ElementHandle.dispatchEvent', { title: 'Dispatch event', slowMo: true, snapshot: true, }],
['ElementHandle.fill', { title: 'Fill', slowMo: true, snapshot: true, pausesBeforeInput: true, }],
['ElementHandle.focus', { title: 'Focus', slowMo: true, snapshot: true, }],
['ElementHandle.generateLocatorString', { internal: true, }],
['ElementHandle.getAttribute', { internal: true, }],
['ElementHandle.hover', { title: 'Hover', slowMo: true, snapshot: true, pausesBeforeInput: true, }],
['ElementHandle.innerHTML', { title: 'Get HTML', snapshot: true, }],
['ElementHandle.innerText', { title: 'Get inner text', snapshot: true, }],
['ElementHandle.inputValue', { title: 'Get input value', snapshot: true, }],
['ElementHandle.isChecked', { title: 'Is checked', snapshot: true, }],
['ElementHandle.isDisabled', { title: 'Is disabled', snapshot: true, }],
['ElementHandle.isEditable', { title: 'Is editable', snapshot: true, }],
['ElementHandle.isEnabled', { title: 'Is enabled', snapshot: true, }],
['ElementHandle.isHidden', { title: 'Is hidden', snapshot: true, }],
['ElementHandle.isVisible', { title: 'Is visible', snapshot: true, }],
['ElementHandle.ownerFrame', { title: 'Get owner frame', }],
['ElementHandle.press', { title: 'Press', slowMo: true, snapshot: true, pausesBeforeInput: true, }],
['ElementHandle.querySelector', { title: 'Query selector', snapshot: true, }],
['ElementHandle.querySelectorAll', { title: 'Query selector all', snapshot: true, }],
['ElementHandle.screenshot', { title: 'Screenshot', snapshot: true, }],
['ElementHandle.scrollIntoViewIfNeeded', { title: 'Scroll into view', slowMo: true, snapshot: true, }],
['ElementHandle.selectOption', { title: 'Select option', slowMo: true, snapshot: true, pausesBeforeInput: true, }],
['ElementHandle.selectText', { title: 'Select text', slowMo: true, snapshot: true, }],
['ElementHandle.setInputFiles', { title: 'Set input files', slowMo: true, snapshot: true, pausesBeforeInput: true, }],
['ElementHandle.tap', { title: 'Tap', slowMo: true, snapshot: true, pausesBeforeInput: true, }],
['ElementHandle.textContent', { title: 'Get text content', snapshot: true, }],
['ElementHandle.type', { title: 'Type', slowMo: true, snapshot: true, pausesBeforeInput: true, }],
['ElementHandle.uncheck', { title: 'Uncheck', slowMo: true, snapshot: true, pausesBeforeInput: true, }],
['ElementHandle.waitForElementState', { title: 'Wait for state', snapshot: true, }],
['ElementHandle.waitForSelector', { title: 'Wait for selector', snapshot: true, }],
['Request.response', { internal: true, }],
['Request.rawRequestHeaders', { internal: true, }],
['Route.redirectNavigationRequest', { internal: true, }],
['Route.abort', { }],
['Route.continue', { internal: true, }],
['Route.fulfill', { internal: true, }],
['WebSocketRoute.connect', { internal: true, }],
['WebSocketRoute.ensureOpened', { internal: true, }],
['WebSocketRoute.sendToPage', { internal: true, }],
['WebSocketRoute.sendToServer', { internal: true, }],
['WebSocketRoute.closePage', { internal: true, }],
['WebSocketRoute.closeServer', { internal: true, }],
['Response.body', { internal: true, }],
['Response.securityDetails', { internal: true, }],
['Response.serverAddr', { internal: true, }],
['Response.rawResponseHeaders', { internal: true, }],
['Response.sizes', { internal: true, }],
['BindingCall.reject', { internal: true, }],
['BindingCall.resolve', { internal: true, }],
['Dialog.accept', { title: 'Accept dialog', }],
['Dialog.dismiss', { title: 'Dismiss dialog', }],
['Tracing.tracingStart', { internal: true, }],
['Tracing.tracingStartChunk', { internal: true, }],
['Tracing.tracingGroup', { title: 'Group', }],
['Tracing.tracingGroupEnd', { title: 'Group end', }],
['Tracing.tracingStopChunk', { internal: true, }],
['Tracing.tracingStop', { internal: true, }],
['Artifact.pathAfterFinished', { internal: true, }],
['Artifact.saveAs', { internal: true, }],
['Artifact.saveAsStream', { internal: true, }],
['Artifact.failure', { internal: true, }],
['Artifact.stream', { internal: true, }],
['Artifact.cancel', { internal: true, }],
['Artifact.delete', { internal: true, }],
['Stream.read', { internal: true, }],
['Stream.close', { internal: true, }],
['WritableStream.write', { internal: true, }],
['WritableStream.close', { internal: true, }],
['CDPSession.send', { internal: true, }],
['CDPSession.detach', { internal: true, }],
['Electron.launch', { title: 'Launch electron', }],
['ElectronApplication.browserWindow', { internal: true, }],
['ElectronApplication.evaluateExpression', { title: 'Evaluate', }],
['ElectronApplication.evaluateExpressionHandle', { title: 'Evaluate', }],
['ElectronApplication.updateSubscription', { internal: true, }],
['Android.devices', { internal: true, }],
['Android.setDefaultTimeoutNoReply', { internal: true, }],
['AndroidSocket.write', { internal: true, }],
['AndroidSocket.close', { internal: true, }],
['AndroidDevice.wait', { }],
['AndroidDevice.fill', { title: 'Fill', }],
['AndroidDevice.tap', { title: 'Tap', }],
['AndroidDevice.drag', { title: 'Drag', }],
['AndroidDevice.fling', { title: 'Fling', }],
['AndroidDevice.longTap', { title: 'Long tap', }],
['AndroidDevice.pinchClose', { title: 'Pinch close', }],
['AndroidDevice.pinchOpen', { title: 'Pinch open', }],
['AndroidDevice.scroll', { title: 'Scroll', }],
['AndroidDevice.swipe', { title: 'Swipe', }],
['AndroidDevice.info', { internal: true, }],
['AndroidDevice.screenshot', { title: 'Screenshot', }],
['AndroidDevice.inputType', { title: 'Type', }],
['AndroidDevice.inputPress', { title: 'Press', }],
['AndroidDevice.inputTap', { title: 'Tap', }],
['AndroidDevice.inputSwipe', { title: 'Swipe', }],
['AndroidDevice.inputDrag', { title: 'Drag', }],
['AndroidDevice.launchBrowser', { title: 'Launch browser', }],
['AndroidDevice.open', { title: 'Open app', }],
['AndroidDevice.shell', { internal: true, }],
['AndroidDevice.installApk', { title: 'Install apk', }],
['AndroidDevice.push', { title: 'Push', }],
['AndroidDevice.setDefaultTimeoutNoReply', { internal: true, }],
['AndroidDevice.connectToWebView', { internal: true, }],
['AndroidDevice.close', { internal: true, }],
['JsonPipe.send', { internal: true, }],
['JsonPipe.close', { internal: true, }]
]);

View File

@ -18,7 +18,7 @@ import { EventEmitter } from 'events';
import { debugMode, isUnderTest, monotonicTime } from '../utils';
import { BrowserContext } from './browserContext';
import { commandsWithTracingSnapshots, pausesBeforeInputActions, slowMoActions } from '../protocol/debug';
import { methodMetainfo } from '../protocol/debug';
import type { CallMetadata, InstrumentationListener, SdkObject } from './instrumentation';
@ -141,9 +141,11 @@ function shouldPauseBeforeStep(metadata: CallMetadata): boolean {
const step = metadata.type + '.' + metadata.method;
// Stop before everything that generates snapshot. But don't stop before those marked as pausesBeforeInputActions
// since we stop in them on a separate instrumentation signal.
return commandsWithTracingSnapshots.has(step) && !pausesBeforeInputActions.has(metadata.type + '.' + metadata.method);
const metainfo = methodMetainfo.get(step);
return !!metainfo?.snapshot && !metainfo.pausesBeforeInput;
}
export function shouldSlowMo(metadata: CallMetadata): boolean {
return slowMoActions.has(metadata.type + '.' + metadata.method);
const metainfo = methodMetainfo.get(metadata.type + '.' + metadata.method);
return !!metainfo?.slowMo;
}

View File

@ -19,7 +19,7 @@ import os from 'os';
import path from 'path';
import { Snapshotter } from './snapshotter';
import { commandsWithTracingSnapshots } from '../../../protocol/debug';
import { methodMetainfo } from '../../../protocol/debug';
import { assert } from '../../../utils/isomorphic/assert';
import { monotonicTime } from '../../../utils/isomorphic/time';
import { eventsHelper } from '../../utils/eventsHelper';
@ -639,7 +639,8 @@ function visitTraceEvent(object: any, sha1s: Set<string>): any {
}
export function shouldCaptureSnapshot(metadata: CallMetadata): boolean {
return commandsWithTracingSnapshots.has(metadata.type + '.' + metadata.method);
const metainfo = methodMetainfo.get(metadata.type + '.' + metadata.method);
return !!metainfo?.snapshot;
}
function createBeforeActionTraceEvent(metadata: CallMetadata, parentId?: string): trace.BeforeActionTraceEvent | null {

File diff suppressed because it is too large Load Diff

View File

@ -103,7 +103,6 @@ test('should record api trace', async ({ runInlineTest, server }, testInfo) => {
' fixture: page',
' fixture: context',
' fixture: request',
' apiRequestContext.dispose',
]);
const trace2 = await parseTrace(testInfo.outputPath('test-results', 'a-api-pass', 'trace.zip'));
expect(trace2.actionTree).toEqual([
@ -128,7 +127,6 @@ test('should record api trace', async ({ runInlineTest, server }, testInfo) => {
' fixture: page',
' fixture: context',
' fixture: request',
' apiRequestContext.dispose',
'Worker Cleanup',
' fixture: browser',
]);
@ -332,7 +330,6 @@ test('should not override trace file in afterAll', async ({ runInlineTest, serve
' apiRequest.newContext',
' apiRequestContext.get',
' fixture: request',
' apiRequestContext.dispose',
'Worker Cleanup',
' fixture: browser',
]);
@ -1264,6 +1261,7 @@ test('should not nest top level expect into unfinished api calls ', {
' browserContext.newPage',
'page.route',
'page.goto',
'route.fetch',
'expect.toBeVisible',
'page.unrouteAll',
'After Hooks',

View File

@ -1224,7 +1224,6 @@ pw:api |apiRequestContext.get(${server.EMPTY_PAGE}) @ a.test.ts:11
pw:api | error: <error message>
hook |After Hooks
fixture | fixture: request
pw:api | apiRequestContext.dispose
fixture | fixture: page
fixture | fixture: context
`);
@ -1517,6 +1516,7 @@ pw:api | browserContext.newPage
test.step |custom step @ a.test.ts:4
pw:api | page.route @ a.test.ts:5
pw:api | page.goto(${server.EMPTY_PAGE}) @ a.test.ts:12
pw:api | route.fetch(${server.EMPTY_PAGE}) @ a.test.ts:6
expect | expect.toBe @ a.test.ts:8
hook |After Hooks
fixture | fixture: page

View File

@ -18,7 +18,6 @@
// @ts-check
const fs = require('fs');
const os = require('os');
const path = require('path');
const yaml = require('yaml');
@ -178,9 +177,7 @@ const debug_ts = [
// This file is generated by ${path.basename(__filename).split(path.sep).join(path.posix.sep)}, do not edit manually.
`];
const slowMoActions = [];
const tracingSnapshots = [];
const pausesBeforeInputActions = [];
const methodMetainfo = [];
const yml = fs.readFileSync(path.join(__dirname, '..', 'packages', 'protocol', 'src', 'protocol.yml'), 'utf-8');
const protocol = yaml.parse(yml);
@ -279,21 +276,14 @@ for (const [name, item] of Object.entries(protocol)) {
for (let [methodName, method] of Object.entries(item.commands || {})) {
if (method === null)
method = {};
if (method.flags?.slowMo) {
slowMoActions.push(name + '.' + methodName);
for (const derived of derivedClasses.get(name) || [])
slowMoActions.push(derived + '.' + methodName);
}
if (method.flags?.snapshot) {
tracingSnapshots.push(name + '.' + methodName);
for (const derived of derivedClasses.get(name) || [])
tracingSnapshots.push(derived + '.' + methodName);
}
if (method.flags?.pausesBeforeInput) {
pausesBeforeInputActions.push(name + '.' + methodName);
for (const derived of derivedClasses.get(name) || [])
pausesBeforeInputActions.push(derived + '.' + methodName);
}
const internalProp = method.internal ? ` internal: ${method.internal},` : '';
const titleProp = method.title ? ` title: '${method.title}',` : '';
const slowMoProp = method.flags?.slowMo ? ` slowMo: ${method.flags.slowMo},` : '';
const snapshotProp = method.flags?.snapshot ? ` snapshot: ${method.flags.snapshot},` : '';
const pauseProp = method.flags?.pausesBeforeInput ? ` pausesBeforeInput: ${method.flags.pausesBeforeInput},` : '';
methodMetainfo.push(`['${name + '.' + methodName}', {${internalProp}${titleProp}${slowMoProp}${snapshotProp}${pauseProp} }]`);
const parameters = objectType(method.parameters || {}, '');
const paramsName = `${channelName}${titleCase(methodName)}Params`;
const optionsName = `${channelName}${titleCase(methodName)}Options`;
@ -335,16 +325,8 @@ for (const [name, item] of Object.entries(protocol)) {
}
}
debug_ts.push(`export const slowMoActions = new Set([
'${slowMoActions.join(`',\n '`)}'
]);`);
debug_ts.push('');
debug_ts.push(`export const commandsWithTracingSnapshots = new Set([
'${tracingSnapshots.join(`',\n '`)}'
]);`);
debug_ts.push('');
debug_ts.push(`export const pausesBeforeInputActions = new Set([
'${pausesBeforeInputActions.join(`',\n '`)}'
debug_ts.push(`export const methodMetainfo = new Map<string, { internal?: boolean, title?: string, slowMo?: boolean, snapshot?: boolean, pausesBeforeInput?: boolean }>([
${methodMetainfo.join(`,\n `)}
]);`);
let hasChanges = false;