diff --git a/src/protocol/channels.ts b/src/protocol/channels.ts index 16e6be9011..691d0f3b25 100644 --- a/src/protocol/channels.ts +++ b/src/protocol/channels.ts @@ -3235,3 +3235,73 @@ export type SocksSocketConnectedResult = void; export type SocksSocketEndParams = {}; export type SocksSocketEndOptions = {}; export type SocksSocketEndResult = void; + +export const commandsWithTracingSnapshots = new Set([ + 'Page.goBack', + 'Page.goForward', + 'Page.reload', + 'Page.setViewportSize', + 'Page.keyboardDown', + 'Page.keyboardUp', + 'Page.keyboardInsertText', + 'Page.keyboardType', + 'Page.keyboardPress', + 'Page.mouseMove', + 'Page.mouseDown', + 'Page.mouseUp', + 'Page.mouseClick', + 'Page.touchscreenTap', + 'Frame.evalOnSelector', + 'Frame.evalOnSelectorAll', + 'Frame.addScriptTag', + 'Frame.addStyleTag', + 'Frame.check', + 'Frame.click', + '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.isEditable', + 'Frame.press', + 'Frame.selectOption', + 'Frame.setContent', + 'Frame.setInputFiles', + 'Frame.tap', + 'Frame.textContent', + 'Frame.type', + 'Frame.uncheck', + 'Frame.waitForFunction', + 'Frame.waitForSelector', + 'JSHandle.evaluateExpression', + 'JSHandle.evaluateExpressionHandle', + 'ElementHandle.evalOnSelector', + 'ElementHandle.evalOnSelectorAll', + 'ElementHandle.check', + 'ElementHandle.click', + 'ElementHandle.dblclick', + 'ElementHandle.dispatchEvent', + 'ElementHandle.fill', + 'ElementHandle.hover', + 'ElementHandle.press', + 'ElementHandle.scrollIntoViewIfNeeded', + 'ElementHandle.selectOption', + 'ElementHandle.selectText', + 'ElementHandle.setInputFiles', + 'ElementHandle.tap', + 'ElementHandle.type', + 'ElementHandle.uncheck', + 'ElementHandle.waitForElementState', + 'ElementHandle.waitForSelector' +]); \ No newline at end of file diff --git a/src/protocol/protocol.yml b/src/protocol/protocol.yml index 28a142db68..708b40d2ad 100644 --- a/src/protocol/protocol.yml +++ b/src/protocol/protocol.yml @@ -764,6 +764,8 @@ Page: - networkidle returns: response: Response? + tracing: + snapshot: true goForward: parameters: @@ -776,6 +778,8 @@ Page: - networkidle returns: response: Response? + tracing: + snapshot: true reload: parameters: @@ -788,6 +792,8 @@ Page: - networkidle returns: response: Response? + tracing: + snapshot: true screenshot: parameters: @@ -821,34 +827,48 @@ Page: properties: width: number height: number + tracing: + snapshot: true keyboardDown: parameters: key: string + tracing: + snapshot: true keyboardUp: parameters: key: string + tracing: + snapshot: true keyboardInsertText: parameters: text: string + tracing: + snapshot: true keyboardType: parameters: text: string delay: number? + tracing: + snapshot: true keyboardPress: parameters: key: string delay: number? + tracing: + snapshot: true mouseMove: parameters: x: number y: number steps: number? + tracing: + snapshot: true mouseDown: parameters: @@ -859,6 +879,8 @@ Page: - right - middle clickCount: number? + tracing: + snapshot: true mouseUp: parameters: @@ -869,6 +891,8 @@ Page: - right - middle clickCount: number? + tracing: + snapshot: true mouseClick: parameters: @@ -882,11 +906,15 @@ Page: - right - middle clickCount: number? + tracing: + snapshot: true touchscreenTap: parameters: x: number y: number + tracing: + snapshot: true accessibilitySnapshot: parameters: @@ -1062,6 +1090,8 @@ Frame: arg: SerializedArgument returns: value: SerializedValue + tracing: + snapshot: true evalOnSelectorAll: parameters: @@ -1071,6 +1101,8 @@ Frame: arg: SerializedArgument returns: value: SerializedValue + tracing: + snapshot: true addScriptTag: parameters: @@ -1079,6 +1111,8 @@ Frame: type: string? returns: element: ElementHandle + tracing: + snapshot: true addStyleTag: parameters: @@ -1086,6 +1120,8 @@ Frame: content: string? returns: element: ElementHandle + tracing: + snapshot: true check: parameters: @@ -1095,6 +1131,8 @@ Frame: position: Point? timeout: number? trial: boolean? + tracing: + snapshot: true click: parameters: @@ -1121,6 +1159,8 @@ Frame: clickCount: number? timeout: number? trial: boolean? + tracing: + snapshot: true content: returns: @@ -1150,6 +1190,8 @@ Frame: - middle timeout: number? trial: boolean? + tracing: + snapshot: true dispatchEvent: parameters: @@ -1157,6 +1199,8 @@ Frame: type: string eventInit: SerializedArgument timeout: number? + tracing: + snapshot: true evaluateExpression: parameters: @@ -1165,6 +1209,8 @@ Frame: arg: SerializedArgument returns: value: SerializedValue + tracing: + snapshot: true evaluateExpressionHandle: parameters: @@ -1173,6 +1219,8 @@ Frame: arg: SerializedArgument returns: handle: JSHandle + tracing: + snapshot: true fill: parameters: @@ -1181,11 +1229,15 @@ Frame: force: boolean? timeout: number? noWaitAfter: boolean? + tracing: + snapshot: true focus: parameters: selector: string timeout: number? + tracing: + snapshot: true frameElement: returns: @@ -1198,6 +1250,8 @@ Frame: timeout: number? returns: value: string? + tracing: + snapshot: true goto: parameters: @@ -1212,6 +1266,8 @@ Frame: referer: string? returns: response: Response? + tracing: + snapshot: true hover: parameters: @@ -1229,6 +1285,8 @@ Frame: position: Point? timeout: number? trial: boolean? + tracing: + snapshot: true innerHTML: parameters: @@ -1236,6 +1294,8 @@ Frame: timeout: number? returns: value: string + tracing: + snapshot: true innerText: parameters: @@ -1243,6 +1303,8 @@ Frame: timeout: number? returns: value: string + tracing: + snapshot: true inputValue: parameters: @@ -1250,6 +1312,8 @@ Frame: timeout: number? returns: value: string + tracing: + snapshot: true isChecked: parameters: @@ -1257,6 +1321,8 @@ Frame: timeout: number? returns: value: boolean + tracing: + snapshot: true isDisabled: parameters: @@ -1264,6 +1330,8 @@ Frame: timeout: number? returns: value: boolean + tracing: + snapshot: true isEnabled: parameters: @@ -1271,6 +1339,8 @@ Frame: timeout: number? returns: value: boolean + tracing: + snapshot: true isHidden: parameters: @@ -1278,6 +1348,8 @@ Frame: timeout: number? returns: value: boolean + tracing: + snapshot: true isVisible: parameters: @@ -1292,6 +1364,8 @@ Frame: timeout: number? returns: value: boolean + tracing: + snapshot: true press: parameters: @@ -1300,6 +1374,8 @@ Frame: delay: number? noWaitAfter: boolean? timeout: number? + tracing: + snapshot: true querySelector: parameters: @@ -1336,6 +1412,8 @@ Frame: values: type: array items: string + tracing: + snapshot: true setContent: parameters: @@ -1347,6 +1425,8 @@ Frame: - load - domcontentloaded - networkidle + tracing: + snapshot: true setInputFiles: parameters: @@ -1361,6 +1441,8 @@ Frame: buffer: binary timeout: number? noWaitAfter: boolean? + tracing: + snapshot: true tap: parameters: @@ -1379,6 +1461,8 @@ Frame: position: Point? timeout: number? trial: boolean? + tracing: + snapshot: true textContent: parameters: @@ -1386,6 +1470,8 @@ Frame: timeout: number? returns: value: string? + tracing: + snapshot: true title: returns: @@ -1398,6 +1484,8 @@ Frame: delay: number? noWaitAfter: boolean? timeout: number? + tracing: + snapshot: true uncheck: parameters: @@ -1407,6 +1495,8 @@ Frame: position: Point? timeout: number? trial: boolean? + tracing: + snapshot: true waitForFunction: parameters: @@ -1418,6 +1508,8 @@ Frame: pollingInterval: number? returns: handle: JSHandle + tracing: + snapshot: true waitForSelector: parameters: @@ -1432,6 +1524,8 @@ Frame: - hidden returns: element: ElementHandle? + tracing: + snapshot: true events: @@ -1508,6 +1602,8 @@ JSHandle: arg: SerializedArgument returns: value: SerializedValue + tracing: + snapshot: true evaluateExpressionHandle: parameters: @@ -1516,6 +1612,8 @@ JSHandle: arg: SerializedArgument returns: handle: JSHandle + tracing: + snapshot: true getPropertyList: returns: @@ -1560,6 +1658,8 @@ ElementHandle: arg: SerializedArgument returns: value: SerializedValue + tracing: + snapshot: true evalOnSelectorAll: parameters: @@ -1569,6 +1669,8 @@ ElementHandle: arg: SerializedArgument returns: value: SerializedValue + tracing: + snapshot: true boundingBox: returns: @@ -1581,6 +1683,8 @@ ElementHandle: position: Point? timeout: number? trial: boolean? + tracing: + snapshot: true click: parameters: @@ -1606,6 +1710,8 @@ ElementHandle: clickCount: number? timeout: number? trial: boolean? + tracing: + snapshot: true contentFrame: returns: @@ -1634,11 +1740,15 @@ ElementHandle: - middle timeout: number? trial: boolean? + tracing: + snapshot: true dispatchEvent: parameters: type: string eventInit: SerializedArgument + tracing: + snapshot: true fill: parameters: @@ -1646,6 +1756,8 @@ ElementHandle: force: boolean? timeout: number? noWaitAfter: boolean? + tracing: + snapshot: true focus: @@ -1670,6 +1782,8 @@ ElementHandle: position: Point? timeout: number? trial: boolean? + tracing: + snapshot: true innerHTML: returns: @@ -1717,6 +1831,8 @@ ElementHandle: delay: number? timeout: number? noWaitAfter: boolean? + tracing: + snapshot: true querySelector: parameters: @@ -1748,6 +1864,8 @@ ElementHandle: scrollIntoViewIfNeeded: parameters: timeout: number? + tracing: + snapshot: true selectOption: parameters: @@ -1769,11 +1887,15 @@ ElementHandle: values: type: array items: string + tracing: + snapshot: true selectText: parameters: force: boolean? timeout: number? + tracing: + snapshot: true setInputFiles: parameters: @@ -1787,6 +1909,8 @@ ElementHandle: buffer: binary timeout: number? noWaitAfter: boolean? + tracing: + snapshot: true tap: parameters: @@ -1804,6 +1928,8 @@ ElementHandle: position: Point? timeout: number? trial: boolean? + tracing: + snapshot: true textContent: returns: @@ -1815,6 +1941,8 @@ ElementHandle: delay: number? noWaitAfter: boolean? timeout: number? + tracing: + snapshot: true uncheck: parameters: @@ -1823,6 +1951,8 @@ ElementHandle: position: Point? timeout: number? trial: boolean? + tracing: + snapshot: true waitForElementState: parameters: @@ -1836,6 +1966,8 @@ ElementHandle: - disabled - editable timeout: number? + tracing: + snapshot: true waitForSelector: parameters: @@ -1850,6 +1982,8 @@ ElementHandle: - hidden returns: element: ElementHandle? + tracing: + snapshot: true Request: diff --git a/src/server/trace/common/traceEvents.ts b/src/server/trace/common/traceEvents.ts index 33ce1949aa..bb7ee284e0 100644 --- a/src/server/trace/common/traceEvents.ts +++ b/src/server/trace/common/traceEvents.ts @@ -35,6 +35,7 @@ export type ScreencastFrameTraceEvent = { export type ActionTraceEvent = { type: 'action' | 'event', + hasSnapshot: boolean, metadata: CallMetadata, }; diff --git a/src/server/trace/recorder/tracing.ts b/src/server/trace/recorder/tracing.ts index 54fe4b3150..9e90a71a04 100644 --- a/src/server/trace/recorder/tracing.ts +++ b/src/server/trace/recorder/tracing.ts @@ -27,7 +27,7 @@ import { CallMetadata, InstrumentationListener, SdkObject } from '../../instrume import { Page } from '../../page'; import * as trace from '../common/traceEvents'; import { TraceSnapshotter } from './traceSnapshotter'; -import { Dialog } from '../../dialog'; +import { commandsWithTracingSnapshots } from '../../../protocol/channels'; export type TracerOptions = { name?: string; @@ -133,13 +133,8 @@ export class Tracing implements InstrumentationListener { return; if (!this._snapshotter.started()) return; - - if (sdkObject instanceof Dialog && name === 'before') { - // A call on the dialog is going to dismiss it and resume the evaluation. - // We can't be capturing the snapshot before dismiss action is performed. + if (!this._shouldCaptureSnapshot(metadata)) return; - } - const snapshotName = `${name}@${metadata.id}`; metadata.snapshots.push({ title: name, snapshotName }); await this._snapshotter!.captureSnapshot(sdkObject.attribution.page, snapshotName, element); @@ -167,7 +162,7 @@ export class Tracing implements InstrumentationListener { } pendingCall.afterSnapshot = this._captureSnapshot('after', sdkObject, metadata); await pendingCall.afterSnapshot; - const event: trace.ActionTraceEvent = { type: 'action', metadata }; + const event: trace.ActionTraceEvent = { type: 'action', metadata, hasSnapshot: this._shouldCaptureSnapshot(metadata) }; this._appendTraceEvent(event); this._pendingCalls.delete(metadata.id); } @@ -175,7 +170,7 @@ export class Tracing implements InstrumentationListener { onEvent(sdkObject: SdkObject, metadata: CallMetadata) { if (!sdkObject.attribution.page) return; - const event: trace.ActionTraceEvent = { type: 'event', metadata }; + const event: trace.ActionTraceEvent = { type: 'event', metadata, hasSnapshot: false }; this._appendTraceEvent(event); } @@ -230,4 +225,8 @@ export class Tracing implements InstrumentationListener { await fs.promises.appendFile(this._traceFile!, JSON.stringify(event) + '\n'); }); } + + private _shouldCaptureSnapshot(metadata: CallMetadata): boolean { + return commandsWithTracingSnapshots.has(metadata.type + '.' + metadata.method); + } } diff --git a/src/server/trace/viewer/traceModel.ts b/src/server/trace/viewer/traceModel.ts index 65b1005604..be96f90cf5 100644 --- a/src/server/trace/viewer/traceModel.ts +++ b/src/server/trace/viewer/traceModel.ts @@ -76,7 +76,8 @@ export class TraceModel { } case 'action': { const metadata = event.metadata; - if (metadata.pageId) + const include = typeof event.hasSnapshot !== 'boolean' || event.hasSnapshot; + if (include && metadata.pageId) this._pageEntry(metadata.pageId).actions.push(event); break; } diff --git a/utils/generate_channels.js b/utils/generate_channels.js index 134aae7966..c22c293b80 100755 --- a/utils/generate_channels.js +++ b/utils/generate_channels.js @@ -167,6 +167,8 @@ export function createScheme(tChannel: (name: string) => Validator): Scheme { }; `]; +const tracingSnapshots = []; + const yml = fs.readFileSync(path.join(__dirname, '..', 'src', 'protocol', 'protocol.yml'), 'utf-8'); const protocol = yaml.parse(yml); @@ -208,6 +210,8 @@ for (const [name, item] of Object.entries(protocol)) { for (let [methodName, method] of Object.entries(item.commands || {})) { if (method === null) method = {}; + if (method.tracing && method.tracing.snapshot) + tracingSnapshots.push(name + '.' + methodName); const parameters = objectType(method.parameters || {}, ''); const paramsName = `${channelName}${titleCase(methodName)}Params`; const optionsName = `${channelName}${titleCase(methodName)}Options`; @@ -238,6 +242,10 @@ for (const [name, item] of Object.entries(protocol)) { } } +channels_ts.push(`export const commandsWithTracingSnapshots = new Set([ + '${tracingSnapshots.join(`',\n '`)}' +]);`); + validator_ts.push(` return scheme; }