Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 13 additions & 17 deletions docs/src/api/class-locator.md
Original file line number Diff line number Diff line change
Expand Up @@ -206,12 +206,25 @@ Below is the HTML markup and the respective ARIA snapshot:
- link "About"
```

### option: Locator.ariaSnapshot.format
* since: v1.59
- `format` <[AriaSnapshotFormat]<"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

Expand Down Expand Up @@ -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
Expand Down
24 changes: 15 additions & 9 deletions docs/src/api/class-page.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.format
* since: v1.59
- `format` <[AriaSnapshotFormat]<"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]>

Expand Down
16 changes: 8 additions & 8 deletions packages/injected/src/ariaSnapshot.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ type AriaRef = {
let lastRef = 0;

export type AriaTreeOptions = {
mode: 'ai' | 'expect' | 'codegen' | 'autoexpect';
format: 'ai' | 'default' | 'codegen' | 'autoexpect';
refPrefix?: string;
doNotRenderActive?: boolean;
depth?: number;
Expand All @@ -54,7 +54,7 @@ type InternalOptions = {
};

function toInternalOptions(options: AriaTreeOptions): InternalOptions {
if (options.mode === 'ai') {
if (options.format === 'ai') {
// For AI consumption.
return {
visibility: 'ariaOrVisible',
Expand All @@ -65,11 +65,11 @@ function toInternalOptions(options: AriaTreeOptions): InternalOptions {
renderCursorPointer: true,
};
}
if (options.mode === 'autoexpect') {
if (options.format === 'autoexpect') {
// To auto-generate assertions on visible elements.
return { visibility: 'ariaAndVisible', refs: 'none' };
}
if (options.mode === 'codegen') {
if (options.format === 'codegen') {
// To generate aria assertion with regex heurisitcs.
return { visibility: 'aria', refs: 'none', renderStringsAsRegex: true };
}
Expand Down Expand Up @@ -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, { format: '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, { format: 'default' }),
regex: renderAriaTree(snapshot, { format: 'codegen' }),
}
};
}

export function getAllElementsMatchingExpectAriaTemplate(rootElement: Element, template: aria.AriaTemplateNode): Element[] {
const root = generateAriaTree(rootElement, { mode: 'expect' }).root;
const root = generateAriaTree(rootElement, { format: 'default' }).root;
const matches = matchesNodeDeep(root, template, true, false);
return matches.map(n => ariaNodeElement(n));
}
Expand Down
2 changes: 1 addition & 1 deletion packages/injected/src/consoleApi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 || { format: 'default' });
},
resume: () => this._resume(),
...new Locator(this._injectedScript, ''),
Expand Down
4 changes: 2 additions & 2 deletions packages/injected/src/injectedScript.ts
Original file line number Diff line number Diff line change
Expand Up @@ -323,8 +323,8 @@ export class InjectedScript {
}

ariaSnapshotForRecorder(): { ariaSnapshot: string, refs: Map<Element, string> } {
const tree = generateAriaTree(this.document.body, { mode: 'ai' });
const ariaSnapshot = renderAriaTree(tree, { mode: 'ai' });
const tree = generateAriaTree(this.document.body, { format: 'ai' });
const ariaSnapshot = renderAriaTree(tree, { format: 'ai' });
return { ariaSnapshot, refs: tree.refs };
}

Expand Down
6 changes: 3 additions & 3 deletions packages/injected/src/recorder/recorder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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, { format: 'codegen' }),
};
} else {
const generated = this._recorder.injectedScript.generateSelector(target, { testIdAttributeName: this._recorder.state.testIdAttributeName, forTextExpect: true });
Expand Down Expand Up @@ -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, { format: 'autoexpect' }) : undefined;
}

async performAction(action: actions.PerformOnRecordAction) {
Expand All @@ -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], { format: 'default' });
void this._delegate.elementPicked?.({ selector, ariaSnapshot });
}
}
Expand Down
100 changes: 49 additions & 51 deletions packages/playwright-client/types/types.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2099,6 +2099,44 @@ export interface Page {
url?: string;
}): Promise<ElementHandle>;

/**
* Captures the aria snapshot of the page. Read more about [aria snapshots](https://playwright.dev/docs/aria-snapshots).
* @param options
*/
ariaSnapshot(options?: {
/**
* When specified, limits the depth of the snapshot.
*/
depth?: number;

/**
* When set to `"ai"`, returns a snapshot optimized for AI consumption with element references. Defaults to
* `"default"`.
*/
format?: "ai"|"default";

/**
* 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<string>;

/**
* Brings page to front (activates tab).
*/
Expand Down Expand Up @@ -4515,38 +4553,6 @@ export interface Page {
height: number;
}): Promise<void>;

/**
* 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<string>;

/**
* **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).
Expand Down Expand Up @@ -12772,6 +12778,17 @@ export interface Locator {
* @param options
*/
ariaSnapshot(options?: {
/**
* When specified, limits the depth of the snapshot.
*/
depth?: number;

/**
* When set to `"ai"`, returns a snapshot optimized for AI consumption with element references. Defaults to
* `"default"`.
*/
format?: "ai"|"default";

/**
* 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
Expand Down Expand Up @@ -14680,25 +14697,6 @@ export interface Locator {
timeout?: number;
}): Promise<void>;

/**
* 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<string>;

/**
* 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.
Expand Down
8 changes: 2 additions & 6 deletions packages/playwright-core/src/client/locator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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<string> {
const result = await this._frame._channel.ariaSnapshot({ ...options, selector: this._selector, timeout: this._frame._timeout(options) });
async ariaSnapshot(options: TimeoutOptions & { format?: 'ai' | 'default', depth?: number } = {}): Promise<string> {
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;
}

Expand Down Expand Up @@ -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<string> {
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, {
Expand Down
4 changes: 2 additions & 2 deletions packages/playwright-core/src/client/page.ts
Original file line number Diff line number Diff line change
Expand Up @@ -851,8 +851,8 @@ export class Page extends ChannelOwner<channels.PageChannel> implements api.Page
return result.pdf;
}

async snapshotForAI(options: TimeoutOptions & { track?: string, mode?: 'full' | 'incremental', depth?: number } = {}): Promise<string> {
const result = await this._channel.snapshotForAI({ timeout: this._timeoutSettings.timeout(options), track: options.track, mode: options.mode, depth: options.depth });
async ariaSnapshot(options: TimeoutOptions & { format?: 'ai' | 'default', track?: string, mode?: 'full' | 'incremental', depth?: number } = {}): Promise<string> {
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;
}

Expand Down
12 changes: 3 additions & 9 deletions packages/playwright-core/src/protocol/validator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1494,14 +1494,15 @@ scheme.PageRequestsParams = tOptional(tObject({}));
scheme.PageRequestsResult = tObject({
requests: tArray(tChannel(['Request'])),
});
scheme.PageSnapshotForAIParams = tObject({
scheme.PageAriaSnapshotParams = tObject({
format: 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({
Expand Down Expand Up @@ -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),
Expand Down
Loading
Loading