mirror of
https://github.com/puppeteer/puppeteer
synced 2024-06-14 14:02:48 +00:00
feat: allow creating ElementHandles from the accessibility tree snapshot (#12233)
This commit is contained in:
parent
f5bc2b53ae
commit
0057f3fe0a
21
docs/api/puppeteer.serializedaxnode.elementhandle.md
Normal file
21
docs/api/puppeteer.serializedaxnode.elementhandle.md
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
---
|
||||||
|
sidebar_label: SerializedAXNode.elementHandle
|
||||||
|
---
|
||||||
|
|
||||||
|
# SerializedAXNode.elementHandle() method
|
||||||
|
|
||||||
|
Get an ElementHandle for this AXNode if available.
|
||||||
|
|
||||||
|
If the underlying DOM element has been disposed, the method might return an error.
|
||||||
|
|
||||||
|
#### Signature:
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
interface SerializedAXNode {
|
||||||
|
elementHandle(): Promise<ElementHandle | null>;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Returns:**
|
||||||
|
|
||||||
|
Promise<[ElementHandle](./puppeteer.elementhandle.md) \| null>
|
@ -502,3 +502,27 @@ A description of the current value.
|
|||||||
|
|
||||||
</td></tr>
|
</td></tr>
|
||||||
</tbody></table>
|
</tbody></table>
|
||||||
|
|
||||||
|
## Methods
|
||||||
|
|
||||||
|
<table><thead><tr><th>
|
||||||
|
|
||||||
|
Method
|
||||||
|
|
||||||
|
</th><th>
|
||||||
|
|
||||||
|
Description
|
||||||
|
|
||||||
|
</th></tr></thead>
|
||||||
|
<tbody><tr><td>
|
||||||
|
|
||||||
|
<span id="elementhandle">[elementHandle()](./puppeteer.serializedaxnode.elementhandle.md)</span>
|
||||||
|
|
||||||
|
</td><td>
|
||||||
|
|
||||||
|
Get an ElementHandle for this AXNode if available.
|
||||||
|
|
||||||
|
If the underlying DOM element has been disposed, the method might return an error.
|
||||||
|
|
||||||
|
</td></tr>
|
||||||
|
</tbody></table>
|
||||||
|
@ -14,6 +14,7 @@ import type {
|
|||||||
WaitForSelectorOptions,
|
WaitForSelectorOptions,
|
||||||
WaitTimeoutOptions,
|
WaitTimeoutOptions,
|
||||||
} from '../api/Page.js';
|
} from '../api/Page.js';
|
||||||
|
import type {Accessibility} from '../cdp/Accessibility.js';
|
||||||
import type {DeviceRequestPrompt} from '../cdp/DeviceRequestPrompt.js';
|
import type {DeviceRequestPrompt} from '../cdp/DeviceRequestPrompt.js';
|
||||||
import type {PuppeteerLifeCycleEvent} from '../cdp/LifecycleWatcher.js';
|
import type {PuppeteerLifeCycleEvent} from '../cdp/LifecycleWatcher.js';
|
||||||
import {EventEmitter, type EventType} from '../common/EventEmitter.js';
|
import {EventEmitter, type EventType} from '../common/EventEmitter.js';
|
||||||
@ -379,6 +380,11 @@ export abstract class Frame extends EventEmitter<FrameEvents> {
|
|||||||
*/
|
*/
|
||||||
abstract get client(): CDPSession;
|
abstract get client(): CDPSession;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @internal
|
||||||
|
*/
|
||||||
|
abstract get accessibility(): Accessibility;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @internal
|
* @internal
|
||||||
*/
|
*/
|
||||||
|
@ -843,7 +843,9 @@ export abstract class Page extends EventEmitter<PageEvents> {
|
|||||||
/**
|
/**
|
||||||
* {@inheritDoc Accessibility}
|
* {@inheritDoc Accessibility}
|
||||||
*/
|
*/
|
||||||
abstract get accessibility(): Accessibility;
|
get accessibility(): Accessibility {
|
||||||
|
return this.mainFrame().accessibility;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An array of all frames attached to the page.
|
* An array of all frames attached to the page.
|
||||||
|
@ -27,6 +27,7 @@ import {
|
|||||||
type WaitForOptions,
|
type WaitForOptions,
|
||||||
} from '../api/Frame.js';
|
} from '../api/Frame.js';
|
||||||
import {PageEvent} from '../api/Page.js';
|
import {PageEvent} from '../api/Page.js';
|
||||||
|
import {Accessibility} from '../cdp/Accessibility.js';
|
||||||
import {
|
import {
|
||||||
ConsoleMessage,
|
ConsoleMessage,
|
||||||
type ConsoleMessageLocation,
|
type ConsoleMessageLocation,
|
||||||
@ -71,6 +72,7 @@ export class BidiFrame extends Frame {
|
|||||||
|
|
||||||
override readonly _id: string;
|
override readonly _id: string;
|
||||||
override readonly client: BidiCdpSession;
|
override readonly client: BidiCdpSession;
|
||||||
|
override readonly accessibility: Accessibility;
|
||||||
|
|
||||||
private constructor(
|
private constructor(
|
||||||
parent: BidiPage | BidiFrame,
|
parent: BidiPage | BidiFrame,
|
||||||
@ -91,6 +93,7 @@ export class BidiFrame extends Frame {
|
|||||||
this
|
this
|
||||||
),
|
),
|
||||||
};
|
};
|
||||||
|
this.accessibility = new Accessibility(this.realms.default);
|
||||||
}
|
}
|
||||||
|
|
||||||
#initialize(): void {
|
#initialize(): void {
|
||||||
|
@ -24,7 +24,6 @@ import {
|
|||||||
type NewDocumentScriptEvaluation,
|
type NewDocumentScriptEvaluation,
|
||||||
type ScreenshotOptions,
|
type ScreenshotOptions,
|
||||||
} from '../api/Page.js';
|
} from '../api/Page.js';
|
||||||
import {Accessibility} from '../cdp/Accessibility.js';
|
|
||||||
import {Coverage} from '../cdp/Coverage.js';
|
import {Coverage} from '../cdp/Coverage.js';
|
||||||
import {EmulationManager} from '../cdp/EmulationManager.js';
|
import {EmulationManager} from '../cdp/EmulationManager.js';
|
||||||
import {Tracing} from '../cdp/Tracing.js';
|
import {Tracing} from '../cdp/Tracing.js';
|
||||||
@ -86,7 +85,6 @@ export class BidiPage extends Page {
|
|||||||
readonly keyboard: BidiKeyboard;
|
readonly keyboard: BidiKeyboard;
|
||||||
readonly mouse: BidiMouse;
|
readonly mouse: BidiMouse;
|
||||||
readonly touchscreen: BidiTouchscreen;
|
readonly touchscreen: BidiTouchscreen;
|
||||||
readonly accessibility: Accessibility;
|
|
||||||
readonly tracing: Tracing;
|
readonly tracing: Tracing;
|
||||||
readonly coverage: Coverage;
|
readonly coverage: Coverage;
|
||||||
readonly #cdpEmulationManager: EmulationManager;
|
readonly #cdpEmulationManager: EmulationManager;
|
||||||
@ -104,7 +102,6 @@ export class BidiPage extends Page {
|
|||||||
this.#frame = BidiFrame.from(this, browsingContext);
|
this.#frame = BidiFrame.from(this, browsingContext);
|
||||||
|
|
||||||
this.#cdpEmulationManager = new EmulationManager(this.#frame.client);
|
this.#cdpEmulationManager = new EmulationManager(this.#frame.client);
|
||||||
this.accessibility = new Accessibility(this.#frame.client);
|
|
||||||
this.tracing = new Tracing(this.#frame.client);
|
this.tracing = new Tracing(this.#frame.client);
|
||||||
this.coverage = new Coverage(this.#frame.client);
|
this.coverage = new Coverage(this.#frame.client);
|
||||||
this.keyboard = new BidiKeyboard(this);
|
this.keyboard = new BidiKeyboard(this);
|
||||||
|
@ -6,8 +6,8 @@
|
|||||||
|
|
||||||
import type {Protocol} from 'devtools-protocol';
|
import type {Protocol} from 'devtools-protocol';
|
||||||
|
|
||||||
import type {CDPSession} from '../api/CDPSession.js';
|
|
||||||
import type {ElementHandle} from '../api/ElementHandle.js';
|
import type {ElementHandle} from '../api/ElementHandle.js';
|
||||||
|
import type {Realm} from '../api/Realm.js';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents a Node and the properties of it that are relevant to Accessibility.
|
* Represents a Node and the properties of it that are relevant to Accessibility.
|
||||||
@ -80,6 +80,14 @@ export interface SerializedAXNode {
|
|||||||
* Children of this node, if there are any.
|
* Children of this node, if there are any.
|
||||||
*/
|
*/
|
||||||
children?: SerializedAXNode[];
|
children?: SerializedAXNode[];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get an ElementHandle for this AXNode if available.
|
||||||
|
*
|
||||||
|
* If the underlying DOM element has been disposed, the method might return an
|
||||||
|
* error.
|
||||||
|
*/
|
||||||
|
elementHandle(): Promise<ElementHandle | null>;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -121,20 +129,13 @@ export interface SnapshotOptions {
|
|||||||
* @public
|
* @public
|
||||||
*/
|
*/
|
||||||
export class Accessibility {
|
export class Accessibility {
|
||||||
#client: CDPSession;
|
#realm: Realm;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @internal
|
* @internal
|
||||||
*/
|
*/
|
||||||
constructor(client: CDPSession) {
|
constructor(realm: Realm) {
|
||||||
this.#client = client;
|
this.#realm = realm;
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @internal
|
|
||||||
*/
|
|
||||||
updateClient(client: CDPSession): void {
|
|
||||||
this.#client = client;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -180,15 +181,20 @@ export class Accessibility {
|
|||||||
options: SnapshotOptions = {}
|
options: SnapshotOptions = {}
|
||||||
): Promise<SerializedAXNode | null> {
|
): Promise<SerializedAXNode | null> {
|
||||||
const {interestingOnly = true, root = null} = options;
|
const {interestingOnly = true, root = null} = options;
|
||||||
const {nodes} = await this.#client.send('Accessibility.getFullAXTree');
|
const {nodes} = await this.#realm.environment.client.send(
|
||||||
|
'Accessibility.getFullAXTree'
|
||||||
|
);
|
||||||
let backendNodeId: number | undefined;
|
let backendNodeId: number | undefined;
|
||||||
if (root) {
|
if (root) {
|
||||||
const {node} = await this.#client.send('DOM.describeNode', {
|
const {node} = await this.#realm.environment.client.send(
|
||||||
|
'DOM.describeNode',
|
||||||
|
{
|
||||||
objectId: root.id,
|
objectId: root.id,
|
||||||
});
|
}
|
||||||
|
);
|
||||||
backendNodeId = node.backendNodeId;
|
backendNodeId = node.backendNodeId;
|
||||||
}
|
}
|
||||||
const defaultRoot = AXNode.createTree(nodes);
|
const defaultRoot = AXNode.createTree(this.#realm, nodes);
|
||||||
let needle: AXNode | null = defaultRoot;
|
let needle: AXNode | null = defaultRoot;
|
||||||
if (backendNodeId) {
|
if (backendNodeId) {
|
||||||
needle = defaultRoot.find(node => {
|
needle = defaultRoot.find(node => {
|
||||||
@ -260,13 +266,14 @@ class AXNode {
|
|||||||
#role: string;
|
#role: string;
|
||||||
#ignored: boolean;
|
#ignored: boolean;
|
||||||
#cachedHasFocusableChild?: boolean;
|
#cachedHasFocusableChild?: boolean;
|
||||||
|
#realm: Realm;
|
||||||
|
|
||||||
constructor(payload: Protocol.Accessibility.AXNode) {
|
constructor(realm: Realm, payload: Protocol.Accessibility.AXNode) {
|
||||||
this.payload = payload;
|
this.payload = payload;
|
||||||
this.#name = this.payload.name ? this.payload.name.value : '';
|
this.#name = this.payload.name ? this.payload.name.value : '';
|
||||||
this.#role = this.payload.role ? this.payload.role.value : 'Unknown';
|
this.#role = this.payload.role ? this.payload.role.value : 'Unknown';
|
||||||
this.#ignored = this.payload.ignored;
|
this.#ignored = this.payload.ignored;
|
||||||
|
this.#realm = realm;
|
||||||
for (const property of this.payload.properties || []) {
|
for (const property of this.payload.properties || []) {
|
||||||
if (property.name === 'editable') {
|
if (property.name === 'editable') {
|
||||||
this.#richlyEditable = property.value.value === 'richtext';
|
this.#richlyEditable = property.value.value === 'richtext';
|
||||||
@ -441,6 +448,14 @@ class AXNode {
|
|||||||
|
|
||||||
const node: SerializedAXNode = {
|
const node: SerializedAXNode = {
|
||||||
role: this.#role,
|
role: this.#role,
|
||||||
|
elementHandle: async (): Promise<ElementHandle | null> => {
|
||||||
|
if (!this.payload.backendDOMNodeId) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return (await this.#realm.adoptBackendNode(
|
||||||
|
this.payload.backendDOMNodeId
|
||||||
|
)) as ElementHandle<Element>;
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
type UserStringProperty =
|
type UserStringProperty =
|
||||||
@ -561,10 +576,13 @@ class AXNode {
|
|||||||
return node;
|
return node;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static createTree(payloads: Protocol.Accessibility.AXNode[]): AXNode {
|
public static createTree(
|
||||||
|
realm: Realm,
|
||||||
|
payloads: Protocol.Accessibility.AXNode[]
|
||||||
|
): AXNode {
|
||||||
const nodeById = new Map<string, AXNode>();
|
const nodeById = new Map<string, AXNode>();
|
||||||
for (const payload of payloads) {
|
for (const payload of payloads) {
|
||||||
nodeById.set(payload.nodeId, new AXNode(payload));
|
nodeById.set(payload.nodeId, new AXNode(realm, payload));
|
||||||
}
|
}
|
||||||
for (const node of nodeById.values()) {
|
for (const node of nodeById.values()) {
|
||||||
for (const childId of node.payload.childIds || []) {
|
for (const childId of node.payload.childIds || []) {
|
||||||
|
@ -15,6 +15,7 @@ import {Deferred} from '../util/Deferred.js';
|
|||||||
import {disposeSymbol} from '../util/disposable.js';
|
import {disposeSymbol} from '../util/disposable.js';
|
||||||
import {isErrorLike} from '../util/ErrorLike.js';
|
import {isErrorLike} from '../util/ErrorLike.js';
|
||||||
|
|
||||||
|
import {Accessibility} from './Accessibility.js';
|
||||||
import type {
|
import type {
|
||||||
DeviceRequestPrompt,
|
DeviceRequestPrompt,
|
||||||
DeviceRequestPromptManager,
|
DeviceRequestPromptManager,
|
||||||
@ -44,6 +45,7 @@ export class CdpFrame extends Frame {
|
|||||||
|
|
||||||
override _id: string;
|
override _id: string;
|
||||||
override _parentId?: string;
|
override _parentId?: string;
|
||||||
|
override accessibility: Accessibility;
|
||||||
|
|
||||||
worlds: IsolatedWorldChart;
|
worlds: IsolatedWorldChart;
|
||||||
|
|
||||||
@ -70,6 +72,8 @@ export class CdpFrame extends Frame {
|
|||||||
),
|
),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
this.accessibility = new Accessibility(this.worlds[MAIN_WORLD]);
|
||||||
|
|
||||||
this.on(FrameEvent.FrameSwappedByActivation, () => {
|
this.on(FrameEvent.FrameSwappedByActivation, () => {
|
||||||
// Emulate loading process for swapped frames.
|
// Emulate loading process for swapped frames.
|
||||||
this._onLoadingStarted();
|
this._onLoadingStarted();
|
||||||
|
@ -56,7 +56,6 @@ import {Deferred} from '../util/Deferred.js';
|
|||||||
import {AsyncDisposableStack} from '../util/disposable.js';
|
import {AsyncDisposableStack} from '../util/disposable.js';
|
||||||
import {isErrorLike} from '../util/ErrorLike.js';
|
import {isErrorLike} from '../util/ErrorLike.js';
|
||||||
|
|
||||||
import {Accessibility} from './Accessibility.js';
|
|
||||||
import {Binding} from './Binding.js';
|
import {Binding} from './Binding.js';
|
||||||
import {CdpCDPSession} from './CDPSession.js';
|
import {CdpCDPSession} from './CDPSession.js';
|
||||||
import {isTargetClosedError} from './Connection.js';
|
import {isTargetClosedError} from './Connection.js';
|
||||||
@ -128,7 +127,6 @@ export class CdpPage extends Page {
|
|||||||
#keyboard: CdpKeyboard;
|
#keyboard: CdpKeyboard;
|
||||||
#mouse: CdpMouse;
|
#mouse: CdpMouse;
|
||||||
#touchscreen: CdpTouchscreen;
|
#touchscreen: CdpTouchscreen;
|
||||||
#accessibility: Accessibility;
|
|
||||||
#frameManager: FrameManager;
|
#frameManager: FrameManager;
|
||||||
#emulationManager: EmulationManager;
|
#emulationManager: EmulationManager;
|
||||||
#tracing: Tracing;
|
#tracing: Tracing;
|
||||||
@ -237,7 +235,6 @@ export class CdpPage extends Page {
|
|||||||
this.#keyboard = new CdpKeyboard(client);
|
this.#keyboard = new CdpKeyboard(client);
|
||||||
this.#mouse = new CdpMouse(client, this.#keyboard);
|
this.#mouse = new CdpMouse(client, this.#keyboard);
|
||||||
this.#touchscreen = new CdpTouchscreen(client, this.#keyboard);
|
this.#touchscreen = new CdpTouchscreen(client, this.#keyboard);
|
||||||
this.#accessibility = new Accessibility(client);
|
|
||||||
this.#frameManager = new FrameManager(client, this, this._timeoutSettings);
|
this.#frameManager = new FrameManager(client, this, this._timeoutSettings);
|
||||||
this.#emulationManager = new EmulationManager(client);
|
this.#emulationManager = new EmulationManager(client);
|
||||||
this.#tracing = new Tracing(client);
|
this.#tracing = new Tracing(client);
|
||||||
@ -315,7 +312,6 @@ export class CdpPage extends Page {
|
|||||||
this.#keyboard.updateClient(newSession);
|
this.#keyboard.updateClient(newSession);
|
||||||
this.#mouse.updateClient(newSession);
|
this.#mouse.updateClient(newSession);
|
||||||
this.#touchscreen.updateClient(newSession);
|
this.#touchscreen.updateClient(newSession);
|
||||||
this.#accessibility.updateClient(newSession);
|
|
||||||
this.#emulationManager.updateClient(newSession);
|
this.#emulationManager.updateClient(newSession);
|
||||||
this.#tracing.updateClient(newSession);
|
this.#tracing.updateClient(newSession);
|
||||||
this.#coverage.updateClient(newSession);
|
this.#coverage.updateClient(newSession);
|
||||||
@ -523,10 +519,6 @@ export class CdpPage extends Page {
|
|||||||
return this.#tracing;
|
return this.#tracing;
|
||||||
}
|
}
|
||||||
|
|
||||||
override get accessibility(): Accessibility {
|
|
||||||
return this.#accessibility;
|
|
||||||
}
|
|
||||||
|
|
||||||
override frames(): Frame[] {
|
override frames(): Frame[] {
|
||||||
return this.#frameManager.frames();
|
return this.#frameManager.frames();
|
||||||
}
|
}
|
||||||
|
@ -294,7 +294,7 @@ describe('Accessibility', function () {
|
|||||||
},
|
},
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
expect(await page.accessibility.snapshot()).toEqual(golden);
|
expect(await page.accessibility.snapshot()).toMatchObject(golden);
|
||||||
});
|
});
|
||||||
it('rich text editable fields should have children', async () => {
|
it('rich text editable fields should have children', async () => {
|
||||||
const {page, isFirefox} = await getTestState();
|
const {page, isFirefox} = await getTestState();
|
||||||
@ -386,7 +386,7 @@ describe('Accessibility', function () {
|
|||||||
const snapshot = await page.accessibility.snapshot();
|
const snapshot = await page.accessibility.snapshot();
|
||||||
assert(snapshot);
|
assert(snapshot);
|
||||||
assert(snapshot.children);
|
assert(snapshot.children);
|
||||||
expect(snapshot.children[0]).toEqual({
|
expect(snapshot.children[0]).toMatchObject({
|
||||||
role: 'textbox',
|
role: 'textbox',
|
||||||
name: '',
|
name: '',
|
||||||
value: 'Edit this image:',
|
value: 'Edit this image:',
|
||||||
@ -416,7 +416,7 @@ describe('Accessibility', function () {
|
|||||||
const snapshot = await page.accessibility.snapshot();
|
const snapshot = await page.accessibility.snapshot();
|
||||||
assert(snapshot);
|
assert(snapshot);
|
||||||
assert(snapshot.children);
|
assert(snapshot.children);
|
||||||
expect(snapshot.children[0]).toEqual(golden);
|
expect(snapshot.children[0]).toMatchObject(golden);
|
||||||
});
|
});
|
||||||
it('checkbox with and tabIndex and label should not have children', async () => {
|
it('checkbox with and tabIndex and label should not have children', async () => {
|
||||||
const {page, isFirefox} = await getTestState();
|
const {page, isFirefox} = await getTestState();
|
||||||
@ -440,7 +440,7 @@ describe('Accessibility', function () {
|
|||||||
const snapshot = await page.accessibility.snapshot();
|
const snapshot = await page.accessibility.snapshot();
|
||||||
assert(snapshot);
|
assert(snapshot);
|
||||||
assert(snapshot.children);
|
assert(snapshot.children);
|
||||||
expect(snapshot.children[0]).toEqual(golden);
|
expect(snapshot.children[0]).toMatchObject(golden);
|
||||||
});
|
});
|
||||||
it('checkbox without label should not have children', async () => {
|
it('checkbox without label should not have children', async () => {
|
||||||
const {page, isFirefox} = await getTestState();
|
const {page, isFirefox} = await getTestState();
|
||||||
@ -464,7 +464,7 @@ describe('Accessibility', function () {
|
|||||||
const snapshot = await page.accessibility.snapshot();
|
const snapshot = await page.accessibility.snapshot();
|
||||||
assert(snapshot);
|
assert(snapshot);
|
||||||
assert(snapshot.children);
|
assert(snapshot.children);
|
||||||
expect(snapshot.children[0]).toEqual(golden);
|
expect(snapshot.children[0]).toMatchObject(golden);
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('root option', function () {
|
describe('root option', function () {
|
||||||
@ -474,10 +474,12 @@ describe('Accessibility', function () {
|
|||||||
await page.setContent(`<button>My Button</button>`);
|
await page.setContent(`<button>My Button</button>`);
|
||||||
|
|
||||||
using button = (await page.$('button'))!;
|
using button = (await page.$('button'))!;
|
||||||
expect(await page.accessibility.snapshot({root: button})).toEqual({
|
expect(await page.accessibility.snapshot({root: button})).toMatchObject(
|
||||||
|
{
|
||||||
role: 'button',
|
role: 'button',
|
||||||
name: 'My Button',
|
name: 'My Button',
|
||||||
});
|
}
|
||||||
|
);
|
||||||
});
|
});
|
||||||
it('should work an input', async () => {
|
it('should work an input', async () => {
|
||||||
const {page} = await getTestState();
|
const {page} = await getTestState();
|
||||||
@ -485,7 +487,7 @@ describe('Accessibility', function () {
|
|||||||
await page.setContent(`<input title="My Input" value="My Value">`);
|
await page.setContent(`<input title="My Input" value="My Value">`);
|
||||||
|
|
||||||
using input = (await page.$('input'))!;
|
using input = (await page.$('input'))!;
|
||||||
expect(await page.accessibility.snapshot({root: input})).toEqual({
|
expect(await page.accessibility.snapshot({root: input})).toMatchObject({
|
||||||
role: 'textbox',
|
role: 'textbox',
|
||||||
name: 'My Input',
|
name: 'My Input',
|
||||||
value: 'My Value',
|
value: 'My Value',
|
||||||
@ -503,7 +505,7 @@ describe('Accessibility', function () {
|
|||||||
`);
|
`);
|
||||||
|
|
||||||
using menu = (await page.$('div[role="menu"]'))!;
|
using menu = (await page.$('div[role="menu"]'))!;
|
||||||
expect(await page.accessibility.snapshot({root: menu})).toEqual({
|
expect(await page.accessibility.snapshot({root: menu})).toMatchObject({
|
||||||
role: 'menu',
|
role: 'menu',
|
||||||
name: 'My Menu',
|
name: 'My Menu',
|
||||||
children: [
|
children: [
|
||||||
@ -548,6 +550,28 @@ describe('Accessibility', function () {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('elementHandle()', () => {
|
||||||
|
it('should get an ElementHandle from a snapshot item', async () => {
|
||||||
|
const {page} = await getTestState();
|
||||||
|
|
||||||
|
await page.setContent(`<button>My Button</button>`);
|
||||||
|
|
||||||
|
using button = (await page.$('button'))!;
|
||||||
|
const snapshot = await page.accessibility.snapshot({root: button});
|
||||||
|
expect(snapshot).toMatchObject({
|
||||||
|
role: 'button',
|
||||||
|
name: 'My Button',
|
||||||
|
});
|
||||||
|
|
||||||
|
using buttonHandle = await snapshot!.elementHandle();
|
||||||
|
expect(
|
||||||
|
await buttonHandle?.evaluate(button => {
|
||||||
|
return button.innerHTML;
|
||||||
|
})
|
||||||
|
).toEqual('My Button');
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
function findFocusedNode(
|
function findFocusedNode(
|
||||||
|
Loading…
Reference in New Issue
Block a user