From b31b23101d4d821a3a0c6f6288c1925db20c8c34 Mon Sep 17 00:00:00 2001 From: Dmitry Gozman Date: Thu, 19 Mar 2026 05:06:12 +0000 Subject: [PATCH 1/2] chore: merge snapshotForAI() into ariaSnapshot() --- docs/src/api/class-locator.md | 30 +++--- docs/src/api/class-page.md | 24 +++-- packages/injected/src/ariaSnapshot.ts | 16 +-- packages/injected/src/consoleApi.ts | 2 +- packages/injected/src/injectedScript.ts | 4 +- packages/injected/src/recorder/recorder.ts | 6 +- packages/playwright-client/types/types.d.ts | 100 +++++++++--------- .../playwright-core/src/client/locator.ts | 8 +- packages/playwright-core/src/client/page.ts | 4 +- .../playwright-core/src/protocol/validator.ts | 12 +-- .../src/server/dispatchers/frameDispatcher.ts | 4 - .../src/server/dispatchers/pageDispatcher.ts | 4 +- packages/playwright-core/src/server/dom.ts | 4 - packages/playwright-core/src/server/frames.ts | 4 - packages/playwright-core/src/server/page.ts | 17 +-- .../playwright-core/src/tools/backend/tab.ts | 4 +- .../src/utils/isomorphic/protocolMetainfo.ts | 3 +- packages/playwright-core/types/types.d.ts | 100 +++++++++--------- packages/playwright/src/index.ts | 2 +- .../playwright/src/mcp/test/browserBackend.ts | 2 +- packages/protocol/src/channels.d.ts | 21 ++-- packages/protocol/src/protocol.yml | 21 ++-- tests/page/page-aria-snapshot-ai.spec.ts | 6 +- 23 files changed, 180 insertions(+), 218 deletions(-) diff --git a/docs/src/api/class-locator.md b/docs/src/api/class-locator.md index 8adf3be21449d..d228df961023a 100644 --- a/docs/src/api/class-locator.md +++ b/docs/src/api/class-locator.md @@ -206,12 +206,25 @@ Below is the HTML markup and the respective ARIA snapshot: - link "About" ``` +### option: Locator.ariaSnapshot.content +* since: v1.59 +- `content` <[AriaSnapshotContent]<"ai"|"default">> + +When set to `"ai"`, returns a snapshot optimized for AI consumption with element references. +Defaults to `"default"`. + ### option: Locator.ariaSnapshot.timeout = %%-input-timeout-%% * since: v1.49 ### option: Locator.ariaSnapshot.timeout = %%-input-timeout-js-%% * since: v1.49 +### option: Locator.ariaSnapshot.depth +* since: v1.59 +- `depth` <[int]> + +When specified, limits the depth of the snapshot. + ## async method: Locator.blur * since: v1.28 @@ -2492,23 +2505,6 @@ This method expects [Locator] to point to an ### option: Locator.setInputFiles.timeout = %%-input-timeout-js-%% * since: v1.14 -## async method: Locator.snapshotForAI -* since: v1.59 -- returns: <[string]> - -Returns an accessibility snapshot of the element's subtree optimized for AI consumption. - -### option: Locator.snapshotForAI.timeout = %%-input-timeout-%% -* since: v1.59 - -### option: Locator.snapshotForAI.timeout = %%-input-timeout-js-%% -* since: v1.59 - -### option: Locator.snapshotForAI.depth -* since: v1.59 -- `depth` <[int]> - -When specified, limits the depth of the snapshot. ## async method: Locator.tap * since: v1.14 diff --git a/docs/src/api/class-page.md b/docs/src/api/class-page.md index 9978466b1bd9c..149c6508c10ee 100644 --- a/docs/src/api/class-page.md +++ b/docs/src/api/class-page.md @@ -4211,34 +4211,40 @@ Page width in pixels. Page height in pixels. -## async method: Page.snapshotForAI +## async method: Page.ariaSnapshot * since: v1.59 - returns: <[string]> -Returns an accessibility snapshot of the page optimized for AI consumption. +Captures the aria snapshot of the page. Read more about [aria snapshots](../aria-snapshots.md). -### option: Page.snapshotForAI.timeout = %%-input-timeout-%% +### option: Page.ariaSnapshot.content * since: v1.59 +- `content` <[AriaSnapshotContent]<"ai"|"default">> -### option: Page.snapshotForAI.timeout = %%-input-timeout-js-%% +When set to `"ai"`, returns a snapshot optimized for AI consumption with element references. Defaults to `"default"`. + +### option: Page.ariaSnapshot.timeout = %%-input-timeout-%% +* since: v1.59 + +### option: Page.ariaSnapshot.timeout = %%-input-timeout-js-%% * since: v1.59 -### option: Page.snapshotForAI.track +### option: Page.ariaSnapshot.track * since: v1.59 - `track` <[string]> When specified, enables incremental snapshots. Subsequent calls with the same track name will track changes between calls. -### option: Page.snapshotForAI.mode +### option: Page.ariaSnapshot.mode * since: v1.59 -- `mode` <[string]> +- `mode` <[AriaSnapshotContent]<"incremental"|"full">> -When set to `"incremental"` and [`option: Page.snapshotForAI.track`] is specified, returns an +When set to `"incremental"` and [`option: Page.ariaSnapshot.track`] is specified, returns an incremental snapshot containing only changes since the last call with the same track name. Defaults to `"full"`. -### option: Page.snapshotForAI.depth +### option: Page.ariaSnapshot.depth * since: v1.59 - `depth` <[int]> diff --git a/packages/injected/src/ariaSnapshot.ts b/packages/injected/src/ariaSnapshot.ts index b661c7fdc6515..adec82ac722a5 100644 --- a/packages/injected/src/ariaSnapshot.ts +++ b/packages/injected/src/ariaSnapshot.ts @@ -37,7 +37,7 @@ type AriaRef = { let lastRef = 0; export type AriaTreeOptions = { - mode: 'ai' | 'expect' | 'codegen' | 'autoexpect'; + content: 'ai' | 'default' | 'codegen' | 'autoexpect'; refPrefix?: string; doNotRenderActive?: boolean; depth?: number; @@ -54,7 +54,7 @@ type InternalOptions = { }; function toInternalOptions(options: AriaTreeOptions): InternalOptions { - if (options.mode === 'ai') { + if (options.content === 'ai') { // For AI consumption. return { visibility: 'ariaOrVisible', @@ -65,11 +65,11 @@ function toInternalOptions(options: AriaTreeOptions): InternalOptions { renderCursorPointer: true, }; } - if (options.mode === 'autoexpect') { + if (options.content === 'autoexpect') { // To auto-generate assertions on visible elements. return { visibility: 'ariaAndVisible', refs: 'none' }; } - if (options.mode === 'codegen') { + if (options.content === 'codegen') { // To generate aria assertion with regex heurisitcs. return { visibility: 'aria', refs: 'none', renderStringsAsRegex: true }; } @@ -387,19 +387,19 @@ export type MatcherReceived = { }; export function matchesExpectAriaTemplate(rootElement: Element, template: aria.AriaTemplateNode): { matches: aria.AriaNode[], received: MatcherReceived } { - const snapshot = generateAriaTree(rootElement, { mode: 'expect' }); + const snapshot = generateAriaTree(rootElement, { content: 'default' }); const matches = matchesNodeDeep(snapshot.root, template, false, false); return { matches, received: { - raw: renderAriaTree(snapshot, { mode: 'expect' }), - regex: renderAriaTree(snapshot, { mode: 'codegen' }), + raw: renderAriaTree(snapshot, { content: 'default' }), + regex: renderAriaTree(snapshot, { content: 'codegen' }), } }; } export function getAllElementsMatchingExpectAriaTemplate(rootElement: Element, template: aria.AriaTemplateNode): Element[] { - const root = generateAriaTree(rootElement, { mode: 'expect' }).root; + const root = generateAriaTree(rootElement, { content: 'default' }).root; const matches = matchesNodeDeep(root, template, true, false); return matches.map(n => ariaNodeElement(n)); } diff --git a/packages/injected/src/consoleApi.ts b/packages/injected/src/consoleApi.ts index 2406c2e894061..fb277bdec6772 100644 --- a/packages/injected/src/consoleApi.ts +++ b/packages/injected/src/consoleApi.ts @@ -93,7 +93,7 @@ export class ConsoleAPI { selector: (element: Element) => this._selector(element), generateLocator: (element: Element, language?: Language) => this._generateLocator(element, language), ariaSnapshot: (element?: Element, options?: AriaTreeOptions) => { - return this._injectedScript.ariaSnapshot(element || this._injectedScript.document.body, options || { mode: 'expect' }); + return this._injectedScript.ariaSnapshot(element || this._injectedScript.document.body, options || { content: 'default' }); }, resume: () => this._resume(), ...new Locator(this._injectedScript, ''), diff --git a/packages/injected/src/injectedScript.ts b/packages/injected/src/injectedScript.ts index 416ef12ea697b..927b35a5eca2e 100644 --- a/packages/injected/src/injectedScript.ts +++ b/packages/injected/src/injectedScript.ts @@ -323,8 +323,8 @@ export class InjectedScript { } ariaSnapshotForRecorder(): { ariaSnapshot: string, refs: Map } { - const tree = generateAriaTree(this.document.body, { mode: 'ai' }); - const ariaSnapshot = renderAriaTree(tree, { mode: 'ai' }); + const tree = generateAriaTree(this.document.body, { content: 'ai' }); + const ariaSnapshot = renderAriaTree(tree, { content: 'ai' }); return { ariaSnapshot, refs: tree.refs }; } diff --git a/packages/injected/src/recorder/recorder.ts b/packages/injected/src/recorder/recorder.ts index 40dbf7df8006d..c38e1775a9e5f 100644 --- a/packages/injected/src/recorder/recorder.ts +++ b/packages/injected/src/recorder/recorder.ts @@ -1064,7 +1064,7 @@ class TextAssertionTool implements RecorderTool { name: 'assertSnapshot', selector: this._hoverHighlight.selector, signals: [], - ariaSnapshot: this._recorder.injectedScript.ariaSnapshot(target, { mode: 'codegen' }), + ariaSnapshot: this._recorder.injectedScript.ariaSnapshot(target, { content: 'codegen' }), }; } else { const generated = this._recorder.injectedScript.generateSelector(target, { testIdAttributeName: this._recorder.state.testIdAttributeName, forTextExpect: true }); @@ -1698,7 +1698,7 @@ export class Recorder { private _captureAutoExpectSnapshot() { const documentElement = this.injectedScript.document.documentElement; - return documentElement ? this.injectedScript.utils.generateAriaTree(documentElement, { mode: 'autoexpect' }) : undefined; + return documentElement ? this.injectedScript.utils.generateAriaTree(documentElement, { content: 'autoexpect' }) : undefined; } async performAction(action: actions.PerformOnRecordAction) { @@ -1723,7 +1723,7 @@ export class Recorder { } elementPicked(selector: string, model: HighlightModel) { - const ariaSnapshot = this.injectedScript.ariaSnapshot(model.elements[0], { mode: 'expect' }); + const ariaSnapshot = this.injectedScript.ariaSnapshot(model.elements[0], { content: 'default' }); void this._delegate.elementPicked?.({ selector, ariaSnapshot }); } } diff --git a/packages/playwright-client/types/types.d.ts b/packages/playwright-client/types/types.d.ts index f4f8525bb0928..003f59f916c97 100644 --- a/packages/playwright-client/types/types.d.ts +++ b/packages/playwright-client/types/types.d.ts @@ -2099,6 +2099,44 @@ export interface Page { url?: string; }): Promise; + /** + * Captures the aria snapshot of the page. Read more about [aria snapshots](https://playwright.dev/docs/aria-snapshots). + * @param options + */ + ariaSnapshot(options?: { + /** + * When set to `"ai"`, returns a snapshot optimized for AI consumption with element references. Defaults to + * `"default"`. + */ + content?: "ai"|"default"; + + /** + * When specified, limits the depth of the snapshot. + */ + depth?: number; + + /** + * When set to `"incremental"` and + * [`track`](https://playwright.dev/docs/api/class-page#page-aria-snapshot-option-track) is specified, returns an + * incremental snapshot containing only changes since the last call with the same track name. Defaults to `"full"`. + */ + mode?: "incremental"|"full"; + + /** + * Maximum time in milliseconds. Defaults to `0` - no timeout. The default value can be changed via `actionTimeout` + * option in the config, or by using the + * [browserContext.setDefaultTimeout(timeout)](https://playwright.dev/docs/api/class-browsercontext#browser-context-set-default-timeout) + * or [page.setDefaultTimeout(timeout)](https://playwright.dev/docs/api/class-page#page-set-default-timeout) methods. + */ + timeout?: number; + + /** + * When specified, enables incremental snapshots. Subsequent calls with the same track name will track changes between + * calls. + */ + track?: string; + }): Promise; + /** * Brings page to front (activates tab). */ @@ -4515,38 +4553,6 @@ export interface Page { height: number; }): Promise; - /** - * Returns an accessibility snapshot of the page optimized for AI consumption. - * @param options - */ - snapshotForAI(options?: { - /** - * When specified, limits the depth of the snapshot. - */ - depth?: number; - - /** - * When set to `"incremental"` and - * [`track`](https://playwright.dev/docs/api/class-page#page-snapshot-for-ai-option-track) is specified, returns an - * incremental snapshot containing only changes since the last call with the same track name. Defaults to `"full"`. - */ - mode?: string; - - /** - * Maximum time in milliseconds. Defaults to `0` - no timeout. The default value can be changed via `actionTimeout` - * option in the config, or by using the - * [browserContext.setDefaultTimeout(timeout)](https://playwright.dev/docs/api/class-browsercontext#browser-context-set-default-timeout) - * or [page.setDefaultTimeout(timeout)](https://playwright.dev/docs/api/class-page#page-set-default-timeout) methods. - */ - timeout?: number; - - /** - * When specified, enables incremental snapshots. Subsequent calls with the same track name will track changes between - * calls. - */ - track?: string; - }): Promise; - /** * **NOTE** Use locator-based [locator.tap([options])](https://playwright.dev/docs/api/class-locator#locator-tap) instead. Read * more about [locators](https://playwright.dev/docs/locators). @@ -12772,6 +12778,17 @@ export interface Locator { * @param options */ ariaSnapshot(options?: { + /** + * When set to `"ai"`, returns a snapshot optimized for AI consumption with element references. Defaults to + * `"default"`. + */ + content?: "ai"|"default"; + + /** + * When specified, limits the depth of the snapshot. + */ + depth?: number; + /** * Maximum time in milliseconds. Defaults to `0` - no timeout. The default value can be changed via `actionTimeout` * option in the config, or by using the @@ -14680,25 +14697,6 @@ export interface Locator { timeout?: number; }): Promise; - /** - * Returns an accessibility snapshot of the element's subtree optimized for AI consumption. - * @param options - */ - snapshotForAI(options?: { - /** - * When specified, limits the depth of the snapshot. - */ - depth?: number; - - /** - * Maximum time in milliseconds. Defaults to `0` - no timeout. The default value can be changed via `actionTimeout` - * option in the config, or by using the - * [browserContext.setDefaultTimeout(timeout)](https://playwright.dev/docs/api/class-browsercontext#browser-context-set-default-timeout) - * or [page.setDefaultTimeout(timeout)](https://playwright.dev/docs/api/class-page#page-set-default-timeout) methods. - */ - timeout?: number; - }): Promise; - /** * Perform a tap gesture on the element matching the locator. For examples of emulating other gestures by manually * dispatching touch events, see the [emulating legacy touch events](https://playwright.dev/docs/touch-events) page. diff --git a/packages/playwright-core/src/client/locator.ts b/packages/playwright-core/src/client/locator.ts index 4d29ce7dcf8d0..0b59618f8ad84 100644 --- a/packages/playwright-core/src/client/locator.ts +++ b/packages/playwright-core/src/client/locator.ts @@ -312,8 +312,8 @@ export class Locator implements api.Locator { return await this._withElement((h, timeout) => h.screenshot({ ...options, mask, timeout }), { title: 'Screenshot', timeout: options.timeout }); } - async ariaSnapshot(options?: TimeoutOptions): Promise { - const result = await this._frame._channel.ariaSnapshot({ ...options, selector: this._selector, timeout: this._frame._timeout(options) }); + async ariaSnapshot(options: TimeoutOptions & { content?: 'ai' | 'default', depth?: number } = {}): Promise { + const result = await this._frame._page!._channel.ariaSnapshot({ timeout: this._frame._timeout(options), content: options.content, selector: this._selector, depth: options.depth }); return result.snapshot; } @@ -378,10 +378,6 @@ export class Locator implements api.Locator { await this._frame._channel.waitForSelector({ selector: this._selector, strict: true, omitReturnValue: true, ...options, timeout: this._frame._timeout(options) }); } - async snapshotForAI(options: TimeoutOptions & { depth?: number } = {}): Promise { - const result = await this._frame._page!._channel.snapshotForAI({ timeout: this._frame._timeout(options), selector: this._selector, depth: options.depth }); - return result.snapshot; - } async _expect(expression: string, options: FrameExpectParams): Promise<{ matches: boolean, received?: any, log?: string[], timedOut?: boolean, errorMessage?: string }> { return this._frame._expect(expression, { diff --git a/packages/playwright-core/src/client/page.ts b/packages/playwright-core/src/client/page.ts index 05f951ac49c04..8a0b78dabbe36 100644 --- a/packages/playwright-core/src/client/page.ts +++ b/packages/playwright-core/src/client/page.ts @@ -851,8 +851,8 @@ export class Page extends ChannelOwner implements api.Page return result.pdf; } - async snapshotForAI(options: TimeoutOptions & { track?: string, mode?: 'full' | 'incremental', depth?: number } = {}): Promise { - const result = await this._channel.snapshotForAI({ timeout: this._timeoutSettings.timeout(options), track: options.track, mode: options.mode, depth: options.depth }); + async ariaSnapshot(options: TimeoutOptions & { content?: 'ai' | 'default', track?: string, mode?: 'full' | 'incremental', depth?: number } = {}): Promise { + const result = await this._channel.ariaSnapshot({ timeout: this._timeoutSettings.timeout(options), content: options.content, track: options.track, mode: options.mode, depth: options.depth }); return result.snapshot; } diff --git a/packages/playwright-core/src/protocol/validator.ts b/packages/playwright-core/src/protocol/validator.ts index 3870bad4d7f8c..7d9a586288477 100644 --- a/packages/playwright-core/src/protocol/validator.ts +++ b/packages/playwright-core/src/protocol/validator.ts @@ -1494,14 +1494,15 @@ scheme.PageRequestsParams = tOptional(tObject({})); scheme.PageRequestsResult = tObject({ requests: tArray(tChannel(['Request'])), }); -scheme.PageSnapshotForAIParams = tObject({ +scheme.PageAriaSnapshotParams = tObject({ + content: tOptional(tEnum(['ai', 'default'])), track: tOptional(tString), mode: tOptional(tEnum(['full', 'incremental'])), selector: tOptional(tString), depth: tOptional(tInt), timeout: tFloat, }); -scheme.PageSnapshotForAIResult = tObject({ +scheme.PageAriaSnapshotResult = tObject({ snapshot: tString, }); scheme.PageStartJSCoverageParams = tObject({ @@ -1630,13 +1631,6 @@ scheme.FrameAddStyleTagParams = tObject({ scheme.FrameAddStyleTagResult = tObject({ element: tChannel(['ElementHandle']), }); -scheme.FrameAriaSnapshotParams = tObject({ - selector: tString, - timeout: tFloat, -}); -scheme.FrameAriaSnapshotResult = tObject({ - snapshot: tString, -}); scheme.FrameBlurParams = tObject({ selector: tString, strict: tOptional(tBoolean), diff --git a/packages/playwright-core/src/server/dispatchers/frameDispatcher.ts b/packages/playwright-core/src/server/dispatchers/frameDispatcher.ts index dc64740578e87..d0b93af295d6d 100644 --- a/packages/playwright-core/src/server/dispatchers/frameDispatcher.ts +++ b/packages/playwright-core/src/server/dispatchers/frameDispatcher.ts @@ -271,8 +271,4 @@ export class FrameDispatcher extends Dispatcher { - return { snapshot: await this._frame.ariaSnapshot(progress, params.selector) }; - } } diff --git a/packages/playwright-core/src/server/dispatchers/pageDispatcher.ts b/packages/playwright-core/src/server/dispatchers/pageDispatcher.ts index c25ae1798c293..259dcbd8c3116 100644 --- a/packages/playwright-core/src/server/dispatchers/pageDispatcher.ts +++ b/packages/playwright-core/src/server/dispatchers/pageDispatcher.ts @@ -344,8 +344,8 @@ export class PageDispatcher extends Dispatcher RequestDispatcher.from(this.parentScope(), request)) }; } - async snapshotForAI(params: channels.PageSnapshotForAIParams, progress: Progress): Promise { - return await this._page.snapshotForAI(progress, params); + async ariaSnapshot(params: channels.PageAriaSnapshotParams, progress: Progress): Promise { + return await this._page.ariaSnapshot(progress, params); } async bringToFront(params: channels.PageBringToFrontParams, progress: Progress): Promise { diff --git a/packages/playwright-core/src/server/dom.ts b/packages/playwright-core/src/server/dom.ts index de84b4165272e..e82f381d565d0 100644 --- a/packages/playwright-core/src/server/dom.ts +++ b/packages/playwright-core/src/server/dom.ts @@ -763,10 +763,6 @@ export class ElementHandle extends js.JSHandle { return this._page.delegate.getBoundingBox(this); } - async ariaSnapshot(): Promise { - return await this.evaluateInUtility(([injected, element]) => injected.ariaSnapshot(element, { mode: 'expect' }), {}); - } - async screenshot(progress: Progress, options: ScreenshotOptions): Promise { return await this._page.screenshotter.screenshotElement(progress, this, options); } diff --git a/packages/playwright-core/src/server/frames.ts b/packages/playwright-core/src/server/frames.ts index 3f7f2cc5fd88b..ddf782b282843 100644 --- a/packages/playwright-core/src/server/frames.ts +++ b/packages/playwright-core/src/server/frames.ts @@ -1376,10 +1376,6 @@ export class Frame extends SdkObject { return progress.wait(timeout); } - async ariaSnapshot(progress: Progress, selector: string): Promise { - return await this._retryWithProgressIfNotConnected(progress, selector, { strict: true, performActionPreChecks: true }, handle => progress.race(handle.ariaSnapshot())); - } - async expect(progress: Progress, selector: string | undefined, options: FrameExpectParams): Promise { progress.log(`${renderTitleForCall(progress.metadata)}${options.timeoutForLogs ? ` with timeout ${options.timeoutForLogs}ms` : ''}`); const lastIntermediateResult: { received?: any, isSet: boolean, errorMessage?: string } = { isSet: false }; diff --git a/packages/playwright-core/src/server/page.ts b/packages/playwright-core/src/server/page.ts index ff45b15766cad..1c9f022b83cbf 100644 --- a/packages/playwright-core/src/server/page.ts +++ b/packages/playwright-core/src/server/page.ts @@ -886,7 +886,7 @@ export class Page extends SdkObject { await Promise.all(this.frames().map(frame => frame.hideHighlight().catch(() => {}))); } - async snapshotForAI(progress: Progress, options: { track?: string, mode?: 'full' | 'incremental', doNotRenderActive?: boolean, selector?: string, depth?: number } = {}): Promise<{ snapshot: string }> { + async ariaSnapshot(progress: Progress, options: { content?: 'ai' | 'default', track?: string, mode?: 'full' | 'incremental', doNotRenderActive?: boolean, selector?: string, depth?: number } = {}): Promise<{ snapshot: string }> { if (options.selector && options.track) throw new Error('Cannot specify both selector and track options'); @@ -902,7 +902,7 @@ export class Page extends SdkObject { frame = this.mainFrame(); } - const result = await snapshotFrameForAI(progress, frame, { ...options, info }); + const result = await ariaSnapshotForFrame(progress, frame, { ...options, info }); const snapshot = options.mode === 'incremental' && result.incremental !== undefined ? result.incremental.join('\n') : result.full.join('\n'); return { snapshot }; } @@ -1048,7 +1048,7 @@ export class InitScript extends DisposableObject { } } -async function snapshotFrameForAI(progress: Progress, frame: frames.Frame, options: { track?: string, doNotRenderActive?: boolean, info?: SelectorInfo, depth?: number } = {}): Promise<{ full: string[], incremental?: string[] }> { +async function ariaSnapshotForFrame(progress: Progress, frame: frames.Frame, options: { content?: 'ai' | 'default', track?: string, doNotRenderActive?: boolean, info?: SelectorInfo, depth?: number } = {}): Promise<{ full: string[], incremental?: string[] }> { // Only await the topmost navigations, inner frames will be empty when racing. const snapshot = await frame.retryWithProgressAndTimeouts(progress, [1000, 2000, 4000, 8000], async continuePolling => { try { @@ -1059,13 +1059,14 @@ async function snapshotFrameForAI(progress: Progress, frame: frames.Frame, optio const element = injected.querySelector(options.info.parsed, injected.document, options.info.strict); if (!element) return false; - return injected.incrementalAriaSnapshot(element, { mode: 'ai', ...options }); + return injected.incrementalAriaSnapshot(element, options); } const node = injected.document.body; if (!node) return true; - return injected.incrementalAriaSnapshot(node, { mode: 'ai', ...options }); + return injected.incrementalAriaSnapshot(node, options); }, { + content: options.content ?? 'default', refPrefix: frame.seq ? 'f' + frame.seq : '', track: options.track, doNotRenderActive: options.doNotRenderActive, @@ -1084,7 +1085,7 @@ async function snapshotFrameForAI(progress: Progress, frame: frames.Frame, optio } }); - const childSnapshotPromises = snapshot.iframeRefs.map(ref => snapshotFrameRefForAI(progress, frame, ref, options)); + const childSnapshotPromises = snapshot.iframeRefs.map(ref => ariaSnapshotFrameRef(progress, frame, ref, options)); const childSnapshots = await Promise.all(childSnapshotPromises); const full = []; @@ -1118,14 +1119,14 @@ async function snapshotFrameForAI(progress: Progress, frame: frames.Frame, optio return { full, incremental }; } -async function snapshotFrameRefForAI(progress: Progress, parentFrame: frames.Frame, frameRef: string, options: { track?: string, mode?: 'full' | 'incremental' }): Promise<{ full: string[], incremental?: string[] }> { +async function ariaSnapshotFrameRef(progress: Progress, parentFrame: frames.Frame, frameRef: string, options: { content?: 'ai' | 'default', track?: string, doNotRenderActive?: boolean, mode?: 'full' | 'incremental' }): Promise<{ full: string[], incremental?: string[] }> { const frameSelector = `aria-ref=${frameRef} >> internal:control=enter-frame`; const frameBodySelector = `${frameSelector} >> body`; const child = await progress.race(parentFrame.selectors.resolveFrameForSelector(frameBodySelector, { strict: true })); if (!child) return { full: [] }; try { - return await snapshotFrameForAI(progress, child.frame, { ...options, info: undefined }); + return await ariaSnapshotForFrame(progress, child.frame, { ...options, info: undefined }); } catch { return { full: [] }; } diff --git a/packages/playwright-core/src/tools/backend/tab.ts b/packages/playwright-core/src/tools/backend/tab.ts index 53d10cc633f4d..aba71d61aa4aa 100644 --- a/packages/playwright-core/src/tools/backend/tab.ts +++ b/packages/playwright-core/src/tools/backend/tab.ts @@ -379,8 +379,8 @@ export class Tab extends EventEmitter { let tabSnapshot: TabSnapshot | undefined; const modalStates = await this._raceAgainstModalStates(async () => { const ariaSnapshot = selector - ? await this.page.locator(selector).snapshotForAI({ depth }) - : await this.page.snapshotForAI({ track: 'response', mode: this._needsFullSnapshot ? 'full' : mode, depth }); + ? await this.page.locator(selector).ariaSnapshot({ content: 'ai', depth }) + : await this.page.ariaSnapshot({ content: 'ai', track: 'response', mode: this._needsFullSnapshot ? 'full' : mode, depth }); tabSnapshot = { ariaSnapshot, modalStates: [], diff --git a/packages/playwright-core/src/utils/isomorphic/protocolMetainfo.ts b/packages/playwright-core/src/utils/isomorphic/protocolMetainfo.ts index 951b109102ba8..a09db6bd60a0a 100644 --- a/packages/playwright-core/src/utils/isomorphic/protocolMetainfo.ts +++ b/packages/playwright-core/src/utils/isomorphic/protocolMetainfo.ts @@ -139,7 +139,7 @@ export const methodMetainfo = new Map; + /** + * Captures the aria snapshot of the page. Read more about [aria snapshots](https://playwright.dev/docs/aria-snapshots). + * @param options + */ + ariaSnapshot(options?: { + /** + * When set to `"ai"`, returns a snapshot optimized for AI consumption with element references. Defaults to + * `"default"`. + */ + content?: "ai"|"default"; + + /** + * When specified, limits the depth of the snapshot. + */ + depth?: number; + + /** + * When set to `"incremental"` and + * [`track`](https://playwright.dev/docs/api/class-page#page-aria-snapshot-option-track) is specified, returns an + * incremental snapshot containing only changes since the last call with the same track name. Defaults to `"full"`. + */ + mode?: "incremental"|"full"; + + /** + * Maximum time in milliseconds. Defaults to `0` - no timeout. The default value can be changed via `actionTimeout` + * option in the config, or by using the + * [browserContext.setDefaultTimeout(timeout)](https://playwright.dev/docs/api/class-browsercontext#browser-context-set-default-timeout) + * or [page.setDefaultTimeout(timeout)](https://playwright.dev/docs/api/class-page#page-set-default-timeout) methods. + */ + timeout?: number; + + /** + * When specified, enables incremental snapshots. Subsequent calls with the same track name will track changes between + * calls. + */ + track?: string; + }): Promise; + /** * Brings page to front (activates tab). */ @@ -4515,38 +4553,6 @@ export interface Page { height: number; }): Promise; - /** - * Returns an accessibility snapshot of the page optimized for AI consumption. - * @param options - */ - snapshotForAI(options?: { - /** - * When specified, limits the depth of the snapshot. - */ - depth?: number; - - /** - * When set to `"incremental"` and - * [`track`](https://playwright.dev/docs/api/class-page#page-snapshot-for-ai-option-track) is specified, returns an - * incremental snapshot containing only changes since the last call with the same track name. Defaults to `"full"`. - */ - mode?: string; - - /** - * Maximum time in milliseconds. Defaults to `0` - no timeout. The default value can be changed via `actionTimeout` - * option in the config, or by using the - * [browserContext.setDefaultTimeout(timeout)](https://playwright.dev/docs/api/class-browsercontext#browser-context-set-default-timeout) - * or [page.setDefaultTimeout(timeout)](https://playwright.dev/docs/api/class-page#page-set-default-timeout) methods. - */ - timeout?: number; - - /** - * When specified, enables incremental snapshots. Subsequent calls with the same track name will track changes between - * calls. - */ - track?: string; - }): Promise; - /** * **NOTE** Use locator-based [locator.tap([options])](https://playwright.dev/docs/api/class-locator#locator-tap) instead. Read * more about [locators](https://playwright.dev/docs/locators). @@ -12772,6 +12778,17 @@ export interface Locator { * @param options */ ariaSnapshot(options?: { + /** + * When set to `"ai"`, returns a snapshot optimized for AI consumption with element references. Defaults to + * `"default"`. + */ + content?: "ai"|"default"; + + /** + * When specified, limits the depth of the snapshot. + */ + depth?: number; + /** * Maximum time in milliseconds. Defaults to `0` - no timeout. The default value can be changed via `actionTimeout` * option in the config, or by using the @@ -14680,25 +14697,6 @@ export interface Locator { timeout?: number; }): Promise; - /** - * Returns an accessibility snapshot of the element's subtree optimized for AI consumption. - * @param options - */ - snapshotForAI(options?: { - /** - * When specified, limits the depth of the snapshot. - */ - depth?: number; - - /** - * Maximum time in milliseconds. Defaults to `0` - no timeout. The default value can be changed via `actionTimeout` - * option in the config, or by using the - * [browserContext.setDefaultTimeout(timeout)](https://playwright.dev/docs/api/class-browsercontext#browser-context-set-default-timeout) - * or [page.setDefaultTimeout(timeout)](https://playwright.dev/docs/api/class-page#page-set-default-timeout) methods. - */ - timeout?: number; - }): Promise; - /** * Perform a tap gesture on the element matching the locator. For examples of emulating other gestures by manually * dispatching touch events, see the [emulating legacy touch events](https://playwright.dev/docs/touch-events) page. diff --git a/packages/playwright/src/index.ts b/packages/playwright/src/index.ts index 2ce170393a4b9..510fefea65d0d 100644 --- a/packages/playwright/src/index.ts +++ b/packages/playwright/src/index.ts @@ -709,7 +709,7 @@ class ArtifactsRecorder { try { // TODO: maybe capture snapshot when the error is created, so it's from the right page and right time await page._wrapApiCall(async () => { - this._pageSnapshot = await page.snapshotForAI({ timeout: 5000 }); + this._pageSnapshot = await page.ariaSnapshot({ content: 'ai', timeout: 5000 }); }, { internal: true }); } catch {} } diff --git a/packages/playwright/src/mcp/test/browserBackend.ts b/packages/playwright/src/mcp/test/browserBackend.ts index 504891b1033bf..4f3299724acd4 100644 --- a/packages/playwright/src/mcp/test/browserBackend.ts +++ b/packages/playwright/src/mcp/test/browserBackend.ts @@ -98,7 +98,7 @@ async function generatePausedMessage(testInfo: TestInfoImpl, context: playwright lines.push( `- Page Snapshot:`, '```yaml', - await page.snapshotForAI(), + await page.ariaSnapshot({ content: 'ai' }), '```', ); } diff --git a/packages/protocol/src/channels.d.ts b/packages/protocol/src/channels.d.ts index 8512dcc0ddf77..dcd9aefa636b0 100644 --- a/packages/protocol/src/channels.d.ts +++ b/packages/protocol/src/channels.d.ts @@ -2140,7 +2140,7 @@ export interface PageChannel extends PageEventTarget, EventTargetChannel { pageErrors(params: PagePageErrorsParams, progress?: Progress): Promise; pdf(params: PagePdfParams, progress?: Progress): Promise; requests(params?: PageRequestsParams, progress?: Progress): Promise; - snapshotForAI(params: PageSnapshotForAIParams, progress?: Progress): Promise; + ariaSnapshot(params: PageAriaSnapshotParams, progress?: Progress): Promise; startJSCoverage(params: PageStartJSCoverageParams, progress?: Progress): Promise; stopJSCoverage(params?: PageStopJSCoverageParams, progress?: Progress): Promise; startCSSCoverage(params: PageStartCSSCoverageParams, progress?: Progress): Promise; @@ -2610,20 +2610,22 @@ export type PageRequestsOptions = {}; export type PageRequestsResult = { requests: RequestChannel[], }; -export type PageSnapshotForAIParams = { +export type PageAriaSnapshotParams = { + content?: 'ai' | 'default', track?: string, mode?: 'full' | 'incremental', selector?: string, depth?: number, timeout: number, }; -export type PageSnapshotForAIOptions = { +export type PageAriaSnapshotOptions = { + content?: 'ai' | 'default', track?: string, mode?: 'full' | 'incremental', selector?: string, depth?: number, }; -export type PageSnapshotForAIResult = { +export type PageAriaSnapshotResult = { snapshot: string, }; export type PageStartJSCoverageParams = { @@ -2767,7 +2769,6 @@ export interface FrameChannel extends FrameEventTarget, Channel { evalOnSelectorAll(params: FrameEvalOnSelectorAllParams, progress?: Progress): Promise; addScriptTag(params: FrameAddScriptTagParams, progress?: Progress): Promise; addStyleTag(params: FrameAddStyleTagParams, progress?: Progress): Promise; - ariaSnapshot(params: FrameAriaSnapshotParams, progress?: Progress): Promise; blur(params: FrameBlurParams, progress?: Progress): Promise; check(params: FrameCheckParams, progress?: Progress): Promise; click(params: FrameClickParams, progress?: Progress): Promise; @@ -2873,16 +2874,6 @@ export type FrameAddStyleTagOptions = { export type FrameAddStyleTagResult = { element: ElementHandleChannel, }; -export type FrameAriaSnapshotParams = { - selector: string, - timeout: number, -}; -export type FrameAriaSnapshotOptions = { - -}; -export type FrameAriaSnapshotResult = { - snapshot: string, -}; export type FrameBlurParams = { selector: string, strict?: boolean, diff --git a/packages/protocol/src/protocol.yml b/packages/protocol/src/protocol.yml index 4c0ab686bce36..96c6258bd3957 100644 --- a/packages/protocol/src/protocol.yml +++ b/packages/protocol/src/protocol.yml @@ -2008,9 +2008,15 @@ Page: type: array items: Request - snapshotForAI: - internal: true + ariaSnapshot: + title: Aria snapshot + group: getter parameters: + content: + type: enum? + literals: + - ai + - default # When track is present, an incremental snapshot is returned when possible. track: string? mode: @@ -2273,17 +2279,6 @@ Frame: snapshot: true pausesBeforeAction: true - ariaSnapshot: - title: Aria snapshot - parameters: - selector: string - timeout: float - returns: - snapshot: string - flags: - snapshot: true - pausesBeforeAction: true - blur: title: Blur parameters: diff --git a/tests/page/page-aria-snapshot-ai.spec.ts b/tests/page/page-aria-snapshot-ai.spec.ts index 55dc17bf316e3..b30d0e5b80a0e 100644 --- a/tests/page/page-aria-snapshot-ai.spec.ts +++ b/tests/page/page-aria-snapshot-ai.spec.ts @@ -19,8 +19,8 @@ import { test as it, expect } from './pageTest'; import { unshift } from '../config/utils'; import type { Page } from 'playwright-core'; -async function snapshotForAI(page: Page, options?: Parameters[0]): Promise { - return await page.snapshotForAI(options); +async function snapshotForAI(page: Page, options?: Omit[0], 'content'>): Promise { + return await page.ariaSnapshot({ ...options, content: 'ai' }); } it('should generate refs', async ({ page }) => { @@ -799,7 +799,7 @@ it('should limit depth', async ({ page }) => { - listitem [ref=e10]: item3 `); - const snapshot4 = await page.locator('#target').snapshotForAI({ depth: 1 }); + const snapshot4 = await page.locator('#target').ariaSnapshot({ content: 'ai', depth: 1 }); expect(snapshot4).toContainYaml(` - list [ref=e6]: - listitem [ref=e7]: item2 From 46d374d4e54314823da296aab57de739c4c83e15 Mon Sep 17 00:00:00 2001 From: Dmitry Gozman Date: Thu, 19 Mar 2026 13:18:22 +0000 Subject: [PATCH 2/2] content -> format --- docs/src/api/class-locator.md | 4 ++-- docs/src/api/class-page.md | 4 ++-- packages/injected/src/ariaSnapshot.ts | 16 +++++++-------- packages/injected/src/consoleApi.ts | 2 +- packages/injected/src/injectedScript.ts | 4 ++-- packages/injected/src/recorder/recorder.ts | 6 +++--- packages/playwright-client/types/types.d.ts | 20 +++++++++---------- .../playwright-core/src/client/locator.ts | 4 ++-- packages/playwright-core/src/client/page.ts | 4 ++-- .../playwright-core/src/protocol/validator.ts | 2 +- packages/playwright-core/src/server/page.ts | 8 ++++---- .../playwright-core/src/tools/backend/tab.ts | 4 ++-- packages/playwright-core/types/types.d.ts | 20 +++++++++---------- packages/playwright/src/index.ts | 2 +- .../playwright/src/mcp/test/browserBackend.ts | 2 +- packages/protocol/src/channels.d.ts | 4 ++-- packages/protocol/src/protocol.yml | 2 +- tests/page/page-aria-snapshot-ai.spec.ts | 6 +++--- 18 files changed, 57 insertions(+), 57 deletions(-) diff --git a/docs/src/api/class-locator.md b/docs/src/api/class-locator.md index d228df961023a..da7139618d229 100644 --- a/docs/src/api/class-locator.md +++ b/docs/src/api/class-locator.md @@ -206,9 +206,9 @@ Below is the HTML markup and the respective ARIA snapshot: - link "About" ``` -### option: Locator.ariaSnapshot.content +### option: Locator.ariaSnapshot.format * since: v1.59 -- `content` <[AriaSnapshotContent]<"ai"|"default">> +- `format` <[AriaSnapshotFormat]<"ai"|"default">> When set to `"ai"`, returns a snapshot optimized for AI consumption with element references. Defaults to `"default"`. diff --git a/docs/src/api/class-page.md b/docs/src/api/class-page.md index 149c6508c10ee..d5194a48f9a9c 100644 --- a/docs/src/api/class-page.md +++ b/docs/src/api/class-page.md @@ -4217,9 +4217,9 @@ Page height in pixels. Captures the aria snapshot of the page. Read more about [aria snapshots](../aria-snapshots.md). -### option: Page.ariaSnapshot.content +### option: Page.ariaSnapshot.format * since: v1.59 -- `content` <[AriaSnapshotContent]<"ai"|"default">> +- `format` <[AriaSnapshotFormat]<"ai"|"default">> When set to `"ai"`, returns a snapshot optimized for AI consumption with element references. Defaults to `"default"`. diff --git a/packages/injected/src/ariaSnapshot.ts b/packages/injected/src/ariaSnapshot.ts index adec82ac722a5..4bb00a1705ee6 100644 --- a/packages/injected/src/ariaSnapshot.ts +++ b/packages/injected/src/ariaSnapshot.ts @@ -37,7 +37,7 @@ type AriaRef = { let lastRef = 0; export type AriaTreeOptions = { - content: 'ai' | 'default' | 'codegen' | 'autoexpect'; + format: 'ai' | 'default' | 'codegen' | 'autoexpect'; refPrefix?: string; doNotRenderActive?: boolean; depth?: number; @@ -54,7 +54,7 @@ type InternalOptions = { }; function toInternalOptions(options: AriaTreeOptions): InternalOptions { - if (options.content === 'ai') { + if (options.format === 'ai') { // For AI consumption. return { visibility: 'ariaOrVisible', @@ -65,11 +65,11 @@ function toInternalOptions(options: AriaTreeOptions): InternalOptions { renderCursorPointer: true, }; } - if (options.content === 'autoexpect') { + if (options.format === 'autoexpect') { // To auto-generate assertions on visible elements. return { visibility: 'ariaAndVisible', refs: 'none' }; } - if (options.content === 'codegen') { + if (options.format === 'codegen') { // To generate aria assertion with regex heurisitcs. return { visibility: 'aria', refs: 'none', renderStringsAsRegex: true }; } @@ -387,19 +387,19 @@ export type MatcherReceived = { }; export function matchesExpectAriaTemplate(rootElement: Element, template: aria.AriaTemplateNode): { matches: aria.AriaNode[], received: MatcherReceived } { - const snapshot = generateAriaTree(rootElement, { content: 'default' }); + const snapshot = generateAriaTree(rootElement, { format: 'default' }); const matches = matchesNodeDeep(snapshot.root, template, false, false); return { matches, received: { - raw: renderAriaTree(snapshot, { content: 'default' }), - regex: renderAriaTree(snapshot, { content: 'codegen' }), + raw: renderAriaTree(snapshot, { format: 'default' }), + regex: renderAriaTree(snapshot, { format: 'codegen' }), } }; } export function getAllElementsMatchingExpectAriaTemplate(rootElement: Element, template: aria.AriaTemplateNode): Element[] { - const root = generateAriaTree(rootElement, { content: 'default' }).root; + const root = generateAriaTree(rootElement, { format: 'default' }).root; const matches = matchesNodeDeep(root, template, true, false); return matches.map(n => ariaNodeElement(n)); } diff --git a/packages/injected/src/consoleApi.ts b/packages/injected/src/consoleApi.ts index fb277bdec6772..38967e3bda11e 100644 --- a/packages/injected/src/consoleApi.ts +++ b/packages/injected/src/consoleApi.ts @@ -93,7 +93,7 @@ export class ConsoleAPI { selector: (element: Element) => this._selector(element), generateLocator: (element: Element, language?: Language) => this._generateLocator(element, language), ariaSnapshot: (element?: Element, options?: AriaTreeOptions) => { - return this._injectedScript.ariaSnapshot(element || this._injectedScript.document.body, options || { content: 'default' }); + return this._injectedScript.ariaSnapshot(element || this._injectedScript.document.body, options || { format: 'default' }); }, resume: () => this._resume(), ...new Locator(this._injectedScript, ''), diff --git a/packages/injected/src/injectedScript.ts b/packages/injected/src/injectedScript.ts index 927b35a5eca2e..89dbad7bdc76e 100644 --- a/packages/injected/src/injectedScript.ts +++ b/packages/injected/src/injectedScript.ts @@ -323,8 +323,8 @@ export class InjectedScript { } ariaSnapshotForRecorder(): { ariaSnapshot: string, refs: Map } { - const tree = generateAriaTree(this.document.body, { content: 'ai' }); - const ariaSnapshot = renderAriaTree(tree, { content: 'ai' }); + const tree = generateAriaTree(this.document.body, { format: 'ai' }); + const ariaSnapshot = renderAriaTree(tree, { format: 'ai' }); return { ariaSnapshot, refs: tree.refs }; } diff --git a/packages/injected/src/recorder/recorder.ts b/packages/injected/src/recorder/recorder.ts index c38e1775a9e5f..724cb2511e44c 100644 --- a/packages/injected/src/recorder/recorder.ts +++ b/packages/injected/src/recorder/recorder.ts @@ -1064,7 +1064,7 @@ class TextAssertionTool implements RecorderTool { name: 'assertSnapshot', selector: this._hoverHighlight.selector, signals: [], - ariaSnapshot: this._recorder.injectedScript.ariaSnapshot(target, { content: 'codegen' }), + ariaSnapshot: this._recorder.injectedScript.ariaSnapshot(target, { format: 'codegen' }), }; } else { const generated = this._recorder.injectedScript.generateSelector(target, { testIdAttributeName: this._recorder.state.testIdAttributeName, forTextExpect: true }); @@ -1698,7 +1698,7 @@ export class Recorder { private _captureAutoExpectSnapshot() { const documentElement = this.injectedScript.document.documentElement; - return documentElement ? this.injectedScript.utils.generateAriaTree(documentElement, { content: 'autoexpect' }) : undefined; + return documentElement ? this.injectedScript.utils.generateAriaTree(documentElement, { format: 'autoexpect' }) : undefined; } async performAction(action: actions.PerformOnRecordAction) { @@ -1723,7 +1723,7 @@ export class Recorder { } elementPicked(selector: string, model: HighlightModel) { - const ariaSnapshot = this.injectedScript.ariaSnapshot(model.elements[0], { content: 'default' }); + const ariaSnapshot = this.injectedScript.ariaSnapshot(model.elements[0], { format: 'default' }); void this._delegate.elementPicked?.({ selector, ariaSnapshot }); } } diff --git a/packages/playwright-client/types/types.d.ts b/packages/playwright-client/types/types.d.ts index 003f59f916c97..6a0316f59b83b 100644 --- a/packages/playwright-client/types/types.d.ts +++ b/packages/playwright-client/types/types.d.ts @@ -2105,15 +2105,15 @@ export interface Page { */ ariaSnapshot(options?: { /** - * When set to `"ai"`, returns a snapshot optimized for AI consumption with element references. Defaults to - * `"default"`. + * When specified, limits the depth of the snapshot. */ - content?: "ai"|"default"; + depth?: number; /** - * When specified, limits the depth of the snapshot. + * When set to `"ai"`, returns a snapshot optimized for AI consumption with element references. Defaults to + * `"default"`. */ - depth?: number; + format?: "ai"|"default"; /** * When set to `"incremental"` and @@ -12779,15 +12779,15 @@ export interface Locator { */ ariaSnapshot(options?: { /** - * When set to `"ai"`, returns a snapshot optimized for AI consumption with element references. Defaults to - * `"default"`. + * When specified, limits the depth of the snapshot. */ - content?: "ai"|"default"; + depth?: number; /** - * When specified, limits the depth of the snapshot. + * When set to `"ai"`, returns a snapshot optimized for AI consumption with element references. Defaults to + * `"default"`. */ - depth?: number; + format?: "ai"|"default"; /** * Maximum time in milliseconds. Defaults to `0` - no timeout. The default value can be changed via `actionTimeout` diff --git a/packages/playwright-core/src/client/locator.ts b/packages/playwright-core/src/client/locator.ts index 0b59618f8ad84..29f2f66e0962e 100644 --- a/packages/playwright-core/src/client/locator.ts +++ b/packages/playwright-core/src/client/locator.ts @@ -312,8 +312,8 @@ export class Locator implements api.Locator { return await this._withElement((h, timeout) => h.screenshot({ ...options, mask, timeout }), { title: 'Screenshot', timeout: options.timeout }); } - async ariaSnapshot(options: TimeoutOptions & { content?: 'ai' | 'default', depth?: number } = {}): Promise { - const result = await this._frame._page!._channel.ariaSnapshot({ timeout: this._frame._timeout(options), content: options.content, selector: this._selector, depth: options.depth }); + async ariaSnapshot(options: TimeoutOptions & { format?: 'ai' | 'default', depth?: number } = {}): Promise { + const result = await this._frame._page!._channel.ariaSnapshot({ timeout: this._frame._timeout(options), format: options.format, selector: this._selector, depth: options.depth }); return result.snapshot; } diff --git a/packages/playwright-core/src/client/page.ts b/packages/playwright-core/src/client/page.ts index 8a0b78dabbe36..ca36eb25ec07d 100644 --- a/packages/playwright-core/src/client/page.ts +++ b/packages/playwright-core/src/client/page.ts @@ -851,8 +851,8 @@ export class Page extends ChannelOwner implements api.Page return result.pdf; } - async ariaSnapshot(options: TimeoutOptions & { content?: 'ai' | 'default', track?: string, mode?: 'full' | 'incremental', depth?: number } = {}): Promise { - const result = await this._channel.ariaSnapshot({ timeout: this._timeoutSettings.timeout(options), content: options.content, track: options.track, mode: options.mode, depth: options.depth }); + async ariaSnapshot(options: TimeoutOptions & { format?: 'ai' | 'default', track?: string, mode?: 'full' | 'incremental', depth?: number } = {}): Promise { + const result = await this._channel.ariaSnapshot({ timeout: this._timeoutSettings.timeout(options), format: options.format, track: options.track, mode: options.mode, depth: options.depth }); return result.snapshot; } diff --git a/packages/playwright-core/src/protocol/validator.ts b/packages/playwright-core/src/protocol/validator.ts index 7d9a586288477..105b242a37ab2 100644 --- a/packages/playwright-core/src/protocol/validator.ts +++ b/packages/playwright-core/src/protocol/validator.ts @@ -1495,7 +1495,7 @@ scheme.PageRequestsResult = tObject({ requests: tArray(tChannel(['Request'])), }); scheme.PageAriaSnapshotParams = tObject({ - content: tOptional(tEnum(['ai', 'default'])), + format: tOptional(tEnum(['ai', 'default'])), track: tOptional(tString), mode: tOptional(tEnum(['full', 'incremental'])), selector: tOptional(tString), diff --git a/packages/playwright-core/src/server/page.ts b/packages/playwright-core/src/server/page.ts index 1c9f022b83cbf..0bcbb3e9b8c65 100644 --- a/packages/playwright-core/src/server/page.ts +++ b/packages/playwright-core/src/server/page.ts @@ -886,7 +886,7 @@ export class Page extends SdkObject { await Promise.all(this.frames().map(frame => frame.hideHighlight().catch(() => {}))); } - async ariaSnapshot(progress: Progress, options: { content?: 'ai' | 'default', track?: string, mode?: 'full' | 'incremental', doNotRenderActive?: boolean, selector?: string, depth?: number } = {}): Promise<{ snapshot: string }> { + async ariaSnapshot(progress: Progress, options: { format?: 'ai' | 'default', track?: string, mode?: 'full' | 'incremental', doNotRenderActive?: boolean, selector?: string, depth?: number } = {}): Promise<{ snapshot: string }> { if (options.selector && options.track) throw new Error('Cannot specify both selector and track options'); @@ -1048,7 +1048,7 @@ export class InitScript extends DisposableObject { } } -async function ariaSnapshotForFrame(progress: Progress, frame: frames.Frame, options: { content?: 'ai' | 'default', track?: string, doNotRenderActive?: boolean, info?: SelectorInfo, depth?: number } = {}): Promise<{ full: string[], incremental?: string[] }> { +async function ariaSnapshotForFrame(progress: Progress, frame: frames.Frame, options: { format?: 'ai' | 'default', track?: string, doNotRenderActive?: boolean, info?: SelectorInfo, depth?: number } = {}): Promise<{ full: string[], incremental?: string[] }> { // Only await the topmost navigations, inner frames will be empty when racing. const snapshot = await frame.retryWithProgressAndTimeouts(progress, [1000, 2000, 4000, 8000], async continuePolling => { try { @@ -1066,7 +1066,7 @@ async function ariaSnapshotForFrame(progress: Progress, frame: frames.Frame, opt return true; return injected.incrementalAriaSnapshot(node, options); }, { - content: options.content ?? 'default', + format: options.format ?? 'default', refPrefix: frame.seq ? 'f' + frame.seq : '', track: options.track, doNotRenderActive: options.doNotRenderActive, @@ -1119,7 +1119,7 @@ async function ariaSnapshotForFrame(progress: Progress, frame: frames.Frame, opt return { full, incremental }; } -async function ariaSnapshotFrameRef(progress: Progress, parentFrame: frames.Frame, frameRef: string, options: { content?: 'ai' | 'default', track?: string, doNotRenderActive?: boolean, mode?: 'full' | 'incremental' }): Promise<{ full: string[], incremental?: string[] }> { +async function ariaSnapshotFrameRef(progress: Progress, parentFrame: frames.Frame, frameRef: string, options: { format?: 'ai' | 'default', track?: string, doNotRenderActive?: boolean, mode?: 'full' | 'incremental' }): Promise<{ full: string[], incremental?: string[] }> { const frameSelector = `aria-ref=${frameRef} >> internal:control=enter-frame`; const frameBodySelector = `${frameSelector} >> body`; const child = await progress.race(parentFrame.selectors.resolveFrameForSelector(frameBodySelector, { strict: true })); diff --git a/packages/playwright-core/src/tools/backend/tab.ts b/packages/playwright-core/src/tools/backend/tab.ts index aba71d61aa4aa..8a37f89e1e176 100644 --- a/packages/playwright-core/src/tools/backend/tab.ts +++ b/packages/playwright-core/src/tools/backend/tab.ts @@ -379,8 +379,8 @@ export class Tab extends EventEmitter { let tabSnapshot: TabSnapshot | undefined; const modalStates = await this._raceAgainstModalStates(async () => { const ariaSnapshot = selector - ? await this.page.locator(selector).ariaSnapshot({ content: 'ai', depth }) - : await this.page.ariaSnapshot({ content: 'ai', track: 'response', mode: this._needsFullSnapshot ? 'full' : mode, depth }); + ? await this.page.locator(selector).ariaSnapshot({ format: 'ai', depth }) + : await this.page.ariaSnapshot({ format: 'ai', track: 'response', mode: this._needsFullSnapshot ? 'full' : mode, depth }); tabSnapshot = { ariaSnapshot, modalStates: [], diff --git a/packages/playwright-core/types/types.d.ts b/packages/playwright-core/types/types.d.ts index 003f59f916c97..6a0316f59b83b 100644 --- a/packages/playwright-core/types/types.d.ts +++ b/packages/playwright-core/types/types.d.ts @@ -2105,15 +2105,15 @@ export interface Page { */ ariaSnapshot(options?: { /** - * When set to `"ai"`, returns a snapshot optimized for AI consumption with element references. Defaults to - * `"default"`. + * When specified, limits the depth of the snapshot. */ - content?: "ai"|"default"; + depth?: number; /** - * When specified, limits the depth of the snapshot. + * When set to `"ai"`, returns a snapshot optimized for AI consumption with element references. Defaults to + * `"default"`. */ - depth?: number; + format?: "ai"|"default"; /** * When set to `"incremental"` and @@ -12779,15 +12779,15 @@ export interface Locator { */ ariaSnapshot(options?: { /** - * When set to `"ai"`, returns a snapshot optimized for AI consumption with element references. Defaults to - * `"default"`. + * When specified, limits the depth of the snapshot. */ - content?: "ai"|"default"; + depth?: number; /** - * When specified, limits the depth of the snapshot. + * When set to `"ai"`, returns a snapshot optimized for AI consumption with element references. Defaults to + * `"default"`. */ - depth?: number; + format?: "ai"|"default"; /** * Maximum time in milliseconds. Defaults to `0` - no timeout. The default value can be changed via `actionTimeout` diff --git a/packages/playwright/src/index.ts b/packages/playwright/src/index.ts index 510fefea65d0d..62b815dce7ed3 100644 --- a/packages/playwright/src/index.ts +++ b/packages/playwright/src/index.ts @@ -709,7 +709,7 @@ class ArtifactsRecorder { try { // TODO: maybe capture snapshot when the error is created, so it's from the right page and right time await page._wrapApiCall(async () => { - this._pageSnapshot = await page.ariaSnapshot({ content: 'ai', timeout: 5000 }); + this._pageSnapshot = await page.ariaSnapshot({ format: 'ai', timeout: 5000 }); }, { internal: true }); } catch {} } diff --git a/packages/playwright/src/mcp/test/browserBackend.ts b/packages/playwright/src/mcp/test/browserBackend.ts index 4f3299724acd4..3c30678dcbb29 100644 --- a/packages/playwright/src/mcp/test/browserBackend.ts +++ b/packages/playwright/src/mcp/test/browserBackend.ts @@ -98,7 +98,7 @@ async function generatePausedMessage(testInfo: TestInfoImpl, context: playwright lines.push( `- Page Snapshot:`, '```yaml', - await page.ariaSnapshot({ content: 'ai' }), + await page.ariaSnapshot({ format: 'ai' }), '```', ); } diff --git a/packages/protocol/src/channels.d.ts b/packages/protocol/src/channels.d.ts index dcd9aefa636b0..583cebd32c1a3 100644 --- a/packages/protocol/src/channels.d.ts +++ b/packages/protocol/src/channels.d.ts @@ -2611,7 +2611,7 @@ export type PageRequestsResult = { requests: RequestChannel[], }; export type PageAriaSnapshotParams = { - content?: 'ai' | 'default', + format?: 'ai' | 'default', track?: string, mode?: 'full' | 'incremental', selector?: string, @@ -2619,7 +2619,7 @@ export type PageAriaSnapshotParams = { timeout: number, }; export type PageAriaSnapshotOptions = { - content?: 'ai' | 'default', + format?: 'ai' | 'default', track?: string, mode?: 'full' | 'incremental', selector?: string, diff --git a/packages/protocol/src/protocol.yml b/packages/protocol/src/protocol.yml index 96c6258bd3957..ef5c771d38d59 100644 --- a/packages/protocol/src/protocol.yml +++ b/packages/protocol/src/protocol.yml @@ -2012,7 +2012,7 @@ Page: title: Aria snapshot group: getter parameters: - content: + format: type: enum? literals: - ai diff --git a/tests/page/page-aria-snapshot-ai.spec.ts b/tests/page/page-aria-snapshot-ai.spec.ts index b30d0e5b80a0e..093b32e675a94 100644 --- a/tests/page/page-aria-snapshot-ai.spec.ts +++ b/tests/page/page-aria-snapshot-ai.spec.ts @@ -19,8 +19,8 @@ import { test as it, expect } from './pageTest'; import { unshift } from '../config/utils'; import type { Page } from 'playwright-core'; -async function snapshotForAI(page: Page, options?: Omit[0], 'content'>): Promise { - return await page.ariaSnapshot({ ...options, content: 'ai' }); +async function snapshotForAI(page: Page, options?: Omit[0], 'format'>): Promise { + return await page.ariaSnapshot({ ...options, format: 'ai' }); } it('should generate refs', async ({ page }) => { @@ -799,7 +799,7 @@ it('should limit depth', async ({ page }) => { - listitem [ref=e10]: item3 `); - const snapshot4 = await page.locator('#target').ariaSnapshot({ content: 'ai', depth: 1 }); + const snapshot4 = await page.locator('#target').ariaSnapshot({ format: 'ai', depth: 1 }); expect(snapshot4).toContainYaml(` - list [ref=e6]: - listitem [ref=e7]: item2