From ca01645d61d9aeef7dab7303dc68f1abb758b896 Mon Sep 17 00:00:00 2001 From: Max Schmitt Date: Thu, 16 Jul 2020 17:45:17 +0200 Subject: [PATCH] tests: added accessibility tests --- playwright/accessibility.py | 4 +- tests/test_accessibility.py | 416 ++++++++++++++++++++++++++++++++++++ 2 files changed, 418 insertions(+), 2 deletions(-) create mode 100644 tests/test_accessibility.py diff --git a/playwright/accessibility.py b/playwright/accessibility.py index ddc6dfc7b..44d0c4549 100644 --- a/playwright/accessibility.py +++ b/playwright/accessibility.py @@ -22,9 +22,9 @@ def __init__(self, channel: Channel) -> None: self._channel = channel async def snapshot( - self, interestingOnly: bool = None, root: ElementHandle = None + self, interestingOnly: bool = True, root: ElementHandle = None ) -> Dict: root = root._channel if root else None return await self._channel.send( - "snapshot", dict(root=root, interestingOnly=interestingOnly) + "accessibilitySnapshot", dict(root=root, interestingOnly=interestingOnly) ) diff --git a/tests/test_accessibility.py b/tests/test_accessibility.py new file mode 100644 index 000000000..c94846ddb --- /dev/null +++ b/tests/test_accessibility.py @@ -0,0 +1,416 @@ +# 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 pytest + + +async def test_accessibility_should_work(page, is_firefox, is_chromium): + await page.setContent( + """ + Accessibility Test + + +

Inputs

