/** * Copyright 2018 Google Inc. All rights reserved. * * 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 expect from 'expect'; import { getTestState, setupTestBrowserHooks, setupTestPageAndContextHooks, describeFailsFirefox, } from './mocha-utils'; // eslint-disable-line import/extensions describeFailsFirefox('Accessibility', function () { setupTestBrowserHooks(); setupTestPageAndContextHooks(); it('should work', async () => { const { page, isFirefox } = getTestState(); await page.setContent(` Accessibility Test
Hello World

Inputs

`); await page.focus('[placeholder="Empty input"]'); const golden = isFirefox ? { role: 'document', name: 'Accessibility Test', 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: 'combobox', name: '', value: 'First Option', haspopup: true, children: [ { role: 'combobox option', name: 'First Option', selected: true, }, { role: 'combobox option', name: 'Second Option' }, ], }, ], } : { role: 'RootWebArea', name: 'Accessibility Test', children: [ { role: 'StaticText', name: 'Hello World' }, { 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: 'placeholder', value: 'and a value', description: 'This is a description!', }, { role: 'combobox', name: '', value: 'First Option', haspopup: 'menu', children: [ { role: 'menuitem', name: 'First Option', selected: true }, { role: 'menuitem', name: 'Second Option' }, ], }, ], }; expect(await page.accessibility.snapshot()).toEqual(golden); }); it('should report uninteresting nodes', async () => { const { page, isFirefox } = getTestState(); await page.setContent(``); await page.focus('textarea'); const golden = isFirefox ? { role: 'entry', name: '', value: 'hi', focused: true, multiline: true, children: [ { role: 'text leaf', name: 'hi', }, ], } : { role: 'textbox', name: '', value: 'hi', focused: true, multiline: true, children: [ { role: 'generic', name: '', children: [ { role: 'StaticText', name: 'hi', }, ], }, ], }; expect( findFocusedNode( await page.accessibility.snapshot({ interestingOnly: false }) ) ).toEqual(golden); }); it('roledescription', async () => { const { page } = getTestState(); await page.setContent( '
Hi
' ); const snapshot = await page.accessibility.snapshot(); // See https://chromium-review.googlesource.com/c/chromium/src/+/3088862 expect(snapshot.children[0].roledescription).toEqual(undefined); }); it('orientation', async () => { const { page } = getTestState(); await page.setContent( '11' ); const snapshot = await page.accessibility.snapshot(); expect(snapshot.children[0].orientation).toEqual('vertical'); }); it('autocomplete', async () => { const { page } = getTestState(); await page.setContent(''); const snapshot = await page.accessibility.snapshot(); expect(snapshot.children[0].autocomplete).toEqual('list'); }); it('multiselectable', async () => { const { page } = getTestState(); await page.setContent( '
hey
' ); const snapshot = await page.accessibility.snapshot(); expect(snapshot.children[0].multiselectable).toEqual(true); }); it('keyshortcuts', async () => { const { page } = getTestState(); await page.setContent( '
hey
' ); const snapshot = await page.accessibility.snapshot(); expect(snapshot.children[0].keyshortcuts).toEqual('foo'); }); describe('filtering children of leaf nodes', function () { it('should not report text nodes inside controls', async () => { const { page, isFirefox } = getTestState(); await page.setContent(`
Tab1
Tab2
`); const golden = isFirefox ? { role: 'document', name: '', children: [ { role: 'pagetab', name: 'Tab1', selected: true, }, { role: 'pagetab', name: 'Tab2', }, ], } : { role: 'RootWebArea', name: '', children: [ { role: 'tab', name: 'Tab1', selected: true, }, { role: 'tab', name: 'Tab2', }, ], }; expect(await page.accessibility.snapshot()).toEqual(golden); }); it('rich text editable fields should have children', async () => { const { page, isFirefox } = getTestState(); await page.setContent(`
Edit this image: my fake image
`); const golden = isFirefox ? { role: 'section', name: '', children: [ { role: 'text leaf', name: 'Edit this image: ', }, { role: 'StaticText', name: 'my fake image', }, ], } : { role: 'generic', name: '', value: 'Edit this image: ', children: [ { role: 'StaticText', name: 'Edit this image:', }, { role: 'img', name: 'my fake image', }, ], }; const snapshot = await page.accessibility.snapshot(); expect(snapshot.children[0]).toEqual(golden); }); it('rich text editable fields with role should have children', async () => { const { page, isFirefox } = getTestState(); await page.setContent(`
Edit this image: my fake image
`); const golden = isFirefox ? { role: 'entry', name: '', value: 'Edit this image: my fake image', children: [ { role: 'StaticText', name: 'my fake image', }, ], } : { role: 'textbox', name: '', value: 'Edit this image: ', multiline: true, children: [ { role: 'StaticText', name: 'Edit this image:', }, { role: 'img', name: 'my fake image', }, ], }; const snapshot = await page.accessibility.snapshot(); expect(snapshot.children[0]).toEqual(golden); }); // Firefox does not support contenteditable="plaintext-only". describeFailsFirefox('plaintext contenteditable', function () { it('plain text field with role should not have children', async () => { const { page } = getTestState(); await page.setContent(`
Edit this image:my fake image
`); const snapshot = await page.accessibility.snapshot(); expect(snapshot.children[0]).toEqual({ role: 'textbox', name: '', value: 'Edit this image:', multiline: true, }); }); }); it('non editable textbox with role and tabIndex and label should not have children', async () => { const { page, isFirefox } = getTestState(); await page.setContent(`
this is the inner content yo
`); const golden = isFirefox ? { role: 'entry', name: 'my favorite textbox', value: 'this is the inner content yo', } : { role: 'textbox', name: 'my favorite textbox', value: 'this is the inner content ', }; const snapshot = await page.accessibility.snapshot(); expect(snapshot.children[0]).toEqual(golden); }); it('checkbox with and tabIndex and label should not have children', async () => { const { page, isFirefox } = getTestState(); await page.setContent(`
this is the inner content yo
`); const golden = isFirefox ? { role: 'checkbutton', name: 'my favorite checkbox', checked: true, } : { role: 'checkbox', name: 'my favorite checkbox', checked: true, }; const snapshot = await page.accessibility.snapshot(); expect(snapshot.children[0]).toEqual(golden); }); it('checkbox without label should not have children', async () => { const { page, isFirefox } = getTestState(); await page.setContent(`
this is the inner content yo
`); const golden = isFirefox ? { role: 'checkbutton', name: 'this is the inner content yo', checked: true, } : { role: 'checkbox', name: 'this is the inner content yo', checked: true, }; const snapshot = await page.accessibility.snapshot(); expect(snapshot.children[0]).toEqual(golden); }); describe('root option', function () { it('should work a button', async () => { const { page } = getTestState(); await page.setContent(``); const button = await page.$('button'); expect(await page.accessibility.snapshot({ root: button })).toEqual({ role: 'button', name: 'My Button', }); }); it('should work an input', async () => { const { page } = getTestState(); await page.setContent(``); const input = await page.$('input'); expect(await page.accessibility.snapshot({ root: input })).toEqual({ role: 'textbox', name: 'My Input', value: 'My Value', }); }); it('should work a menu', async () => { const { page } = getTestState(); await page.setContent(`
First Item
Second Item
Third Item
`); const menu = await page.$('div[role="menu"]'); expect(await page.accessibility.snapshot({ root: menu })).toEqual({ role: 'menu', name: 'My Menu', children: [ { role: 'menuitem', name: 'First Item' }, { role: 'menuitem', name: 'Second Item' }, { role: 'menuitem', name: 'Third Item' }, ], orientation: 'vertical', }); }); it('should return null when the element is no longer in DOM', async () => { const { page } = getTestState(); await page.setContent(``); const button = await page.$('button'); await page.$eval('button', (button) => button.remove()); expect(await page.accessibility.snapshot({ root: button })).toEqual( null ); }); it('should support the interestingOnly option', async () => { const { page } = getTestState(); 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: 'StaticText', name: 'My Button' }], }, ], }); }); }); }); function findFocusedNode(node) { if (node.focused) { return node; } for (const child of node.children || []) { const focusedChild = findFocusedNode(child); if (focusedChild) { return focusedChild; } } return null; } });