tor-browser

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

test_click.py (17391B)


      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 from urllib.parse import quote
      6 
      7 from marionette_driver import By, errors
      8 
      9 from marionette_harness import (
     10    MarionetteTestCase,
     11    WindowManagerMixin,
     12 )
     13 
     14 
     15 def inline(doc):
     16    return "data:text/html;charset=utf-8,{}".format(quote(doc))
     17 
     18 
     19 # The <a> element in the following HTML is not interactable because it
     20 # is hidden by an overlay when scrolled into the top of the viewport.
     21 # It should be interactable when scrolled in at the bottom of the
     22 # viewport.
     23 fixed_overlay = inline(
     24    """
     25 <style>
     26 * { margin: 0; padding: 0; }
     27 body { height: 300vh }
     28 div, a { display: block }
     29 div {
     30  background-color: pink;
     31  position: fixed;
     32  width: 100%;
     33  height: 40px;
     34  top: 0;
     35 }
     36 a {
     37  margin-top: 1000px;
     38 }
     39 </style>
     40 
     41 <div>overlay</div>
     42 <a href=#>link</a>
     43 
     44 <script>
     45 window.clicked = false;
     46 
     47 let link = document.querySelector("a");
     48 link.addEventListener("click", () => window.clicked = true);
     49 </script>
     50 """
     51 )
     52 
     53 
     54 obscured_overlay = inline(
     55    """
     56 <style>
     57 * { margin: 0; padding: 0; }
     58 body { height: 100vh }
     59 #overlay {
     60  background-color: pink;
     61  position: absolute;
     62  width: 100%;
     63  height: 100%;
     64 }
     65 </style>
     66 
     67 <div id=overlay></div>
     68 <a id=obscured href=#>link</a>
     69 
     70 <script>
     71 window.clicked = false;
     72 
     73 let link = document.querySelector("#obscured");
     74 link.addEventListener("click", () => window.clicked = true);
     75 </script>
     76 """
     77 )
     78 
     79 
     80 class ClickBaseTestCase(WindowManagerMixin, MarionetteTestCase):
     81    def setUp(self):
     82        super(ClickBaseTestCase, self).setUp()
     83 
     84        # Always use a blank new tab for an empty history
     85        self.new_tab = self.open_tab()
     86        self.marionette.switch_to_window(self.new_tab)
     87 
     88    def tearDown(self):
     89        self.close_all_tabs()
     90 
     91    def test_click(self):
     92        self.marionette.navigate(
     93            inline(
     94                """
     95            <button>click me</button>
     96            <script>
     97              window.clicks = 0;
     98              let button = document.querySelector("button");
     99              button.addEventListener("click", () => window.clicks++);
    100            </script>
    101        """
    102            )
    103        )
    104        button = self.marionette.find_element(By.TAG_NAME, "button")
    105        button.click()
    106        self.assertEqual(
    107            1, self.marionette.execute_script("return window.clicks", sandbox=None)
    108        )
    109 
    110    def test_click_number_link(self):
    111        test_html = self.marionette.absolute_url("clicks.html")
    112        self.marionette.navigate(test_html)
    113        self.marionette.find_element(By.LINK_TEXT, "333333").click()
    114        self.marionette.find_element(By.ID, "testDiv")
    115        self.assertEqual(self.marionette.title, "Marionette Test")
    116 
    117    def test_clicking_an_element_that_is_not_displayed_raises(self):
    118        self.marionette.navigate(
    119            inline(
    120                """
    121            <p hidden>foo</p>
    122        """
    123            )
    124        )
    125 
    126        with self.assertRaises(errors.ElementNotInteractableException):
    127            self.marionette.find_element(By.TAG_NAME, "p").click()
    128 
    129    def test_clicking_on_a_multiline_link(self):
    130        test_html = self.marionette.absolute_url("clicks.html")
    131        self.marionette.navigate(test_html)
    132        self.marionette.find_element(By.ID, "overflowLink").click()
    133        self.marionette.find_element(By.ID, "testDiv")
    134        self.assertEqual(self.marionette.title, "Marionette Test")
    135 
    136    def test_click_mathml(self):
    137        self.marionette.navigate(
    138            inline(
    139                """
    140            <math><mtext id="target">click me</mtext></math>
    141            <script>
    142              window.clicks = 0;
    143              let mtext = document.getElementById("target");
    144              mtext.addEventListener("click", () => window.clicks++);
    145            </script>
    146        """
    147            )
    148        )
    149        mtext = self.marionette.find_element(By.ID, "target")
    150        mtext.click()
    151        self.assertEqual(
    152            1, self.marionette.execute_script("return window.clicks", sandbox=None)
    153        )
    154 
    155    def test_scroll_into_view_near_end(self):
    156        self.marionette.navigate(fixed_overlay)
    157        link = self.marionette.find_element(By.TAG_NAME, "a")
    158        link.click()
    159        self.assertTrue(
    160            self.marionette.execute_script("return window.clicked", sandbox=None)
    161        )
    162 
    163    def test_inclusive_descendant(self):
    164        self.marionette.navigate(
    165            inline(
    166                """
    167            <select multiple>
    168              <option>first
    169              <option>second
    170              <option>third
    171             </select>"""
    172            )
    173        )
    174        select = self.marionette.find_element(By.TAG_NAME, "select")
    175 
    176        # This tests that the pointer-interactability test does not
    177        # cause an ElementClickInterceptedException.
    178        #
    179        # At a <select multiple>'s in-view centre point, you might
    180        # find a fully rendered <option>.  Marionette should test that
    181        # the paint tree at this point _contains_ <option>, not that the
    182        # first element of the paint tree is _equal_ to <select>.
    183        select.click()
    184 
    185        # Bug 1413821 - Click does not select an option on Android
    186        if self.marionette.session_capabilities["browserName"] != "fennec":
    187            self.assertNotEqual(select.get_property("selectedIndex"), -1)
    188 
    189    def test_container_is_select(self):
    190        self.marionette.navigate(
    191            inline(
    192                """
    193            <select>
    194              <option>foo</option>
    195            </select>"""
    196            )
    197        )
    198        option = self.marionette.find_element(By.TAG_NAME, "option")
    199        option.click()
    200        self.assertTrue(option.get_property("selected"))
    201 
    202    def test_container_is_button(self):
    203        self.marionette.navigate(
    204            inline(
    205                """
    206          <button onclick="window.clicked = true;">
    207            <span><em>foo</em></span>
    208          </button>"""
    209            )
    210        )
    211        span = self.marionette.find_element(By.TAG_NAME, "span")
    212        span.click()
    213        self.assertTrue(
    214            self.marionette.execute_script("return window.clicked", sandbox=None)
    215        )
    216 
    217    def test_container_element_outside_view(self):
    218        self.marionette.navigate(
    219            inline(
    220                """
    221            <select style="margin-top: 100vh">
    222              <option>foo</option>
    223            </select>"""
    224            )
    225        )
    226        option = self.marionette.find_element(By.TAG_NAME, "option")
    227        option.click()
    228        self.assertTrue(option.get_property("selected"))
    229 
    230    def test_table_tr(self):
    231        self.marionette.navigate(
    232            inline(
    233                """
    234          <table>
    235            <tr><td onclick="window.clicked = true;">
    236              foo
    237            </td></tr>
    238          </table>"""
    239            )
    240        )
    241        tr = self.marionette.find_element(By.TAG_NAME, "tr")
    242        tr.click()
    243        self.assertTrue(
    244            self.marionette.execute_script("return window.clicked", sandbox=None)
    245        )
    246 
    247 
    248 class TestLegacyClick(ClickBaseTestCase):
    249    """Uses legacy Selenium element displayedness checks."""
    250 
    251    def setUp(self):
    252        super(TestLegacyClick, self).setUp()
    253 
    254        self.marionette.delete_session()
    255        self.marionette.start_session({"moz:webdriverClick": False})
    256 
    257 
    258 class TestClick(ClickBaseTestCase):
    259    """Uses WebDriver specification compatible element interactability checks."""
    260 
    261    def setUp(self):
    262        super(TestClick, self).setUp()
    263 
    264        self.marionette.delete_session()
    265        self.marionette.start_session({"moz:webdriverClick": True})
    266 
    267    def test_click_element_obscured_by_absolute_positioned_element(self):
    268        self.marionette.navigate(obscured_overlay)
    269        overlay = self.marionette.find_element(By.ID, "overlay")
    270        obscured = self.marionette.find_element(By.ID, "obscured")
    271 
    272        overlay.click()
    273        with self.assertRaises(errors.ElementClickInterceptedException):
    274            obscured.click()
    275 
    276    def test_centre_outside_viewport_vertically(self):
    277        self.marionette.navigate(
    278            inline(
    279                """
    280            <style>
    281            * { margin: 0; padding: 0; }
    282            div {
    283             display: block;
    284             position: absolute;
    285             background-color: blue;
    286             width: 200px;
    287             height: 200px;
    288 
    289             /* move centre point off viewport vertically */
    290             top: -105px;
    291            }
    292            </style>
    293 
    294            <div onclick="window.clicked = true;"></div>"""
    295            )
    296        )
    297 
    298        self.marionette.find_element(By.TAG_NAME, "div").click()
    299        self.assertTrue(
    300            self.marionette.execute_script("return window.clicked", sandbox=None)
    301        )
    302 
    303    def test_centre_outside_viewport_horizontally(self):
    304        self.marionette.navigate(
    305            inline(
    306                """
    307            <style>
    308            * { margin: 0; padding: 0; }
    309            div {
    310             display: block;
    311             position: absolute;
    312             background-color: blue;
    313             width: 200px;
    314             height: 200px;
    315 
    316             /* move centre point off viewport horizontally */
    317             left: -105px;
    318            }
    319            </style>
    320 
    321            <div onclick="window.clicked = true;"></div>"""
    322            )
    323        )
    324 
    325        self.marionette.find_element(By.TAG_NAME, "div").click()
    326        self.assertTrue(
    327            self.marionette.execute_script("return window.clicked", sandbox=None)
    328        )
    329 
    330    def test_centre_outside_viewport(self):
    331        self.marionette.navigate(
    332            inline(
    333                """
    334            <style>
    335            * { margin: 0; padding: 0; }
    336            div {
    337             display: block;
    338             position: absolute;
    339             background-color: blue;
    340             width: 200px;
    341             height: 200px;
    342 
    343             /* move centre point off viewport */
    344             left: -105px;
    345             top: -105px;
    346            }
    347            </style>
    348 
    349            <div onclick="window.clicked = true;"></div>"""
    350            )
    351        )
    352 
    353        self.marionette.find_element(By.TAG_NAME, "div").click()
    354        self.assertTrue(
    355            self.marionette.execute_script("return window.clicked", sandbox=None)
    356        )
    357 
    358    def test_css_transforms(self):
    359        self.marionette.navigate(
    360            inline(
    361                """
    362            <style>
    363            * { margin: 0; padding: 0; }
    364            div {
    365             display: block;
    366             background-color: blue;
    367             width: 200px;
    368             height: 200px;
    369 
    370             transform: translateX(-105px);
    371            }
    372            </style>
    373 
    374            <div onclick="window.clicked = true;"></div>"""
    375            )
    376        )
    377 
    378        self.marionette.find_element(By.TAG_NAME, "div").click()
    379        self.assertTrue(
    380            self.marionette.execute_script("return window.clicked", sandbox=None)
    381        )
    382 
    383    def test_input_file(self):
    384        self.marionette.navigate(inline("<input type=file>"))
    385        with self.assertRaises(errors.InvalidArgumentException):
    386            self.marionette.find_element(By.TAG_NAME, "input").click()
    387 
    388    def test_obscured_element(self):
    389        self.marionette.navigate(obscured_overlay)
    390        overlay = self.marionette.find_element(By.ID, "overlay")
    391        obscured = self.marionette.find_element(By.ID, "obscured")
    392 
    393        overlay.click()
    394        with self.assertRaises(errors.ElementClickInterceptedException):
    395            obscured.click()
    396        self.assertFalse(
    397            self.marionette.execute_script("return window.clicked", sandbox=None)
    398        )
    399 
    400    def test_pointer_events_none(self):
    401        self.marionette.navigate(
    402            inline(
    403                """
    404            <button style="pointer-events: none">click me</button>
    405            <script>
    406              window.clicked = false;
    407              let button = document.querySelector("button");
    408              button.addEventListener("click", () => window.clicked = true);
    409            </script>
    410        """
    411            )
    412        )
    413        button = self.marionette.find_element(By.TAG_NAME, "button")
    414        self.assertEqual("none", button.value_of_css_property("pointer-events"))
    415 
    416        with self.assertRaisesRegex(
    417            errors.ElementClickInterceptedException,
    418            "does not have pointer events enabled",
    419        ):
    420            button.click()
    421        self.assertFalse(
    422            self.marionette.execute_script("return window.clicked", sandbox=None)
    423        )
    424 
    425    def test_prevent_default(self):
    426        self.marionette.navigate(
    427            inline(
    428                """
    429            <button>click me</button>
    430            <script>
    431              let button = document.querySelector("button");
    432              button.addEventListener("click", event => event.preventDefault());
    433            </script>
    434        """
    435            )
    436        )
    437        button = self.marionette.find_element(By.TAG_NAME, "button")
    438        # should not time out
    439        button.click()
    440 
    441    def test_stop_propagation(self):
    442        self.marionette.navigate(
    443            inline(
    444                """
    445            <button>click me</button>
    446            <script>
    447              let button = document.querySelector("button");
    448              button.addEventListener("click", event => event.stopPropagation());
    449            </script>
    450        """
    451            )
    452        )
    453        button = self.marionette.find_element(By.TAG_NAME, "button")
    454        # should not time out
    455        button.click()
    456 
    457    def test_stop_immediate_propagation(self):
    458        self.marionette.navigate(
    459            inline(
    460                """
    461            <button>click me</button>
    462            <script>
    463              let button = document.querySelector("button");
    464              button.addEventListener("click", event => event.stopImmediatePropagation());
    465            </script>
    466        """
    467            )
    468        )
    469        button = self.marionette.find_element(By.TAG_NAME, "button")
    470        # should not time out
    471        button.click()
    472 
    473 
    474 class TestClickNavigation(WindowManagerMixin, MarionetteTestCase):
    475    def setUp(self):
    476        super(TestClickNavigation, self).setUp()
    477 
    478        # Always use a blank new tab for an empty history
    479        self.new_tab = self.open_tab()
    480        self.marionette.switch_to_window(self.new_tab)
    481 
    482        self.test_page = self.marionette.absolute_url("clicks.html")
    483        self.marionette.navigate(self.test_page)
    484 
    485    def tearDown(self):
    486        self.close_all_tabs()
    487 
    488    def close_notification(self):
    489        try:
    490            with self.marionette.using_context("chrome"):
    491                elem = self.marionette.find_element(
    492                    By.CSS_SELECTOR,
    493                    "#notification-popup popupnotification .popup-notification-closebutton",
    494                )
    495                elem.click()
    496        except errors.NoSuchElementException:
    497            pass
    498 
    499    def test_click_link_page_load(self):
    500        self.marionette.find_element(By.LINK_TEXT, "333333").click()
    501        self.assertNotEqual(self.marionette.get_url(), self.test_page)
    502        self.assertEqual(self.marionette.title, "Marionette Test")
    503 
    504    def test_click_link_anchor(self):
    505        self.marionette.find_element(By.ID, "anchor").click()
    506        self.assertEqual(self.marionette.get_url(), "{}#".format(self.test_page))
    507 
    508    def test_click_link_install_addon(self):
    509        try:
    510            self.marionette.find_element(By.ID, "install-addon").click()
    511            self.assertEqual(self.marionette.get_url(), self.test_page)
    512        finally:
    513            self.close_notification()
    514 
    515    def test_click_no_link(self):
    516        self.marionette.find_element(By.ID, "links").click()
    517        self.assertEqual(self.marionette.get_url(), self.test_page)
    518 
    519    def test_click_option_navigate(self):
    520        self.marionette.find_element(By.ID, "option").click()
    521        self.marionette.find_element(By.ID, "delay")
    522 
    523    def test_click_remoteness_change(self):
    524        self.marionette.navigate("about:robots")
    525        self.marionette.navigate(self.test_page)
    526        self.marionette.find_element(By.ID, "anchor")
    527 
    528        self.marionette.navigate("about:robots")
    529        with self.assertRaises(errors.NoSuchElementException):
    530            self.marionette.find_element(By.ID, "anchor")
    531 
    532        self.marionette.go_back()
    533        self.marionette.find_element(By.ID, "anchor")
    534 
    535        self.marionette.find_element(By.ID, "history-back").click()
    536        with self.assertRaises(errors.NoSuchElementException):
    537            self.marionette.find_element(By.ID, "anchor")
    538 
    539 
    540 class TestClickCloseContext(WindowManagerMixin, MarionetteTestCase):
    541    def setUp(self):
    542        super(TestClickCloseContext, self).setUp()
    543 
    544        self.test_page = self.marionette.absolute_url("clicks.html")
    545 
    546    def tearDown(self):
    547        self.close_all_tabs()
    548 
    549        super(TestClickCloseContext, self).tearDown()
    550 
    551    def test_click_close_tab(self):
    552        new_tab = self.open_tab()
    553        self.marionette.switch_to_window(new_tab)
    554 
    555        self.marionette.navigate(self.test_page)
    556        self.marionette.find_element(By.ID, "close-window").click()
    557 
    558    def test_click_close_window(self):
    559        new_tab = self.open_window()
    560        self.marionette.switch_to_window(new_tab)
    561 
    562        self.marionette.navigate(self.test_page)
    563        self.marionette.find_element(By.ID, "close-window").click()