diff --git a/packages/playwright-core/src/tools/backend/response.ts b/packages/playwright-core/src/tools/backend/response.ts index d28de344964bf..6b83f6e7d7870 100644 --- a/packages/playwright-core/src/tools/backend/response.ts +++ b/packages/playwright-core/src/tools/backend/response.ts @@ -270,7 +270,7 @@ export class Response { addSection('Ran Playwright code', this._code, 'js'); // Render tab titles upon changes or when more than one tab. - const tabSnapshot = this._context.currentTab() ? await this._context.currentTabOrDie().captureSnapshot(this._includeSnapshotRoot, this._includeSnapshotDepth, this._includeSnapshotBoxes, this._clientWorkspace) : undefined; + const tabSnapshot = this._context.currentTab() ? await this._context.currentTabOrDie().captureSnapshot(this._includeSnapshotRoot, this._includeSnapshotDepth, this._includeSnapshotBoxes, this._clientWorkspace, this._includeSnapshot !== 'none') : undefined; const tabHeaders = await Promise.all(this._context.tabs().map(tab => tab.headerSnapshot())); if (this._includeSnapshot !== 'none' || tabHeaders.some(header => header.changed)) { if (tabHeaders.length !== 1) diff --git a/packages/playwright-core/src/tools/backend/tab.ts b/packages/playwright-core/src/tools/backend/tab.ts index 441f8a9f75b00..cff0839a70103 100644 --- a/packages/playwright-core/src/tools/backend/tab.ts +++ b/packages/playwright-core/src/tools/backend/tab.ts @@ -406,19 +406,31 @@ export class Tab extends EventEmitter { this._requests.length = 0; } - async captureSnapshot(root: playwright.Locator | undefined, depth: number | undefined, boxes: boolean | undefined, relativeTo: string | undefined): Promise { + async captureSnapshot(root: playwright.Locator | undefined, depth: number | undefined, boxes: boolean | undefined, relativeTo: string | undefined, includeAria: boolean = true): Promise { await this._initializedPromise; let tabSnapshot: TabSnapshot | undefined; - const modalStates = await this._raceAgainstModalStates(async () => { - const ariaSnapshot = root - ? await root.ariaSnapshot({ mode: 'ai', depth, boxes }) - : await this.page.ariaSnapshot({ mode: 'ai', depth, boxes }); - tabSnapshot = { - ariaSnapshot, - modalStates: [], - events: [], - }; - }); + let modalStates: ModalState[] = []; + if (includeAria) { + modalStates = await this._raceAgainstModalStates(async () => { + const ariaSnapshot = root + ? await root.ariaSnapshot({ mode: 'ai', depth, boxes }) + : await this.page.ariaSnapshot({ mode: 'ai', depth, boxes }); + tabSnapshot = { + ariaSnapshot, + modalStates: [], + events: [], + }; + }); + } else if (this.modalStates().length) { + // Matches the aria path's modal fallback below, without the race: there + // is no tree walk for a modal to interrupt. + modalStates = this.modalStates(); + } else { + // The caller will not render the aria snapshot, so skip the accessibility + // tree walk, which dominates response latency on heavy pages. Console and + // events are still reported via the shared tail below. + tabSnapshot = { ariaSnapshot: '', modalStates: [], events: [] }; + } if (tabSnapshot) { tabSnapshot.consoleLink = await this._consoleLog.take(relativeTo); tabSnapshot.events = this._recentEventEntries;