tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

test_accessibility.py (8995B)


      1 # This Source Code Form is subject to the terms of the Mozilla Public
      2 # License, v. 2.0. If a copy of the MPL was not distributed with this
      3 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
      4 
      5 import sys
      6 import unittest
      7 
      8 from marionette_driver.by import By
      9 from marionette_driver.errors import (
     10    ElementNotAccessibleException,
     11    ElementNotInteractableException,
     12    ElementClickInterceptedException,
     13 )
     14 
     15 from marionette_harness import MarionetteTestCase
     16 
     17 
     18 class TestAccessibility(MarionetteTestCase):
     19    def setUp(self):
     20        super(TestAccessibility, self).setUp()
     21        self.marionette.set_pref("dom.ipc.processCount", 1)
     22 
     23    def tearDown(self):
     24        self.marionette.clear_pref("dom.ipc.processCount")
     25 
     26    # Elements that are accessible with and without the accessibliity API
     27    valid_elementIDs = [
     28        # Button1 is an accessible button with a valid accessible name
     29        # computed from subtree
     30        "button1",
     31        # Button2 is an accessible button with a valid accessible name
     32        # computed from aria-label
     33        "button2",
     34        # Button13 is an accessible button that is implemented via role="button"
     35        # and is explorable using tabindex="0"
     36        "button13",
     37        # button17 is an accessible button that overrides parent's
     38        # pointer-events:none; property with its own pointer-events:all;
     39        "button17",
     40    ]
     41 
     42    # Elements that are not accessible with the accessibility API
     43    invalid_elementIDs = [
     44        # Button3 does not have an accessible object
     45        "button3",
     46        # Button4 does not support any accessible actions
     47        "button4",
     48        # Button5 does not have a correct accessibility role and may not be
     49        # manipulated via the accessibility API
     50        "button5",
     51        # Button6 is missing an accessible name
     52        "button6",
     53        # Button7 is not currently visible via the accessibility API and may
     54        # not be manipulated by it
     55        "button7",
     56        # Button8 is not currently visible via the accessibility API and may
     57        # not be manipulated by it (in hidden subtree)
     58        "button8",
     59        # Button14 is accessible button but is not explorable because of lack
     60        # of tabindex that would make it focusable.
     61        "button14",
     62    ]
     63 
     64    # Elements that are either accessible to accessibility API or not accessible
     65    # at all
     66    falsy_elements = [
     67        # Element is only visible to the accessibility API and may be
     68        # manipulated by it
     69        "button9",
     70        # Element is not currently visible
     71        "button10",
     72    ]
     73 
     74    displayed_elementIDs = ["button1", "button2", "button4", "button5", "button6"]
     75 
     76    displayed_but_have_no_accessible_elementIDs = [
     77        # Button3 does not have an accessible object
     78        "button3",
     79        # Button 7 is hidden with aria-hidden set to true
     80        "button7",
     81        # Button 8 is inside an element with aria-hidden set to true
     82        "button8",
     83        "no_accessible_but_displayed",
     84    ]
     85 
     86    disabled_elementIDs = ["button11", "no_accessible_but_disabled"]
     87 
     88    # Elements that are enabled but otherwise disabled or not explorable
     89    # via the accessibility API
     90    aria_disabled_elementIDs = ["button12"]
     91 
     92    # pointer-events: "none", which will return
     93    # ElementClickInterceptedException if clicked
     94    # when Marionette switches
     95    # to using WebDriver conforming interaction
     96    pointer_events_none_elementIDs = ["button15", "button16"]
     97 
     98    # Elements that are reporting selected state
     99    valid_option_elementIDs = ["option1", "option2"]
    100 
    101    def run_element_test(self, ids, testFn):
    102        for id in ids:
    103            element = self.marionette.find_element(By.ID, id)
    104            testFn(element)
    105 
    106    def setup_accessibility(self, enable_a11y_checks=True, navigate=True):
    107        self.marionette.delete_session()
    108        self.marionette.start_session({"moz:accessibilityChecks": enable_a11y_checks})
    109        self.assertEqual(
    110            self.marionette.session_capabilities["moz:accessibilityChecks"],
    111            enable_a11y_checks,
    112        )
    113 
    114        # Navigate to test_accessibility.html
    115        if navigate:
    116            test_accessibility = self.marionette.absolute_url("test_accessibility.html")
    117            self.marionette.navigate(test_accessibility)
    118 
    119    def test_valid_click(self):
    120        self.setup_accessibility()
    121        # No exception should be raised
    122        self.run_element_test(self.valid_elementIDs, lambda button: button.click())
    123 
    124    def test_click_raises_element_not_accessible(self):
    125        self.setup_accessibility()
    126        self.run_element_test(
    127            self.invalid_elementIDs,
    128            lambda button: self.assertRaises(
    129                ElementNotAccessibleException, button.click
    130            ),
    131        )
    132        self.run_element_test(
    133            self.falsy_elements,
    134            lambda button: self.assertRaises(
    135                ElementNotInteractableException, button.click
    136            ),
    137        )
    138 
    139    def test_click_raises_no_exceptions(self):
    140        self.setup_accessibility(False, True)
    141        # No exception should be raised
    142        self.run_element_test(self.invalid_elementIDs, lambda button: button.click())
    143        # Elements are invisible
    144        self.run_element_test(
    145            self.falsy_elements,
    146            lambda button: self.assertRaises(
    147                ElementNotInteractableException, button.click
    148            ),
    149        )
    150 
    151    def test_element_visible_but_not_visible_to_accessbility(self):
    152        self.setup_accessibility()
    153        # Elements are displayed but hidden from accessibility API
    154        self.run_element_test(
    155            self.displayed_but_have_no_accessible_elementIDs,
    156            lambda element: self.assertRaises(
    157                ElementNotAccessibleException, element.is_displayed
    158            ),
    159        )
    160 
    161    def test_element_is_visible_to_accessibility(self):
    162        self.setup_accessibility()
    163        # No exception should be raised
    164        self.run_element_test(
    165            self.displayed_elementIDs, lambda element: element.is_displayed()
    166        )
    167 
    168    def test_element_is_not_enabled_to_accessbility(self):
    169        self.setup_accessibility()
    170        # Buttons are enabled but disabled/not-explorable via the accessibility API
    171        self.run_element_test(
    172            self.aria_disabled_elementIDs,
    173            lambda element: self.assertRaises(
    174                ElementNotAccessibleException, element.is_enabled
    175            ),
    176        )
    177        self.run_element_test(
    178            self.pointer_events_none_elementIDs,
    179            lambda element: self.assertRaises(
    180                ElementNotAccessibleException, element.is_enabled
    181            ),
    182        )
    183 
    184        # Buttons are enabled but disabled/not-explorable via
    185        # the accessibility API and thus are not clickable via the
    186        # accessibility API.
    187        self.run_element_test(
    188            self.aria_disabled_elementIDs,
    189            lambda element: self.assertRaises(
    190                ElementNotAccessibleException, element.click
    191            ),
    192        )
    193        # To be removed with bug 1405967
    194        if not self.marionette.session_capabilities["moz:webdriverClick"]:
    195            self.run_element_test(
    196                self.pointer_events_none_elementIDs,
    197                lambda element: self.assertRaises(
    198                    ElementNotAccessibleException, element.click
    199                ),
    200            )
    201 
    202        self.setup_accessibility(False, False)
    203        self.run_element_test(
    204            self.aria_disabled_elementIDs, lambda element: element.is_enabled()
    205        )
    206        self.run_element_test(
    207            self.pointer_events_none_elementIDs, lambda element: element.is_enabled()
    208        )
    209        self.run_element_test(
    210            self.aria_disabled_elementIDs, lambda element: element.click()
    211        )
    212        # To be removed with bug 1405967
    213        if not self.marionette.session_capabilities["moz:webdriverClick"]:
    214            self.run_element_test(
    215                self.pointer_events_none_elementIDs, lambda element: element.click()
    216            )
    217 
    218    def test_element_is_enabled_to_accessibility(self):
    219        self.setup_accessibility()
    220        # No exception should be raised
    221        self.run_element_test(
    222            self.disabled_elementIDs, lambda element: element.is_enabled()
    223        )
    224 
    225    def test_send_keys_raises_no_exception(self):
    226        self.setup_accessibility()
    227        # Sending keys to valid input should not raise any exceptions
    228        self.run_element_test(["input1"], lambda element: element.send_keys("a"))
    229 
    230    def test_is_selected_raises_no_exception(self):
    231        self.setup_accessibility()
    232        # No exception should be raised for valid options
    233        self.run_element_test(
    234            self.valid_option_elementIDs, lambda element: element.is_selected()
    235        )
    236        # No exception should be raised for non-selectable elements
    237        self.run_element_test(
    238            self.valid_elementIDs, lambda element: element.is_selected()
    239        )