From 1fbbcbbcc27dcf054e69a343390916e6c66f8735 Mon Sep 17 00:00:00 2001 From: Thomas Wisniewski Date: Fri, 11 Feb 2022 17:27:45 +0000 Subject: [PATCH] Bug 1715900 - Add initial tests (and helpers) for the mach test-interventions command; r=jgraham Depends on D138384 Differential Revision: https://phabricator.services.mozilla.com/D138540 --- .../webcompat/interventions/tests/helpers.py | 131 ++++++++++++++++++ .../interventions/tests/test_1452707_absa.py | 17 +++ .../tests/test_1457335_histography.py | 16 +++ .../tests/test_1472075_bankofamerica.py | 19 +++ .../tests/test_1570108_steamcommunity.py | 92 ++++++++++++ .../tests/test_1610026_mobilesuica.py | 43 ++++++ .../tests/test_1631811_datastudio.py | 42 ++++++ .../tests/test_1711082_aliexpress.py | 21 +++ .../tests/test_1712833_desuca.py | 17 +++ .../tests/test_1719859_saxoinvestor.py | 28 ++++ .../interventions/tests/test_1719870_lcbo.py | 28 ++++ .../test_1738313_curriculum_gov_bc_ca.py | 25 ++++ 12 files changed, 479 insertions(+) create mode 100644 testing/webcompat/interventions/tests/helpers.py create mode 100644 testing/webcompat/interventions/tests/test_1452707_absa.py create mode 100644 testing/webcompat/interventions/tests/test_1457335_histography.py create mode 100644 testing/webcompat/interventions/tests/test_1472075_bankofamerica.py create mode 100644 testing/webcompat/interventions/tests/test_1570108_steamcommunity.py create mode 100644 testing/webcompat/interventions/tests/test_1610026_mobilesuica.py create mode 100644 testing/webcompat/interventions/tests/test_1631811_datastudio.py create mode 100644 testing/webcompat/interventions/tests/test_1711082_aliexpress.py create mode 100644 testing/webcompat/interventions/tests/test_1712833_desuca.py create mode 100644 testing/webcompat/interventions/tests/test_1719859_saxoinvestor.py create mode 100644 testing/webcompat/interventions/tests/test_1719870_lcbo.py create mode 100644 testing/webcompat/interventions/tests/test_1738313_curriculum_gov_bc_ca.py diff --git a/testing/webcompat/interventions/tests/helpers.py b/testing/webcompat/interventions/tests/helpers.py new file mode 100644 index 000000000000..716b4962d28f --- /dev/null +++ b/testing/webcompat/interventions/tests/helpers.py @@ -0,0 +1,131 @@ +# 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 pytest +import time + +from selenium.common.exceptions import NoAlertPresentException, NoSuchElementException +from selenium.webdriver.common.by import By + + +def expect_alert(session, text=None): + try: + alert = session.switch_to.alert + if text is not None: + assert alert.text == text + alert.dismiss() + except NoAlertPresentException: + pass + + +def is_float_cleared(session, elem1, elem2): + return session.execute_script( + """return (function(a, b) { + // Ensure that a is placed under + // (and not to the right of) b + return a?.offsetTop >= b?.offsetTop + b?.offsetHeight && + a?.offsetLeft < b?.offsetLeft + b?.offsetWidth; + }(arguments[0], arguments[1]));""", + elem1, + elem2, + ) + + +def await_getUserMedia_call_on_click(session, elem_to_click): + return session.execute_script( + """ + navigator.mediaDevices.getUserMedia = + navigator.mozGetUserMedia = + navigator.getUserMedia = + () => { window.__gumCalled = true; }; + """ + ) + elem_to_click.click() + return session.execute_async_script( + """ + var done = arguments[0]; + setInterval(500, () => { + if (window.__gumCalled === true) { + done(); + } + }); + """ + ) + + +class Xpath: + by = By.XPATH + + def __init__(self, value): + self.value = value + + +class Css: + by = By.CSS_SELECTOR + + def __init__(self, value): + self.value = value + + +class Text: + by = By.XPATH + + def __init__(self, value): + self.value = f"//*[contains(text(),'{value}')]" + + +Missing = object() + + +def find_elements(session, selector, all=True, default=Missing): + try: + if all: + return session.find_elements(selector.by, selector.value) + return session.find_element(selector.by, selector.value) + except NoSuchElementException: + if default is not Missing: + return default + raise + + +def find_element(session, selector, default=Missing): + return find_elements(session, selector, all=False, default=default) + + +def assert_not_element(session, sel): + with pytest.raises(NoSuchElementException): + find_element(session, sel) + + +def await_first_element_of(session, selectors, timeout=None, is_displayed=False): + t0 = time.time() + exc = None + if timeout is None: + timeout = 10 + found = [None for sel in selectors] + while time.time() < t0 + timeout: + for i, selector in enumerate(selectors): + try: + ele = find_element(session, selector) + if not is_displayed or ele.is_displayed(): + found[i] = ele + return found + except NoSuchElementException as e: + exc = e + time.sleep(0.5) + raise exc if exc is not None else NoSuchElementException + return found + + +def await_element(session, selector, timeout=None): + return await_first_element_of(session, [selector], timeout)[0] + + +def load_page_and_wait_for_iframe(session, url, selector, loads=1, timeout=None): + while loads > 0: + session.get(url) + frame = await_element(session, selector, timeout=timeout) + loads -= 1 + session.switch_to.frame(frame) + return frame diff --git a/testing/webcompat/interventions/tests/test_1452707_absa.py b/testing/webcompat/interventions/tests/test_1452707_absa.py new file mode 100644 index 000000000000..0cc1d28dcd2e --- /dev/null +++ b/testing/webcompat/interventions/tests/test_1452707_absa.py @@ -0,0 +1,17 @@ +import pytest +from helpers import Css, expect_alert, find_element + +URL = "https://ib.absa.co.za/absa-online/login.jsp" + + +@pytest.mark.with_interventions +def test_enabled(session): + session.get(URL) + assert find_element(session, Css("html.gecko")) + + +@pytest.mark.without_interventions +def test_disabled(session): + session.get(URL) + expect_alert(session, text="Browser unsupported") + assert find_element(session, Css("html.unknown")) diff --git a/testing/webcompat/interventions/tests/test_1457335_histography.py b/testing/webcompat/interventions/tests/test_1457335_histography.py new file mode 100644 index 000000000000..dbd11cd9cbbc --- /dev/null +++ b/testing/webcompat/interventions/tests/test_1457335_histography.py @@ -0,0 +1,16 @@ +import pytest + + +URL = "http://histography.io/" + + +@pytest.mark.with_interventions +def test_enabled(session): + session.get(URL) + assert session.current_url == URL + + +@pytest.mark.without_interventions +def test_disabled(session): + session.get(URL) + assert session.current_url == "http://histography.io/browser_support.htm" diff --git a/testing/webcompat/interventions/tests/test_1472075_bankofamerica.py b/testing/webcompat/interventions/tests/test_1472075_bankofamerica.py new file mode 100644 index 000000000000..4668bc8b63c2 --- /dev/null +++ b/testing/webcompat/interventions/tests/test_1472075_bankofamerica.py @@ -0,0 +1,19 @@ +import time +import pytest +from helpers import Css, await_element, find_element + +URL = "https://www.bankofamerica.com/" + + +@pytest.mark.with_interventions +def test_enabled(session): + session.get(URL) + time.sleep(3) + assert find_element(session, Css("#browserUpgradeNoticeBar"), default=None) is None + + +@pytest.mark.without_interventions +def test_disabled(session): + session.get(URL) + warning = await_element(session, Css("#browserUpgradeNoticeBar"), timeout=3) + assert warning.is_displayed() diff --git a/testing/webcompat/interventions/tests/test_1570108_steamcommunity.py b/testing/webcompat/interventions/tests/test_1570108_steamcommunity.py new file mode 100644 index 000000000000..f1469e6c1ef1 --- /dev/null +++ b/testing/webcompat/interventions/tests/test_1570108_steamcommunity.py @@ -0,0 +1,92 @@ +import pytest +from helpers import ( + Css, + Text, + Xpath, + await_element, + await_first_element_of, + await_getUserMedia_call_on_click, + find_element, + assert_not_element, +) + + +URL = "https://steamcommunity.com/chat" + + +USERID_CSS = Css("input#input_username") +PASSWORD_CSS = Css("input#input_password") +SIGNIN_CSS = Css("#login_btn_signin button") +GEAR_CSS = Css(".friendSettingsButton") +AUTH_CSS = Css("input#authcode") +RATE_TEXT = Text("too many login failures") +VOICE_XPATH = Xpath( + "//*[contains(text(), 'Voice') and " + "contains(@class, 'pagedsettings_PagedSettingsDialog_PageListItem')]" +) +MIC_BUTTON_CSS = Css("button.LocalMicTestButton") +UNSUPPORTED_TEXT = Text("currently unsupported in Firefox") + + +def load_mic_test(session, credentials): + session.get(URL) + + userid = find_element(session, USERID_CSS) + password = find_element(session, PASSWORD_CSS) + submit = find_element(session, SIGNIN_CSS) + assert userid.is_displayed() + assert password.is_displayed() + assert submit.is_displayed() + + userid.send_keys(credentials["username"]) + password.send_keys(credentials["password"]) + submit.click() + + while True: + [gear, auth, rate] = await_first_element_of( + session, [GEAR_CSS, AUTH_CSS, RATE_TEXT], is_displayed=True, timeout=20 + ) + if rate: + pytest.skip( + "Too many Steam login attempts detected in a short time; try again later." + ) + return None + elif auth: + pytest.skip("Two-factor authentication requested; disable Steam Guard.") + return None + else: + break + assert gear + gear.click() + + voice = await_element(session, VOICE_XPATH) + assert voice.is_displayed() + voice.click() + + mic_test = await_element(session, MIC_BUTTON_CSS) + assert mic_test.is_displayed() + + return mic_test + + +@pytest.mark.with_interventions +def test_enabled(session, credentials): + mic_test = load_mic_test(session, credentials) + if not mic_test: + return + + await_getUserMedia_call_on_click(session, mic_test) + + assert_not_element(session, UNSUPPORTED_TEXT) + + +@pytest.mark.without_interventions +def test_disabled(session, credentials): + mic_test = load_mic_test(session, credentials) + if not mic_test: + return + + mic_test.click() + + unsupported = await_element(session, UNSUPPORTED_TEXT) + assert unsupported.is_displayed() diff --git a/testing/webcompat/interventions/tests/test_1610026_mobilesuica.py b/testing/webcompat/interventions/tests/test_1610026_mobilesuica.py new file mode 100644 index 000000000000..4d02439ac685 --- /dev/null +++ b/testing/webcompat/interventions/tests/test_1610026_mobilesuica.py @@ -0,0 +1,43 @@ +import pytest +from helpers import Css, Text, find_element + + +def load_site(session): + session.get("https://www.mobilesuica.com/") + + address = find_element(session, Css("input[name=MailAddress]"), default=None) + password = find_element(session, Css("input[name=Password]"), default=None) + error = find_element(session, Css("input[name=winclosebutton]"), default=None) + + # The page can be down at certain times, making testing impossible. For instance: + # "モバイルSuicaサービスが可能な時間は4:00~翌日2:00です。 + # 時間をお確かめの上、再度実行してください。" + # "Mobile Suica service is available from 4:00 to 2:00 the next day. + # Please check the time and try again." + site_is_down = None is not find_element( + session, Text("時間をお確かめの上、再度実行してください。"), default=None + ) + if site_is_down: + pytest.xfail("Site is currently down") + + return address, password, error, site_is_down + + +@pytest.mark.with_interventions +def test_enabled(session): + address, password, error, site_is_down = load_site(session) + if site_is_down: + return + assert address.is_displayed() + assert password.is_displayed() + assert error is None + + +@pytest.mark.without_interventions +def test_disabled(session): + address, password, error, site_is_down = load_site(session) + if site_is_down: + return + assert address is None + assert password is None + assert error.is_displayed() diff --git a/testing/webcompat/interventions/tests/test_1631811_datastudio.py b/testing/webcompat/interventions/tests/test_1631811_datastudio.py new file mode 100644 index 000000000000..9704d3602606 --- /dev/null +++ b/testing/webcompat/interventions/tests/test_1631811_datastudio.py @@ -0,0 +1,42 @@ +import pytest +from helpers import ( + Css, + Text, + await_element, + load_page_and_wait_for_iframe, + find_element, +) + +# Note that we have to load this page twice, as when it is loaded the +# first time by the tests, the webcompat issue is avoided somehow +# (presumably a script race condition somewhere). However, it is +# triggered consistently the second time it is loaded. + + +URL = "https://ageor.dipot.com/2020/03/Covid-19-in-Greece.html" +FRAME_SELECTOR = Css("iframe[src^='https://datastudio.google.com/']") +FRAME_TEXT = Text("Coranavirus SARS-CoV-2 (Covid-19) in Greece") + + +@pytest.mark.with_interventions +def test_enabled(session): + load_page_and_wait_for_iframe(session, URL, FRAME_SELECTOR, loads=2) + assert session.execute_script("return window.indexedDB === undefined;") + success_text = await_element(session, FRAME_TEXT) + assert success_text.is_displayed() + + +@pytest.mark.without_interventions +def test_disabled(session): + load_page_and_wait_for_iframe(session, URL, FRAME_SELECTOR, loads=2) + assert session.execute_script( + """ + try { + window.indexedDB; + return false; + } catch (_) { + return true; + } + """ + ) + assert find_element(session, FRAME_TEXT, default=None) is None diff --git a/testing/webcompat/interventions/tests/test_1711082_aliexpress.py b/testing/webcompat/interventions/tests/test_1711082_aliexpress.py new file mode 100644 index 000000000000..94c217f793fb --- /dev/null +++ b/testing/webcompat/interventions/tests/test_1711082_aliexpress.py @@ -0,0 +1,21 @@ +import pytest +from helpers import Css, await_element, find_element + + +URL = "https://m.aliexpress.com/?tracelog=wwwhome2mobilesitehome" +SELECTOR = Css("#header input[placeholder]") +DISABLED_SELECTOR = Css(f"{SELECTOR.value}:disabled") + + +@pytest.mark.with_interventions +def test_enabled(session): + session.get(URL) + find_element(session, SELECTOR) + assert find_element(session, DISABLED_SELECTOR, default=None) is None + + +@pytest.mark.without_interventions +def test_disabled(session): + session.get(URL) + await_element(session, SELECTOR) + find_element(session, DISABLED_SELECTOR) diff --git a/testing/webcompat/interventions/tests/test_1712833_desuca.py b/testing/webcompat/interventions/tests/test_1712833_desuca.py new file mode 100644 index 000000000000..b9598916f322 --- /dev/null +++ b/testing/webcompat/interventions/tests/test_1712833_desuca.py @@ -0,0 +1,17 @@ +import pytest + + +URL = "https://buskocchi.desuca.co.jp/smartPhone.html" +SCRIPT = """return document.getElementById("map_canvas")?.clientHeight;""" + + +@pytest.mark.with_interventions +def test_enabled(session): + session.get(URL) + assert session.execute_script(SCRIPT) + + +@pytest.mark.without_interventions +def test_disabled(session): + session.get(URL) + assert session.execute_script(SCRIPT) diff --git a/testing/webcompat/interventions/tests/test_1719859_saxoinvestor.py b/testing/webcompat/interventions/tests/test_1719859_saxoinvestor.py new file mode 100644 index 000000000000..314e5e42c58a --- /dev/null +++ b/testing/webcompat/interventions/tests/test_1719859_saxoinvestor.py @@ -0,0 +1,28 @@ +import pytest +from helpers import Css, find_element + + +URL = ( + "https://www.saxoinvestor.fr/login/?adobe_mc=" + "MCORGID%3D173338B35278510F0A490D4C%2540AdobeOrg%7CTS%3D1621688498" +) + + +@pytest.mark.skip_platforms("linux") +@pytest.mark.with_interventions +def test_enabled(session): + session.get(URL) + userid = find_element(session, Css("input#field_userid")) + password = find_element(session, Css("input#field_password")) + submit = find_element(session, Css("input#button_login")) + assert userid.is_displayed() + assert password.is_displayed() + assert submit.is_displayed() + + +@pytest.mark.skip_platforms("linux") +@pytest.mark.without_interventions +def test_disabled(session): + session.get(URL) + warning = find_element(session, Css("#browser_support_section")) + assert warning.is_displayed() diff --git a/testing/webcompat/interventions/tests/test_1719870_lcbo.py b/testing/webcompat/interventions/tests/test_1719870_lcbo.py new file mode 100644 index 000000000000..f37c43a0f7c0 --- /dev/null +++ b/testing/webcompat/interventions/tests/test_1719870_lcbo.py @@ -0,0 +1,28 @@ +import pytest +from helpers import Css, is_float_cleared, find_element + + +URL = ( + "https://www.lcbo.com/webapp/wcs/stores/servlet/PhysicalStoreInventoryView" + "?langId=-1&storeId=10203&catalogId=10051&productId=54875" +) + + +PRODUCT_INFO = Css("#content > div") +LOCATIONS = Css("#inventoryTable") + + +@pytest.mark.with_interventions +def test_enabled(session): + session.get(URL) + product_info = find_element(session, PRODUCT_INFO) + locations = find_element(session, LOCATIONS) + assert is_float_cleared(session, locations, product_info) + + +@pytest.mark.without_interventions +def test_disabled(session): + session.get(URL) + product_info = find_element(session, PRODUCT_INFO) + locations = find_element(session, LOCATIONS) + assert not is_float_cleared(session, locations, product_info) diff --git a/testing/webcompat/interventions/tests/test_1738313_curriculum_gov_bc_ca.py b/testing/webcompat/interventions/tests/test_1738313_curriculum_gov_bc_ca.py new file mode 100644 index 000000000000..3beed9963b39 --- /dev/null +++ b/testing/webcompat/interventions/tests/test_1738313_curriculum_gov_bc_ca.py @@ -0,0 +1,25 @@ +import pytest +from helpers import Css, is_float_cleared, find_element + + +URL = "https://curriculum.gov.bc.ca/curriculum/arts-education/10/media-arts" + + +SHOULD_CLEAR = Css(".curriculum_big_ideas") +SHOULD_BE_CLEARED = Css(".view-display-id-attachment_1") + + +@pytest.mark.with_interventions +def test_enabled(session): + session.get(URL) + should_clear = find_element(session, SHOULD_CLEAR) + should_be_cleared = find_element(session, SHOULD_BE_CLEARED) + assert is_float_cleared(session, should_clear, should_be_cleared) + + +@pytest.mark.without_interventions +def test_disabled(session): + session.get(URL) + should_clear = find_element(session, SHOULD_CLEAR) + should_be_cleared = find_element(session, SHOULD_BE_CLEARED) + assert not is_float_cleared(session, should_clear, should_be_cleared)