summaryrefslogtreecommitdiffstats
path: root/testing/marionette/harness/marionette_harness/tests/unit/test_click.py
diff options
context:
space:
mode:
Diffstat (limited to 'testing/marionette/harness/marionette_harness/tests/unit/test_click.py')
-rw-r--r--testing/marionette/harness/marionette_harness/tests/unit/test_click.py571
1 files changed, 571 insertions, 0 deletions
diff --git a/testing/marionette/harness/marionette_harness/tests/unit/test_click.py b/testing/marionette/harness/marionette_harness/tests/unit/test_click.py
new file mode 100644
index 0000000000..5936be1e69
--- /dev/null
+++ b/testing/marionette/harness/marionette_harness/tests/unit/test_click.py
@@ -0,0 +1,571 @@
+# 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
+from unittest import skipIf
+
+from six.moves.urllib.parse import quote
+
+from marionette_driver import By, errors
+from marionette_driver.marionette import Alert
+
+from marionette_harness import (
+ MarionetteTestCase,
+ WindowManagerMixin,
+)
+
+
+def inline(doc):
+ return "data:text/html;charset=utf-8,{}".format(quote(doc))
+
+
+# The <a> element in the following HTML is not interactable because it
+# is hidden by an overlay when scrolled into the top of the viewport.
+# It should be interactable when scrolled in at the bottom of the
+# viewport.
+fixed_overlay = inline(
+ """
+<style>
+* { margin: 0; padding: 0; }
+body { height: 300vh }
+div, a { display: block }
+div {
+ background-color: pink;
+ position: fixed;
+ width: 100%;
+ height: 40px;
+ top: 0;
+}
+a {
+ margin-top: 1000px;
+}
+</style>
+
+<div>overlay</div>
+<a href=#>link</a>
+
+<script>
+window.clicked = false;
+
+let link = document.querySelector("a");
+link.addEventListener("click", () => window.clicked = true);
+</script>
+"""
+)
+
+
+obscured_overlay = inline(
+ """
+<style>
+* { margin: 0; padding: 0; }
+body { height: 100vh }
+#overlay {
+ background-color: pink;
+ position: absolute;
+ width: 100%;
+ height: 100%;
+}
+</style>
+
+<div id=overlay></div>
+<a id=obscured href=#>link</a>
+
+<script>
+window.clicked = false;
+
+let link = document.querySelector("#obscured");
+link.addEventListener("click", () => window.clicked = true);
+</script>
+"""
+)
+
+
+class ClickBaseTestCase(WindowManagerMixin, MarionetteTestCase):
+ def setUp(self):
+ super(ClickBaseTestCase, self).setUp()
+
+ # Always use a blank new tab for an empty history
+ self.new_tab = self.open_tab()
+ self.marionette.switch_to_window(self.new_tab)
+
+ def tearDown(self):
+ self.close_all_tabs()
+
+ def test_click(self):
+ self.marionette.navigate(
+ inline(
+ """
+ <button>click me</button>
+ <script>
+ window.clicks = 0;
+ let button = document.querySelector("button");
+ button.addEventListener("click", () => window.clicks++);
+ </script>
+ """
+ )
+ )
+ button = self.marionette.find_element(By.TAG_NAME, "button")
+ button.click()
+ self.assertEqual(
+ 1, self.marionette.execute_script("return window.clicks", sandbox=None)
+ )
+
+ def test_click_number_link(self):
+ test_html = self.marionette.absolute_url("clicks.html")
+ self.marionette.navigate(test_html)
+ self.marionette.find_element(By.LINK_TEXT, "333333").click()
+ self.marionette.find_element(By.ID, "testDiv")
+ self.assertEqual(self.marionette.title, "Marionette Test")
+
+ def test_clicking_an_element_that_is_not_displayed_raises(self):
+ self.marionette.navigate(
+ inline(
+ """
+ <p hidden>foo</p>
+ """
+ )
+ )
+
+ with self.assertRaises(errors.ElementNotInteractableException):
+ self.marionette.find_element(By.TAG_NAME, "p").click()
+
+ def test_clicking_on_a_multiline_link(self):
+ test_html = self.marionette.absolute_url("clicks.html")
+ self.marionette.navigate(test_html)
+ self.marionette.find_element(By.ID, "overflowLink").click()
+ self.marionette.find_element(By.ID, "testDiv")
+ self.assertEqual(self.marionette.title, "Marionette Test")
+
+ def test_click_mathml(self):
+ self.marionette.navigate(
+ inline(
+ """
+ <math><mtext id="target">click me</mtext></math>
+ <script>
+ window.clicks = 0;
+ let mtext = document.getElementById("target");
+ mtext.addEventListener("click", () => window.clicks++);
+ </script>
+ """
+ )
+ )
+ mtext = self.marionette.find_element(By.ID, "target")
+ mtext.click()
+ self.assertEqual(
+ 1, self.marionette.execute_script("return window.clicks", sandbox=None)
+ )
+
+ def test_scroll_into_view_near_end(self):
+ self.marionette.navigate(fixed_overlay)
+ link = self.marionette.find_element(By.TAG_NAME, "a")
+ link.click()
+ self.assertTrue(
+ self.marionette.execute_script("return window.clicked", sandbox=None)
+ )
+
+ def test_inclusive_descendant(self):
+ self.marionette.navigate(
+ inline(
+ """
+ <select multiple>
+ <option>first
+ <option>second
+ <option>third
+ </select>"""
+ )
+ )
+ select = self.marionette.find_element(By.TAG_NAME, "select")
+
+ # This tests that the pointer-interactability test does not
+ # cause an ElementClickInterceptedException.
+ #
+ # At a <select multiple>'s in-view centre point, you might
+ # find a fully rendered <option>. Marionette should test that
+ # the paint tree at this point _contains_ <option>, not that the
+ # first element of the paint tree is _equal_ to <select>.
+ select.click()
+
+ # Bug 1413821 - Click does not select an option on Android
+ if self.marionette.session_capabilities["browserName"] != "fennec":
+ self.assertNotEqual(select.get_property("selectedIndex"), -1)
+
+ def test_container_is_select(self):
+ self.marionette.navigate(
+ inline(
+ """
+ <select>
+ <option>foo</option>
+ </select>"""
+ )
+ )
+ option = self.marionette.find_element(By.TAG_NAME, "option")
+ option.click()
+ self.assertTrue(option.get_property("selected"))
+
+ def test_container_is_button(self):
+ self.marionette.navigate(
+ inline(
+ """
+ <button onclick="window.clicked = true;">
+ <span><em>foo</em></span>
+ </button>"""
+ )
+ )
+ span = self.marionette.find_element(By.TAG_NAME, "span")
+ span.click()
+ self.assertTrue(
+ self.marionette.execute_script("return window.clicked", sandbox=None)
+ )
+
+ def test_container_element_outside_view(self):
+ self.marionette.navigate(
+ inline(
+ """
+ <select style="margin-top: 100vh">
+ <option>foo</option>
+ </select>"""
+ )
+ )
+ option = self.marionette.find_element(By.TAG_NAME, "option")
+ option.click()
+ self.assertTrue(option.get_property("selected"))
+
+ def test_table_tr(self):
+ self.marionette.navigate(
+ inline(
+ """
+ <table>
+ <tr><td onclick="window.clicked = true;">
+ foo
+ </td></tr>
+ </table>"""
+ )
+ )
+ tr = self.marionette.find_element(By.TAG_NAME, "tr")
+ tr.click()
+ self.assertTrue(
+ self.marionette.execute_script("return window.clicked", sandbox=None)
+ )
+
+
+class TestLegacyClick(ClickBaseTestCase):
+ """Uses legacy Selenium element displayedness checks."""
+
+ def setUp(self):
+ super(TestLegacyClick, self).setUp()
+
+ self.marionette.delete_session()
+ self.marionette.start_session({"moz:webdriverClick": False})
+
+
+class TestClick(ClickBaseTestCase):
+ """Uses WebDriver specification compatible element interactability checks."""
+
+ def setUp(self):
+ super(TestClick, self).setUp()
+
+ self.marionette.delete_session()
+ self.marionette.start_session({"moz:webdriverClick": True})
+
+ def test_click_element_obscured_by_absolute_positioned_element(self):
+ self.marionette.navigate(obscured_overlay)
+ overlay = self.marionette.find_element(By.ID, "overlay")
+ obscured = self.marionette.find_element(By.ID, "obscured")
+
+ overlay.click()
+ with self.assertRaises(errors.ElementClickInterceptedException):
+ obscured.click()
+
+ def test_centre_outside_viewport_vertically(self):
+ self.marionette.navigate(
+ inline(
+ """
+ <style>
+ * { margin: 0; padding: 0; }
+ div {
+ display: block;
+ position: absolute;
+ background-color: blue;
+ width: 200px;
+ height: 200px;
+
+ /* move centre point off viewport vertically */
+ top: -105px;
+ }
+ </style>
+
+ <div onclick="window.clicked = true;"></div>"""
+ )
+ )
+
+ self.marionette.find_element(By.TAG_NAME, "div").click()
+ self.assertTrue(
+ self.marionette.execute_script("return window.clicked", sandbox=None)
+ )
+
+ def test_centre_outside_viewport_horizontally(self):
+ self.marionette.navigate(
+ inline(
+ """
+ <style>
+ * { margin: 0; padding: 0; }
+ div {
+ display: block;
+ position: absolute;
+ background-color: blue;
+ width: 200px;
+ height: 200px;
+
+ /* move centre point off viewport horizontally */
+ left: -105px;
+ }
+ </style>
+
+ <div onclick="window.clicked = true;"></div>"""
+ )
+ )
+
+ self.marionette.find_element(By.TAG_NAME, "div").click()
+ self.assertTrue(
+ self.marionette.execute_script("return window.clicked", sandbox=None)
+ )
+
+ def test_centre_outside_viewport(self):
+ self.marionette.navigate(
+ inline(
+ """
+ <style>
+ * { margin: 0; padding: 0; }
+ div {
+ display: block;
+ position: absolute;
+ background-color: blue;
+ width: 200px;
+ height: 200px;
+
+ /* move centre point off viewport */
+ left: -105px;
+ top: -105px;
+ }
+ </style>
+
+ <div onclick="window.clicked = true;"></div>"""
+ )
+ )
+
+ self.marionette.find_element(By.TAG_NAME, "div").click()
+ self.assertTrue(
+ self.marionette.execute_script("return window.clicked", sandbox=None)
+ )
+
+ def test_css_transforms(self):
+ self.marionette.navigate(
+ inline(
+ """
+ <style>
+ * { margin: 0; padding: 0; }
+ div {
+ display: block;
+ background-color: blue;
+ width: 200px;
+ height: 200px;
+
+ transform: translateX(-105px);
+ }
+ </style>
+
+ <div onclick="window.clicked = true;"></div>"""
+ )
+ )
+
+ self.marionette.find_element(By.TAG_NAME, "div").click()
+ self.assertTrue(
+ self.marionette.execute_script("return window.clicked", sandbox=None)
+ )
+
+ def test_input_file(self):
+ self.marionette.navigate(inline("<input type=file>"))
+ with self.assertRaises(errors.InvalidArgumentException):
+ self.marionette.find_element(By.TAG_NAME, "input").click()
+
+ def test_obscured_element(self):
+ self.marionette.navigate(obscured_overlay)
+ overlay = self.marionette.find_element(By.ID, "overlay")
+ obscured = self.marionette.find_element(By.ID, "obscured")
+
+ overlay.click()
+ with self.assertRaises(errors.ElementClickInterceptedException):
+ obscured.click()
+ self.assertFalse(
+ self.marionette.execute_script("return window.clicked", sandbox=None)
+ )
+
+ def test_pointer_events_none(self):
+ self.marionette.navigate(
+ inline(
+ """
+ <button style="pointer-events: none">click me</button>
+ <script>
+ window.clicked = false;
+ let button = document.querySelector("button");
+ button.addEventListener("click", () => window.clicked = true);
+ </script>
+ """
+ )
+ )
+ button = self.marionette.find_element(By.TAG_NAME, "button")
+ self.assertEqual("none", button.value_of_css_property("pointer-events"))
+
+ with self.assertRaisesRegexp(
+ errors.ElementClickInterceptedException,
+ "does not have pointer events enabled",
+ ):
+ button.click()
+ self.assertFalse(
+ self.marionette.execute_script("return window.clicked", sandbox=None)
+ )
+
+ def test_prevent_default(self):
+ self.marionette.navigate(
+ inline(
+ """
+ <button>click me</button>
+ <script>
+ let button = document.querySelector("button");
+ button.addEventListener("click", event => event.preventDefault());
+ </script>
+ """
+ )
+ )
+ button = self.marionette.find_element(By.TAG_NAME, "button")
+ # should not time out
+ button.click()
+
+ def test_stop_propagation(self):
+ self.marionette.navigate(
+ inline(
+ """
+ <button>click me</button>
+ <script>
+ let button = document.querySelector("button");
+ button.addEventListener("click", event => event.stopPropagation());
+ </script>
+ """
+ )
+ )
+ button = self.marionette.find_element(By.TAG_NAME, "button")
+ # should not time out
+ button.click()
+
+ def test_stop_immediate_propagation(self):
+ self.marionette.navigate(
+ inline(
+ """
+ <button>click me</button>
+ <script>
+ let button = document.querySelector("button");
+ button.addEventListener("click", event => event.stopImmediatePropagation());
+ </script>
+ """
+ )
+ )
+ button = self.marionette.find_element(By.TAG_NAME, "button")
+ # should not time out
+ button.click()
+
+
+class TestClickNavigation(WindowManagerMixin, MarionetteTestCase):
+ def setUp(self):
+ super(TestClickNavigation, self).setUp()
+
+ # Always use a blank new tab for an empty history
+ self.new_tab = self.open_tab()
+ self.marionette.switch_to_window(self.new_tab)
+
+ self.test_page = self.marionette.absolute_url("clicks.html")
+ self.marionette.navigate(self.test_page)
+
+ def tearDown(self):
+ self.close_all_tabs()
+
+ def close_notification(self):
+ try:
+ with self.marionette.using_context("chrome"):
+ elem = self.marionette.find_element(
+ By.CSS_SELECTOR,
+ "#notification-popup popupnotification .popup-notification-closebutton",
+ )
+ elem.click()
+ except errors.NoSuchElementException:
+ pass
+
+ def test_click_link_page_load(self):
+ self.marionette.find_element(By.LINK_TEXT, "333333").click()
+ self.assertNotEqual(self.marionette.get_url(), self.test_page)
+ self.assertEqual(self.marionette.title, "Marionette Test")
+
+ def test_click_link_anchor(self):
+ self.marionette.find_element(By.ID, "anchor").click()
+ self.assertEqual(self.marionette.get_url(), "{}#".format(self.test_page))
+
+ @skipIf(
+ sys.platform.startswith("win"),
+ "Bug 1627965 - Skip on Windows for frequent failures",
+ )
+ def test_click_link_install_addon(self):
+ try:
+ self.marionette.find_element(By.ID, "install-addon").click()
+ self.assertEqual(self.marionette.get_url(), self.test_page)
+ finally:
+ self.close_notification()
+
+ def test_click_no_link(self):
+ self.marionette.find_element(By.ID, "links").click()
+ self.assertEqual(self.marionette.get_url(), self.test_page)
+
+ def test_click_option_navigate(self):
+ self.marionette.find_element(By.ID, "option").click()
+ self.marionette.find_element(By.ID, "delay")
+
+ def test_click_remoteness_change(self):
+ self.marionette.navigate("about:robots")
+ self.marionette.navigate(self.test_page)
+ self.marionette.find_element(By.ID, "anchor")
+
+ self.marionette.navigate("about:robots")
+ with self.assertRaises(errors.NoSuchElementException):
+ self.marionette.find_element(By.ID, "anchor")
+
+ self.marionette.go_back()
+ self.marionette.find_element(By.ID, "anchor")
+
+ self.marionette.find_element(By.ID, "history-back").click()
+ with self.assertRaises(errors.NoSuchElementException):
+ self.marionette.find_element(By.ID, "anchor")
+
+
+class TestClickCloseContext(WindowManagerMixin, MarionetteTestCase):
+ def setUp(self):
+ super(TestClickCloseContext, self).setUp()
+
+ self.test_page = self.marionette.absolute_url("clicks.html")
+
+ def tearDown(self):
+ self.close_all_tabs()
+
+ super(TestClickCloseContext, self).tearDown()
+
+ def test_click_close_tab(self):
+ new_tab = self.open_tab()
+ self.marionette.switch_to_window(new_tab)
+
+ self.marionette.navigate(self.test_page)
+ self.marionette.find_element(By.ID, "close-window").click()
+
+ def test_click_close_window(self):
+ new_tab = self.open_window()
+ self.marionette.switch_to_window(new_tab)
+
+ self.marionette.navigate(self.test_page)
+ self.marionette.find_element(By.ID, "close-window").click()