From f2d3ac68ae4fb0cf09c4836e409037885419e8d4 Mon Sep 17 00:00:00 2001 From: Joel Einbinder Date: Fri, 10 Jan 2020 13:15:03 -0800 Subject: [PATCH] basic root accessibility stuff --- src/accessibility.ts | 15 ++++----------- src/chromium/crAccessibility.ts | 10 +++++++--- src/chromium/crPage.ts | 4 ++-- src/firefox/ffAccessibility.ts | 25 +++++++++++++++++++------ src/firefox/ffPage.ts | 4 ++-- src/page.ts | 2 +- src/webkit/wkAccessibility.ts | 21 +++++++++++++++++---- src/webkit/wkPage.ts | 4 ++-- test/accessibility.spec.js | 4 ++-- 9 files changed, 56 insertions(+), 33 deletions(-) diff --git a/src/accessibility.ts b/src/accessibility.ts index f4ce8a4b9d..058e771a32 100644 --- a/src/accessibility.ts +++ b/src/accessibility.ts @@ -55,13 +55,12 @@ export interface AXNode { isLeafNode(): boolean; isControl(): boolean; serialize(): SerializedAXNode; - findElement(element: dom.ElementHandle): Promise; children(): Iterable; } export class Accessibility { - private _getAXTree: () => Promise; - constructor(getAXTree: () => Promise) { + private _getAXTree: (needle?: dom.ElementHandle) => Promise<{tree: AXNode, needle?: AXNode}>; + constructor(getAXTree: (needle?: dom.ElementHandle) => Promise<{tree: AXNode, needle?: AXNode}>) { this._getAXTree = getAXTree; } @@ -73,18 +72,12 @@ export class Accessibility { interestingOnly = true, root = null, } = options; - const defaultRoot = await this._getAXTree(); - let needle: AXNode | null = defaultRoot; - if (root) { - needle = await defaultRoot.findElement(root); - if (!needle) - return null; - } + const {tree, needle} = await this._getAXTree(root); if (!interestingOnly) return serializeTree(needle)[0]; const interestingNodes: Set = new Set(); - collectInterestingNodes(interestingNodes, defaultRoot, false); + collectInterestingNodes(interestingNodes, tree, false); if (root && !interestingNodes.has(needle)) return null; return serializeTree(needle, interestingNodes)[0]; diff --git a/src/chromium/crAccessibility.ts b/src/chromium/crAccessibility.ts index c582946ee8..1876b4d277 100644 --- a/src/chromium/crAccessibility.ts +++ b/src/chromium/crAccessibility.ts @@ -20,9 +20,13 @@ import { Protocol } from './protocol'; import * as dom from '../dom'; import * as accessibility from '../accessibility'; -export async function getAccessibilityTree(client: CRSession) : Promise { +export async function getAccessibilityTree(client: CRSession, needle?: dom.ElementHandle) : Promise<{tree: accessibility.AXNode, needle?: accessibility.AXNode}> { const {nodes} = await client.send('Accessibility.getFullAXTree'); - return CRAXNode.createTree(client, nodes); + const tree = CRAXNode.createTree(client, nodes); + return { + tree, + needle: needle && await tree._findElement(needle) + }; } class CRAXNode implements accessibility.AXNode { @@ -90,7 +94,7 @@ class CRAXNode implements accessibility.AXNode { return this._children; } - async findElement(element: dom.ElementHandle): Promise { + async _findElement(element: dom.ElementHandle): Promise { const remoteObject = element._remoteObject as Protocol.Runtime.RemoteObject; const {node: {backendNodeId}} = await this._client.send('DOM.describeNode', {objectId: remoteObject.objectId}); const needle = this.find(node => node._payload.backendDOMNodeId === backendNodeId); diff --git a/src/chromium/crPage.ts b/src/chromium/crPage.ts index 955e35692b..776809c9cd 100644 --- a/src/chromium/crPage.ts +++ b/src/chromium/crPage.ts @@ -480,8 +480,8 @@ export class CRPage implements PageDelegate { return to._createHandle(result.object).asElement()!; } - async getAccessibilityTree(): Promise { - return getAccessibilityTree(this._client); + async getAccessibilityTree(needle?: dom.ElementHandle) { + return getAccessibilityTree(this._client, needle); } async pdf(options?: types.PDFOptions): Promise { diff --git a/src/firefox/ffAccessibility.ts b/src/firefox/ffAccessibility.ts index 8dd4f49b7d..04518a6f99 100644 --- a/src/firefox/ffAccessibility.ts +++ b/src/firefox/ffAccessibility.ts @@ -18,15 +18,21 @@ import * as accessibility from '../accessibility'; import { FFSession } from './ffConnection'; import { Protocol } from './protocol'; +import * as dom from '../dom'; -export async function getAccessibilityTree(session: FFSession) : Promise { - const { tree } = await session.send('Accessibility.getFullAXTree'); - return new FFAXNode(tree); +export async function getAccessibilityTree(session: FFSession, needle: dom.ElementHandle) : Promise<{tree: accessibility.AXNode, needle?: accessibility.AXNode}> { + const objectId = needle ? needle._remoteObject.objectId : undefined; + const { tree } = await session.send('Accessibility.getFullAXTree', { objectId }); + const axNode = new FFAXNode(tree); + return { + tree: axNode, + needle: needle && axNode._findNeedle() + }; } class FFAXNode implements accessibility.AXNode { _children: FFAXNode[]; - private _payload: any; + private _payload: Protocol.AXTree; private _editable: boolean; private _richlyEditable: boolean; private _focusable: boolean; @@ -77,8 +83,15 @@ class FFAXNode implements accessibility.AXNode { return this._children; } - async findElement(): Promise { - throw new Error('Not implimented'); + _findNeedle(): FFAXNode | null { + if (this._payload.foundObject) + return this; + for (const child of this._children) { + const found = child._findNeedle(); + if (found) + return found; + } + return null; } isLeafNode(): boolean { diff --git a/src/firefox/ffPage.ts b/src/firefox/ffPage.ts index ccf2d61be1..6b834d933f 100644 --- a/src/firefox/ffPage.ts +++ b/src/firefox/ffPage.ts @@ -355,8 +355,8 @@ export class FFPage implements PageDelegate { return handle; } - async getAccessibilityTree() : Promise { - return getAccessibilityTree(this._session); + async getAccessibilityTree(needle?: dom.ElementHandle) { + return getAccessibilityTree(this._session, needle); } coverage(): Coverage | undefined { diff --git a/src/page.ts b/src/page.ts index 47bf3304e0..2af482a45c 100644 --- a/src/page.ts +++ b/src/page.ts @@ -68,7 +68,7 @@ export interface PageDelegate { setInputFiles(handle: dom.ElementHandle, files: types.FilePayload[]): Promise; getBoundingBox(handle: dom.ElementHandle): Promise; - getAccessibilityTree(): Promise; + getAccessibilityTree(needle?: dom.ElementHandle): Promise<{tree: accessibility.AXNode, needle?: accessibility.AXNode}>; pdf?: (options?: types.PDFOptions) => Promise; coverage(): Coverage | undefined; } diff --git a/src/webkit/wkAccessibility.ts b/src/webkit/wkAccessibility.ts index d957f4b988..7a8b45509f 100644 --- a/src/webkit/wkAccessibility.ts +++ b/src/webkit/wkAccessibility.ts @@ -16,10 +16,16 @@ import * as accessibility from '../accessibility'; import { WKSession } from './wkConnection'; import { Protocol } from './protocol'; +import * as dom from '../dom'; -export async function getAccessibilityTree(session: WKSession) { - const {axNode} = await session.send('Page.accessibilitySnapshot'); - return new WKAXNode(axNode); +export async function getAccessibilityTree(session: WKSession, needle?: dom.ElementHandle) { + const objectId = needle ? needle._remoteObject.objectId : undefined; + const {axNode} = await session.send('Page.accessibilitySnapshot', { objectId }); + const tree = new WKAXNode(axNode); + return { + tree, + needle: needle && tree._findNeedle() + }; } class WKAXNode implements accessibility.AXNode { @@ -38,7 +44,14 @@ class WKAXNode implements accessibility.AXNode { return this._children; } - async findElement() { + _findNeedle() : WKAXNode { + if (this._payload.found) + return this; + for (const child of this._children) { + const found = child._findNeedle() + if (found) + return found; + } return null; } diff --git a/src/webkit/wkPage.ts b/src/webkit/wkPage.ts index 6416b8a8f3..276bc7cf8d 100644 --- a/src/webkit/wkPage.ts +++ b/src/webkit/wkPage.ts @@ -497,8 +497,8 @@ export class WKPage implements PageDelegate { return to._createHandle(result.object) as dom.ElementHandle; } - async getAccessibilityTree() : Promise { - return getAccessibilityTree(this._session); + async getAccessibilityTree(needle?: dom.ElementHandle) : Promise<{tree: accessibility.AXNode, needle?: accessibility.AXNode}> { + return getAccessibilityTree(this._session, needle); } coverage(): Coverage | undefined { diff --git a/test/accessibility.spec.js b/test/accessibility.spec.js index 7c13afd9c0..9a37a52b01 100644 --- a/test/accessibility.spec.js +++ b/test/accessibility.spec.js @@ -363,8 +363,8 @@ module.exports.describe = function({testRunner, expect, FFOX, CHROMIUM, WEBKIT}) expect(snapshot.children[0]).toEqual(golden); }); - describe.skip(FFOX || WEBKIT)('root option', function() { - it('should work a button', async({page}) => { + describe('root option', function() { + fit('should work a button', async({page}) => { await page.setContent(``); const button = await page.$('button');