From 36b77efdc0555ed8a87069ec201675e0ba3eeaf6 Mon Sep 17 00:00:00 2001 From: Joel Einbinder Date: Tue, 14 Jan 2020 13:35:31 -0800 Subject: [PATCH] normalize all the test output --- src/accessibility.ts | 9 +- src/firefox/ffAccessibility.ts | 29 ++++++- src/webkit/wkAccessibility.ts | 44 +++++++++- test/accessibility.spec.js | 149 +++++++-------------------------- 4 files changed, 103 insertions(+), 128 deletions(-) diff --git a/src/accessibility.ts b/src/accessibility.ts index 058e771a32..11ff5f743a 100644 --- a/src/accessibility.ts +++ b/src/accessibility.ts @@ -73,14 +73,17 @@ export class Accessibility { root = null, } = options; const {tree, needle} = await this._getAXTree(root); - if (!interestingOnly) - return serializeTree(needle)[0]; + if (!interestingOnly) { + if (root) + return needle && serializeTree(needle)[0]; + return serializeTree(tree)[0]; + } const interestingNodes: Set = new Set(); collectInterestingNodes(interestingNodes, tree, false); if (root && !interestingNodes.has(needle)) return null; - return serializeTree(needle, interestingNodes)[0]; + return serializeTree(needle || tree, interestingNodes)[0]; } } diff --git a/src/firefox/ffAccessibility.ts b/src/firefox/ffAccessibility.ts index 04518a6f99..015026c7fd 100644 --- a/src/firefox/ffAccessibility.ts +++ b/src/firefox/ffAccessibility.ts @@ -30,6 +30,33 @@ export async function getAccessibilityTree(session: FFSession, needle: dom.Eleme }; } +const FFRoleToARIARole = new Map(Object.entries({ + 'pushbutton': 'button', + 'checkbutton': 'checkbox', + 'editcombobox': 'combobox', + 'content deletion': 'deletion', + 'footnote': 'doc-footnote', + 'non-native document': 'document', + 'grouping': 'group', + 'graphic': 'img', + 'content insertion': 'insertion', + 'animation': 'marquee', + 'flat equation': 'math', + 'menupopup': 'menu', + 'check menu item': 'menuitemcheckbox', + 'radio menu item': 'menuitemradio', + 'listbox option': 'option', + 'radiobutton': 'radio', + 'statusbar': 'status', + 'pagetab': 'tab', + 'pagetablist': 'tablist', + 'propertypage': 'tabpanel', + 'entry': 'textbox', + 'outline': 'tree', + 'tree table': 'treegrid', + 'outlineitem': 'treeitem', +})); + class FFAXNode implements accessibility.AXNode { _children: FFAXNode[]; private _payload: Protocol.AXTree; @@ -173,7 +200,7 @@ class FFAXNode implements accessibility.AXNode { serialize(): accessibility.SerializedAXNode { const node: {[x in keyof accessibility.SerializedAXNode]: any} = { - role: this._role, + role: FFRoleToARIARole.get(this._role) || this._role, name: this._name || '' }; const userStringProperties: Array = [ diff --git a/src/webkit/wkAccessibility.ts b/src/webkit/wkAccessibility.ts index 7a8b45509f..012a549b28 100644 --- a/src/webkit/wkAccessibility.ts +++ b/src/webkit/wkAccessibility.ts @@ -28,6 +28,30 @@ export async function getAccessibilityTree(session: WKSession, needle?: dom.Elem }; } +const WKRoleToARIARole = new Map(Object.entries({ + 'TextField': 'textbox', +})); + +// WebKit localizes role descriptions on mac, but the english versions only add noise. +const WKUnhelpfulRoleDescriptions = new Map(Object.entries({ + 'WebArea': 'HTML content', + 'Summary': 'summary', + 'DescriptionList': 'description list', + 'ImageMap': 'image map', + 'ListMarker': 'list marker', + 'Video': 'video playback', + 'Mark': 'highlighted', + 'contentinfo': 'content information', + 'Details': 'details', + 'DescriptionListDetail': 'description', + 'DescriptionListTerm': 'term', + 'alertdialog': 'web alert dialog', + 'dialog': 'web dialog', + 'status': 'application status', + 'tabpanel': 'tab panel', + 'application': 'web application', +})); + class WKAXNode implements accessibility.AXNode { private _payload: Protocol.Page.AXNode; private _children: WKAXNode[]; @@ -48,7 +72,7 @@ class WKAXNode implements accessibility.AXNode { if (this._payload.found) return this; for (const child of this._children) { - const found = child._findNeedle() + const found = child._findNeedle(); if (found) return found; } @@ -111,15 +135,27 @@ class WKAXNode implements accessibility.AXNode { serialize(): accessibility.SerializedAXNode { const node : accessibility.SerializedAXNode = { - role: this._payload.role, + role: WKRoleToARIARole.get(this._payload.role) || this._payload.role, name: this._payload.name || '', }; + if ('description' in this._payload && this._payload.description !== node.name) + node.description = this._payload.description; + + if ('roledescription' in this._payload) { + const roledescription = this._payload.roledescription; + if (roledescription !== this._payload.role && WKUnhelpfulRoleDescriptions.get(this._payload.role) !== roledescription) + node.roledescription = roledescription; + } + + type AXPropertyOfType = { + [Key in keyof Protocol.Page.AXNode]: + Protocol.Page.AXNode[Key] extends Type ? Key : never + }[keyof Protocol.Page.AXNode]; + const userStringProperties: string[] = [ 'value', - 'description', 'keyshortcuts', - 'roledescription', 'valuetext' ]; for (const userStringProperty of userStringProperties) { diff --git a/test/accessibility.spec.js b/test/accessibility.spec.js index 9a37a52b01..78eb527d3d 100644 --- a/test/accessibility.spec.js +++ b/test/accessibility.spec.js @@ -21,7 +21,7 @@ module.exports.describe = function({testRunner, expect, FFOX, CHROMIUM, WEBKIT}) const {beforeAll, beforeEach, afterAll, afterEach} = testRunner; describe('Accessibility', function() { - it.skip(WEBKIT)('should work', async function({page}) { + it('should work', async function({page}) { await page.setContent(` Accessibility Test @@ -51,13 +51,13 @@ module.exports.describe = function({testRunner, expect, FFOX, CHROMIUM, WEBKIT}) children: [ {role: 'text leaf', name: 'Hello World'}, {role: 'heading', name: 'Inputs', level: 1}, - {role: 'entry', name: 'Empty input', focused: true}, - {role: 'entry', name: 'readonly input', readonly: true}, - {role: 'entry', name: 'disabled input', disabled: true}, - {role: 'entry', name: 'Input with whitespace', value: ' '}, - {role: 'entry', name: '', value: 'value only'}, - {role: 'entry', name: '', value: 'and a value'}, // firefox doesn't use aria-placeholder for the name - {role: 'entry', name: '', value: 'and a value', description: 'This is a description!'}, // and here + {role: 'textbox', name: 'Empty input', focused: true}, + {role: 'textbox', name: 'readonly input', readonly: true}, + {role: 'textbox', name: 'disabled input', disabled: true}, + {role: 'textbox', name: 'Input with whitespace', value: ' '}, + {role: 'textbox', name: '', value: 'value only'}, + {role: 'textbox', name: '', value: 'and a value'}, // firefox doesn't use aria-placeholder for the name + {role: 'textbox', name: '', value: 'and a value', description: 'This is a description!'}, // and here {role: 'combobox', name: '', value: 'First Option', haspopup: true, children: [ {role: 'combobox option', name: 'First Option', selected: true}, {role: 'combobox option', name: 'Second Option'}] @@ -83,14 +83,14 @@ module.exports.describe = function({testRunner, expect, FFOX, CHROMIUM, WEBKIT}) role: 'WebArea', name: 'Accessibility Test', children: [ - {role: 'heading', name: 'Inputs', level: 1 }, - {role: 'TextField', name: 'Empty input', focused: true, readonly: true}, - {role: 'TextField', name: 'readonly input', readonly: true }, - {role: 'TextField', name: 'disabled input', disabled: true, readonly: true}, - {role: 'TextField', name: 'Input with whitespace', value: ' ', description: 'Input with whitespace', readonly: true}, - {role: 'TextField', name: '', value: 'value only', readonly: true }, - {role: 'TextField', name: 'placeholder',value: 'and a value',readonly: true}, - {role: 'TextField', name: 'This is a description!',value: 'and a value',readonly: true}, + {role: 'heading', name: 'Inputs', level: 1}, + {role: 'textbox', name: 'Empty input', focused: true}, + {role: 'textbox', name: 'readonly input', readonly: true}, + {role: 'textbox', name: 'disabled input', disabled: true}, + {role: 'textbox', name: 'Input with whitespace', value: ' ' }, + {role: 'textbox', name: '', value: 'value only' }, + {role: 'textbox', name: 'placeholder',value: 'and a value'}, + {role: 'textbox', name: 'This is a description!',value: 'and a value'}, // webkit uses the description over placeholder for the name {role: 'button', name: '', value: 'First Option', children: [ { role: 'MenuListOption', name: '', value: 'First Option', selected: true }, { role: 'MenuListOption', name: '', value: 'Second Option' }] @@ -99,42 +99,6 @@ module.exports.describe = function({testRunner, expect, FFOX, CHROMIUM, WEBKIT}) }; expect(await page.accessibility.snapshot()).toEqual(golden); }); - it.skip(WEBKIT)('should report uninteresting nodes', async function({page}) { - await page.setContent(``); - // autofocus happens after a delay in chrome these days - await page.waitForFunction(() => document.activeElement.hasAttribute('autofocus')); - const golden = FFOX ? { - role: 'entry', - name: '', - value: 'hi', - focused: true, - multiline: true, - children: [{ - role: 'text leaf', - name: 'hi' - }] - } : CHROMIUM ? { - role: 'textbox', - name: '', - value: 'hi', - focused: true, - multiline: true, - children: [{ - role: 'generic', - name: '', - children: [{ - role: 'text', name: 'hi' - }] - }] - } : { - role: 'textbox', - name: '', - value: 'hi', - focused: true, - multiline: true - }; - expect(findFocusedNode(await page.accessibility.snapshot({interestingOnly: false}))).toEqual(golden); - }); it('roledescription', async({page}) => { await page.setContent('
Hi
'); const snapshot = await page.accessibility.snapshot(); @@ -145,8 +109,8 @@ module.exports.describe = function({testRunner, expect, FFOX, CHROMIUM, WEBKIT}) const snapshot = await page.accessibility.snapshot(); expect(snapshot.children[0].orientation).toEqual('vertical'); }); - it.skip(FFOX || WEBKIT)('autocomplete', async({page}) => { - await page.setContent(''); + it('autocomplete', async({page}) => { + await page.setContent('
hi
'); const snapshot = await page.accessibility.snapshot(); expect(snapshot.children[0].autocomplete).toEqual('list'); }); @@ -167,33 +131,8 @@ module.exports.describe = function({testRunner, expect, FFOX, CHROMIUM, WEBKIT})
Tab1
Tab2
`); - const golden = FFOX ? { - role: 'document', - name: '', - children: [{ - role: 'pagetab', - name: 'Tab1', - selected: true - }, { - role: 'pagetab', - name: 'Tab2' - }] - } : WEBKIT ? { - role: 'WebArea', - name: '', - roledescription: 'HTML content', - children: [{ - role: 'tab', - name: 'Tab1', - roledescription: 'tab', - selected: true - }, { - role: 'tab', - name: 'Tab2', - roledescription: 'tab', - }] - } : { - role: 'WebArea', + const golden = { + role: FFOX ? 'document' : 'WebArea', name: '', children: [{ role: 'tab', @@ -244,7 +183,7 @@ module.exports.describe = function({testRunner, expect, FFOX, CHROMIUM, WEBKIT}) Edit this image: my fake image `); const golden = FFOX ? { - role: 'entry', + role: 'textbox', name: '', value: 'Edit this image: my fake image', children: [{ @@ -305,7 +244,7 @@ module.exports.describe = function({testRunner, expect, FFOX, CHROMIUM, WEBKIT}) yo `); const golden = FFOX ? { - role: 'entry', + role: 'textbox', name: 'my favorite textbox', value: 'this is the inner content yo' } : CHROMIUM ? { @@ -313,10 +252,9 @@ module.exports.describe = function({testRunner, expect, FFOX, CHROMIUM, WEBKIT}) name: 'my favorite textbox', value: 'this is the inner content ' } : { - role: 'TextField', + role: 'textbox', name: 'my favorite textbox', value: 'this is the inner content ', - description: 'my favorite textbox' }; const snapshot = await page.accessibility.snapshot(); expect(snapshot.children[0]).toEqual(golden); @@ -327,19 +265,10 @@ module.exports.describe = function({testRunner, expect, FFOX, CHROMIUM, WEBKIT}) this is the inner content yo `); - const golden = FFOX ? { - role: 'checkbutton', - name: 'my favorite checkbox', - checked: true - } : CHROMIUM ? { + const golden = { role: 'checkbox', name: 'my favorite checkbox', checked: true - } : { - role: 'checkbox', - name: 'my favorite checkbox', - description: "my favorite checkbox", - checked: true }; const snapshot = await page.accessibility.snapshot(); expect(snapshot.children[0]).toEqual(golden); @@ -351,7 +280,7 @@ module.exports.describe = function({testRunner, expect, FFOX, CHROMIUM, WEBKIT}) yo `); const golden = FFOX ? { - role: 'checkbutton', + role: 'checkbox', name: 'this is the inner content yo', checked: true } : { @@ -364,7 +293,7 @@ module.exports.describe = function({testRunner, expect, FFOX, CHROMIUM, WEBKIT}) }); describe('root option', function() { - fit('should work a button', async({page}) => { + it('should work a button', async({page}) => { await page.setContent(``); const button = await page.$('button'); @@ -383,7 +312,7 @@ module.exports.describe = function({testRunner, expect, FFOX, CHROMIUM, WEBKIT}) value: 'My Value' }); }); - it('should work a menu', async({page}) => { + it('should work on a menu', async({page}) => { await page.setContent(`
First Item
@@ -399,7 +328,8 @@ module.exports.describe = function({testRunner, expect, FFOX, CHROMIUM, WEBKIT}) children: [ { role: 'menuitem', name: 'First Item' }, { role: 'menuitem', name: 'Second Item' }, - { role: 'menuitem', name: 'Third Item' } ] + { role: 'menuitem', name: 'Third Item' } ], + orientation: WEBKIT ? 'vertical' : undefined }); }); it('should return null when the element is no longer in DOM', async({page}) => { @@ -408,27 +338,6 @@ module.exports.describe = function({testRunner, expect, FFOX, CHROMIUM, WEBKIT}) await page.$eval('button', button => button.remove()); expect(await page.accessibility.snapshot({root: button})).toEqual(null); }); - it('should support the interestingOnly option', async({page}) => { - await page.setContent(`
`); - const div = await page.$('div'); - expect(await page.accessibility.snapshot({root: div})).toEqual(null); - expect(await page.accessibility.snapshot({root: div, interestingOnly: false})).toEqual({ - role: 'generic', - name: '', - children: [ - { - role: 'button', - name: 'My Button', - children: [ - { - role: "text", - name: "My Button" - } - ], - } - ] - }); - }); }); }); function findFocusedNode(node) {