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 )