chore: implement locator.ariaSnapshot (#33125)
This commit is contained in:
parent
b92b855638
commit
94321fef1c
|
@ -150,6 +150,63 @@ var button = page.GetByRole(AriaRole.Button).And(page.GetByTitle("Subscribe"));
|
|||
|
||||
Additional locator to match.
|
||||
|
||||
## async method: Locator.ariaSnapshot
|
||||
* since: v1.49
|
||||
- returns: <[string]>
|
||||
|
||||
Captures the aria snapshot of the given element. See [`method: LocatorAssertions.toMatchAriaSnapshot`] for the corresponding assertion.
|
||||
|
||||
**Usage**
|
||||
|
||||
```js
|
||||
await page.getByRole('link').ariaSnapshot();
|
||||
```
|
||||
|
||||
```java
|
||||
page.getByRole(AriaRole.LINK).ariaSnapshot();
|
||||
```
|
||||
|
||||
```python async
|
||||
await page.get_by_role("link").aria_snapshot()
|
||||
```
|
||||
|
||||
```python sync
|
||||
page.get_by_role("link").aria_snapshot()
|
||||
```
|
||||
|
||||
```csharp
|
||||
await page.GetByRole(AriaRole.Link).AriaSnapshotAsync();
|
||||
```
|
||||
|
||||
**Details**
|
||||
|
||||
This method captures the aria snapshot of the given element. The snapshot is a string that represents the state of the element and its children.
|
||||
The snapshot can be used to assert the state of the element in the test, or to compare it to state in the future.
|
||||
|
||||
The ARIA snapshot is represented using [YAML](https://yaml.org/spec/1.2.2/) markup language:
|
||||
* The keys of the objects are the roles and optional accessible names of the elements.
|
||||
* The values are either text content or an array of child elements.
|
||||
* Generic static text can be represented with the `text` key.
|
||||
|
||||
Below is the HTML markup and the respective ARIA snapshot:
|
||||
|
||||
```html
|
||||
<ul aria-label="Links">
|
||||
<li><a href="/">Home</a></li>
|
||||
<li><a href="/about">About</a></li>
|
||||
<ul>
|
||||
```
|
||||
|
||||
```yml
|
||||
- list "Links":
|
||||
- listitem:
|
||||
- link "Home"
|
||||
- listitem:
|
||||
- link "About"
|
||||
```
|
||||
|
||||
### option: Locator.ariaSnapshot.timeout = %%-input-timeout-js-%%
|
||||
* since: v1.49
|
||||
|
||||
## async method: Locator.blur
|
||||
* since: v1.28
|
||||
|
|
|
@ -288,6 +288,11 @@ export class Locator implements api.Locator {
|
|||
return await this._withElement((h, timeout) => h.screenshot({ ...options, timeout }), options.timeout);
|
||||
}
|
||||
|
||||
async ariaSnapshot(options?: TimeoutOptions): Promise<string> {
|
||||
const result = await this._frame._channel.ariaSnapshot({ ...options, selector: this._selector });
|
||||
return result.snapshot;
|
||||
}
|
||||
|
||||
async scrollIntoViewIfNeeded(options: channels.ElementHandleScrollIntoViewIfNeededOptions = {}) {
|
||||
return await this._withElement((h, timeout) => h.scrollIntoViewIfNeeded({ ...options, timeout }), options.timeout);
|
||||
}
|
||||
|
|
|
@ -1424,6 +1424,13 @@ scheme.FrameAddStyleTagParams = tObject({
|
|||
scheme.FrameAddStyleTagResult = tObject({
|
||||
element: tChannel(['ElementHandle']),
|
||||
});
|
||||
scheme.FrameAriaSnapshotParams = tObject({
|
||||
selector: tString,
|
||||
timeout: tOptional(tNumber),
|
||||
});
|
||||
scheme.FrameAriaSnapshotResult = tObject({
|
||||
snapshot: tString,
|
||||
});
|
||||
scheme.FrameBlurParams = tObject({
|
||||
selector: tString,
|
||||
strict: tOptional(tBoolean),
|
||||
|
|
|
@ -267,4 +267,8 @@ export class FrameDispatcher extends Dispatcher<Frame, channels.FrameChannel, Br
|
|||
result.received = serializeResult(result.received);
|
||||
return result;
|
||||
}
|
||||
|
||||
async ariaSnapshot(params: channels.FrameAriaSnapshotParams, metadata: CallMetadata): Promise<channels.FrameAriaSnapshotResult> {
|
||||
return { snapshot: await this._frame.ariaSnapshot(metadata, params.selector, params) };
|
||||
}
|
||||
}
|
||||
|
|
|
@ -789,6 +789,10 @@ export class ElementHandle<T extends Node = Node> extends js.JSHandle<T> {
|
|||
return this._page._delegate.getBoundingBox(this);
|
||||
}
|
||||
|
||||
async ariaSnapshot(): Promise<string> {
|
||||
return await this.evaluateInUtility(([injected, element]) => injected.ariaSnapshot(element), {});
|
||||
}
|
||||
|
||||
async screenshot(metadata: CallMetadata, options: ScreenshotOptions & TimeoutOptions = {}): Promise<Buffer> {
|
||||
const controller = new ProgressController(metadata, this);
|
||||
return controller.run(
|
||||
|
|
|
@ -1405,6 +1405,13 @@ export class Frame extends SdkObject {
|
|||
});
|
||||
}
|
||||
|
||||
async ariaSnapshot(metadata: CallMetadata, selector: string, options: types.TimeoutOptions = {}): Promise<string> {
|
||||
const controller = new ProgressController(metadata, this);
|
||||
return controller.run(async progress => {
|
||||
return await this._retryWithProgressIfNotConnected(progress, selector, true /* strict */, true /* performActionPreChecks */, handle => handle.ariaSnapshot());
|
||||
}, this._page._timeoutSettings.timeout(options));
|
||||
}
|
||||
|
||||
async expect(metadata: CallMetadata, selector: string, options: FrameExpectParams): Promise<{ matches: boolean, received?: any, log?: string[], timedOut?: boolean }> {
|
||||
const result = await this._expectImpl(metadata, selector, options);
|
||||
// Library mode special case for the expect errors which are return values, not exceptions.
|
||||
|
|
|
@ -20,7 +20,6 @@ import { escapeForTextSelector } from '../../utils/isomorphic/stringUtils';
|
|||
import { asLocator } from '../../utils/isomorphic/locatorGenerators';
|
||||
import type { Language } from '../../utils/isomorphic/locatorGenerators';
|
||||
import type { InjectedScript } from './injectedScript';
|
||||
import { renderedAriaTree } from './ariaSnapshot';
|
||||
|
||||
const selectorSymbol = Symbol('selector');
|
||||
|
||||
|
@ -86,7 +85,7 @@ class ConsoleAPI {
|
|||
inspect: (selector: string) => this._inspect(selector),
|
||||
selector: (element: Element) => this._selector(element),
|
||||
generateLocator: (element: Element, language?: Language) => this._generateLocator(element, language),
|
||||
ariaSnapshot: (element?: Element) => renderedAriaTree(element || this._injectedScript.document.body),
|
||||
ariaSnapshot: (element?: Element) => this._injectedScript.ariaSnapshot(element || this._injectedScript.document.body),
|
||||
resume: () => this._resume(),
|
||||
...new Locator(injectedScript, ''),
|
||||
};
|
||||
|
|
|
@ -206,8 +206,10 @@ export class InjectedScript {
|
|||
return new Set<Element>(result.map(r => r.element));
|
||||
}
|
||||
|
||||
renderedAriaTree(target: Element): string {
|
||||
return renderedAriaTree(target);
|
||||
ariaSnapshot(node: Node): string {
|
||||
if (node.nodeType !== Node.ELEMENT_NODE)
|
||||
throw this.createStacklessError('Can only capture aria snapshot of Element nodes.');
|
||||
return renderedAriaTree(node as Element);
|
||||
}
|
||||
|
||||
querySelectorAll(selector: ParsedSelector, root: Node): Element[] {
|
||||
|
|
|
@ -714,7 +714,7 @@ class TextAssertionTool implements RecorderTool {
|
|||
name: 'assertSnapshot',
|
||||
selector: this._hoverHighlight.selector,
|
||||
signals: [],
|
||||
snapshot: this._recorder.injectedScript.renderedAriaTree(target),
|
||||
snapshot: this._recorder.injectedScript.ariaSnapshot(target),
|
||||
};
|
||||
} else {
|
||||
this._hoverHighlight = this._recorder.injectedScript.generateSelector(target, { testIdAttributeName: this._recorder.state.testIdAttributeName, forTextExpect: true });
|
||||
|
|
|
@ -12424,6 +12424,57 @@ export interface Locator {
|
|||
*/
|
||||
and(locator: Locator): Locator;
|
||||
|
||||
/**
|
||||
* Captures the aria snapshot of the given element. See
|
||||
* [expect(locator).toMatchAriaSnapshot(expected[, options])](https://playwright.dev/docs/api/class-locatorassertions#locator-assertions-to-match-aria-snapshot)
|
||||
* for the corresponding assertion.
|
||||
*
|
||||
* **Usage**
|
||||
*
|
||||
* ```js
|
||||
* await page.getByRole('link').ariaSnapshot();
|
||||
* ```
|
||||
*
|
||||
* **Details**
|
||||
*
|
||||
* This method captures the aria snapshot of the given element. The snapshot is a string that represents the state of
|
||||
* the element and its children. The snapshot can be used to assert the state of the element in the test, or to
|
||||
* compare it to state in the future.
|
||||
*
|
||||
* The ARIA snapshot is represented using [YAML](https://yaml.org/spec/1.2.2/) markup language:
|
||||
* - The keys of the objects are the roles and optional accessible names of the elements.
|
||||
* - The values are either text content or an array of child elements.
|
||||
* - Generic static text can be represented with the `text` key.
|
||||
*
|
||||
* Below is the HTML markup and the respective ARIA snapshot:
|
||||
*
|
||||
* ```html
|
||||
* <ul aria-label="Links">
|
||||
* <li><a href="/">Home</a></li>
|
||||
* <li><a href="/about">About</a></li>
|
||||
* <ul>
|
||||
* ```
|
||||
*
|
||||
* ```yml
|
||||
* - list "Links":
|
||||
* - listitem:
|
||||
* - link "Home"
|
||||
* - listitem:
|
||||
* - link "About"
|
||||
* ```
|
||||
*
|
||||
* @param options
|
||||
*/
|
||||
ariaSnapshot(options?: {
|
||||
/**
|
||||
* 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>;
|
||||
|
||||
/**
|
||||
* Calls [blur](https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/blur) on the element.
|
||||
* @param options
|
||||
|
|
|
@ -2509,6 +2509,7 @@ export interface FrameChannel extends FrameEventTarget, Channel {
|
|||
evalOnSelectorAll(params: FrameEvalOnSelectorAllParams, metadata?: CallMetadata): Promise<FrameEvalOnSelectorAllResult>;
|
||||
addScriptTag(params: FrameAddScriptTagParams, metadata?: CallMetadata): Promise<FrameAddScriptTagResult>;
|
||||
addStyleTag(params: FrameAddStyleTagParams, metadata?: CallMetadata): Promise<FrameAddStyleTagResult>;
|
||||
ariaSnapshot(params: FrameAriaSnapshotParams, metadata?: CallMetadata): Promise<FrameAriaSnapshotResult>;
|
||||
blur(params: FrameBlurParams, metadata?: CallMetadata): Promise<FrameBlurResult>;
|
||||
check(params: FrameCheckParams, metadata?: CallMetadata): Promise<FrameCheckResult>;
|
||||
click(params: FrameClickParams, metadata?: CallMetadata): Promise<FrameClickResult>;
|
||||
|
@ -2613,6 +2614,16 @@ export type FrameAddStyleTagOptions = {
|
|||
export type FrameAddStyleTagResult = {
|
||||
element: ElementHandleChannel,
|
||||
};
|
||||
export type FrameAriaSnapshotParams = {
|
||||
selector: string,
|
||||
timeout?: number,
|
||||
};
|
||||
export type FrameAriaSnapshotOptions = {
|
||||
timeout?: number,
|
||||
};
|
||||
export type FrameAriaSnapshotResult = {
|
||||
snapshot: string,
|
||||
};
|
||||
export type FrameBlurParams = {
|
||||
selector: string,
|
||||
strict?: boolean,
|
||||
|
|
|
@ -1875,6 +1875,13 @@ Frame:
|
|||
flags:
|
||||
snapshot: true
|
||||
|
||||
ariaSnapshot:
|
||||
parameters:
|
||||
selector: string
|
||||
timeout: number?
|
||||
returns:
|
||||
snapshot: string
|
||||
|
||||
blur:
|
||||
parameters:
|
||||
selector: string
|
||||
|
|
|
@ -0,0 +1,40 @@
|
|||
/**
|
||||
* Copyright 2018 Google Inc. All rights reserved.
|
||||
* Modifications copyright (c) Microsoft Corporation.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { test as it, expect } from './pageTest';
|
||||
|
||||
it('should snapshot the check box @smoke', async ({ page }) => {
|
||||
await page.setContent(`<input id='checkbox' type='checkbox'></input>`);
|
||||
expect(await page.locator('body').ariaSnapshot()).toBe('- checkbox');
|
||||
});
|
||||
|
||||
it('should snapshot nested element', async ({ page }) => {
|
||||
await page.setContent(`
|
||||
<div>
|
||||
<input id='checkbox' type='checkbox'></input>
|
||||
</div>`);
|
||||
expect(await page.locator('body').ariaSnapshot()).toBe('- checkbox');
|
||||
});
|
||||
|
||||
it('should snapshot fragment', async ({ page }) => {
|
||||
await page.setContent(`
|
||||
<div>
|
||||
<a href="about:blank">Link</a>
|
||||
<a href="about:blank">Link</a>
|
||||
</div>`);
|
||||
expect(await page.locator('body').ariaSnapshot()).toBe(`- link "Link"\n- link "Link"`);
|
||||
});
|
Loading…
Reference in New Issue