+ + + + + + + + + """ + ) + # autofocus happens after a delay in chrome these days + await page.waitForFunction("document.activeElement.hasAttribute('autofocus')") + + if is_firefox: + golden = { + "role": "document", + "name": "Accessibility Test", + "children": [ + {"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": "", + "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 + ], + } + elif is_chromium: + golden = { + "role": "WebArea", + "name": "Accessibility Test", + "children": [ + {"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!", + }, + ], + } + else: + golden = { + "role": "WebArea", + "name": "Accessibility Test", + "children": [ + {"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 + ], + } + assert await page.accessibility.snapshot() == golden + + +async def test_accessibility_should_work_with_regular_text(page, is_firefox): + await page.setContent("
Hello World
") + snapshot = await page.accessibility.snapshot() + assert snapshot["children"][0] == { + "role": "text leaf" if is_firefox else "text", + "name": "Hello World", + } + + +async def test_accessibility_roledescription(page): + await page.setContent('
Hi
') + snapshot = await page.accessibility.snapshot() + assert snapshot["children"][0]["roledescription"] == "foo" + + +async def test_accessibility_orientation(page): + await page.setContent('11') + snapshot = await page.accessibility.snapshot() + assert snapshot["children"][0]["orientation"] == "vertical" + + +async def test_accessibility_autocomplete(page): + await page.setContent('
hi
') + snapshot = await page.accessibility.snapshot() + assert snapshot["children"][0]["autocomplete"] == "list" + + +async def test_accessibility_multiselectable(page): + await page.setContent( + '
hey
' + ) + snapshot = await page.accessibility.snapshot() + assert snapshot["children"][0]["multiselectable"] + + +async def test_accessibility_keyshortcuts(page): + await page.setContent( + '
hey
' + ) + snapshot = await page.accessibility.snapshot() + assert snapshot["children"][0]["keyshortcuts"] == "foo" + + +async def test_accessibility_filtering_children_of_leaf_nodes_should_not_report_text_nodes_inside_controls( + page, is_firefox +): + await page.setContent( + """ +
+
Tab1
+
Tab2
+
""" + ) + golden = { + "role": "document" if is_firefox else "WebArea", + "name": "", + "children": [ + {"role": "tab", "name": "Tab1", "selected": True}, + {"role": "tab", "name": "Tab2"}, + ], + } + assert await page.accessibility.snapshot() == golden + + +# WebKit rich text accessibility is iffy +@pytest.mark.skip_browser("webkit") +async def test_accessibility_filtering_children_of_leaf_nodes_rich_text_editable_fields_should_have_children( + page, is_firefox +): + await page.setContent( + """ +
+ Edit this image: my fake image +
""" + ) + golden = ( + { + "role": "section", + "name": "", + "children": [ + {"role": "text leaf", "name": "Edit this image: "}, + {"role": "text", "name": "my fake image"}, + ], + } + if is_firefox + else { + "role": "generic", + "name": "", + "value": "Edit this image: ", + "children": [ + {"role": "text", "name": "Edit this image:"}, + {"role": "img", "name": "my fake image"}, + ], + } + ) + snapshot = await page.accessibility.snapshot() + assert snapshot["children"][0] == golden + + +# WebKit rich text accessibility is iffy +@pytest.mark.skip_browser("webkit") +async def test_accessibility_filtering_children_of_leaf_nodes_rich_text_editable_fields_with_role_should_have_children( + page, is_firefox, +): + await page.setContent( + """ +
+ Edit this image: my fake image +
""" + ) + if is_firefox: + golden = { + "role": "textbox", + "name": "", + "value": "Edit this image: my fake image", + "children": [{"role": "text", "name": "my fake image"}], + } + else: + golden = { + "role": "textbox", + "name": "", + "value": "Edit this image: ", + "children": [ + {"role": "text", "name": "Edit this image:"}, + {"role": "img", "name": "my fake image"}, + ], + } + snapshot = await page.accessibility.snapshot() + assert snapshot["children"][0] == golden + + +# Firefox does not support contenteditable="plaintext-only". +# WebKit rich text accessibility is iffy +@pytest.mark.only_browser("chromium") +async def test_accessibility_plain_text_field_with_role_should_not_have_children(page): + await page.setContent( + """ +
Edit this image:my fake image
""" + ) + snapshot = await page.accessibility.snapshot() + assert snapshot["children"][0] == { + "role": "textbox", + "name": "", + "value": "Edit this image:", + } + + +@pytest.mark.only_browser("chromium") +async def test_accessibility_plain_text_field_without_role_should_not_have_content( + page, +): + await page.setContent( + """ +
Edit this image:my fake image
""" + ) + snapshot = await page.accessibility.snapshot() + assert snapshot["children"][0] == {"role": "generic", "name": ""} + + +@pytest.mark.only_browser("chromium") +async def test_accessibility_plain_text_field_with_tabindex_and_without_role_should_not_have_content( + page, +): + await page.setContent( + """ +
Edit this image:my fake image
""" + ) + snapshot = await page.accessibility.snapshot() + assert snapshot["children"][0] == {"role": "generic", "name": ""} + + +async def test_accessibility_non_editable_textbox_with_role_and_tabIndex_and_label_should_not_have_children( + page, is_chromium, is_firefox +): + await page.setContent( + """ +
+ this is the inner content + yo +
""" + ) + if is_firefox: + golden = { + "role": "textbox", + "name": "my favorite textbox", + "value": "this is the inner content yo", + } + elif is_chromium: + golden = { + "role": "textbox", + "name": "my favorite textbox", + "value": "this is the inner content ", + } + else: + golden = { + "role": "textbox", + "name": "my favorite textbox", + "value": "this is the inner content ", + } + snapshot = await page.accessibility.snapshot() + assert snapshot["children"][0] == golden + + +async def test_accessibility_checkbox_with_and_tabIndex_and_label_should_not_have_children( + page, +): + await page.setContent( + """ +
+ this is the inner content + yo +
""" + ) + golden = {"role": "checkbox", "name": "my favorite checkbox", "checked": True} + snapshot = await page.accessibility.snapshot() + assert snapshot["children"][0] == golden + + +async def test_accessibility_checkbox_without_label_should_not_have_children( + page, is_firefox +): + await page.setContent( + """ +
+ this is the inner content + yo +
""" + ) + golden = { + "role": "checkbox", + "name": "this is the inner content yo", + "checked": True, + } + snapshot = await page.accessibility.snapshot() + assert snapshot["children"][0] == golden + + +async def test_accessibility_should_work_a_button(page): + await page.setContent("") + + button = await page.querySelector("button") + assert await page.accessibility.snapshot(root=button) == { + "role": "button", + "name": "My Button", + } + + +async def test_accessibility_should_work_an_input(page): + await page.setContent('') + + input = await page.querySelector("input") + assert await page.accessibility.snapshot(root=input) == { + "role": "textbox", + "name": "My Input", + "value": "My Value", + } + + +async def test_accessibility_should_work_on_a_menu(page, is_webkit): + await page.setContent( + """ +
+
First Item
+
Second Item
+
Third Item
+
+ """ + ) + + menu = await page.querySelector('div[role="menu"]') + golden = { + "role": "menu", + "name": "My Menu", + "children": [ + {"role": "menuitem", "name": "First Item"}, + {"role": "menuitem", "name": "Second Item"}, + {"role": "menuitem", "name": "Third Item"}, + ], + } + if is_webkit: + golden["orientation"] = "vertical" + assert await page.accessibility.snapshot(root=menu) == golden + + +async def test_accessibility_should_return_null_when_the_element_is_no_longer_in_DOM( + page, +): + await page.setContent("") + button = await page.querySelector("button") + await page.evalOnSelector("button", "button => button.remove()") + assert await page.accessibility.snapshot(root=button) is None + + +async def test_accessibility_should_show_uninteresting_nodes(page): + await page.setContent( + """ +
+
+ hello +
+ world +
+
+
+ """ + ) + + root = await page.querySelector("#root") + snapshot = await page.accessibility.snapshot(root=root, interestingOnly=False) + assert snapshot["role"] == "textbox" + assert "hello" in snapshot["value"] + assert "world" in snapshot["value"] + assert snapshot["children"]