# This Source Code Form is subject to the terms of the Mozilla Public # License, v. 2.0. If a copy of the MPL was not distributed with this # file, You can obtain one at http://mozilla.org/MPL/2.0/. import sys import unittest from marionette_driver.by import By from marionette_driver.errors import ( ElementNotAccessibleException, ElementNotInteractableException, ElementClickInterceptedException, ) from marionette_harness import MarionetteTestCase class TestAccessibility(MarionetteTestCase): def setUp(self): super(TestAccessibility, self).setUp() with self.marionette.using_context("chrome"): self.marionette.set_pref("dom.ipc.processCount", 1) def tearDown(self): with self.marionette.using_context("chrome"): self.marionette.clear_pref("dom.ipc.processCount") # Elements that are accessible with and without the accessibliity API valid_elementIDs = [ # Button1 is an accessible button with a valid accessible name # computed from subtree "button1", # Button2 is an accessible button with a valid accessible name # computed from aria-label "button2", # Button13 is an accessible button that is implemented via role="button" # and is explorable using tabindex="0" "button13", # button17 is an accessible button that overrides parent's # pointer-events:none; property with its own pointer-events:all; "button17", ] # Elements that are not accessible with the accessibility API invalid_elementIDs = [ # Button3 does not have an accessible object "button3", # Button4 does not support any accessible actions "button4", # Button5 does not have a correct accessibility role and may not be # manipulated via the accessibility API "button5", # Button6 is missing an accessible name "button6", # Button7 is not currently visible via the accessibility API and may # not be manipulated by it "button7", # Button8 is not currently visible via the accessibility API and may # not be manipulated by it (in hidden subtree) "button8", # Button14 is accessible button but is not explorable because of lack # of tabindex that would make it focusable. "button14", ] # Elements that are either accessible to accessibility API or not accessible # at all falsy_elements = [ # Element is only visible to the accessibility API and may be # manipulated by it "button9", # Element is not currently visible "button10", ] displayed_elementIDs = ["button1", "button2", "button4", "button5", "button6"] displayed_but_have_no_accessible_elementIDs = [ # Button3 does not have an accessible object "button3", # Button 7 is hidden with aria-hidden set to true "button7", # Button 8 is inside an element with aria-hidden set to true "button8", "no_accessible_but_displayed", ] disabled_elementIDs = ["button11", "no_accessible_but_disabled"] # Elements that are enabled but otherwise disabled or not explorable # via the accessibility API aria_disabled_elementIDs = ["button12"] # pointer-events: "none", which will return # ElementClickInterceptedException if clicked # when Marionette switches # to using WebDriver conforming interaction pointer_events_none_elementIDs = ["button15", "button16"] # Elements that are reporting selected state valid_option_elementIDs = ["option1", "option2"] def run_element_test(self, ids, testFn): for id in ids: element = self.marionette.find_element(By.ID, id) testFn(element) def setup_accessibility(self, enable_a11y_checks=True, navigate=True): self.marionette.delete_session() self.marionette.start_session({"moz:accessibilityChecks": enable_a11y_checks}) self.assertEqual( self.marionette.session_capabilities["moz:accessibilityChecks"], enable_a11y_checks, ) # Navigate to test_accessibility.html if navigate: test_accessibility = self.marionette.absolute_url("test_accessibility.html") self.marionette.navigate(test_accessibility) def test_valid_click(self): self.setup_accessibility() # No exception should be raised self.run_element_test(self.valid_elementIDs, lambda button: button.click()) def test_click_raises_element_not_accessible(self): self.setup_accessibility() self.run_element_test( self.invalid_elementIDs, lambda button: self.assertRaises( ElementNotAccessibleException, button.click ), ) self.run_element_test( self.falsy_elements, lambda button: self.assertRaises( ElementNotInteractableException, button.click ), ) def test_click_raises_no_exceptions(self): self.setup_accessibility(False, True) # No exception should be raised self.run_element_test(self.invalid_elementIDs, lambda button: button.click()) # Elements are invisible self.run_element_test( self.falsy_elements, lambda button: self.assertRaises( ElementNotInteractableException, button.click ), ) def test_element_visible_but_not_visible_to_accessbility(self): self.setup_accessibility() # Elements are displayed but hidden from accessibility API self.run_element_test( self.displayed_but_have_no_accessible_elementIDs, lambda element: self.assertRaises( ElementNotAccessibleException, element.is_displayed ), ) def test_element_is_visible_to_accessibility(self): self.setup_accessibility() # No exception should be raised self.run_element_test( self.displayed_elementIDs, lambda element: element.is_displayed() ) def test_element_is_not_enabled_to_accessbility(self): self.setup_accessibility() # Buttons are enabled but disabled/not-explorable via the accessibility API self.run_element_test( self.aria_disabled_elementIDs, lambda element: self.assertRaises( ElementNotAccessibleException, element.is_enabled ), ) self.run_element_test( self.pointer_events_none_elementIDs, lambda element: self.assertRaises( ElementNotAccessibleException, element.is_enabled ), ) # Buttons are enabled but disabled/not-explorable via # the accessibility API and thus are not clickable via the # accessibility API. self.run_element_test( self.aria_disabled_elementIDs, lambda element: self.assertRaises( ElementNotAccessibleException, element.click ), ) # To be removed with bug 1405967 if not self.marionette.session_capabilities["moz:webdriverClick"]: self.run_element_test( self.pointer_events_none_elementIDs, lambda element: self.assertRaises( ElementNotAccessibleException, element.click ), ) self.setup_accessibility(False, False) self.run_element_test( self.aria_disabled_elementIDs, lambda element: element.is_enabled() ) self.run_element_test( self.pointer_events_none_elementIDs, lambda element: element.is_enabled() ) self.run_element_test( self.aria_disabled_elementIDs, lambda element: element.click() ) # To be removed with bug 1405967 if not self.marionette.session_capabilities["moz:webdriverClick"]: self.run_element_test( self.pointer_events_none_elementIDs, lambda element: element.click() ) def test_element_is_enabled_to_accessibility(self): self.setup_accessibility() # No exception should be raised self.run_element_test( self.disabled_elementIDs, lambda element: element.is_enabled() ) def test_send_keys_raises_no_exception(self): self.setup_accessibility() # Sending keys to valid input should not raise any exceptions self.run_element_test(["input1"], lambda element: element.send_keys("a")) def test_is_selected_raises_no_exception(self): self.setup_accessibility() # No exception should be raised for valid options self.run_element_test( self.valid_option_elementIDs, lambda element: element.is_selected() ) # No exception should be raised for non-selectable elements self.run_element_test( self.valid_elementIDs, lambda element: element.is_selected() )