summaryrefslogtreecommitdiffstats
path: root/testing/webcompat
diff options
context:
space:
mode:
Diffstat (limited to 'testing/webcompat')
-rw-r--r--testing/webcompat/__init__.py0
-rw-r--r--testing/webcompat/client.py759
-rw-r--r--testing/webcompat/config.json.sample5
-rw-r--r--testing/webcompat/fixtures.py255
-rw-r--r--testing/webcompat/interventions/__init__.py0
-rw-r--r--testing/webcompat/interventions/conftest.py56
-rw-r--r--testing/webcompat/interventions/pytest.ini7
-rw-r--r--testing/webcompat/interventions/tests/__init__.py0
-rw-r--r--testing/webcompat/interventions/tests/test_1385206_rakuten_co_jp.py23
-rw-r--r--testing/webcompat/interventions/tests/test_1448747_124385_drafthouse_com.py24
-rw-r--r--testing/webcompat/interventions/tests/test_1448747_bathpublishing_com.py33
-rw-r--r--testing/webcompat/interventions/tests/test_1448747_bluetokaicoffee_com.py24
-rw-r--r--testing/webcompat/interventions/tests/test_1448747_co2meter_com.py41
-rw-r--r--testing/webcompat/interventions/tests/test_1448747_discountcoffee_co_uk.py24
-rw-r--r--testing/webcompat/interventions/tests/test_1448747_dylantalkstone_com.py24
-rw-r--r--testing/webcompat/interventions/tests/test_1448747_fourbarrelcoffee_com.py24
-rw-r--r--testing/webcompat/interventions/tests/test_1448747_franmar_com.py24
-rw-r--r--testing/webcompat/interventions/tests/test_1448747_renewd_com_au.py24
-rw-r--r--testing/webcompat/interventions/tests/test_1448747_thehawksmoor_com.py32
-rw-r--r--testing/webcompat/interventions/tests/test_1452707_absa.py20
-rw-r--r--testing/webcompat/interventions/tests/test_1457335_histography.py20
-rw-r--r--testing/webcompat/interventions/tests/test_1472075_bankofamerica.py19
-rw-r--r--testing/webcompat/interventions/tests/test_1509873_viewer_zmags_com.py21
-rw-r--r--testing/webcompat/interventions/tests/test_1570108_steamcommunity.py122
-rw-r--r--testing/webcompat/interventions/tests/test_1570328_developer_apple_com.py29
-rw-r--r--testing/webcompat/interventions/tests/test_1574522_enuri_com.py23
-rw-r--r--testing/webcompat/interventions/tests/test_1575000_lloydsbank.py53
-rw-r--r--testing/webcompat/interventions/tests/test_1577267_metfone_com_kh.py20
-rw-r--r--testing/webcompat/interventions/tests/test_1577519_stream_directv_com.py37
-rw-r--r--testing/webcompat/interventions/tests/test_1579159_m_tailieu_vn.py26
-rw-r--r--testing/webcompat/interventions/tests/test_1582582_sling_com.py26
-rw-r--r--testing/webcompat/interventions/tests/test_1595215_uniqlo_com.py21
-rw-r--r--testing/webcompat/interventions/tests/test_1610026_mobilesuica.py49
-rw-r--r--testing/webcompat/interventions/tests/test_1610344_directv_com_co.py55
-rw-r--r--testing/webcompat/interventions/tests/test_1622063_wp1-ext_usps_gov.py21
-rw-r--r--testing/webcompat/interventions/tests/test_1631811_datastudio.py40
-rw-r--r--testing/webcompat/interventions/tests/test_1644830_missingmail_usps.py63
-rw-r--r--testing/webcompat/interventions/tests/test_1650317_livescience_com.py40
-rw-r--r--testing/webcompat/interventions/tests/test_1651292_www_jp_square-enix_com.py20
-rw-r--r--testing/webcompat/interventions/tests/test_1651917_www_teletrader_com.py27
-rw-r--r--testing/webcompat/interventions/tests/test_1654877_preev_com.py32
-rw-r--r--testing/webcompat/interventions/tests/test_1654907_reactine_ca.py20
-rw-r--r--testing/webcompat/interventions/tests/test_1694470_m_myvidster_com.py31
-rw-r--r--testing/webcompat/interventions/tests/test_1697324_mobile2_bmo_com.py21
-rw-r--r--testing/webcompat/interventions/tests/test_1704673_app_xiaomi_com.py24
-rw-r--r--testing/webcompat/interventions/tests/test_1712807_www_dealnews_com.py31
-rw-r--r--testing/webcompat/interventions/tests/test_1712833_desuca.py18
-rw-r--r--testing/webcompat/interventions/tests/test_1719859_saxoinvestor.py28
-rw-r--r--testing/webcompat/interventions/tests/test_1722954_granbluefantasy_jp.py27
-rw-r--r--testing/webcompat/interventions/tests/test_1722955_frontgate_com.py26
-rw-r--r--testing/webcompat/interventions/tests/test_1738317_vmos_cn.py23
-rw-r--r--testing/webcompat/interventions/tests/test_1738319_yebocasino_co_za.py29
-rw-r--r--testing/webcompat/interventions/tests/test_1741234_alphalabs.py35
-rw-r--r--testing/webcompat/interventions/tests/test_1743627_renrenaud-bray_com.py25
-rw-r--r--testing/webcompat/interventions/tests/test_1743751_slrclub_com.py23
-rw-r--r--testing/webcompat/interventions/tests/test_1743754_workflow_base_vn.py20
-rw-r--r--testing/webcompat/interventions/tests/test_1753461_naver.py22
-rw-r--r--testing/webcompat/interventions/tests/test_1756872_www_dolcegabbana_com.py33
-rw-r--r--testing/webcompat/interventions/tests/test_1765947_veniceincoming_com.py42
-rw-r--r--testing/webcompat/interventions/tests/test_1774490_rainews_it.py32
-rw-r--r--testing/webcompat/interventions/tests/test_1776897_www_edencast_fr.py59
-rw-r--r--testing/webcompat/interventions/tests/test_1778168_watch_antennaplus_gr.py22
-rw-r--r--testing/webcompat/interventions/tests/test_1784141_acuvue_com.py22
-rw-r--r--testing/webcompat/interventions/tests/test_1784141_aveeno_com.py22
-rw-r--r--testing/webcompat/interventions/tests/test_1784199_aptsovation_com.py20
-rw-r--r--testing/webcompat/interventions/tests/test_1784199_avanabayview_com.py20
-rw-r--r--testing/webcompat/interventions/tests/test_1784199_breakpointeandcoronado_com.py20
-rw-r--r--testing/webcompat/interventions/tests/test_1784199_entrata_platform.py20
-rw-r--r--testing/webcompat/interventions/tests/test_1784199_liveatlasathens_com.py20
-rw-r--r--testing/webcompat/interventions/tests/test_1784199_liveobserverpark_com.py20
-rw-r--r--testing/webcompat/interventions/tests/test_1784199_midwayurban_com.py20
-rw-r--r--testing/webcompat/interventions/tests/test_1784199_nhcalaska_com.py20
-rw-r--r--testing/webcompat/interventions/tests/test_1784199_securityproperties_com.py20
-rw-r--r--testing/webcompat/interventions/tests/test_1784199_theloftsorlando_com.py20
-rw-r--r--testing/webcompat/interventions/tests/test_1784199_thepointeatcentral_prospectportal_com.py20
-rw-r--r--testing/webcompat/interventions/tests/test_1784199_vanallenapartments_com.py20
-rw-r--r--testing/webcompat/interventions/tests/test_1784361_coldwellbankerhomes_com.py23
-rw-r--r--testing/webcompat/interventions/tests/test_1786404_business_help_royalmail_com.py70
-rw-r--r--testing/webcompat/interventions/tests/test_1793761_schoolnutritionandfitness_com.py32
-rw-r--r--testing/webcompat/interventions/tests/test_1795490_www_china-airlines_com.py25
-rw-r--r--testing/webcompat/interventions/tests/test_1797400_mobilevikings_be.py31
-rw-r--r--testing/webcompat/interventions/tests/test_1799968_samsung_com.py21
-rw-r--r--testing/webcompat/interventions/tests/test_1800000_www_honda_co_uk.py53
-rw-r--r--testing/webcompat/interventions/tests/test_1800241_mms_telekom_de.py19
-rw-r--r--testing/webcompat/interventions/tests/test_1800936_cov19ent_kdca_go_kr.py30
-rw-r--r--testing/webcompat/interventions/tests/test_1817520_ersthelfer_tv.py36
-rw-r--r--testing/webcompat/interventions/tests/test_1818818_wellcare_com.py31
-rw-r--r--testing/webcompat/interventions/tests/test_1819450_cmbchina_com.py21
-rw-r--r--testing/webcompat/interventions/tests/test_1819476_axisbank_com.py18
-rw-r--r--testing/webcompat/interventions/tests/test_1819702_feelgoodcontacts_com.py21
-rw-r--r--testing/webcompat/interventions/tests/test_1829126_otsuka_co_jp.py28
-rw-r--r--testing/webcompat/interventions/tests/test_1829944_411_ca.py18
-rw-r--r--testing/webcompat/interventions/tests/test_1829947_torguard_net.py24
-rw-r--r--testing/webcompat/interventions/tests/test_1830739_casinoextreme_eu.py28
-rw-r--r--testing/webcompat/interventions/tests/test_1830739_www_cryptoloko_com.py26
-rw-r--r--testing/webcompat/interventions/tests/test_1830739_www_heapsofwins_com.py35
-rw-r--r--testing/webcompat/interventions/tests/test_1830739_www_planet7casino_com.py34
-rw-r--r--testing/webcompat/interventions/tests/test_1830747_babbel_com.py35
-rw-r--r--testing/webcompat/interventions/tests/test_1830752_afisha_ru.py45
-rw-r--r--testing/webcompat/interventions/tests/test_1830761_91mobiles_com.py27
-rw-r--r--testing/webcompat/interventions/tests/test_1830776_blueshieldca_com.py18
-rw-r--r--testing/webcompat/interventions/tests/test_1830796_copyleaks_com.py26
-rw-r--r--testing/webcompat/interventions/tests/test_1830810_interceramic_com.py21
-rw-r--r--testing/webcompat/interventions/tests/test_1830813_onstove_com.py29
-rw-r--r--testing/webcompat/interventions/tests/test_1830821_enjoy_point_auone_jp.py21
-rw-r--r--testing/webcompat/interventions/tests/test_1830821_webcartop_jp.py23
-rw-r--r--testing/webcompat/interventions/tests/test_1836103_autostar_novoross_ru.py29
-rw-r--r--testing/webcompat/interventions/tests/test_1836109_watch_tonton_com_my.py26
-rw-r--r--testing/webcompat/interventions/tests/test_1836112_www_capcut_cn.py19
-rw-r--r--testing/webcompat/interventions/tests/test_1836116_www_slushy_com.py20
-rw-r--r--testing/webcompat/interventions/tests/test_1836135_gts_pro_sdimedia_com.py19
-rw-r--r--testing/webcompat/interventions/tests/test_1836140_indices_iriworldwide_com.py18
-rw-r--r--testing/webcompat/interventions/tests/test_1836141_yabbycasino_com.py24
-rw-r--r--testing/webcompat/interventions/tests/test_1836157_thai_masszazs_net.py26
-rw-r--r--testing/webcompat/interventions/tests/test_1836178_atracker_pro.py23
-rw-r--r--testing/webcompat/interventions/tests/test_1836182_www_flatsatshadowglen_com.py18
-rw-r--r--testing/webcompat/interventions/tests/test_1848711_vio_com.py43
-rw-r--r--testing/webcompat/interventions/tests/test_1848716_elal_com.py22
-rw-r--r--testing/webcompat/interventions/tests/test_1849018_carefirst_com.py19
-rw-r--r--testing/webcompat/interventions/tests/test_1849019_123068_axa_assistance_pl.py39
-rw-r--r--testing/webcompat/interventions/tests/test_1849029_publi24_ro.py28
-rw-r--r--testing/webcompat/interventions/tests/test_1849058_nicochannel_jp.py19
-rw-r--r--testing/webcompat/interventions/tests/test_1855014_77221_eksiseyler_com.py21
-rw-r--r--testing/webcompat/interventions/tests/test_1855071_121197_www_meteoam_it.py36
-rw-r--r--testing/webcompat/interventions/tests/test_1855088_125039_hrmis2_eghrmis_gov_my.py25
-rw-r--r--testing/webcompat/interventions/tests/test_1855102_121877_my_southerncross_co_nz.py23
-rw-r--r--testing/webcompat/interventions/tests/test_969844_mobile_de.py27
-rw-r--r--testing/webcompat/mach_commands.py338
-rw-r--r--testing/webcompat/requirements.txt3
-rw-r--r--testing/webcompat/runner.py263
-rw-r--r--testing/webcompat/shims/__init__.py0
-rw-r--r--testing/webcompat/shims/conftest.py68
-rw-r--r--testing/webcompat/shims/pytest.ini11
-rw-r--r--testing/webcompat/shims/tests/__init__.py0
-rw-r--r--testing/webcompat/shims/tests/common.py84
-rw-r--r--testing/webcompat/shims/tests/test_1624914_google_trends.py29
-rw-r--r--testing/webcompat/shims/tests/test_1694168_google_analytics_and_tag_manager.py23
-rw-r--r--testing/webcompat/shims/tests/test_1701685_advertising_com.py22
-rw-r--r--testing/webcompat/shims/tests/test_1717806_adsafeprotected.py37
-rw-r--r--testing/webcompat/shims/tests/test_1717806_stickyadstv.py22
-rw-r--r--testing/webcompat/shims/tests/test_1762851_google_publisher_tags.py23
-rw-r--r--testing/webcompat/shims/tests/test_1767270_rva311_com_pbm_fix.py26
-rw-r--r--testing/webcompat/shims/tests/test_1775099_google_publisher_tags.py23
143 files changed, 5403 insertions, 0 deletions
diff --git a/testing/webcompat/__init__.py b/testing/webcompat/__init__.py
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/testing/webcompat/__init__.py
diff --git a/testing/webcompat/client.py b/testing/webcompat/client.py
new file mode 100644
index 0000000000..88208e5507
--- /dev/null
+++ b/testing/webcompat/client.py
@@ -0,0 +1,759 @@
+# 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 asyncio
+import contextlib
+import time
+from urllib.parse import quote
+
+import webdriver
+from webdriver.bidi.modules.script import ContextTarget
+
+
+class Client:
+ def __init__(self, session, event_loop):
+ self.session = session
+ self.event_loop = event_loop
+ self.content_blocker_loaded = False
+
+ @property
+ def current_url(self):
+ return self.session.url
+
+ @property
+ def alert(self):
+ return self.session.alert
+
+ @property
+ def context(self):
+ return self.session.send_session_command("GET", "moz/context")
+
+ @context.setter
+ def context(self, context):
+ self.session.send_session_command("POST", "moz/context", {"context": context})
+
+ @contextlib.contextmanager
+ def using_context(self, context):
+ orig_context = self.context
+ needs_change = context != orig_context
+
+ if needs_change:
+ self.context = context
+
+ try:
+ yield
+ finally:
+ if needs_change:
+ self.context = orig_context
+
+ def wait_for_content_blocker(self):
+ if not self.content_blocker_loaded:
+ with self.using_context("chrome"):
+ self.session.execute_async_script(
+ """
+ const done = arguments[0],
+ signal = "safebrowsing-update-finished";
+ function finish() {
+ Services.obs.removeObserver(finish, signal);
+ done();
+ }
+ Services.obs.addObserver(finish, signal);
+ """
+ )
+ self.content_blocker_loaded = True
+
+ @property
+ def keyboard(self):
+ return self.session.actions.sequence("key", "keyboard_id")
+
+ @property
+ def mouse(self):
+ return self.session.actions.sequence(
+ "pointer", "pointer_id", {"pointerType": "mouse"}
+ )
+
+ @property
+ def pen(self):
+ return self.session.actions.sequence(
+ "pointer", "pointer_id", {"pointerType": "pen"}
+ )
+
+ @property
+ def touch(self):
+ return self.session.actions.sequence(
+ "pointer", "pointer_id", {"pointerType": "touch"}
+ )
+
+ @property
+ def wheel(self):
+ return self.session.actions.sequence("wheel", "wheel_id")
+
+ @property
+ def modifier_key(self):
+ if self.session.capabilities["platformName"] == "mac":
+ return "\ue03d" # meta (command)
+ else:
+ return "\ue009" # control
+
+ def inline(self, doc):
+ return "data:text/html;charset=utf-8,{}".format(quote(doc))
+
+ async def top_context(self):
+ contexts = await self.session.bidi_session.browsing_context.get_tree()
+ return contexts[0]
+
+ async def navigate(self, url, timeout=None, **kwargs):
+ return await asyncio.wait_for(
+ asyncio.ensure_future(self._navigate(url, **kwargs)), timeout=timeout
+ )
+
+ async def _navigate(self, url, wait="complete", await_console_message=None):
+ if self.session.test_config.get("use_pbm") or self.session.test_config.get(
+ "use_strict_etp"
+ ):
+ print("waiting for content blocker...")
+ self.wait_for_content_blocker()
+ if await_console_message is not None:
+ console_message = await self.promise_console_message_listener(
+ await_console_message
+ )
+ if wait == "load":
+ page_load = await self.promise_readystate_listener("load", url=url)
+ try:
+ await self.session.bidi_session.browsing_context.navigate(
+ context=(await self.top_context())["context"],
+ url=url,
+ wait=wait if wait != "load" else None,
+ )
+ except webdriver.bidi.error.UnknownErrorException as u:
+ m = str(u)
+ if (
+ "NS_BINDING_ABORTED" not in m
+ and "NS_ERROR_ABORT" not in m
+ and "NS_ERROR_WONT_HANDLE_CONTENT" not in m
+ ):
+ raise u
+ if wait == "load":
+ await page_load
+ if await_console_message is not None:
+ await console_message
+
+ async def promise_event_listener(self, events, check_fn=None, timeout=20):
+ if type(events) is not list:
+ events = [events]
+
+ await self.session.bidi_session.session.subscribe(events=events)
+
+ future = self.event_loop.create_future()
+
+ listener_removers = []
+
+ def remove_listeners():
+ for listener_remover in listener_removers:
+ try:
+ listener_remover()
+ except Exception:
+ pass
+
+ async def on_event(method, data):
+ print("on_event", method, data)
+ val = None
+ if check_fn is not None:
+ val = check_fn(method, data)
+ if val is None:
+ return
+ future.set_result(val)
+
+ for event in events:
+ r = self.session.bidi_session.add_event_listener(event, on_event)
+ listener_removers.append(r)
+
+ async def task():
+ try:
+ return await asyncio.wait_for(future, timeout=timeout)
+ finally:
+ remove_listeners()
+ try:
+ await asyncio.wait_for(
+ self.session.bidi_session.session.unsubscribe(events=events),
+ timeout=4,
+ )
+ except asyncio.exceptions.TimeoutError:
+ print("Unexpectedly timed out unsubscribing", events)
+ pass
+
+ return asyncio.create_task(task())
+
+ async def promise_console_message_listener(self, msg, **kwargs):
+ def check(method, data):
+ if "text" in data:
+ if msg in data["text"]:
+ return data
+ if "args" in data and len(data["args"]):
+ for arg in data["args"]:
+ if "value" in arg and msg in arg["value"]:
+ return data
+
+ return await self.promise_event_listener("log.entryAdded", check, **kwargs)
+
+ async def is_console_message(self, message):
+ try:
+ await (await self.promise_console_message_listener(message, timeout=2))
+ return True
+ except asyncio.exceptions.TimeoutError:
+ return False
+
+ async def promise_readystate_listener(self, state, url=None, **kwargs):
+ event = f"browsingContext.{state}"
+
+ def check(method, data):
+ if url is None or url in data["url"]:
+ return data
+
+ return await self.promise_event_listener(event, check, **kwargs)
+
+ async def promise_frame_listener(self, url, state="domContentLoaded", **kwargs):
+ event = f"browsingContext.{state}"
+
+ def check(method, data):
+ if url is None or url in data["url"]:
+ return Client.Context(self, data["context"])
+
+ return await self.promise_event_listener(event, check, **kwargs)
+
+ async def find_frame_context_by_url(self, url):
+ def find_in(arr, url):
+ for context in arr:
+ if url in context["url"]:
+ return context
+ for context in arr:
+ found = find_in(context["children"], url)
+ if found:
+ return found
+
+ return find_in([await self.top_context()], url)
+
+ class Context:
+ def __init__(self, client, id):
+ self.client = client
+ self.target = ContextTarget(id)
+
+ async def find_css(self, selector, all=False):
+ all = "All" if all else ""
+ return await self.client.session.bidi_session.script.evaluate(
+ expression=f"document.querySelector{all}('{selector}')",
+ target=self.target,
+ await_promise=False,
+ )
+
+ def timed_js(self, timeout, poll, fn, is_displayed=False):
+ return f"""() => new Promise((_good, _bad) => {{
+ {self.is_displayed_js()}
+ var _poll = {poll} * 1000;
+ var _time = {timeout} * 1000;
+ var _done = false;
+ var resolve = val => {{
+ if ({is_displayed}) {{
+ if (val.length) {{
+ val = val.filter(v = is_displayed(v));
+ }} else {{
+ val = is_displayed(val) && val;
+ }}
+ if (!val.length && !val.matches) {{
+ return;
+ }}
+ }}
+ _done = true;
+ clearInterval(_int);
+ _good(val);
+ }};
+ var reject = str => {{
+ _done = true;
+ clearInterval(_int);
+ _bad(val);
+ }};
+ var _int = setInterval(() => {{
+ {fn};
+ if (!_done) {{
+ _time -= _poll;
+ if (_time <= 0) {{
+ reject();
+ }}
+ }}
+ }}, poll);
+ }})"""
+
+ def is_displayed_js(self):
+ return """
+ function is_displayed(e) {
+ const s = window.getComputedStyle(e),
+ v = s.visibility === "visible",
+ o = Math.abs(parseFloat(s.opacity));
+ return e.getClientRects().length > 0 && v && (isNaN(o) || o === 1.0);
+ }
+ """
+
+ async def await_css(
+ self,
+ selector,
+ all=False,
+ timeout=10,
+ poll=0.25,
+ condition=False,
+ is_displayed=False,
+ ):
+ all = "All" if all else ""
+ condition = (
+ f"var elem=arguments[0]; if ({condition})" if condition else False
+ )
+ return await self.client.session.bidi_session.script.evaluate(
+ expression=self.timed_js(
+ timeout,
+ poll,
+ f"""
+ var ele = document.querySelector{all}('{selector}')";
+ if (ele && (!"length" in ele || ele.length > 0)) {{
+ '{condition}'
+ resolve(ele);
+ }}
+ """,
+ ),
+ target=self.target,
+ await_promise=True,
+ )
+
+ async def await_text(self, text, **kwargs):
+ xpath = f"//*[contains(text(),'{text}')]"
+ return await self.await_xpath(self, xpath, **kwargs)
+
+ async def await_xpath(
+ self, xpath, all=False, timeout=10, poll=0.25, is_displayed=False
+ ):
+ all = "true" if all else "false"
+ return await self.client.session.bidi_session.script.evaluate(
+ expression=self.timed_js(
+ timeout,
+ poll,
+ """
+ var ret = [];
+ var r, res = document.evaluate(`{xpath}`, document, null, 4);
+ while (r = res.iterateNext()) {
+ ret.push(r);
+ }
+ resolve({all} ? ret : ret[0]);
+ """,
+ ),
+ target=self.target,
+ await_promise=True,
+ )
+
+ def wrap_script_args(self, args):
+ if args is None:
+ return args
+ out = []
+ for arg in args:
+ if arg is None:
+ out.append({"type": "undefined"})
+ continue
+ t = type(arg)
+ if t == int or t == float:
+ out.append({"type": "number", "value": arg})
+ elif t == bool:
+ out.append({"type": "boolean", "value": arg})
+ elif t == str:
+ out.append({"type": "string", "value": arg})
+ else:
+ if "type" in arg:
+ out.push(arg)
+ continue
+ raise ValueError(f"Unhandled argument type: {t}")
+ return out
+
+ class PreloadScript:
+ def __init__(self, client, script, target):
+ self.client = client
+ self.script = script
+ if type(target) == list:
+ self.target = target[0]
+ else:
+ self.target = target
+
+ def stop(self):
+ return self.client.session.bidi_session.script.remove_preload_script(
+ script=self.script
+ )
+
+ async def run(self, fn, *args, await_promise=False):
+ val = await self.client.session.bidi_session.script.call_function(
+ arguments=self.client.wrap_script_args(args),
+ await_promise=await_promise,
+ function_declaration=fn,
+ target=self.target,
+ )
+ if val and "value" in val:
+ return val["value"]
+ return val
+
+ async def make_preload_script(self, text, sandbox, args=None, context=None):
+ if not context:
+ context = (await self.top_context())["context"]
+ target = ContextTarget(context, sandbox)
+ if args is None:
+ text = f"() => {{ {text} }}"
+ script = await self.session.bidi_session.script.add_preload_script(
+ function_declaration=text,
+ arguments=self.wrap_script_args(args),
+ sandbox=sandbox,
+ )
+ return Client.PreloadScript(self, script, target)
+
+ async def await_alert(self, text):
+ if not hasattr(self, "alert_preload_script"):
+ self.alert_preload_script = await self.make_preload_script(
+ """
+ window.__alerts = [];
+ window.wrappedJSObject.alert = function(text) {
+ window.__alerts.push(text);
+ }
+ """,
+ "alert_detector",
+ )
+ return self.alert_preload_script.run(
+ """(msg) => new Promise(done => {
+ const to = setInterval(() => {
+ if (window.__alerts.includes(msg)) {
+ clearInterval(to);
+ done();
+ }
+ }, 200);
+ })
+ """,
+ text,
+ await_promise=True,
+ )
+
+ async def await_popup(self, url=None):
+ if not hasattr(self, "popup_preload_script"):
+ self.popup_preload_script = await self.make_preload_script(
+ """
+ window.__popups = [];
+ window.wrappedJSObject.open = function(url) {
+ window.__popups.push(url);
+ }
+ """,
+ "popup_detector",
+ )
+ return self.popup_preload_script.run(
+ """(url) => new Promise(done => {
+ const to = setInterval(() => {
+ if (url === undefined && window.__popups.length) {
+ clearInterval(to);
+ return done(window.__popups[0]);
+ }
+ const found = window.__popups.find(u => u.includes(url));
+ if (found !== undefined) {
+ clearInterval(to);
+ done(found);
+ }
+ }, 1000);
+ })
+ """,
+ url,
+ await_promise=True,
+ )
+
+ async def track_listener(self, type, selector):
+ if not hasattr(self, "listener_preload_script"):
+ self.listener_preload_script = await self.make_preload_script(
+ """
+ window.__listeners = {};
+ var proto = EventTarget.wrappedJSObject.prototype;
+ var def = Object.getOwnPropertyDescriptor(proto, "addEventListener");
+ var old = def.value;
+ def.value = function(type, fn, opts) {
+ if ("matches" in this) {
+ if (!window.__listeners[type]) {
+ window.__listeners[type] = new Set();
+ }
+ window.__listeners[type].add(this);
+ }
+ return old.call(this, type, fn, opts)
+ };
+ Object.defineProperty(proto, "addEventListener", def);
+ """,
+ "listener_detector",
+ )
+ return Client.ListenerTracker(self.listener_preload_script, type, selector)
+
+ @contextlib.asynccontextmanager
+ async def preload_script(self, text, *args):
+ script = await self.make_preload_script(text, "preload", args=args)
+ yield script
+ await script.stop()
+
+ def back(self):
+ self.session.back()
+
+ def switch_to_frame(self, frame):
+ return self.session.transport.send(
+ "POST",
+ "session/{session_id}/frame".format(**vars(self.session)),
+ {"id": frame},
+ encoder=webdriver.protocol.Encoder,
+ decoder=webdriver.protocol.Decoder,
+ session=self.session,
+ )
+
+ def switch_frame(self, frame):
+ self.session.switch_frame(frame)
+
+ async def load_page_and_wait_for_iframe(
+ self, url, finder, loads=1, timeout=None, **kwargs
+ ):
+ while loads > 0:
+ await self.navigate(url, **kwargs)
+ frame = self.await_element(finder, timeout=timeout)
+ loads -= 1
+ self.switch_frame(frame)
+ return frame
+
+ def execute_script(self, script, *args):
+ return self.session.execute_script(script, args=args)
+
+ def execute_async_script(self, script, *args, **kwargs):
+ return self.session.execute_async_script(script, args, **kwargs)
+
+ def clear_all_cookies(self):
+ self.session.transport.send(
+ "DELETE", "session/%s/cookie" % self.session.session_id
+ )
+
+ def send_element_command(self, element, method, uri, body=None):
+ url = "element/%s/%s" % (element.id, uri)
+ return self.session.send_session_command(method, url, body)
+
+ def get_element_attribute(self, element, name):
+ return self.send_element_command(element, "GET", "attribute/%s" % name)
+
+ def _do_is_displayed_check(self, ele, is_displayed):
+ if ele is None:
+ return None
+
+ if type(ele) in [list, tuple]:
+ return [x for x in ele if self._do_is_displayed_check(x, is_displayed)]
+
+ if is_displayed is False and ele and self.is_displayed(ele):
+ return None
+ if is_displayed is True and ele and not self.is_displayed(ele):
+ return None
+ return ele
+
+ def find_css(self, *args, all=False, is_displayed=None, **kwargs):
+ try:
+ ele = self.session.find.css(*args, all=all, **kwargs)
+ return self._do_is_displayed_check(ele, is_displayed)
+ except webdriver.error.NoSuchElementException:
+ return None
+
+ def find_xpath(self, xpath, all=False, is_displayed=None):
+ route = "elements" if all else "element"
+ body = {"using": "xpath", "value": xpath}
+ try:
+ ele = self.session.send_session_command("POST", route, body)
+ return self._do_is_displayed_check(ele, is_displayed)
+ except webdriver.error.NoSuchElementException:
+ return None
+
+ def find_text(self, text, is_displayed=None, **kwargs):
+ try:
+ ele = self.find_xpath(f"//*[contains(text(),'{text}')]", **kwargs)
+ return self._do_is_displayed_check(ele, is_displayed)
+ except webdriver.error.NoSuchElementException:
+ return None
+
+ def find_element(self, finder, is_displayed=None, **kwargs):
+ ele = finder.find(self, **kwargs)
+ return self._do_is_displayed_check(ele, is_displayed)
+
+ def await_css(self, selector, **kwargs):
+ return self.await_element(self.css(selector), **kwargs)
+
+ def await_xpath(self, selector, **kwargs):
+ return self.await_element(self.xpath(selector), **kwargs)
+
+ def await_text(self, selector, *args, **kwargs):
+ return self.await_element(self.text(selector), **kwargs)
+
+ def await_element(self, finder, **kwargs):
+ return self.await_first_element_of([finder], **kwargs)[0]
+
+ class css:
+ def __init__(self, selector):
+ self.selector = selector
+
+ def find(self, client, **kwargs):
+ return client.find_css(self.selector, **kwargs)
+
+ class xpath:
+ def __init__(self, selector):
+ self.selector = selector
+
+ def find(self, client, **kwargs):
+ return client.find_xpath(self.selector, **kwargs)
+
+ class text:
+ def __init__(self, selector):
+ self.selector = selector
+
+ def find(self, client, **kwargs):
+ return client.find_text(self.selector, **kwargs)
+
+ def await_first_element_of(
+ self, finders, timeout=None, delay=0.25, condition=False, **kwargs
+ ):
+ t0 = time.time()
+ condition = f"var elem=arguments[0]; return {condition}" if condition else False
+
+ if timeout is None:
+ timeout = 10
+
+ found = [None for finder in finders]
+
+ exc = None
+ while time.time() < t0 + timeout:
+ for i, finder in enumerate(finders):
+ try:
+ result = finder.find(self, **kwargs)
+ if result and (
+ not condition
+ or self.session.execute_script(condition, [result])
+ ):
+ found[i] = result
+ return found
+ except webdriver.error.NoSuchElementException as e:
+ exc = e
+ time.sleep(delay)
+ raise exc if exc is not None else webdriver.error.NoSuchElementException
+ return found
+
+ async def dom_ready(self, timeout=None):
+ if timeout is None:
+ timeout = 20
+
+ async def wait():
+ return self.session.execute_async_script(
+ """
+ const cb = arguments[0];
+ setInterval(() => {
+ if (document.readyState === "complete") {
+ cb();
+ }
+ }, 500);
+ """
+ )
+
+ task = asyncio.create_task(wait())
+ return await asyncio.wait_for(task, timeout)
+
+ def is_float_cleared(self, elem1, elem2):
+ return self.session.execute_script(
+ """return (function(a, b) {
+ // Ensure that a is placed under b (and not to its right)
+ return a?.offsetTop >= b?.offsetTop + b?.offsetHeight &&
+ a?.offsetLeft < b?.offsetLeft + b?.offsetWidth;
+ }(arguments[0], arguments[1]));""",
+ elem1,
+ elem2,
+ )
+
+ @contextlib.contextmanager
+ def assert_getUserMedia_called(self):
+ self.execute_script(
+ """
+ navigator.mediaDevices.getUserMedia =
+ navigator.mozGetUserMedia =
+ navigator.getUserMedia =
+ () => { window.__gumCalled = true; };
+ """
+ )
+ yield
+ assert self.execute_script("return window.__gumCalled === true;")
+
+ def await_element_hidden(self, finder, timeout=None, delay=0.25):
+ t0 = time.time()
+
+ if timeout is None:
+ timeout = 20
+
+ elem = finder.find(self)
+ while time.time() < t0 + timeout:
+ try:
+ if not self.is_displayed(elem):
+ return
+ time.sleep(delay)
+ except webdriver.error.StaleElementReferenceException:
+ return
+
+ def soft_click(self, element):
+ self.execute_script("arguments[0].click()", element)
+
+ def remove_element(self, element):
+ self.execute_script("arguments[0].remove()", element)
+
+ def scroll_into_view(self, element):
+ self.execute_script(
+ "arguments[0].scrollIntoView({block:'center', inline:'center', behavior: 'instant'})",
+ element,
+ )
+
+ @contextlib.asynccontextmanager
+ async def ensure_fastclick_activates(self):
+ fastclick_preload_script = await self.make_preload_script(
+ """
+ var _ = document.createElement("webcompat_test");
+ _.style = "position:absolute;right:-1px;width:1px;height:1px";
+ document.documentElement.appendChild(_);
+ """,
+ "fastclick_forcer",
+ )
+ yield
+ fastclick_preload_script.stop()
+
+ def test_for_fastclick(self, element):
+ # FastClick cancels touchend, breaking default actions on Fenix.
+ # It instead fires a mousedown or click, which we can detect.
+ self.execute_script(
+ """
+ const sel = arguments[0];
+ window.fastclicked = false;
+ const evt = sel.nodeName === "SELECT" ? "mousedown" : "click";
+ document.addEventListener(evt, e => {
+ if (e.target === sel && !e.isTrusted) {
+ window.fastclicked = true;
+ }
+ }, true);
+ """,
+ element,
+ )
+ self.scroll_into_view(element)
+ # tap a few times in case the site's other code interferes
+ self.touch.click(element=element).perform()
+ self.touch.click(element=element).perform()
+ self.touch.click(element=element).perform()
+ return self.execute_script("return window.fastclicked")
+
+ def is_displayed(self, element):
+ if element is None:
+ return False
+
+ return self.session.execute_script(
+ """
+ const e = arguments[0],
+ s = window.getComputedStyle(e),
+ v = s.visibility === "visible",
+ o = Math.abs(parseFloat(s.opacity));
+ return e.getClientRects().length > 0 && v && (isNaN(o) || o === 1.0);
+ """,
+ args=[element],
+ )
diff --git a/testing/webcompat/config.json.sample b/testing/webcompat/config.json.sample
new file mode 100644
index 0000000000..8f1b2275e0
--- /dev/null
+++ b/testing/webcompat/config.json.sample
@@ -0,0 +1,5 @@
+{
+ "bug number 1": {"username": "name", "password": "pass"}],
+ "bug number 2": {"username": "name", "password": "pass"}]
+}
+
diff --git a/testing/webcompat/fixtures.py b/testing/webcompat/fixtures.py
new file mode 100644
index 0000000000..443adfae81
--- /dev/null
+++ b/testing/webcompat/fixtures.py
@@ -0,0 +1,255 @@
+# 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 asyncio
+import json
+import re
+import subprocess
+
+import pytest
+import webdriver
+
+from client import Client
+
+APS_PREF = "privacy.partition.always_partition_third_party_non_cookie_storage"
+CB_PBM_PREF = "network.cookie.cookieBehavior.pbmode"
+CB_PREF = "network.cookie.cookieBehavior"
+INJECTIONS_PREF = "extensions.webcompat.perform_injections"
+PBM_PREF = "browser.privatebrowsing.autostart"
+PIP_OVERRIDES_PREF = "extensions.webcompat.enable_picture_in_picture_overrides"
+SHIMS_PREF = "extensions.webcompat.enable_shims"
+STRICT_ETP_PREF = "privacy.trackingprotection.enabled"
+UA_OVERRIDES_PREF = "extensions.webcompat.perform_ua_overrides"
+
+
+class WebDriver:
+ def __init__(self, config):
+ self.browser_binary = config.getoption("browser_binary")
+ self.device_serial = config.getoption("device_serial")
+ self.package_name = config.getoption("package_name")
+ self.addon = config.getoption("addon")
+ self.webdriver_binary = config.getoption("webdriver_binary")
+ self.port = config.getoption("webdriver_port")
+ self.wsPort = config.getoption("webdriver_ws_port")
+ self.headless = config.getoption("headless")
+ self.proc = None
+
+ def command_line_driver(self):
+ raise NotImplementedError
+
+ def capabilities(self, test_config):
+ raise NotImplementedError
+
+ def __enter__(self):
+ assert self.proc is None
+ self.proc = subprocess.Popen(self.command_line_driver())
+ return self
+
+ def __exit__(self, *args, **kwargs):
+ self.proc.kill()
+
+
+class FirefoxWebDriver(WebDriver):
+ def command_line_driver(self):
+ return [
+ self.webdriver_binary,
+ "--port",
+ str(self.port),
+ "--websocket-port",
+ str(self.wsPort),
+ "-vv",
+ ]
+
+ def capabilities(self, test_config):
+ prefs = {}
+
+ if "aps" in test_config:
+ prefs[APS_PREF] = test_config["aps"]
+
+ if "use_interventions" in test_config:
+ value = test_config["use_interventions"]
+ prefs[INJECTIONS_PREF] = value
+ prefs[UA_OVERRIDES_PREF] = value
+ prefs[PIP_OVERRIDES_PREF] = value
+
+ if "use_pbm" in test_config:
+ prefs[PBM_PREF] = test_config["use_pbm"]
+
+ if "use_shims" in test_config:
+ prefs[SHIMS_PREF] = test_config["use_shims"]
+
+ if "use_strict_etp" in test_config:
+ prefs[STRICT_ETP_PREF] = test_config["use_strict_etp"]
+
+ # remote/cdp/CDP.sys.mjs sets cookieBehavior to 0,
+ # which we definitely do not want, so set it back to 5.
+ cookieBehavior = 4 if test_config.get("without_tcp") else 5
+ prefs[CB_PREF] = cookieBehavior
+ prefs[CB_PBM_PREF] = cookieBehavior
+
+ fx_options = {"prefs": prefs}
+
+ if self.browser_binary:
+ fx_options["binary"] = self.browser_binary
+ if self.headless:
+ fx_options["args"] = ["--headless"]
+
+ if self.device_serial:
+ fx_options["androidDeviceSerial"] = self.device_serial
+ fx_options["androidPackage"] = self.package_name
+
+ if self.addon:
+ prefs["xpinstall.signatures.required"] = False
+ prefs["extensions.experiments.enabled"] = True
+
+ return {
+ "pageLoadStrategy": "normal",
+ "moz:firefoxOptions": fx_options,
+ }
+
+
+@pytest.fixture(scope="session")
+def should_do_2fa(request):
+ return request.config.getoption("do2fa", False)
+
+
+@pytest.fixture(scope="session")
+def config_file(request):
+ path = request.config.getoption("config")
+ if not path:
+ return None
+ with open(path) as f:
+ return json.load(f)
+
+
+@pytest.fixture
+def bug_number(request):
+ return re.findall("\d+", str(request.fspath.basename))[0]
+
+
+@pytest.fixture
+def credentials(bug_number, config_file):
+ if not config_file:
+ pytest.skip(f"login info required for bug #{bug_number}")
+ return None
+
+ try:
+ credentials = config_file[bug_number]
+ except KeyError:
+ pytest.skip(f"no login for bug #{bug_number} found")
+ return
+
+ return {"username": credentials["username"], "password": credentials["password"]}
+
+
+@pytest.fixture(scope="session")
+def driver(pytestconfig):
+ if pytestconfig.getoption("browser") == "firefox":
+ cls = FirefoxWebDriver
+ else:
+ assert False
+
+ with cls(pytestconfig) as driver_instance:
+ yield driver_instance
+
+
+@pytest.fixture(scope="session")
+def event_loop():
+ return asyncio.get_event_loop_policy().new_event_loop()
+
+
+@pytest.fixture(scope="function")
+async def client(session, event_loop):
+ return Client(session, event_loop)
+
+
+def install_addon(session, addon_file_path):
+ context = session.send_session_command("GET", "moz/context")
+ session.send_session_command("POST", "moz/context", {"context": "chrome"})
+ session.execute_async_script(
+ """
+ const addon_file_path = arguments[0];
+ const cb = arguments[1];
+ const { AddonManager } = ChromeUtils.import(
+ "resource://gre/modules/AddonManager.jsm"
+ );
+ const { ExtensionPermissions } = ChromeUtils.import(
+ "resource://gre/modules/ExtensionPermissions.jsm"
+ );
+ const { FileUtils } = ChromeUtils.importESModule(
+ "resource://gre/modules/FileUtils.sys.mjs"
+ );
+ const file = new FileUtils.File(arguments[0]);
+ AddonManager.installTemporaryAddon(file).then(addon => {
+ // also make sure the addon works in private browsing mode
+ const incognitoPermission = {
+ permissions: ["internal:privateBrowsingAllowed"],
+ origins: [],
+ };
+ ExtensionPermissions.add(addon.id, incognitoPermission).then(() => {
+ addon.reload().then(cb);
+ });
+ });
+ """,
+ [addon_file_path],
+ )
+ session.send_session_command("POST", "moz/context", {"context": context})
+
+
+@pytest.fixture(scope="function")
+async def session(driver, test_config):
+ caps = driver.capabilities(test_config)
+ caps.update(
+ {
+ "acceptInsecureCerts": True,
+ "webSocketUrl": True,
+ }
+ )
+ caps = {"alwaysMatch": caps}
+ print(caps)
+
+ session = None
+ for i in range(0, 15):
+ try:
+ if not session:
+ session = webdriver.Session(
+ "localhost", driver.port, capabilities=caps, enable_bidi=True
+ )
+ session.test_config = test_config
+ session.start()
+ break
+ except (ConnectionRefusedError, webdriver.error.TimeoutException):
+ await asyncio.sleep(0.5)
+
+ await session.bidi_session.start()
+
+ if driver.addon:
+ install_addon(session, driver.addon)
+
+ yield session
+
+ await session.bidi_session.end()
+ session.end()
+
+
+@pytest.fixture(autouse=True)
+def platform(session):
+ return session.capabilities["platformName"]
+
+
+@pytest.fixture(autouse=True)
+def only_platforms(bug_number, platform, request, session):
+ if request.node.get_closest_marker("only_platforms"):
+ for only in request.node.get_closest_marker("only_platforms").args:
+ if only == platform:
+ return
+ pytest.skip(f"Bug #{bug_number} skipped on platform ({platform})")
+
+
+@pytest.fixture(autouse=True)
+def skip_platforms(bug_number, platform, request, session):
+ if request.node.get_closest_marker("skip_platforms"):
+ for skipped in request.node.get_closest_marker("skip_platforms").args:
+ if skipped == platform:
+ pytest.skip(f"Bug #{bug_number} skipped on platform ({platform})")
diff --git a/testing/webcompat/interventions/__init__.py b/testing/webcompat/interventions/__init__.py
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/testing/webcompat/interventions/__init__.py
diff --git a/testing/webcompat/interventions/conftest.py b/testing/webcompat/interventions/conftest.py
new file mode 100644
index 0000000000..3cfd25436b
--- /dev/null
+++ b/testing/webcompat/interventions/conftest.py
@@ -0,0 +1,56 @@
+# 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/.
+
+
+from ..fixtures import * # noqa: F403
+
+
+def pytest_generate_tests(metafunc):
+ """Generate tests based on markers."""
+
+ if "session" not in metafunc.fixturenames:
+ return
+
+ marks = [mark.name for mark in metafunc.function.pytestmark]
+
+ otherargs = {}
+ argvalues = []
+ ids = []
+
+ if "only_platforms" in marks:
+ for mark in metafunc.function.pytestmark:
+ if mark.name == "only_platforms":
+ otherargs["only_platforms"] = mark.args
+
+ if "skip_platforms" in marks:
+ for mark in metafunc.function.pytestmark:
+ if mark.name == "skip_platforms":
+ otherargs["skip_platforms"] = mark.args
+
+ if "with_interventions" in marks:
+ argvalues.append([dict({"interventions": True}, **otherargs)])
+ ids.append("with_interventions")
+
+ if "without_interventions" in marks:
+ argvalues.append([dict({"interventions": False}, **otherargs)])
+ ids.append("without_interventions")
+
+ metafunc.parametrize(["session"], argvalues, ids=ids, indirect=True)
+
+
+@pytest.fixture(scope="function") # noqa: F405
+async def test_config(request, driver):
+ params = request.node.callspec.params.get("session")
+
+ use_interventions = params.get("interventions")
+ print(f"use_interventions {use_interventions}")
+ if use_interventions is None:
+ raise ValueError(
+ "Missing intervention marker in %s:%s"
+ % (request.fspath, request.function.__name__)
+ )
+
+ return {
+ "use_interventions": use_interventions,
+ }
diff --git a/testing/webcompat/interventions/pytest.ini b/testing/webcompat/interventions/pytest.ini
new file mode 100644
index 0000000000..270690aed7
--- /dev/null
+++ b/testing/webcompat/interventions/pytest.ini
@@ -0,0 +1,7 @@
+[pytest]
+console_output_style = classic
+markers =
+ only_platforms: only run tests on specific platforms (mac, linux, windows, android)
+ skip_platforms: skip tests on specific platforms (mac, linux, windows, android)
+ with_interventions: enable web-compat interventions
+ without_interventions: disable web-compat interventions
diff --git a/testing/webcompat/interventions/tests/__init__.py b/testing/webcompat/interventions/tests/__init__.py
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/testing/webcompat/interventions/tests/__init__.py
diff --git a/testing/webcompat/interventions/tests/test_1385206_rakuten_co_jp.py b/testing/webcompat/interventions/tests/test_1385206_rakuten_co_jp.py
new file mode 100644
index 0000000000..a5dec6d9d8
--- /dev/null
+++ b/testing/webcompat/interventions/tests/test_1385206_rakuten_co_jp.py
@@ -0,0 +1,23 @@
+import pytest
+
+URL = "http://www.rakuten.co.jp/"
+MOBILE_CSS = "#top-area"
+DESKTOP_CSS = "#header-group"
+
+
+@pytest.mark.only_platforms("android")
+@pytest.mark.asyncio
+@pytest.mark.with_interventions
+async def test_enabled(client):
+ await client.navigate(URL)
+ assert client.await_css(MOBILE_CSS)
+ assert not client.find_css(DESKTOP_CSS)
+
+
+@pytest.mark.only_platforms("android")
+@pytest.mark.asyncio
+@pytest.mark.without_interventions
+async def test_disabled(client):
+ await client.navigate(URL)
+ assert client.await_css(DESKTOP_CSS)
+ assert not client.find_css(MOBILE_CSS)
diff --git a/testing/webcompat/interventions/tests/test_1448747_124385_drafthouse_com.py b/testing/webcompat/interventions/tests/test_1448747_124385_drafthouse_com.py
new file mode 100644
index 0000000000..e212eb0991
--- /dev/null
+++ b/testing/webcompat/interventions/tests/test_1448747_124385_drafthouse_com.py
@@ -0,0 +1,24 @@
+import pytest
+
+URL = "https://drafthouse.com/food-and-drink"
+SELECT_CSS = "select[name='market']"
+
+
+async def is_fastclick_active(client):
+ async with client.ensure_fastclick_activates():
+ await client.navigate(URL)
+ return client.test_for_fastclick(client.await_css(SELECT_CSS))
+
+
+@pytest.mark.only_platforms("android")
+@pytest.mark.asyncio
+@pytest.mark.with_interventions
+async def test_enabled(client):
+ assert not await is_fastclick_active(client)
+
+
+@pytest.mark.only_platforms("android")
+@pytest.mark.asyncio
+@pytest.mark.without_interventions
+async def test_disabled(client):
+ assert await is_fastclick_active(client)
diff --git a/testing/webcompat/interventions/tests/test_1448747_bathpublishing_com.py b/testing/webcompat/interventions/tests/test_1448747_bathpublishing_com.py
new file mode 100644
index 0000000000..862664c8ea
--- /dev/null
+++ b/testing/webcompat/interventions/tests/test_1448747_bathpublishing_com.py
@@ -0,0 +1,33 @@
+import pytest
+from webdriver.error import NoSuchElementException
+
+URL = "https://bathpublishing.com/products/clinical-negligence-made-clear-a-guide-for-patients-professionals"
+POPUP_CSS = "button.recommendation-modal__close-button"
+SELECT_CSS = "select#productSelect--product-template-option-0"
+
+
+async def is_fastclick_active(client):
+ async with client.ensure_fastclick_activates():
+ await client.navigate(URL)
+
+ try:
+ client.soft_click(client.await_css(POPUP_CSS, timeout=3))
+ client.await_element_hidden(client.css(POPUP_CSS))
+ except NoSuchElementException:
+ pass
+
+ return client.test_for_fastclick(client.await_css(SELECT_CSS))
+
+
+@pytest.mark.only_platforms("android")
+@pytest.mark.asyncio
+@pytest.mark.with_interventions
+async def test_enabled(client):
+ assert not await is_fastclick_active(client)
+
+
+@pytest.mark.only_platforms("android")
+@pytest.mark.asyncio
+@pytest.mark.without_interventions
+async def test_disabled(client):
+ assert await is_fastclick_active(client)
diff --git a/testing/webcompat/interventions/tests/test_1448747_bluetokaicoffee_com.py b/testing/webcompat/interventions/tests/test_1448747_bluetokaicoffee_com.py
new file mode 100644
index 0000000000..4636b75da8
--- /dev/null
+++ b/testing/webcompat/interventions/tests/test_1448747_bluetokaicoffee_com.py
@@ -0,0 +1,24 @@
+import pytest
+
+URL = "https://bluetokaicoffee.com/collections/cold-brew-can"
+SELECT_CSS = "select#SortBy"
+
+
+async def is_fastclick_active(client):
+ async with client.ensure_fastclick_activates():
+ await client.navigate(URL)
+ return client.test_for_fastclick(client.await_css(SELECT_CSS))
+
+
+@pytest.mark.only_platforms("android")
+@pytest.mark.asyncio
+@pytest.mark.with_interventions
+async def test_enabled(client):
+ assert not await is_fastclick_active(client)
+
+
+@pytest.mark.only_platforms("android")
+@pytest.mark.asyncio
+@pytest.mark.without_interventions
+async def test_disabled(client):
+ assert await is_fastclick_active(client)
diff --git a/testing/webcompat/interventions/tests/test_1448747_co2meter_com.py b/testing/webcompat/interventions/tests/test_1448747_co2meter_com.py
new file mode 100644
index 0000000000..3e94d02a2f
--- /dev/null
+++ b/testing/webcompat/interventions/tests/test_1448747_co2meter_com.py
@@ -0,0 +1,41 @@
+import pytest
+from webdriver.error import NoSuchElementException
+
+URL = "https://www.co2meter.com/collections/restaurants-food"
+ITEM_CSS = "a.grid__image"
+ADD_CSS = "#AddToCart"
+SELECT_CSS = "select#address_country"
+POPUP_CLOSE_CSS = "button.needsclick.klaviyo-close-form"
+
+
+async def is_fastclick_active(client):
+ async with client.ensure_fastclick_activates():
+ await client.navigate(URL)
+
+ client.soft_click(client.await_css(ITEM_CSS))
+ client.soft_click(client.await_css(ADD_CSS))
+
+ try:
+ popup_close_finder = client.css(POPUP_CLOSE_CSS)
+ popup_close = client.await_element(popup_close_finder, timeout=5)
+ if popup_close:
+ client.soft_click(popup_close)
+ client.await_element_hidden(popup_close_finder)
+ except NoSuchElementException:
+ pass
+
+ return client.test_for_fastclick(client.await_css(SELECT_CSS))
+
+
+@pytest.mark.only_platforms("android")
+@pytest.mark.asyncio
+@pytest.mark.with_interventions
+async def test_enabled(client):
+ assert not await is_fastclick_active(client)
+
+
+@pytest.mark.only_platforms("android")
+@pytest.mark.asyncio
+@pytest.mark.without_interventions
+async def test_disabled(client):
+ assert await is_fastclick_active(client)
diff --git a/testing/webcompat/interventions/tests/test_1448747_discountcoffee_co_uk.py b/testing/webcompat/interventions/tests/test_1448747_discountcoffee_co_uk.py
new file mode 100644
index 0000000000..6e895c923d
--- /dev/null
+++ b/testing/webcompat/interventions/tests/test_1448747_discountcoffee_co_uk.py
@@ -0,0 +1,24 @@
+import pytest
+
+URL = "https://www.discountcoffee.co.uk/collections/wholesale-coffee-beans"
+SELECT_CSS = "#collection-filter-type select"
+
+
+async def is_fastclick_active(client):
+ async with client.ensure_fastclick_activates():
+ await client.navigate(URL)
+ return client.test_for_fastclick(client.await_css(SELECT_CSS))
+
+
+@pytest.mark.only_platforms("android")
+@pytest.mark.asyncio
+@pytest.mark.with_interventions
+async def test_enabled(client):
+ assert not await is_fastclick_active(client)
+
+
+@pytest.mark.only_platforms("android")
+@pytest.mark.asyncio
+@pytest.mark.without_interventions
+async def test_disabled(client):
+ assert await is_fastclick_active(client)
diff --git a/testing/webcompat/interventions/tests/test_1448747_dylantalkstone_com.py b/testing/webcompat/interventions/tests/test_1448747_dylantalkstone_com.py
new file mode 100644
index 0000000000..cf271fb91e
--- /dev/null
+++ b/testing/webcompat/interventions/tests/test_1448747_dylantalkstone_com.py
@@ -0,0 +1,24 @@
+import pytest
+
+URL = "https://dylantalkstone.com/collections/tele-pickups/products/flat-6-tele-pickups"
+SELECT_CSS = "select#productSelect-option-0"
+
+
+async def is_fastclick_active(client):
+ async with client.ensure_fastclick_activates():
+ await client.navigate(URL)
+ return client.test_for_fastclick(client.await_css(SELECT_CSS))
+
+
+@pytest.mark.only_platforms("android")
+@pytest.mark.asyncio
+@pytest.mark.with_interventions
+async def test_enabled(client):
+ assert not await is_fastclick_active(client)
+
+
+@pytest.mark.only_platforms("android")
+@pytest.mark.asyncio
+@pytest.mark.without_interventions
+async def test_disabled(client):
+ assert await is_fastclick_active(client)
diff --git a/testing/webcompat/interventions/tests/test_1448747_fourbarrelcoffee_com.py b/testing/webcompat/interventions/tests/test_1448747_fourbarrelcoffee_com.py
new file mode 100644
index 0000000000..7ac39c05d1
--- /dev/null
+++ b/testing/webcompat/interventions/tests/test_1448747_fourbarrelcoffee_com.py
@@ -0,0 +1,24 @@
+import pytest
+
+URL = "https://www.fourbarrelcoffee.com/"
+ADD_TO_CART_CSS = "#container a#menu-link"
+
+
+async def is_fastclick_active(client):
+ async with client.ensure_fastclick_activates():
+ await client.navigate(URL)
+ return client.test_for_fastclick(client.await_css(ADD_TO_CART_CSS))
+
+
+@pytest.mark.only_platforms("android")
+@pytest.mark.asyncio
+@pytest.mark.with_interventions
+async def test_enabled(client):
+ assert not await is_fastclick_active(client)
+
+
+@pytest.mark.only_platforms("android")
+@pytest.mark.asyncio
+@pytest.mark.without_interventions
+async def test_disabled(client):
+ assert await is_fastclick_active(client)
diff --git a/testing/webcompat/interventions/tests/test_1448747_franmar_com.py b/testing/webcompat/interventions/tests/test_1448747_franmar_com.py
new file mode 100644
index 0000000000..8667f094df
--- /dev/null
+++ b/testing/webcompat/interventions/tests/test_1448747_franmar_com.py
@@ -0,0 +1,24 @@
+import pytest
+
+URL = "https://products.franmar.com/collections/consumer-products/"
+SELECT_CSS = "#sortBy"
+
+
+async def is_fastclick_active(client):
+ async with client.ensure_fastclick_activates():
+ await client.navigate(URL)
+ return client.test_for_fastclick(client.await_css(SELECT_CSS))
+
+
+@pytest.mark.only_platforms("android")
+@pytest.mark.asyncio
+@pytest.mark.with_interventions
+async def test_enabled(client):
+ assert not await is_fastclick_active(client)
+
+
+@pytest.mark.only_platforms("android")
+@pytest.mark.asyncio
+@pytest.mark.without_interventions
+async def test_disabled(client):
+ assert await is_fastclick_active(client)
diff --git a/testing/webcompat/interventions/tests/test_1448747_renewd_com_au.py b/testing/webcompat/interventions/tests/test_1448747_renewd_com_au.py
new file mode 100644
index 0000000000..0ebc2025ab
--- /dev/null
+++ b/testing/webcompat/interventions/tests/test_1448747_renewd_com_au.py
@@ -0,0 +1,24 @@
+import pytest
+
+URL = "https://renewd.com.au"
+ADD_TO_CART_CSS = "button[id].btn.add_to_cart"
+
+
+async def is_fastclick_active(client):
+ async with client.ensure_fastclick_activates():
+ await client.navigate(URL)
+ return client.test_for_fastclick(client.await_css(ADD_TO_CART_CSS))
+
+
+@pytest.mark.only_platforms("android")
+@pytest.mark.asyncio
+@pytest.mark.with_interventions
+async def test_enabled(client):
+ assert not await is_fastclick_active(client)
+
+
+@pytest.mark.only_platforms("android")
+@pytest.mark.asyncio
+@pytest.mark.without_interventions
+async def test_disabled(client):
+ assert await is_fastclick_active(client)
diff --git a/testing/webcompat/interventions/tests/test_1448747_thehawksmoor_com.py b/testing/webcompat/interventions/tests/test_1448747_thehawksmoor_com.py
new file mode 100644
index 0000000000..eecfc8a9c3
--- /dev/null
+++ b/testing/webcompat/interventions/tests/test_1448747_thehawksmoor_com.py
@@ -0,0 +1,32 @@
+import pytest
+from webdriver.error import NoSuchElementException
+
+URL = "https://thehawksmoor.com/book-a-table/?location=11335"
+POPUP_CSS = ".pum-overlay button.pum-close"
+SELECT_CSS = "select#booktable_restaurants"
+
+
+async def is_fastclick_active(client):
+ async with client.ensure_fastclick_activates():
+ await client.navigate(URL)
+
+ try:
+ client.soft_click(client.await_css(POPUP_CSS, timeout=3))
+ except NoSuchElementException:
+ pass
+
+ return client.test_for_fastclick(client.await_css(SELECT_CSS))
+
+
+@pytest.mark.only_platforms("android")
+@pytest.mark.asyncio
+@pytest.mark.with_interventions
+async def test_enabled(client):
+ assert not await is_fastclick_active(client)
+
+
+@pytest.mark.only_platforms("android")
+@pytest.mark.asyncio
+@pytest.mark.without_interventions
+async def test_disabled(client):
+ assert await is_fastclick_active(client)
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 0000000000..a7e3c4d0fc
--- /dev/null
+++ b/testing/webcompat/interventions/tests/test_1452707_absa.py
@@ -0,0 +1,20 @@
+import pytest
+
+URL = "https://ib.absa.co.za/absa-online/login.jsp"
+UNSUPPORTED_ALERT = "Browser unsupported"
+
+
+@pytest.mark.asyncio
+@pytest.mark.with_interventions
+async def test_enabled(client):
+ await client.navigate(URL)
+ assert client.find_css("html.gecko")
+
+
+@pytest.mark.asyncio
+@pytest.mark.without_interventions
+async def test_disabled(client):
+ alert = await client.await_alert(UNSUPPORTED_ALERT)
+ await client.navigate(URL)
+ await alert
+ assert client.find_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 0000000000..af87ca43aa
--- /dev/null
+++ b/testing/webcompat/interventions/tests/test_1457335_histography.py
@@ -0,0 +1,20 @@
+import pytest
+
+URL = "http://histography.io/"
+SUPPORT_URL = "http://histography.io/browser_support.htm"
+
+
+@pytest.mark.skip_platforms("android")
+@pytest.mark.asyncio
+@pytest.mark.with_interventions
+async def test_enabled(client):
+ await client.navigate(URL, wait="load")
+ assert client.current_url == URL
+
+
+@pytest.mark.skip_platforms("android")
+@pytest.mark.asyncio
+@pytest.mark.without_interventions
+async def test_disabled(client):
+ await client.navigate(URL, wait="load")
+ assert client.current_url == SUPPORT_URL
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 0000000000..d49c141c46
--- /dev/null
+++ b/testing/webcompat/interventions/tests/test_1472075_bankofamerica.py
@@ -0,0 +1,19 @@
+import pytest
+
+URL = "https://www.bankofamerica.com/"
+
+
+@pytest.mark.asyncio
+@pytest.mark.skip_platforms("android", "windows")
+@pytest.mark.with_interventions
+async def test_enabled(client):
+ await client.navigate(URL)
+ assert not client.find_css("#browserUpgradeNoticeBar")
+
+
+@pytest.mark.asyncio
+@pytest.mark.skip_platforms("android", "windows")
+@pytest.mark.without_interventions
+async def test_disabled(client):
+ await client.navigate(URL)
+ assert client.is_displayed(client.await_css("#browserUpgradeNoticeBar"))
diff --git a/testing/webcompat/interventions/tests/test_1509873_viewer_zmags_com.py b/testing/webcompat/interventions/tests/test_1509873_viewer_zmags_com.py
new file mode 100644
index 0000000000..50a48f1ccd
--- /dev/null
+++ b/testing/webcompat/interventions/tests/test_1509873_viewer_zmags_com.py
@@ -0,0 +1,21 @@
+import pytest
+
+URL = "https://secure.viewer.zmags.com/services/htmlviewer/content/ddc3f3b9?pubVersion=46&locale=en&viewerID=85cfd72f#/ddc3f3b9/1"
+BLOCKED_TEXT = "This browser is not supported"
+UNBLOCKED_CSS = "#viewer .MainViewEnabled"
+
+
+@pytest.mark.only_platforms("android")
+@pytest.mark.asyncio
+@pytest.mark.with_interventions
+async def test_enabled(client):
+ await client.navigate(URL)
+ assert client.await_css(UNBLOCKED_CSS)
+
+
+@pytest.mark.only_platforms("android")
+@pytest.mark.asyncio
+@pytest.mark.without_interventions
+async def test_disabled(client):
+ await client.navigate(URL)
+ assert client.await_text(BLOCKED_TEXT)
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 0000000000..e76f6cabaf
--- /dev/null
+++ b/testing/webcompat/interventions/tests/test_1570108_steamcommunity.py
@@ -0,0 +1,122 @@
+import pytest
+
+URL = "https://steamcommunity.com/chat"
+
+
+USERID_CSS = "input[type='text'][class*='newlogindialog']"
+PASSWORD_CSS = "input[type='password'][class*='newlogindialog']"
+SIGNIN_CSS = "button[type='submit'][class*='newlogindialog']"
+GEAR_CSS = ".friendListButton"
+LOGIN_FAIL_XPATH = (
+ "//*[contains(text(), 'try again') and " "contains(@class, 'FormError')]"
+)
+AUTH_CSS = "[class*='newlogindialog_ProtectingAccount']"
+AUTH_DIGITS_CSS = "[class*='segmentedinputs_SegmentedCharacterInput'] input.Focusable"
+AUTH_RETRY_CSS = "[class*='newlogindialog_FormError']"
+AUTH_RETRY_LOADER_CSS = "[class*='newlogindialog_Loading']"
+RATE_TEXT = "too many login failures"
+VOICE_XPATH = (
+ "//*[contains(text(), 'Voice') and "
+ "contains(@class, 'pagedsettings_PagedSettingsDialog_PageListItem')]"
+)
+MIC_BUTTON_CSS = "button.LocalMicTestButton"
+UNSUPPORTED_TEXT = "currently unsupported in Firefox"
+
+
+async def do_2fa(client):
+ digits = client.await_css(AUTH_DIGITS_CSS, all=True)
+ assert len(digits) > 0
+
+ loader = client.css(AUTH_RETRY_LOADER_CSS)
+ if client.find_element(loader):
+ client.await_element_hidden(loader)
+ for digit in digits:
+ if digit.property("value"):
+ digit.send_keys("\ue003") # backspace
+
+ code = input("**** Enter two-factor authentication code: ")
+ for i, digit in enumerate(code):
+ if len(digits) > i:
+ digits[i].send_keys(digit)
+
+ client.await_element(loader, timeout=10)
+ client.await_element_hidden(loader)
+
+
+async def load_mic_test(client, credentials, should_do_2fa):
+ await client.navigate(URL)
+
+ async def login():
+ userid = client.await_css(USERID_CSS)
+ password = client.find_css(PASSWORD_CSS)
+ submit = client.find_css(SIGNIN_CSS)
+ assert client.is_displayed(userid)
+ assert client.is_displayed(password)
+ assert client.is_displayed(submit)
+
+ userid.send_keys(credentials["username"])
+ password.send_keys(credentials["password"])
+ submit.click()
+
+ await login()
+
+ while True:
+ auth, retry, gear, fail, rate = client.await_first_element_of(
+ [
+ client.css(AUTH_CSS),
+ client.css(AUTH_RETRY_CSS),
+ client.css(GEAR_CSS),
+ client.xpath(LOGIN_FAIL_XPATH),
+ client.text(RATE_TEXT),
+ ],
+ is_displayed=True,
+ timeout=20,
+ )
+
+ if retry:
+ await do_2fa(client)
+ continue
+
+ if rate:
+ pytest.skip(
+ "Too many Steam login attempts in a short time; try again later."
+ )
+ return None
+ elif auth:
+ if should_do_2fa:
+ await do_2fa(client)
+ continue
+
+ pytest.skip(
+ "Two-factor authentication requested; disable Steam Guard"
+ " or run this test with --do-2fa to live-input codes"
+ )
+ return None
+ elif fail:
+ pytest.skip("Invalid login provided.")
+ return None
+ else:
+ break
+
+ assert gear
+ gear.click()
+
+ voice = client.await_xpath(VOICE_XPATH, is_displayed=True)
+ voice.click()
+
+ mic_test = client.await_css(MIC_BUTTON_CSS, is_displayed=True)
+ return mic_test
+
+
+@pytest.mark.skip_platforms("android")
+@pytest.mark.asyncio
+@pytest.mark.with_interventions
+async def test_enabled(client, credentials, should_do_2fa):
+ mic_test = await load_mic_test(client, credentials, should_do_2fa)
+ if not mic_test:
+ return
+
+ with client.assert_getUserMedia_called():
+ mic_test.click()
+
+ assert not client.find_text(UNSUPPORTED_TEXT)
diff --git a/testing/webcompat/interventions/tests/test_1570328_developer_apple_com.py b/testing/webcompat/interventions/tests/test_1570328_developer_apple_com.py
new file mode 100644
index 0000000000..c40b530a62
--- /dev/null
+++ b/testing/webcompat/interventions/tests/test_1570328_developer_apple_com.py
@@ -0,0 +1,29 @@
+import pytest
+
+URL = "https://developer.apple.com/library/archive/documentation/CoreFoundation/Conceptual/CFBundles/AboutBundles/AboutBundles.html"
+TOC_CSS = "#toc"
+
+
+def toc_is_onscreen(client):
+ assert client.await_css(TOC_CSS, is_displayed=True)
+ return client.execute_script(
+ """
+ return document.querySelector("#toc").getBoundingClientRect().left >= 0;
+ """
+ )
+
+
+@pytest.mark.only_platforms("android")
+@pytest.mark.asyncio
+@pytest.mark.with_interventions
+async def test_enabled(client):
+ await client.navigate(URL)
+ assert toc_is_onscreen(client)
+
+
+@pytest.mark.only_platforms("android")
+@pytest.mark.asyncio
+@pytest.mark.without_interventions
+async def test_disabled(client):
+ await client.navigate(URL)
+ assert not toc_is_onscreen(client)
diff --git a/testing/webcompat/interventions/tests/test_1574522_enuri_com.py b/testing/webcompat/interventions/tests/test_1574522_enuri_com.py
new file mode 100644
index 0000000000..156d2b2f36
--- /dev/null
+++ b/testing/webcompat/interventions/tests/test_1574522_enuri_com.py
@@ -0,0 +1,23 @@
+import pytest
+
+# The site puts an exclamation mark in front of width="703"
+# for Chrome UAs, but not for Firefox.
+
+URL = "http://enuri.com/knowcom/detail.jsp?kbno=474985"
+CHECK_CSS = "td[width='703']"
+
+
+@pytest.mark.only_platforms("android")
+@pytest.mark.asyncio
+@pytest.mark.with_interventions
+async def test_enabled(client):
+ await client.navigate(URL)
+ assert not client.find_css(CHECK_CSS)
+
+
+@pytest.mark.only_platforms("android")
+@pytest.mark.asyncio
+@pytest.mark.without_interventions
+async def test_disabled(client):
+ await client.navigate(URL)
+ assert client.find_css(CHECK_CSS)
diff --git a/testing/webcompat/interventions/tests/test_1575000_lloydsbank.py b/testing/webcompat/interventions/tests/test_1575000_lloydsbank.py
new file mode 100644
index 0000000000..cfa3d18d87
--- /dev/null
+++ b/testing/webcompat/interventions/tests/test_1575000_lloydsbank.py
@@ -0,0 +1,53 @@
+import pytest
+from webdriver.error import NoSuchElementException
+
+URL = (
+ "https://apply.lloydsbank.co.uk/sales-content/cwa/l/pca/index-app.html"
+ "?product=classicaccountLTB"
+)
+
+NO_CSS = "[data-selector='existingCustomer-toggle-button-no']"
+CONT_CSS = "[data-selector='ib-yes-continue-without-login-not-existing-customer-continue-button']"
+CONT2_CSS = "[data-selector='beforeYouStart-continue-button']"
+RADIO_CSS = "[name='aboutYou-gender-radio'] + span"
+ACCEPT_COOKIES_CSS = "#lbganalyticsCookies button#accept"
+
+
+async def get_radio_position(client):
+ loadedProperly = False
+ for i in range(0, 10):
+ await client.navigate(URL, wait="none")
+ try:
+ loadedProperly = client.await_css(NO_CSS, timeout=3)
+ except NoSuchElementException:
+ continue
+ accept = client.find_css(ACCEPT_COOKIES_CSS)
+ if accept:
+ accept.click()
+ client.await_css(NO_CSS).click()
+ client.await_css(NO_CSS).click()
+ client.await_css(CONT_CSS).click()
+ client.await_css(CONT2_CSS).click()
+ break
+ if not loadedProperly:
+ pytest.xfail("The website seems to not be loading properly")
+ return False
+ radio = client.await_css(RADIO_CSS)
+ return client.execute_script(
+ """
+ return window.getComputedStyle(arguments[0]).position;
+ """,
+ radio,
+ )
+
+
+@pytest.mark.asyncio
+@pytest.mark.with_interventions
+async def test_enabled(client):
+ assert "relative" == await get_radio_position(client)
+
+
+@pytest.mark.asyncio
+@pytest.mark.without_interventions
+async def test_disabled(client):
+ assert "relative" != await get_radio_position(client)
diff --git a/testing/webcompat/interventions/tests/test_1577267_metfone_com_kh.py b/testing/webcompat/interventions/tests/test_1577267_metfone_com_kh.py
new file mode 100644
index 0000000000..3cc4870b42
--- /dev/null
+++ b/testing/webcompat/interventions/tests/test_1577267_metfone_com_kh.py
@@ -0,0 +1,20 @@
+import pytest
+
+URL = "https://www.metfone.com.kh/en/4g-lte"
+DESKTOP_CSS = "section.quickLinks"
+
+
+@pytest.mark.only_platforms("android")
+@pytest.mark.asyncio
+@pytest.mark.with_interventions
+async def test_enabled(client):
+ await client.navigate(URL)
+ assert not client.find_css(DESKTOP_CSS)
+
+
+@pytest.mark.only_platforms("android")
+@pytest.mark.asyncio
+@pytest.mark.without_interventions
+async def test_disabled(client):
+ await client.navigate(URL)
+ assert client.find_css(DESKTOP_CSS)
diff --git a/testing/webcompat/interventions/tests/test_1577519_stream_directv_com.py b/testing/webcompat/interventions/tests/test_1577519_stream_directv_com.py
new file mode 100644
index 0000000000..fde07de470
--- /dev/null
+++ b/testing/webcompat/interventions/tests/test_1577519_stream_directv_com.py
@@ -0,0 +1,37 @@
+import pytest
+
+URL = "https://stream.directv.com/"
+LOGIN_CSS = "#userID"
+UNSUPPORTED_CSS = ".title-new-browse-ff"
+DENIED_XPATH = "//h1[text()='Access Denied']"
+
+
+async def check_site(client, should_pass):
+ await client.navigate(URL)
+
+ denied, login, unsupported = client.await_first_element_of(
+ [
+ client.xpath(DENIED_XPATH),
+ client.css(LOGIN_CSS),
+ client.css(UNSUPPORTED_CSS),
+ ],
+ is_displayed=True,
+ )
+
+ if denied:
+ pytest.skip("Region-locked, cannot test. Try using a VPN set to USA.")
+ return
+
+ assert (should_pass and login) or (not should_pass and unsupported)
+
+
+@pytest.mark.asyncio
+@pytest.mark.with_interventions
+async def test_enabled(client):
+ await check_site(client, should_pass=True)
+
+
+@pytest.mark.asyncio
+@pytest.mark.without_interventions
+async def test_disabled(client):
+ await check_site(client, should_pass=False)
diff --git a/testing/webcompat/interventions/tests/test_1579159_m_tailieu_vn.py b/testing/webcompat/interventions/tests/test_1579159_m_tailieu_vn.py
new file mode 100644
index 0000000000..59f48097f3
--- /dev/null
+++ b/testing/webcompat/interventions/tests/test_1579159_m_tailieu_vn.py
@@ -0,0 +1,26 @@
+import pytest
+
+URL = (
+ "https://m.tailieu.vn/index/mobile/id/1654340/hash/654d7ea9c2853e5be07e2c792ea7f168"
+)
+PAGE_CSS = "#viewer > #pageContainer1"
+OK_ERROR_CSS = "#errorMessage"
+ERROR_MSG = "may not load data from http://tailieu.vn/html5/pdf.js"
+
+
+@pytest.mark.only_platforms("android")
+@pytest.mark.asyncio
+@pytest.mark.with_interventions
+async def test_enabled(client):
+ await client.navigate(URL)
+ page, ok_error = client.await_first_element_of(
+ [client.css(PAGE_CSS), client.css(OK_ERROR_CSS)], is_displayed=True
+ )
+ assert page or ok_error
+
+
+@pytest.mark.only_platforms("android")
+@pytest.mark.asyncio
+@pytest.mark.without_interventions
+async def test_disabled(client):
+ await client.navigate(URL, await_console_message=ERROR_MSG)
diff --git a/testing/webcompat/interventions/tests/test_1582582_sling_com.py b/testing/webcompat/interventions/tests/test_1582582_sling_com.py
new file mode 100644
index 0000000000..71f6910af9
--- /dev/null
+++ b/testing/webcompat/interventions/tests/test_1582582_sling_com.py
@@ -0,0 +1,26 @@
+import pytest
+
+URL = "https://watch.sling.com/"
+INCOMPATIBLE_CSS = "[class*='unsupported-browser']"
+LOADER_CSS = ".loader-container"
+VPN_TEXT = "ONLY AVAILABLE INSIDE THE US"
+
+
+@pytest.mark.skip_platforms("android")
+@pytest.mark.asyncio
+@pytest.mark.with_interventions
+async def test_enabled(client):
+ await client.navigate(URL)
+ loader, vpn = client.await_first_element_of(
+ [client.css(LOADER_CSS), client.text(VPN_TEXT)], timeout=20
+ )
+ assert loader or vpn
+ assert not client.find_css(INCOMPATIBLE_CSS)
+
+
+@pytest.mark.skip_platforms("android")
+@pytest.mark.asyncio
+@pytest.mark.without_interventions
+async def test_disabled(client):
+ await client.navigate(URL)
+ assert client.await_css(INCOMPATIBLE_CSS, timeout=2000)
diff --git a/testing/webcompat/interventions/tests/test_1595215_uniqlo_com.py b/testing/webcompat/interventions/tests/test_1595215_uniqlo_com.py
new file mode 100644
index 0000000000..f87f8349b0
--- /dev/null
+++ b/testing/webcompat/interventions/tests/test_1595215_uniqlo_com.py
@@ -0,0 +1,21 @@
+import pytest
+
+URL = "https://store-kr.uniqlo.com/"
+REDIRECT_CSS = "[onload='javascript:redirectPage();']"
+APP_CSS = "uni-root"
+
+
+@pytest.mark.only_platforms("android")
+@pytest.mark.asyncio
+@pytest.mark.with_interventions
+async def test_enabled(client):
+ await client.navigate(URL)
+ assert client.await_css(APP_CSS)
+
+
+@pytest.mark.only_platforms("android")
+@pytest.mark.asyncio
+@pytest.mark.without_interventions
+async def test_disabled(client):
+ await client.navigate(URL)
+ assert client.await_css(REDIRECT_CSS)
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 0000000000..444fe8471f
--- /dev/null
+++ b/testing/webcompat/interventions/tests/test_1610026_mobilesuica.py
@@ -0,0 +1,49 @@
+import pytest
+
+ADDRESS_CSS = "input[name=MailAddress]"
+PASSWORD_CSS = "input[name=Password]"
+CLOSE_BUTTON_CSS = "input[name=winclosebutton]"
+UNAVAILABLE_TEXT = "時間をお確かめの上、再度実行してください。"
+UNSUPPORTED_TEXT = "ご利用のブラウザでは正しく"
+
+
+async def load_site(client):
+ await client.navigate("https://www.mobilesuica.com/")
+
+ address = client.find_css(ADDRESS_CSS)
+ password = client.find_css(PASSWORD_CSS)
+ error1 = client.find_css(CLOSE_BUTTON_CSS)
+ error2 = client.find_text(UNSUPPORTED_TEXT)
+
+ # 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 = client.find_text(UNAVAILABLE_TEXT)
+ if site_is_down is not None:
+ pytest.xfail("Site is currently down")
+
+ return address, password, error1 or error2, site_is_down
+
+
+@pytest.mark.asyncio
+@pytest.mark.with_interventions
+async def test_enabled(client):
+ address, password, error, site_is_down = await load_site(client)
+ if site_is_down:
+ return
+ assert client.is_displayed(address)
+ assert client.is_displayed(password)
+ assert error is None
+
+
+@pytest.mark.asyncio
+@pytest.mark.without_interventions
+async def test_disabled(client):
+ address, password, error, site_is_down = await load_site(client)
+ if site_is_down:
+ return
+ assert address is None
+ assert password is None
+ assert client.is_displayed(error)
diff --git a/testing/webcompat/interventions/tests/test_1610344_directv_com_co.py b/testing/webcompat/interventions/tests/test_1610344_directv_com_co.py
new file mode 100644
index 0000000000..ec74d4609f
--- /dev/null
+++ b/testing/webcompat/interventions/tests/test_1610344_directv_com_co.py
@@ -0,0 +1,55 @@
+import pytest
+
+URL = "https://www.directv.com.co/"
+IFRAME_CSS = "#main-iframe"
+INCOMPATIBLE_CSS = ".browser-compatible.compatible.incompatible"
+BLOCKED_TEXT = "blocked by the security rules"
+DENIED_TEXT = "not available in your region"
+FORBIDDEN_TEXT = "403 Forbidden"
+CAPTCHA_CSS = "img[src*='https://captcha.perfdrive.com/']"
+
+
+async def check_unsupported_visibility(client, should_show):
+ await client.navigate(URL)
+
+ # their region-block page sometimes wraps the content in an iframe
+ frame = client.find_css(IFRAME_CSS)
+ if frame:
+ client.switch_frame(frame)
+
+ denied, blocked, forbidden, captcha, incompatible = client.await_first_element_of(
+ [
+ client.text(DENIED_TEXT),
+ client.text(BLOCKED_TEXT),
+ client.text(FORBIDDEN_TEXT),
+ client.css(CAPTCHA_CSS),
+ client.css(INCOMPATIBLE_CSS),
+ ]
+ )
+
+ if captcha:
+ pytest.skip(
+ "Got a Captcha page. Please visit https://www.directv.com.co/, pass the captcha, and re-run this test."
+ )
+ return
+
+ if denied or blocked or forbidden:
+ pytest.skip("Region-locked, cannot test. Try using a VPN set to USA.")
+ return
+
+ shown = client.is_displayed(incompatible)
+ assert (should_show and shown) or (not should_show and not shown)
+
+
+@pytest.mark.skip_platforms("android")
+@pytest.mark.asyncio
+@pytest.mark.with_interventions
+async def test_enabled(client):
+ await check_unsupported_visibility(client, should_show=False)
+
+
+@pytest.mark.skip_platforms("android")
+@pytest.mark.asyncio
+@pytest.mark.without_interventions
+async def test_disabled(client):
+ await check_unsupported_visibility(client, should_show=True)
diff --git a/testing/webcompat/interventions/tests/test_1622063_wp1-ext_usps_gov.py b/testing/webcompat/interventions/tests/test_1622063_wp1-ext_usps_gov.py
new file mode 100644
index 0000000000..e55adf0dbe
--- /dev/null
+++ b/testing/webcompat/interventions/tests/test_1622063_wp1-ext_usps_gov.py
@@ -0,0 +1,21 @@
+import pytest
+
+URL = "https://wp1-ext.usps.gov/sap/bc/webdynpro/sap/hrrcf_a_unreg_job_search"
+BLOCKED_TEXT = "This browser is not supported"
+UNBLOCKED_CSS = "#sapwd_main_window_root_"
+
+
+@pytest.mark.only_platforms("android")
+@pytest.mark.asyncio
+@pytest.mark.with_interventions
+async def test_enabled(client):
+ await client.navigate(URL)
+ assert client.await_css(UNBLOCKED_CSS)
+
+
+@pytest.mark.only_platforms("android")
+@pytest.mark.asyncio
+@pytest.mark.without_interventions
+async def test_disabled(client):
+ await client.navigate(URL)
+ assert client.await_text(BLOCKED_TEXT)
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 0000000000..adb68d9e20
--- /dev/null
+++ b/testing/webcompat/interventions/tests/test_1631811_datastudio.py
@@ -0,0 +1,40 @@
+import pytest
+
+# the problem is intermittent, so we have to try loading the page a few times
+# and check whether a SecurityError was thrown by any of them, and that text
+# in the iframe didn't load in order to be relatively sure that it failed.
+# The problem seems even more intermittent on Android, so not running there.
+
+
+URL = "https://ageor.dipot.com/2020/03/Covid-19-in-Greece.html"
+FRAME_FINAL_URL = "lookerstudio.google.com/embed/reporting/"
+FRAME_TEXT = "Coranavirus SARS-CoV-2 (Covid-19) in Greece"
+FAIL_CONSOLE_MSG = "SecurityError"
+
+
+async def checkFrameFailsToLoadForAnyAttempt(client, timeout=10):
+ for i in range(5):
+ promise = await client.promise_frame_listener(FRAME_FINAL_URL, timeout=timeout)
+ await client.navigate(URL, wait="none")
+ frame = await promise
+ if frame is None:
+ return True
+ if not await frame.await_text(FRAME_TEXT, is_displayed=True, timeout=5):
+ return True
+ if await client.is_console_message(FAIL_CONSOLE_MSG):
+ return True
+ return False
+
+
+@pytest.mark.skip_platforms("android")
+@pytest.mark.asyncio
+@pytest.mark.with_interventions
+async def test_enabled(client):
+ assert not await checkFrameFailsToLoadForAnyAttempt(client, timeout=None)
+
+
+@pytest.mark.skip_platforms("android")
+@pytest.mark.asyncio
+@pytest.mark.without_interventions
+async def test_disabled(client):
+ assert await checkFrameFailsToLoadForAnyAttempt(client)
diff --git a/testing/webcompat/interventions/tests/test_1644830_missingmail_usps.py b/testing/webcompat/interventions/tests/test_1644830_missingmail_usps.py
new file mode 100644
index 0000000000..39a2db683a
--- /dev/null
+++ b/testing/webcompat/interventions/tests/test_1644830_missingmail_usps.py
@@ -0,0 +1,63 @@
+import pytest
+
+URL = (
+ "https://missingmail.usps.com/?_gl=1*veidlp*_gcl_aw*R0NMLjE1OTE3MjUyNTkuRUF"
+ "JYUlRb2JDaE1Jd3AzaTBhYjE2UUlWa01EQUNoMlBBUVlrRUFBWUFTQUFFZ0lMeFBEX0J3RQ..*"
+ "_gcl_dc*R0NMLjE1OTE3MjUyNTkuRUFJYUlRb2JDaE1Jd3AzaTBhYjE2UUlWa01EQUNoMlBBUV"
+ "lrRUFBWUFTQUFFZ0lMeFBEX0J3RQ..#/"
+)
+
+USERNAME_CSS = "#username"
+PASSWORD_CSS = "#password"
+SIGN_IN_CSS = "#btn-submit"
+TERMS_CHECKBOX_CSS = "#tc-checkbox"
+TERMS_FAUX_CHECKBOX_CSS = "#tc-checkbox + .mrc-custom-checkbox"
+
+# The USPS missing mail website takes a very long time to load (multiple
+# minutes). We give them a very generous amount of time here, but will
+# give up after that and just skip the rest of the test.
+TIMEOUT = 900
+TIMEOUT_MESSAGE = "USPS website is too slow, skipping test"
+
+
+async def are_checkboxes_clickable(client, credentials):
+ await client.navigate(URL)
+
+ username = client.await_css(USERNAME_CSS)
+ password = client.find_css(PASSWORD_CSS)
+ sign_in = client.find_css(SIGN_IN_CSS)
+ assert client.is_displayed(username)
+ assert client.is_displayed(password)
+ assert client.is_displayed(sign_in)
+
+ username.send_keys(credentials["username"])
+ password.send_keys(credentials["password"])
+ sign_in.click()
+
+ tc = client.await_css(TERMS_CHECKBOX_CSS, timeout=TIMEOUT)
+ if tc is None:
+ pytest.skip(TIMEOUT_MESSAGE)
+ return
+
+ assert not tc.selected
+
+ # we need to simulate a real click on the checkbox
+ tfc = client.find_css(TERMS_FAUX_CHECKBOX_CSS)
+ await client.dom_ready()
+ client.execute_script("arguments[0].scrollIntoView(true)", tfc)
+ client.mouse.click(tfc).perform()
+ return tc.selected
+
+
+@pytest.mark.skip_platforms("android")
+@pytest.mark.asyncio
+@pytest.mark.with_interventions
+async def test_enabled(client, credentials):
+ assert await are_checkboxes_clickable(client, credentials)
+
+
+@pytest.mark.skip_platforms("android")
+@pytest.mark.asyncio
+@pytest.mark.without_interventions
+async def test_disabled(client, credentials):
+ assert not await are_checkboxes_clickable(client, credentials)
diff --git a/testing/webcompat/interventions/tests/test_1650317_livescience_com.py b/testing/webcompat/interventions/tests/test_1650317_livescience_com.py
new file mode 100644
index 0000000000..f9cb71c571
--- /dev/null
+++ b/testing/webcompat/interventions/tests/test_1650317_livescience_com.py
@@ -0,0 +1,40 @@
+import pytest
+
+URL = "https://www.livescience.com/"
+TEXT_TO_TEST = ".trending__link"
+
+
+async def is_text_visible(client):
+ # the page does not properly load, so we just time out
+ # and wait for the element we're interested in to appear
+ await client.navigate(URL, timeout=1)
+ link = client.await_css(TEXT_TO_TEST)
+ assert client.is_displayed(link)
+ return client.execute_async_script(
+ """
+ const link = arguments[0];
+ const cb = arguments[1];
+ const fullHeight = link.scrollHeight;
+ const parentVisibleHeight = link.parentElement.clientHeight;
+ link.style.paddingBottom = "0";
+ window.requestAnimationFrame(() => {
+ const bottomPaddingHeight = fullHeight - link.scrollHeight;
+ cb(fullHeight - parentVisibleHeight <= bottomPaddingHeight);
+ });
+ """,
+ link,
+ )
+
+
+@pytest.mark.skip_platforms("android", "mac")
+@pytest.mark.asyncio
+@pytest.mark.with_interventions
+async def test_enabled(client):
+ assert await is_text_visible(client)
+
+
+@pytest.mark.skip_platforms("android", "mac")
+@pytest.mark.asyncio
+@pytest.mark.without_interventions
+async def test_disabled(client):
+ assert not await is_text_visible(client)
diff --git a/testing/webcompat/interventions/tests/test_1651292_www_jp_square-enix_com.py b/testing/webcompat/interventions/tests/test_1651292_www_jp_square-enix_com.py
new file mode 100644
index 0000000000..d8c7844c54
--- /dev/null
+++ b/testing/webcompat/interventions/tests/test_1651292_www_jp_square-enix_com.py
@@ -0,0 +1,20 @@
+import pytest
+
+URL = "https://www.jp.square-enix.com/music/sem/page/FF7R/ost/"
+ERROR_MSG = 'can\'t access property "slice", e.match(...) is null'
+UNBLOCKED_CSS = "h1.logo.visible"
+
+
+@pytest.mark.only_platforms("android")
+@pytest.mark.asyncio
+@pytest.mark.with_interventions
+async def test_enabled(client):
+ await client.navigate(URL)
+ assert client.await_css(UNBLOCKED_CSS)
+
+
+@pytest.mark.only_platforms("android")
+@pytest.mark.asyncio
+@pytest.mark.without_interventions
+async def test_disabled(client):
+ await client.navigate(URL, await_console_message=ERROR_MSG)
diff --git a/testing/webcompat/interventions/tests/test_1651917_www_teletrader_com.py b/testing/webcompat/interventions/tests/test_1651917_www_teletrader_com.py
new file mode 100644
index 0000000000..deda666ebb
--- /dev/null
+++ b/testing/webcompat/interventions/tests/test_1651917_www_teletrader_com.py
@@ -0,0 +1,27 @@
+import pytest
+
+URL = "https://www.teletrader.com/"
+
+
+def body_is_offscreen(client):
+ return client.execute_script(
+ """
+ return document.body.getBoundingClientRect().left > 0;
+ """
+ )
+
+
+@pytest.mark.only_platforms("android")
+@pytest.mark.asyncio
+@pytest.mark.with_interventions
+async def test_enabled(client):
+ await client.navigate(URL)
+ assert not body_is_offscreen(client)
+
+
+@pytest.mark.only_platforms("android")
+@pytest.mark.asyncio
+@pytest.mark.without_interventions
+async def test_disabled(client):
+ await client.navigate(URL)
+ assert body_is_offscreen(client)
diff --git a/testing/webcompat/interventions/tests/test_1654877_preev_com.py b/testing/webcompat/interventions/tests/test_1654877_preev_com.py
new file mode 100644
index 0000000000..398dd8b6e3
--- /dev/null
+++ b/testing/webcompat/interventions/tests/test_1654877_preev_com.py
@@ -0,0 +1,32 @@
+import pytest
+
+URL = "http://preev.com/"
+INPUT_CSS = "#invField"
+
+
+def no_number_arrows_appear(client):
+ inp = client.await_css(INPUT_CSS, is_displayed=True)
+ assert inp
+ inp.click()
+ return client.execute_script(
+ """
+ return getComputedStyle(arguments[0]).appearance == "textfield";
+ """,
+ inp,
+ )
+
+
+@pytest.mark.only_platforms("android")
+@pytest.mark.asyncio
+@pytest.mark.with_interventions
+async def test_enabled(client):
+ await client.navigate(URL)
+ assert no_number_arrows_appear(client)
+
+
+@pytest.mark.only_platforms("android")
+@pytest.mark.asyncio
+@pytest.mark.without_interventions
+async def test_disabled(client):
+ await client.navigate(URL)
+ assert not no_number_arrows_appear(client)
diff --git a/testing/webcompat/interventions/tests/test_1654907_reactine_ca.py b/testing/webcompat/interventions/tests/test_1654907_reactine_ca.py
new file mode 100644
index 0000000000..6b277db8c0
--- /dev/null
+++ b/testing/webcompat/interventions/tests/test_1654907_reactine_ca.py
@@ -0,0 +1,20 @@
+import pytest
+
+URL = "https://www.reactine.ca/products/"
+BANNER_CSS = "#browser-alert"
+
+
+@pytest.mark.only_platforms("android")
+@pytest.mark.asyncio
+@pytest.mark.with_interventions
+async def test_enabled(client):
+ await client.navigate(URL)
+ assert client.await_css(BANNER_CSS, is_displayed=False)
+
+
+@pytest.mark.only_platforms("android")
+@pytest.mark.asyncio
+@pytest.mark.without_interventions
+async def test_disabled(client):
+ await client.navigate(URL)
+ assert client.await_css(BANNER_CSS, is_displayed=True)
diff --git a/testing/webcompat/interventions/tests/test_1694470_m_myvidster_com.py b/testing/webcompat/interventions/tests/test_1694470_m_myvidster_com.py
new file mode 100644
index 0000000000..1daec3fa83
--- /dev/null
+++ b/testing/webcompat/interventions/tests/test_1694470_m_myvidster_com.py
@@ -0,0 +1,31 @@
+import pytest
+
+URL = "https://m.myvidster.com/"
+CONTAINER_CSS = "#home_refresh_var"
+
+
+def content_is_cropped(client):
+ container = client.await_css(CONTAINER_CSS, is_displayed=True)
+ assert container
+ return client.execute_script(
+ """
+ return arguments[0].clientHeight < 100
+ """,
+ container,
+ )
+
+
+@pytest.mark.only_platforms("android")
+@pytest.mark.asyncio
+@pytest.mark.with_interventions
+async def test_enabled(client):
+ await client.navigate(URL)
+ assert not content_is_cropped(client)
+
+
+@pytest.mark.only_platforms("android")
+@pytest.mark.asyncio
+@pytest.mark.without_interventions
+async def test_disabled(client):
+ await client.navigate(URL)
+ assert content_is_cropped(client)
diff --git a/testing/webcompat/interventions/tests/test_1697324_mobile2_bmo_com.py b/testing/webcompat/interventions/tests/test_1697324_mobile2_bmo_com.py
new file mode 100644
index 0000000000..72f3e16fa5
--- /dev/null
+++ b/testing/webcompat/interventions/tests/test_1697324_mobile2_bmo_com.py
@@ -0,0 +1,21 @@
+import pytest
+
+URL = "https://mobile2.bmo.com/"
+BLOCKED_TEXT = "This browser is out-of-date"
+UNBLOCKED_CSS = "auth-field"
+
+
+@pytest.mark.only_platforms("android")
+@pytest.mark.asyncio
+@pytest.mark.with_interventions
+async def test_enabled(client):
+ await client.navigate(URL)
+ assert client.await_css(UNBLOCKED_CSS)
+
+
+@pytest.mark.only_platforms("android")
+@pytest.mark.asyncio
+@pytest.mark.without_interventions
+async def test_disabled(client):
+ await client.navigate(URL)
+ assert client.await_text(BLOCKED_TEXT)
diff --git a/testing/webcompat/interventions/tests/test_1704673_app_xiaomi_com.py b/testing/webcompat/interventions/tests/test_1704673_app_xiaomi_com.py
new file mode 100644
index 0000000000..14f32373fe
--- /dev/null
+++ b/testing/webcompat/interventions/tests/test_1704673_app_xiaomi_com.py
@@ -0,0 +1,24 @@
+import pytest
+from webdriver.bidi.error import UnknownErrorException
+
+# This site fails with a redirect loop with a Firefox UA
+
+URL = "http://app.xiaomi.com/"
+REDIR_FAILURE_EXC = r".*NS_ERROR_REDIRECT_LOOP.*"
+SUCCESS_CSS = "#J_mingleList"
+
+
+@pytest.mark.only_platforms("android")
+@pytest.mark.asyncio
+@pytest.mark.with_interventions
+async def test_enabled(client):
+ await client.navigate(URL)
+ assert client.await_css(SUCCESS_CSS)
+
+
+@pytest.mark.only_platforms("android")
+@pytest.mark.asyncio
+@pytest.mark.without_interventions
+async def test_disabled(client):
+ with pytest.raises(UnknownErrorException, match=REDIR_FAILURE_EXC):
+ await client.navigate(URL, timeout=15)
diff --git a/testing/webcompat/interventions/tests/test_1712807_www_dealnews_com.py b/testing/webcompat/interventions/tests/test_1712807_www_dealnews_com.py
new file mode 100644
index 0000000000..d33bc01a76
--- /dev/null
+++ b/testing/webcompat/interventions/tests/test_1712807_www_dealnews_com.py
@@ -0,0 +1,31 @@
+import pytest
+
+URL = "https://www.dealnews.com/"
+
+
+# The page sets the style.width of images too wide on
+# Firefox, making the page wider than the viewport
+
+
+def is_wider_than_window(client):
+ return client.execute_script(
+ """
+ return document.body.scrollWidth > document.body.clientWidth;
+ """
+ )
+
+
+@pytest.mark.only_platforms("android")
+@pytest.mark.asyncio
+@pytest.mark.with_interventions
+async def test_enabled(client):
+ await client.navigate(URL)
+ assert not is_wider_than_window(client)
+
+
+@pytest.mark.only_platforms("android")
+@pytest.mark.asyncio
+@pytest.mark.without_interventions
+async def test_disabled(client):
+ await client.navigate(URL)
+ assert is_wider_than_window(client)
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 0000000000..60796fa54f
--- /dev/null
+++ b/testing/webcompat/interventions/tests/test_1712833_desuca.py
@@ -0,0 +1,18 @@
+import pytest
+
+URL = "https://buskocchi.desuca.co.jp/smartPhone.html"
+SCRIPT = "return document.getElementById('map_canvas')?.clientHeight"
+
+
+@pytest.mark.asyncio
+@pytest.mark.with_interventions
+async def test_enabled(client):
+ await client.navigate(URL)
+ assert client.execute_script(SCRIPT)
+
+
+@pytest.mark.asyncio
+@pytest.mark.without_interventions
+async def test_disabled(client):
+ await client.navigate(URL)
+ assert client.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 0000000000..c46259b150
--- /dev/null
+++ b/testing/webcompat/interventions/tests/test_1719859_saxoinvestor.py
@@ -0,0 +1,28 @@
+import pytest
+
+URL = (
+ "https://www.saxoinvestor.fr/login/?adobe_mc="
+ "MCORGID%3D173338B35278510F0A490D4C%2540AdobeOrg%7CTS%3D1621688498"
+)
+
+
+@pytest.mark.only_platforms("android")
+@pytest.mark.asyncio
+@pytest.mark.with_interventions
+async def test_enabled(client):
+ await client.navigate(URL)
+ userid = client.find_css("input#field_userid")
+ password = client.find_css("input#field_password")
+ submit = client.find_css("input#button_login")
+ assert client.is_displayed(userid)
+ assert client.is_displayed(password)
+ assert client.is_displayed(submit)
+
+
+@pytest.mark.only_platforms("android")
+@pytest.mark.asyncio
+@pytest.mark.without_interventions
+async def test_disabled(client):
+ await client.navigate(URL)
+ warning = client.find_css("#browser_support_section")
+ assert client.is_displayed(warning)
diff --git a/testing/webcompat/interventions/tests/test_1722954_granbluefantasy_jp.py b/testing/webcompat/interventions/tests/test_1722954_granbluefantasy_jp.py
new file mode 100644
index 0000000000..93fdf940b7
--- /dev/null
+++ b/testing/webcompat/interventions/tests/test_1722954_granbluefantasy_jp.py
@@ -0,0 +1,27 @@
+import pytest
+
+URL = "https://game.granbluefantasy.jp/"
+
+
+def content_is_full_width(client):
+ return client.execute_script(
+ """
+ return document.querySelector("#wrapper").offsetWidth == document.body.clientWidth;
+ """
+ )
+
+
+@pytest.mark.only_platforms("android")
+@pytest.mark.asyncio
+@pytest.mark.with_interventions
+async def test_enabled(client):
+ await client.navigate(URL)
+ assert content_is_full_width(client)
+
+
+@pytest.mark.only_platforms("android")
+@pytest.mark.asyncio
+@pytest.mark.without_interventions
+async def test_disabled(client):
+ await client.navigate(URL)
+ assert not content_is_full_width(client)
diff --git a/testing/webcompat/interventions/tests/test_1722955_frontgate_com.py b/testing/webcompat/interventions/tests/test_1722955_frontgate_com.py
new file mode 100644
index 0000000000..1b33259b58
--- /dev/null
+++ b/testing/webcompat/interventions/tests/test_1722955_frontgate_com.py
@@ -0,0 +1,26 @@
+import pytest
+
+URL = "https://www.frontgate.com/resort-cotton-bath-towels/155771"
+MOBILE_CSS = "#app"
+DESKTOP_CSS = "#cbiBody"
+
+# Note that this intervention doesn't seem to work 100% of
+# the time so this test may intermittently fail.
+
+
+@pytest.mark.only_platforms("android")
+@pytest.mark.asyncio
+@pytest.mark.with_interventions
+async def test_enabled(client):
+ await client.navigate(URL)
+ assert client.await_css(MOBILE_CSS, timeout=30)
+ assert not client.find_css(DESKTOP_CSS)
+
+
+@pytest.mark.only_platforms("android")
+@pytest.mark.asyncio
+@pytest.mark.without_interventions
+async def test_disabled(client):
+ await client.navigate(URL)
+ assert client.await_css(DESKTOP_CSS, timeout=30)
+ assert not client.find_css(MOBILE_CSS)
diff --git a/testing/webcompat/interventions/tests/test_1738317_vmos_cn.py b/testing/webcompat/interventions/tests/test_1738317_vmos_cn.py
new file mode 100644
index 0000000000..275b8b453d
--- /dev/null
+++ b/testing/webcompat/interventions/tests/test_1738317_vmos_cn.py
@@ -0,0 +1,23 @@
+import pytest
+
+URL = "https://www.vmos.cn/"
+MOBILE_CSS = ".bg_wave_blue"
+DESKTOP_CSS = "#content"
+
+
+@pytest.mark.only_platforms("android")
+@pytest.mark.asyncio
+@pytest.mark.with_interventions
+async def test_enabled(client):
+ await client.navigate(URL)
+ assert client.await_css(MOBILE_CSS)
+ assert not client.find_css(DESKTOP_CSS)
+
+
+@pytest.mark.only_platforms("android")
+@pytest.mark.asyncio
+@pytest.mark.without_interventions
+async def test_disabled(client):
+ await client.navigate(URL)
+ assert client.await_css(DESKTOP_CSS)
+ assert not client.find_css(MOBILE_CSS)
diff --git a/testing/webcompat/interventions/tests/test_1738319_yebocasino_co_za.py b/testing/webcompat/interventions/tests/test_1738319_yebocasino_co_za.py
new file mode 100644
index 0000000000..6b09cbec87
--- /dev/null
+++ b/testing/webcompat/interventions/tests/test_1738319_yebocasino_co_za.py
@@ -0,0 +1,29 @@
+import pytest
+
+URL = "https://www.yebocasino.co.za/webplay/"
+PRACTICE_CSS = "#lobbybox_featuredgames .gamebox .cta.practice"
+IFRAME_CSS = "#gameiframe"
+UNSUPPORTED_CSS = ".unsupported-device-box"
+SUPPORTED_CSS = "#game_main"
+
+
+async def get_to_page(client):
+ await client.navigate(URL)
+ client.soft_click(client.await_css(PRACTICE_CSS))
+ client.switch_to_frame(client.await_css(IFRAME_CSS))
+
+
+@pytest.mark.only_platforms("android")
+@pytest.mark.asyncio
+@pytest.mark.with_interventions
+async def test_enabled(client):
+ await get_to_page(client)
+ assert client.await_css(SUPPORTED_CSS)
+
+
+@pytest.mark.only_platforms("android")
+@pytest.mark.asyncio
+@pytest.mark.without_interventions
+async def test_disabled(client):
+ await get_to_page(client)
+ assert client.await_css(UNSUPPORTED_CSS)
diff --git a/testing/webcompat/interventions/tests/test_1741234_alphalabs.py b/testing/webcompat/interventions/tests/test_1741234_alphalabs.py
new file mode 100644
index 0000000000..65fd9c06dc
--- /dev/null
+++ b/testing/webcompat/interventions/tests/test_1741234_alphalabs.py
@@ -0,0 +1,35 @@
+import pytest
+
+URL = "https://patient.alphalabs.ca/Report/CovidReport"
+
+CONTINUE_CSS = ".btnNormal[type='submit']"
+FOOTER_CSS = "footer"
+
+
+async def is_continue_above_footer(client):
+ await client.navigate(URL)
+ cont = client.find_css(CONTINUE_CSS)
+ assert cont
+ footer = client.find_css(FOOTER_CSS)
+ assert footer
+ return client.execute_script(
+ """
+ const cont = arguments[0].getClientRects()[0];
+ const footer = arguments[1].getClientRects()[0];
+ return cont.bottom < footer.top;
+ """,
+ cont,
+ footer,
+ )
+
+
+@pytest.mark.asyncio
+@pytest.mark.with_interventions
+async def test_enabled(client):
+ assert await is_continue_above_footer(client)
+
+
+@pytest.mark.asyncio
+@pytest.mark.without_interventions
+async def test_disabled(client):
+ assert not await is_continue_above_footer(client)
diff --git a/testing/webcompat/interventions/tests/test_1743627_renrenaud-bray_com.py b/testing/webcompat/interventions/tests/test_1743627_renrenaud-bray_com.py
new file mode 100644
index 0000000000..51752b3a04
--- /dev/null
+++ b/testing/webcompat/interventions/tests/test_1743627_renrenaud-bray_com.py
@@ -0,0 +1,25 @@
+import pytest
+
+URL = "https://www.renaud-bray.com/accueil.aspx"
+ERROR_MSG = "ua.split(...)[1] is undefined"
+MENU_CSS = "#pageHeader_menuStores"
+
+
+@pytest.mark.only_platforms("android")
+@pytest.mark.asyncio
+@pytest.mark.with_interventions
+async def test_enabled(client):
+ await client.navigate(URL)
+ assert client.execute_script(
+ """
+ return arguments[0].style.zIndex == "7000";
+ """,
+ client.await_css(MENU_CSS),
+ )
+
+
+@pytest.mark.only_platforms("android")
+@pytest.mark.asyncio
+@pytest.mark.without_interventions
+async def test_disabled(client):
+ await client.navigate(URL, await_console_message=ERROR_MSG)
diff --git a/testing/webcompat/interventions/tests/test_1743751_slrclub_com.py b/testing/webcompat/interventions/tests/test_1743751_slrclub_com.py
new file mode 100644
index 0000000000..30b52fc98b
--- /dev/null
+++ b/testing/webcompat/interventions/tests/test_1743751_slrclub_com.py
@@ -0,0 +1,23 @@
+import pytest
+
+URL = "http://www.slrclub.com/"
+MOBILE_CSS = "#logo-wrap"
+DESKTOP_CSS = "#header"
+
+
+@pytest.mark.only_platforms("android")
+@pytest.mark.asyncio
+@pytest.mark.with_interventions
+async def test_enabled(client):
+ await client.navigate(URL)
+ assert client.await_css(MOBILE_CSS)
+ assert not client.find_css(DESKTOP_CSS)
+
+
+@pytest.mark.only_platforms("android")
+@pytest.mark.asyncio
+@pytest.mark.without_interventions
+async def test_disabled(client):
+ await client.navigate(URL)
+ assert client.await_css(DESKTOP_CSS)
+ assert not client.find_css(MOBILE_CSS)
diff --git a/testing/webcompat/interventions/tests/test_1743754_workflow_base_vn.py b/testing/webcompat/interventions/tests/test_1743754_workflow_base_vn.py
new file mode 100644
index 0000000000..a64c02b227
--- /dev/null
+++ b/testing/webcompat/interventions/tests/test_1743754_workflow_base_vn.py
@@ -0,0 +1,20 @@
+import pytest
+
+URL = "https://workflow.base.vn/todos"
+ERROR_MSG = 'can\'t access property "split", a.ua.split(...)[1] is undefined'
+SUCCESS_CSS = "#authform"
+
+
+@pytest.mark.only_platforms("android")
+@pytest.mark.asyncio
+@pytest.mark.with_interventions
+async def test_enabled(client):
+ await client.navigate(URL)
+ assert client.await_css(SUCCESS_CSS)
+
+
+@pytest.mark.only_platforms("android")
+@pytest.mark.asyncio
+@pytest.mark.without_interventions
+async def test_disabled(client):
+ await client.navigate(URL, await_console_message=ERROR_MSG)
diff --git a/testing/webcompat/interventions/tests/test_1753461_naver.py b/testing/webcompat/interventions/tests/test_1753461_naver.py
new file mode 100644
index 0000000000..03266ef415
--- /dev/null
+++ b/testing/webcompat/interventions/tests/test_1753461_naver.py
@@ -0,0 +1,22 @@
+import pytest
+
+URL = "https://serieson.naver.com/v2/movie/335790?isWebtoonAgreePopUp=true"
+
+SUPPORTED_CSS = "#playerWrapper"
+UNSUPPORTED_CSS = ".end_player_unavailable .download_links"
+
+
+@pytest.mark.skip_platforms("android")
+@pytest.mark.asyncio
+@pytest.mark.with_interventions
+async def test_enabled(client):
+ await client.navigate(URL)
+ assert client.await_css(SUPPORTED_CSS)
+
+
+@pytest.mark.skip_platforms("android")
+@pytest.mark.asyncio
+@pytest.mark.without_interventions
+async def test_disabled(client):
+ await client.navigate(URL)
+ assert client.await_css(UNSUPPORTED_CSS)
diff --git a/testing/webcompat/interventions/tests/test_1756872_www_dolcegabbana_com.py b/testing/webcompat/interventions/tests/test_1756872_www_dolcegabbana_com.py
new file mode 100644
index 0000000000..f498aa0e3c
--- /dev/null
+++ b/testing/webcompat/interventions/tests/test_1756872_www_dolcegabbana_com.py
@@ -0,0 +1,33 @@
+import pytest
+from webdriver.error import NoSuchElementException
+
+URL = "https://www.dolcegabbana.com/en/"
+COOKIES_CSS = "#CybotCookiebotDialogBodyLevelButtonLevelOptinAllowAll"
+FAIL_CSS = "#p-homepage"
+SUCCESS_CSS = "#app"
+
+
+async def load_page(client):
+ await client.navigate(URL)
+ try:
+ client.remove_element(client.await_css(COOKIES_CSS, timeout=4))
+ except NoSuchElementException:
+ pass
+
+
+@pytest.mark.only_platforms("android")
+@pytest.mark.asyncio
+@pytest.mark.with_interventions
+async def test_enabled(client):
+ await load_page(client)
+ assert client.await_css(SUCCESS_CSS)
+ assert not client.find_css(FAIL_CSS)
+
+
+@pytest.mark.only_platforms("android")
+@pytest.mark.asyncio
+@pytest.mark.without_interventions
+async def test_disabled(client):
+ await load_page(client)
+ assert client.await_css(FAIL_CSS)
+ assert not client.find_css(SUCCESS_CSS)
diff --git a/testing/webcompat/interventions/tests/test_1765947_veniceincoming_com.py b/testing/webcompat/interventions/tests/test_1765947_veniceincoming_com.py
new file mode 100644
index 0000000000..84ad99a3a4
--- /dev/null
+++ b/testing/webcompat/interventions/tests/test_1765947_veniceincoming_com.py
@@ -0,0 +1,42 @@
+import pytest
+from webdriver.error import NoSuchElementException
+
+URL = "https://veniceincoming.com/it_IT/search_results?page=1&start_date_start=&start_date_end=&k="
+COOKIES_CSS = "[aria-label='cookieconsent'] .cc-allow"
+IMG_CSS = ".tour-details"
+TOUR_DATA_CSS = "#tour_data"
+
+
+async def check_filter_opens(client):
+ await client.navigate(URL)
+
+ try:
+ cookies = client.await_css(COOKIES_CSS, is_displayed=True, timeout=5)
+ client.soft_click(cookies)
+ client.await_element_hidden(client.css(COOKIES_CSS))
+ except NoSuchElementException:
+ pass
+
+ img = client.await_css(IMG_CSS)
+ client.scroll_into_view(img)
+ client.mouse.click(element=img).perform()
+
+ try:
+ client.await_css(TOUR_DATA_CSS, is_displayed=True, timeout=5)
+ except NoSuchElementException:
+ return False
+ return True
+
+
+@pytest.mark.only_platforms("android")
+@pytest.mark.asyncio
+@pytest.mark.with_interventions
+async def test_enabled(client):
+ assert await check_filter_opens(client)
+
+
+@pytest.mark.only_platforms("android")
+@pytest.mark.asyncio
+@pytest.mark.without_interventions
+async def test_disabled(client):
+ assert not await check_filter_opens(client)
diff --git a/testing/webcompat/interventions/tests/test_1774490_rainews_it.py b/testing/webcompat/interventions/tests/test_1774490_rainews_it.py
new file mode 100644
index 0000000000..33a6c5a776
--- /dev/null
+++ b/testing/webcompat/interventions/tests/test_1774490_rainews_it.py
@@ -0,0 +1,32 @@
+import pytest
+
+URL = (
+ "https://www.rainews.it/photogallery/2022/06/fotorassegna-stampa-le-prime"
+ "-pagine-dei-quotidiani-di-sabato-04-giugno--cd5414b3-65b9-429b-85d5-e53fadd59f4c.html"
+)
+
+PICTURE_CSS = ".swiper-slide picture"
+GALLERY_CSS = ".swiper-wrapper"
+
+
+async def get_picture_width(client):
+ await client.navigate(URL, wait="load")
+ client.await_css(GALLERY_CSS)
+ return client.execute_script(
+ f"""
+ const p = document.querySelector("{PICTURE_CSS}");
+ return window.getComputedStyle(p).width;
+ """
+ )
+
+
+@pytest.mark.asyncio
+@pytest.mark.with_interventions
+async def test_enabled(client):
+ assert "0px" != await get_picture_width(client)
+
+
+@pytest.mark.asyncio
+@pytest.mark.without_interventions
+async def test_disabled(client):
+ assert "0px" == await get_picture_width(client)
diff --git a/testing/webcompat/interventions/tests/test_1776897_www_edencast_fr.py b/testing/webcompat/interventions/tests/test_1776897_www_edencast_fr.py
new file mode 100644
index 0000000000..4823aa0c39
--- /dev/null
+++ b/testing/webcompat/interventions/tests/test_1776897_www_edencast_fr.py
@@ -0,0 +1,59 @@
+import pytest
+
+# The page will asyncronously write out an <audio> element on success, but
+# will not do anything easily detectable otherwise.
+#
+# The problem is that the podPressShowHidePlayer function thinks to write
+# out a Flash embed unless WebKit is in the UA string, but ends up doing
+# nothing at all. However, it calls a different function for html5 vs SWF,
+# and we can detect which one it calls to confirm it calls the html5 one.
+
+URL = "https://www.edencast.fr/zoomcastlost-in-blindness/"
+
+AUDIO_CSS = "audio#podpresshtml5_1"
+
+SCRIPT = """
+ var done = arguments[0];
+
+ if (!window?.podPressShowHidePlayer) {
+ done("none");
+ }
+
+ window.podPressenprintHTML5audio = function() {
+ done("html5");
+ };
+
+ window.podpress_audioplayer_swfobject = {
+ embedSWF: function() {
+ done("swf");
+ },
+ };
+
+ const d = document.createElement("div");
+ d.id = "podPressPlayerSpace_test";
+ document.documentElement.appendChild(d);
+
+ const p = document.createElement("div");
+ p.id = "podPressPlayerSpace_test_PlayLink";
+ document.documentElement.appendChild(p);
+
+ podPressShowHidePlayer("test", "https//x/test.mp3", 100, 100, "force");
+ """
+
+
+@pytest.mark.asyncio
+@pytest.mark.with_interventions
+async def test_enabled(client):
+ await client.navigate(URL)
+ await client.dom_ready()
+ assert "html5" == client.execute_async_script(SCRIPT)
+ assert client.find_css(AUDIO_CSS)
+
+
+@pytest.mark.asyncio
+@pytest.mark.without_interventions
+async def test_disabled(client):
+ await client.navigate(URL)
+ await client.dom_ready()
+ assert "swf" == client.execute_async_script(SCRIPT)
+ assert not client.find_css(AUDIO_CSS)
diff --git a/testing/webcompat/interventions/tests/test_1778168_watch_antennaplus_gr.py b/testing/webcompat/interventions/tests/test_1778168_watch_antennaplus_gr.py
new file mode 100644
index 0000000000..ab145fe21d
--- /dev/null
+++ b/testing/webcompat/interventions/tests/test_1778168_watch_antennaplus_gr.py
@@ -0,0 +1,22 @@
+import pytest
+
+URL = (
+ "https://watch.antennaplus.gr/#/shows/agries_melisses/seasons/"
+ "3/episode/agries_melisses_S03_E137_v1"
+)
+
+
+@pytest.mark.skip_platforms("android")
+@pytest.mark.asyncio
+@pytest.mark.with_interventions
+async def test_enabled(client):
+ await client.navigate(URL)
+ assert client.await_css(".login-pf-page")
+
+
+@pytest.mark.skip_platforms("android")
+@pytest.mark.asyncio
+@pytest.mark.without_interventions
+async def test_disabled(client):
+ await client.navigate(URL)
+ assert client.await_css(".ua-barrier")
diff --git a/testing/webcompat/interventions/tests/test_1784141_acuvue_com.py b/testing/webcompat/interventions/tests/test_1784141_acuvue_com.py
new file mode 100644
index 0000000000..9d61591576
--- /dev/null
+++ b/testing/webcompat/interventions/tests/test_1784141_acuvue_com.py
@@ -0,0 +1,22 @@
+import pytest
+
+URL = "https://www.acuvue.com/"
+UNSUPPORTED_CSS = "#browser-alert"
+
+
+@pytest.mark.only_platforms("android")
+@pytest.mark.asyncio
+@pytest.mark.with_interventions
+async def test_enabled(client):
+ await client.navigate(URL)
+ unsupported = client.await_css(UNSUPPORTED_CSS)
+ assert not client.is_displayed(unsupported)
+
+
+@pytest.mark.only_platforms("android")
+@pytest.mark.asyncio
+@pytest.mark.without_interventions
+async def test_disabled(client):
+ await client.navigate(URL)
+ unsupported = client.await_css(UNSUPPORTED_CSS)
+ assert client.is_displayed(unsupported)
diff --git a/testing/webcompat/interventions/tests/test_1784141_aveeno_com.py b/testing/webcompat/interventions/tests/test_1784141_aveeno_com.py
new file mode 100644
index 0000000000..fe787cd044
--- /dev/null
+++ b/testing/webcompat/interventions/tests/test_1784141_aveeno_com.py
@@ -0,0 +1,22 @@
+import pytest
+
+URL = "https://www.aveeno.com/products/calm-restore-oat-gel-moisturizer-for-sensitive-skin"
+UNSUPPORTED_CSS = "#browser-alert"
+
+
+@pytest.mark.only_platforms("android")
+@pytest.mark.asyncio
+@pytest.mark.with_interventions
+async def test_enabled(client):
+ await client.navigate(URL)
+ unsupported = client.await_css(UNSUPPORTED_CSS)
+ assert not client.is_displayed(unsupported)
+
+
+@pytest.mark.only_platforms("android")
+@pytest.mark.asyncio
+@pytest.mark.without_interventions
+async def test_disabled(client):
+ await client.navigate(URL)
+ unsupported = client.await_css(UNSUPPORTED_CSS)
+ assert client.is_displayed(unsupported)
diff --git a/testing/webcompat/interventions/tests/test_1784199_aptsovation_com.py b/testing/webcompat/interventions/tests/test_1784199_aptsovation_com.py
new file mode 100644
index 0000000000..a7f22edac0
--- /dev/null
+++ b/testing/webcompat/interventions/tests/test_1784199_aptsovation_com.py
@@ -0,0 +1,20 @@
+import pytest
+
+URL = "https://www.aptsovation.com/"
+UNSUPPORTED_CSS = "[aria-label='Your browser is not supported']"
+
+
+@pytest.mark.asyncio
+@pytest.mark.with_interventions
+async def test_enabled(client):
+ await client.navigate(URL)
+ unsupported = client.await_css(UNSUPPORTED_CSS)
+ assert not client.is_displayed(unsupported)
+
+
+@pytest.mark.asyncio
+@pytest.mark.without_interventions
+async def test_disabled(client):
+ await client.navigate(URL)
+ unsupported = client.await_css(UNSUPPORTED_CSS)
+ assert client.is_displayed(unsupported)
diff --git a/testing/webcompat/interventions/tests/test_1784199_avanabayview_com.py b/testing/webcompat/interventions/tests/test_1784199_avanabayview_com.py
new file mode 100644
index 0000000000..17ab789bee
--- /dev/null
+++ b/testing/webcompat/interventions/tests/test_1784199_avanabayview_com.py
@@ -0,0 +1,20 @@
+import pytest
+
+URL = "https://www.avanabayview.com/"
+UNSUPPORTED_CSS = "[aria-label='Your browser is not supported']"
+
+
+@pytest.mark.asyncio
+@pytest.mark.with_interventions
+async def test_enabled(client):
+ await client.navigate(URL)
+ unsupported = client.await_css(UNSUPPORTED_CSS)
+ assert not client.is_displayed(unsupported)
+
+
+@pytest.mark.asyncio
+@pytest.mark.without_interventions
+async def test_disabled(client):
+ await client.navigate(URL)
+ unsupported = client.await_css(UNSUPPORTED_CSS)
+ assert client.is_displayed(unsupported)
diff --git a/testing/webcompat/interventions/tests/test_1784199_breakpointeandcoronado_com.py b/testing/webcompat/interventions/tests/test_1784199_breakpointeandcoronado_com.py
new file mode 100644
index 0000000000..3af30def4a
--- /dev/null
+++ b/testing/webcompat/interventions/tests/test_1784199_breakpointeandcoronado_com.py
@@ -0,0 +1,20 @@
+import pytest
+
+URL = "https://www.breakpointeandcoronado.com/"
+UNSUPPORTED_CSS = "[aria-label='Your browser is not supported']"
+
+
+@pytest.mark.asyncio
+@pytest.mark.with_interventions
+async def test_enabled(client):
+ await client.navigate(URL)
+ unsupported = client.await_css(UNSUPPORTED_CSS)
+ assert not client.is_displayed(unsupported)
+
+
+@pytest.mark.asyncio
+@pytest.mark.without_interventions
+async def test_disabled(client):
+ await client.navigate(URL)
+ unsupported = client.await_css(UNSUPPORTED_CSS)
+ assert client.is_displayed(unsupported)
diff --git a/testing/webcompat/interventions/tests/test_1784199_entrata_platform.py b/testing/webcompat/interventions/tests/test_1784199_entrata_platform.py
new file mode 100644
index 0000000000..d10776d5fd
--- /dev/null
+++ b/testing/webcompat/interventions/tests/test_1784199_entrata_platform.py
@@ -0,0 +1,20 @@
+import pytest
+
+URL = "https://www.liveobserverpark.com/"
+UNSUPPORTED_BANNER = ".banner_overlay"
+
+
+@pytest.mark.asyncio
+@pytest.mark.with_interventions
+async def test_enabled(client):
+ await client.navigate(URL)
+ unsupported_banner = client.await_css(UNSUPPORTED_BANNER)
+ assert not client.is_displayed(unsupported_banner)
+
+
+@pytest.mark.asyncio
+@pytest.mark.without_interventions
+async def test_disabled(client):
+ await client.navigate(URL)
+ unsupported_banner = client.await_css(UNSUPPORTED_BANNER)
+ assert client.is_displayed(unsupported_banner)
diff --git a/testing/webcompat/interventions/tests/test_1784199_liveatlasathens_com.py b/testing/webcompat/interventions/tests/test_1784199_liveatlasathens_com.py
new file mode 100644
index 0000000000..37b5f0cf4a
--- /dev/null
+++ b/testing/webcompat/interventions/tests/test_1784199_liveatlasathens_com.py
@@ -0,0 +1,20 @@
+import pytest
+
+URL = "https://www.liveatlasathens.com/"
+UNSUPPORTED_CSS = "[aria-label='Your browser is not supported']"
+
+
+@pytest.mark.asyncio
+@pytest.mark.with_interventions
+async def test_enabled(client):
+ await client.navigate(URL)
+ unsupported = client.await_css(UNSUPPORTED_CSS)
+ assert not client.is_displayed(unsupported)
+
+
+@pytest.mark.asyncio
+@pytest.mark.without_interventions
+async def test_disabled(client):
+ await client.navigate(URL)
+ unsupported = client.await_css(UNSUPPORTED_CSS)
+ assert client.is_displayed(unsupported)
diff --git a/testing/webcompat/interventions/tests/test_1784199_liveobserverpark_com.py b/testing/webcompat/interventions/tests/test_1784199_liveobserverpark_com.py
new file mode 100644
index 0000000000..ffc6b2d702
--- /dev/null
+++ b/testing/webcompat/interventions/tests/test_1784199_liveobserverpark_com.py
@@ -0,0 +1,20 @@
+import pytest
+
+URL = "https://www.liveobserverpark.com/"
+UNSUPPORTED_CSS = "[aria-label='Your browser is not supported']"
+
+
+@pytest.mark.asyncio
+@pytest.mark.with_interventions
+async def test_enabled(client):
+ await client.navigate(URL)
+ unsupported = client.await_css(UNSUPPORTED_CSS)
+ assert not client.is_displayed(unsupported)
+
+
+@pytest.mark.asyncio
+@pytest.mark.without_interventions
+async def test_disabled(client):
+ await client.navigate(URL)
+ unsupported = client.await_css(UNSUPPORTED_CSS)
+ assert client.is_displayed(unsupported)
diff --git a/testing/webcompat/interventions/tests/test_1784199_midwayurban_com.py b/testing/webcompat/interventions/tests/test_1784199_midwayurban_com.py
new file mode 100644
index 0000000000..2c90d9468e
--- /dev/null
+++ b/testing/webcompat/interventions/tests/test_1784199_midwayurban_com.py
@@ -0,0 +1,20 @@
+import pytest
+
+URL = "https://www.midwayurban.com/"
+UNSUPPORTED_CSS = "[aria-label='Your browser is not supported']"
+
+
+@pytest.mark.asyncio
+@pytest.mark.with_interventions
+async def test_enabled(client):
+ await client.navigate(URL)
+ unsupported = client.await_css(UNSUPPORTED_CSS)
+ assert not client.is_displayed(unsupported)
+
+
+@pytest.mark.asyncio
+@pytest.mark.without_interventions
+async def test_disabled(client):
+ await client.navigate(URL)
+ unsupported = client.await_css(UNSUPPORTED_CSS)
+ assert client.is_displayed(unsupported)
diff --git a/testing/webcompat/interventions/tests/test_1784199_nhcalaska_com.py b/testing/webcompat/interventions/tests/test_1784199_nhcalaska_com.py
new file mode 100644
index 0000000000..37ebce9be0
--- /dev/null
+++ b/testing/webcompat/interventions/tests/test_1784199_nhcalaska_com.py
@@ -0,0 +1,20 @@
+import pytest
+
+URL = "https://www.nhcalaska.com/"
+UNSUPPORTED_CSS = "[aria-label='Your browser is not supported']"
+
+
+@pytest.mark.asyncio
+@pytest.mark.with_interventions
+async def test_enabled(client):
+ await client.navigate(URL)
+ unsupported = client.await_css(UNSUPPORTED_CSS)
+ assert not client.is_displayed(unsupported)
+
+
+@pytest.mark.asyncio
+@pytest.mark.without_interventions
+async def test_disabled(client):
+ await client.navigate(URL)
+ unsupported = client.await_css(UNSUPPORTED_CSS)
+ assert client.is_displayed(unsupported)
diff --git a/testing/webcompat/interventions/tests/test_1784199_securityproperties_com.py b/testing/webcompat/interventions/tests/test_1784199_securityproperties_com.py
new file mode 100644
index 0000000000..bb7840ec66
--- /dev/null
+++ b/testing/webcompat/interventions/tests/test_1784199_securityproperties_com.py
@@ -0,0 +1,20 @@
+import pytest
+
+URL = "https://www.securityproperties.com/thornton/reserve-at-thornton-i-ii-apartments"
+UNSUPPORTED_CSS = "[aria-label='Your browser is not supported']"
+
+
+@pytest.mark.asyncio
+@pytest.mark.with_interventions
+async def test_enabled(client):
+ await client.navigate(URL)
+ unsupported = client.await_css(UNSUPPORTED_CSS)
+ assert not client.is_displayed(unsupported)
+
+
+@pytest.mark.asyncio
+@pytest.mark.without_interventions
+async def test_disabled(client):
+ await client.navigate(URL)
+ unsupported = client.await_css(UNSUPPORTED_CSS)
+ assert client.is_displayed(unsupported)
diff --git a/testing/webcompat/interventions/tests/test_1784199_theloftsorlando_com.py b/testing/webcompat/interventions/tests/test_1784199_theloftsorlando_com.py
new file mode 100644
index 0000000000..6aea9fd25c
--- /dev/null
+++ b/testing/webcompat/interventions/tests/test_1784199_theloftsorlando_com.py
@@ -0,0 +1,20 @@
+import pytest
+
+URL = "https://www.theloftsorlando.com/"
+UNSUPPORTED_CSS = "[aria-label='Your browser is not supported']"
+
+
+@pytest.mark.asyncio
+@pytest.mark.with_interventions
+async def test_enabled(client):
+ await client.navigate(URL)
+ unsupported = client.await_css(UNSUPPORTED_CSS)
+ assert not client.is_displayed(unsupported)
+
+
+@pytest.mark.asyncio
+@pytest.mark.without_interventions
+async def test_disabled(client):
+ await client.navigate(URL)
+ unsupported = client.await_css(UNSUPPORTED_CSS)
+ assert client.is_displayed(unsupported)
diff --git a/testing/webcompat/interventions/tests/test_1784199_thepointeatcentral_prospectportal_com.py b/testing/webcompat/interventions/tests/test_1784199_thepointeatcentral_prospectportal_com.py
new file mode 100644
index 0000000000..50ec397f75
--- /dev/null
+++ b/testing/webcompat/interventions/tests/test_1784199_thepointeatcentral_prospectportal_com.py
@@ -0,0 +1,20 @@
+import pytest
+
+URL = "https://thepointeatcentral.prospectportal.com/Apartments/module/application_authentication?_ga=2.250831814.1585158328.1588776830-1793033216.1588776830"
+UNSUPPORTED_CSS = "[aria-label='Your browser is not supported']"
+
+
+@pytest.mark.asyncio
+@pytest.mark.with_interventions
+async def test_enabled(client):
+ await client.navigate(URL)
+ unsupported = client.await_css(UNSUPPORTED_CSS)
+ assert not client.is_displayed(unsupported)
+
+
+@pytest.mark.asyncio
+@pytest.mark.without_interventions
+async def test_disabled(client):
+ await client.navigate(URL)
+ unsupported = client.await_css(UNSUPPORTED_CSS)
+ assert client.is_displayed(unsupported)
diff --git a/testing/webcompat/interventions/tests/test_1784199_vanallenapartments_com.py b/testing/webcompat/interventions/tests/test_1784199_vanallenapartments_com.py
new file mode 100644
index 0000000000..aff3d7d3ad
--- /dev/null
+++ b/testing/webcompat/interventions/tests/test_1784199_vanallenapartments_com.py
@@ -0,0 +1,20 @@
+import pytest
+
+URL = "https://www.vanallenapartments.com/"
+UNSUPPORTED_CSS = "[aria-label='Your browser is not supported']"
+
+
+@pytest.mark.asyncio
+@pytest.mark.with_interventions
+async def test_enabled(client):
+ await client.navigate(URL)
+ unsupported = client.await_css(UNSUPPORTED_CSS)
+ assert not client.is_displayed(unsupported)
+
+
+@pytest.mark.asyncio
+@pytest.mark.without_interventions
+async def test_disabled(client):
+ await client.navigate(URL)
+ unsupported = client.await_css(UNSUPPORTED_CSS)
+ assert client.is_displayed(unsupported)
diff --git a/testing/webcompat/interventions/tests/test_1784361_coldwellbankerhomes_com.py b/testing/webcompat/interventions/tests/test_1784361_coldwellbankerhomes_com.py
new file mode 100644
index 0000000000..32892b2087
--- /dev/null
+++ b/testing/webcompat/interventions/tests/test_1784361_coldwellbankerhomes_com.py
@@ -0,0 +1,23 @@
+import pytest
+
+# The gallery on this page only sets img[src] to a non-1x1-spacer if the error
+# doesn't happen during page load, which doesn't happen with a Chrome UA.
+
+URL = "https://www.coldwellbankerhomes.com/ri/little-compton/kvc-17_1,17_2/"
+ERROR_MSG = 'can\'t access property "dataset", v[0] is undefined'
+SUCCESS_CSS = "img.psr-lazy:not([src*='spacer'])"
+
+
+@pytest.mark.only_platforms("android")
+@pytest.mark.asyncio
+@pytest.mark.with_interventions
+async def test_enabled(client):
+ await client.navigate(URL)
+ assert client.await_css(SUCCESS_CSS)
+
+
+@pytest.mark.only_platforms("android")
+@pytest.mark.asyncio
+@pytest.mark.without_interventions
+async def test_disabled(client):
+ await client.navigate(URL, await_console_message=ERROR_MSG)
diff --git a/testing/webcompat/interventions/tests/test_1786404_business_help_royalmail_com.py b/testing/webcompat/interventions/tests/test_1786404_business_help_royalmail_com.py
new file mode 100644
index 0000000000..59a95ff8d9
--- /dev/null
+++ b/testing/webcompat/interventions/tests/test_1786404_business_help_royalmail_com.py
@@ -0,0 +1,70 @@
+import time
+
+import pytest
+from webdriver.error import StaleElementReferenceException
+
+URL = "https://business.help.royalmail.com/app/webforms/stampsenquiries"
+
+COOKIES_ACCEPT_CSS = "#consent_prompt_submit"
+POSTCODE_CSS = "input[name='rn_AddressControl_16textbox']"
+OPTION_CSS = "option.addr_pick_line:not(:disabled)"
+ADDY1_CSS = "[name='Incident.CustomFields.c.address1_1']"
+
+
+async def getResult(client):
+ await client.navigate(URL)
+
+ client.await_css(COOKIES_ACCEPT_CSS).click()
+
+ # the viewport scrolls around while we try to interact
+ # with the postcode in webdriver, so we take care here
+ code = client.await_css(POSTCODE_CSS)
+ client.scroll_into_view(code)
+ time.sleep(1)
+ client.mouse.click(element=code).perform()
+ code.send_keys("W1A 1AV")
+
+ # now wait for the options to open, scroll them back
+ # into view, and carefully click on the first one.
+ time.sleep(1)
+ client.scroll_into_view(code)
+ opt = client.await_css(OPTION_CSS)
+ client.mouse.click(element=opt).perform()
+
+ # the options should hide on click
+ time.sleep(2)
+ try:
+ if client.is_displayed(opt):
+ return False
+ except StaleElementReferenceException:
+ return False
+
+ # a address line should end up being prefilled
+ addy = client.await_css(ADDY1_CSS)
+ return client.execute_async_script(
+ """
+ var addy = arguments[0];
+ var done = arguments[1];
+ var to = setTimeout(() => {
+ if (addy.value) {
+ clearTimeout(to);
+ done(true);
+ }
+ }, 200);
+ """,
+ addy,
+ )
+
+
+@pytest.mark.skip_platforms("android")
+@pytest.mark.asyncio
+@pytest.mark.with_interventions
+async def test_enabled(client):
+ assert await getResult(client)
+
+
+@pytest.mark.skip_platforms("android")
+@pytest.mark.asyncio
+@pytest.mark.without_interventions
+async def test_disabled(client):
+ assert not await getResult(client)
diff --git a/testing/webcompat/interventions/tests/test_1793761_schoolnutritionandfitness_com.py b/testing/webcompat/interventions/tests/test_1793761_schoolnutritionandfitness_com.py
new file mode 100644
index 0000000000..9bb0be584c
--- /dev/null
+++ b/testing/webcompat/interventions/tests/test_1793761_schoolnutritionandfitness_com.py
@@ -0,0 +1,32 @@
+import pytest
+
+URL = "https://www.schoolnutritionandfitness.com/webmenus2/#/view?id=6331c49ce96f1e9c468b45be&siteCode=1641"
+ELEM_CSS = "react-app td > div"
+HEIGHT_CUTOFF = 10
+
+
+def get_elem_height(client):
+ elem = client.await_css(ELEM_CSS, is_displayed=True)
+ assert elem
+ return client.execute_script(
+ """
+ return arguments[0].getBoundingClientRect().height;
+ """,
+ elem,
+ )
+
+
+@pytest.mark.only_platforms("android")
+@pytest.mark.asyncio
+@pytest.mark.with_interventions
+async def test_enabled(client):
+ await client.navigate(URL)
+ assert get_elem_height(client) > HEIGHT_CUTOFF
+
+
+@pytest.mark.only_platforms("android")
+@pytest.mark.asyncio
+@pytest.mark.without_interventions
+async def test_disabled(client):
+ await client.navigate(URL)
+ assert get_elem_height(client) < HEIGHT_CUTOFF
diff --git a/testing/webcompat/interventions/tests/test_1795490_www_china-airlines_com.py b/testing/webcompat/interventions/tests/test_1795490_www_china-airlines_com.py
new file mode 100644
index 0000000000..8a38a633a4
--- /dev/null
+++ b/testing/webcompat/interventions/tests/test_1795490_www_china-airlines_com.py
@@ -0,0 +1,25 @@
+import pytest
+
+URL = "https://www.china-airlines.com/tw/en/booking/book-flights/flight-search?lang=en-us&deptstn=TPE&arrstn=LAX"
+DATE_CSS = "#departureDateMobile"
+DATE_DISABLED_CSS = "#departureDateMobile[disabled]"
+
+
+async def check_date_disabled(client):
+ await client.navigate(URL)
+ client.await_css(DATE_CSS)
+ return client.find_css(DATE_DISABLED_CSS)
+
+
+@pytest.mark.only_platforms("android")
+@pytest.mark.asyncio
+@pytest.mark.with_interventions
+async def test_enabled(client):
+ assert not await check_date_disabled(client)
+
+
+@pytest.mark.only_platforms("android")
+@pytest.mark.asyncio
+@pytest.mark.without_interventions
+async def test_disabled(client):
+ assert await check_date_disabled(client)
diff --git a/testing/webcompat/interventions/tests/test_1797400_mobilevikings_be.py b/testing/webcompat/interventions/tests/test_1797400_mobilevikings_be.py
new file mode 100644
index 0000000000..c3575b9735
--- /dev/null
+++ b/testing/webcompat/interventions/tests/test_1797400_mobilevikings_be.py
@@ -0,0 +1,31 @@
+import pytest
+from webdriver.error import NoSuchElementException
+
+URL = "https://mobilevikings.be/en/registration/?product_id=155536813-1-1"
+COOKIES_CSS = "#btn-accept-cookies"
+DATE_CSS = "input.date-input[name='birth_date']"
+
+
+async def date_after_typing(client):
+ await client.navigate(URL)
+ try:
+ client.await_css(COOKIES_CSS, timeout=3).click()
+ client.await_element_hidden(client.css(COOKIES_CSS))
+ except NoSuchElementException:
+ pass
+ date = client.await_css(DATE_CSS)
+ client.scroll_into_view(date)
+ date.send_keys("1")
+ return date.property("value")
+
+
+@pytest.mark.asyncio
+@pytest.mark.with_interventions
+async def test_enabled(client):
+ assert "1_/__/____" == await date_after_typing(client)
+
+
+@pytest.mark.asyncio
+@pytest.mark.without_interventions
+async def test_disabled(client):
+ assert "__/__/____" == await date_after_typing(client)
diff --git a/testing/webcompat/interventions/tests/test_1799968_samsung_com.py b/testing/webcompat/interventions/tests/test_1799968_samsung_com.py
new file mode 100644
index 0000000000..4b4fc408aa
--- /dev/null
+++ b/testing/webcompat/interventions/tests/test_1799968_samsung_com.py
@@ -0,0 +1,21 @@
+import pytest
+
+URL = "https://www.samsung.com/in/watches/galaxy-watch/galaxy-watch5-44mm-graphite-bluetooth-sm-r910nzaainu/"
+GOOD_CSS = "html.linux.firefox"
+ERROR_MSG = "DOMTokenList.add: The empty string is not a valid token."
+
+
+@pytest.mark.only_platforms("linux")
+@pytest.mark.asyncio
+@pytest.mark.with_interventions
+async def test_enabled(client):
+ await client.navigate(URL)
+ assert client.await_css(GOOD_CSS)
+
+
+@pytest.mark.only_platforms("linux")
+@pytest.mark.asyncio
+@pytest.mark.without_interventions
+async def test_disabled(client):
+ await client.navigate(URL, await_console_message=ERROR_MSG)
+ assert not client.find_css(GOOD_CSS)
diff --git a/testing/webcompat/interventions/tests/test_1800000_www_honda_co_uk.py b/testing/webcompat/interventions/tests/test_1800000_www_honda_co_uk.py
new file mode 100644
index 0000000000..55065cde71
--- /dev/null
+++ b/testing/webcompat/interventions/tests/test_1800000_www_honda_co_uk.py
@@ -0,0 +1,53 @@
+import time
+
+import pytest
+from webdriver.error import NoSuchElementException
+
+URL = "https://www.honda.co.uk/cars/book-a-service.html#search?query=tf33bu"
+COOKIES_CSS = "#onetrust-accept-btn-handler"
+CHOOSE_DEALER_CSS = "a[data-analytics-template*='Make a booking']"
+BOOK_ONLINE_CSS = "select#requiredService"
+SITE_DOWN_CSS = ".no-results"
+
+
+async def check_choose_dealer_works(client):
+ await client.navigate(URL)
+
+ cookies = client.css(COOKIES_CSS)
+ client.await_element(cookies).click()
+ client.await_element_hidden(cookies)
+ time.sleep(0.5)
+
+ down, dealer = client.await_first_element_of(
+ [
+ client.css(SITE_DOWN_CSS),
+ client.css(CHOOSE_DEALER_CSS),
+ ],
+ timeout=5,
+ )
+
+ if down:
+ pytest.skip("Service is down right now, so testing is impossible")
+ return True
+
+ client.scroll_into_view(dealer)
+ client.mouse.click(element=dealer).perform()
+ try:
+ client.await_css(BOOK_ONLINE_CSS, is_displayed=True, timeout=3)
+ except NoSuchElementException:
+ return False
+ return True
+
+
+@pytest.mark.skip_platforms("android")
+@pytest.mark.asyncio
+@pytest.mark.with_interventions
+async def test_enabled(client):
+ assert await check_choose_dealer_works(client)
+
+
+@pytest.mark.skip_platforms("android")
+@pytest.mark.asyncio
+@pytest.mark.without_interventions
+async def test_disabled(client):
+ assert not await check_choose_dealer_works(client)
diff --git a/testing/webcompat/interventions/tests/test_1800241_mms_telekom_de.py b/testing/webcompat/interventions/tests/test_1800241_mms_telekom_de.py
new file mode 100644
index 0000000000..37778ea804
--- /dev/null
+++ b/testing/webcompat/interventions/tests/test_1800241_mms_telekom_de.py
@@ -0,0 +1,19 @@
+import pytest
+
+URL = "https://www.mms.telekom.de/OTP/"
+LOGIN_CSS = ".LoginbackgroundDiv"
+
+
+@pytest.mark.asyncio
+@pytest.mark.with_interventions
+async def test_enabled(client):
+ await client.navigate(URL)
+ login_element = client.await_css(LOGIN_CSS)
+ assert client.is_displayed(login_element)
+
+
+@pytest.mark.asyncio
+@pytest.mark.without_interventions
+async def test_disabled(client):
+ await client.navigate(URL)
+ assert not client.find_css(LOGIN_CSS)
diff --git a/testing/webcompat/interventions/tests/test_1800936_cov19ent_kdca_go_kr.py b/testing/webcompat/interventions/tests/test_1800936_cov19ent_kdca_go_kr.py
new file mode 100644
index 0000000000..8caa33b53d
--- /dev/null
+++ b/testing/webcompat/interventions/tests/test_1800936_cov19ent_kdca_go_kr.py
@@ -0,0 +1,30 @@
+import pytest
+
+URL = "https://cov19ent.kdca.go.kr/cpassportal/biz/beffatstmnt/step1.do?flightPortSe=1&lang=en"
+HEADER_CSS = "header.mouseOn"
+
+
+# This site can be very slow, and prone to not finishing loading
+
+
+async def get_header_position(client):
+ await client.navigate(URL, wait="complete")
+ header = client.await_css(HEADER_CSS, timeout=60)
+ return client.execute_script(
+ """
+ return window.getComputedStyle(arguments[0]).position;
+ """,
+ header,
+ )
+
+
+@pytest.mark.asyncio
+@pytest.mark.with_interventions
+async def test_enabled(client):
+ assert "absolute" == await get_header_position(client)
+
+
+@pytest.mark.asyncio
+@pytest.mark.without_interventions
+async def test_disabled(client):
+ assert "absolute" != await get_header_position(client)
diff --git a/testing/webcompat/interventions/tests/test_1817520_ersthelfer_tv.py b/testing/webcompat/interventions/tests/test_1817520_ersthelfer_tv.py
new file mode 100644
index 0000000000..e8addbeb68
--- /dev/null
+++ b/testing/webcompat/interventions/tests/test_1817520_ersthelfer_tv.py
@@ -0,0 +1,36 @@
+import pytest
+
+URL = "https://book.ersthelfer.tv/"
+DATE = "12.12.2020"
+PLACEHOLDER = "__.__.____"
+
+CITY_CSS = "#city_id"
+PERSON_CSS = "#no_of_person"
+CITY_OPTION_XPATH = "//select[@name='city_id']/option[2]"
+PERSON_OPTION_XPATH = "//select[@name='no_of_person']/option[2]"
+DATE_PICKER_CSS = "[class*='date-picker-custom-wrapper'] input"
+
+
+async def set_date(client):
+ client.await_css(CITY_CSS).click()
+ client.await_xpath(CITY_OPTION_XPATH).click()
+
+ client.await_css(PERSON_CSS).click()
+ client.await_xpath(PERSON_OPTION_XPATH).click()
+ date_input = client.await_css(DATE_PICKER_CSS, is_displayed=True)
+ date_input.send_keys(DATE)
+ return date_input.property("value")
+
+
+@pytest.mark.asyncio
+@pytest.mark.with_interventions
+async def test_enabled(client):
+ await client.navigate(URL)
+ assert DATE == await set_date(client)
+
+
+@pytest.mark.asyncio
+@pytest.mark.without_interventions
+async def test_disabled(client):
+ await client.navigate(URL)
+ assert PLACEHOLDER == await set_date(client)
diff --git a/testing/webcompat/interventions/tests/test_1818818_wellcare_com.py b/testing/webcompat/interventions/tests/test_1818818_wellcare_com.py
new file mode 100644
index 0000000000..caacf5df0b
--- /dev/null
+++ b/testing/webcompat/interventions/tests/test_1818818_wellcare_com.py
@@ -0,0 +1,31 @@
+import pytest
+
+URL = "https://www.wellcare.com/en/Oregon/Members/Prescription-Drug-Plans-2023/Wellcare-Value-Script"
+ALERT_CSS = ".alert-box radius"
+MENU_CSS = "a[title='login DDL']"
+SELECT_CSS = "select#userSelect"
+
+
+async def is_fastclick_active(client):
+ async with client.ensure_fastclick_activates():
+ await client.navigate(URL, wait="load")
+ alert = client.find_css(ALERT_CSS)
+ if alert:
+ client.remove_element(alert)
+ menu = client.await_css(MENU_CSS)
+ client.soft_click(menu)
+ return client.test_for_fastclick(client.await_css(SELECT_CSS))
+
+
+@pytest.mark.only_platforms("android")
+@pytest.mark.asyncio
+@pytest.mark.with_interventions
+async def test_enabled(client):
+ assert not await is_fastclick_active(client)
+
+
+@pytest.mark.only_platforms("android")
+@pytest.mark.asyncio
+@pytest.mark.without_interventions
+async def test_disabled(client):
+ assert await is_fastclick_active(client)
diff --git a/testing/webcompat/interventions/tests/test_1819450_cmbchina_com.py b/testing/webcompat/interventions/tests/test_1819450_cmbchina_com.py
new file mode 100644
index 0000000000..c711596114
--- /dev/null
+++ b/testing/webcompat/interventions/tests/test_1819450_cmbchina_com.py
@@ -0,0 +1,21 @@
+import pytest
+
+URL = "https://www.cmbchina.com/"
+DESKTOP_CSS = "#aspnetForm"
+MOBILE_CSS = "#app .mobile-header"
+
+
+@pytest.mark.only_platforms("android")
+@pytest.mark.asyncio
+@pytest.mark.with_interventions
+async def test_enabled(client):
+ await client.navigate(URL)
+ assert client.await_css(MOBILE_CSS)
+
+
+@pytest.mark.only_platforms("android")
+@pytest.mark.asyncio
+@pytest.mark.without_interventions
+async def test_disabled(client):
+ await client.navigate(URL)
+ assert client.await_css(DESKTOP_CSS)
diff --git a/testing/webcompat/interventions/tests/test_1819476_axisbank_com.py b/testing/webcompat/interventions/tests/test_1819476_axisbank_com.py
new file mode 100644
index 0000000000..a53ed14538
--- /dev/null
+++ b/testing/webcompat/interventions/tests/test_1819476_axisbank_com.py
@@ -0,0 +1,18 @@
+import pytest
+
+URL = "https://www.axisbank.com/retail/cards/credit-card"
+SITE_CSS = "#ulCreditCard:not(:empty)"
+ERROR_MSG = "webkitSpeechRecognition is not defined"
+
+
+@pytest.mark.asyncio
+@pytest.mark.with_interventions
+async def test_enabled(client):
+ await client.navigate(URL)
+ assert client.await_css(SITE_CSS, timeout=120) # the site can be slow
+
+
+@pytest.mark.asyncio
+@pytest.mark.without_interventions
+async def test_disabled(client):
+ await client.navigate(URL, await_console_message=ERROR_MSG, timeout=10)
diff --git a/testing/webcompat/interventions/tests/test_1819702_feelgoodcontacts_com.py b/testing/webcompat/interventions/tests/test_1819702_feelgoodcontacts_com.py
new file mode 100644
index 0000000000..039e08cadc
--- /dev/null
+++ b/testing/webcompat/interventions/tests/test_1819702_feelgoodcontacts_com.py
@@ -0,0 +1,21 @@
+import pytest
+
+URL = "https://feelgoodcontacts.com/"
+MOBILE_CSS = "#aHomeMob"
+DESKTOP_CSS = "#mmenu"
+
+
+@pytest.mark.only_platforms("android")
+@pytest.mark.asyncio
+@pytest.mark.with_interventions
+async def test_enabled(client):
+ await client.navigate(URL)
+ assert client.await_css(MOBILE_CSS)
+
+
+@pytest.mark.only_platforms("android")
+@pytest.mark.asyncio
+@pytest.mark.without_interventions
+async def test_disabled(client):
+ await client.navigate(URL)
+ assert client.await_css(DESKTOP_CSS)
diff --git a/testing/webcompat/interventions/tests/test_1829126_otsuka_co_jp.py b/testing/webcompat/interventions/tests/test_1829126_otsuka_co_jp.py
new file mode 100644
index 0000000000..fe7ad3071c
--- /dev/null
+++ b/testing/webcompat/interventions/tests/test_1829126_otsuka_co_jp.py
@@ -0,0 +1,28 @@
+import pytest
+
+URL = "https://www.otsuka.co.jp/fib/"
+SPLASH_CSS = ".splash"
+
+# Note this site can be really slow
+
+
+async def splash_is_hidden(client):
+ await client.navigate(URL, wait="none")
+ splash = client.await_css(SPLASH_CSS, is_displayed=True, timeout=20)
+ assert splash
+ client.await_element_hidden(client.css(SPLASH_CSS), timeout=20)
+ return not client.is_displayed(splash)
+
+
+@pytest.mark.only_platforms("android")
+@pytest.mark.asyncio
+@pytest.mark.with_interventions
+async def test_enabled(client):
+ assert await splash_is_hidden(client)
+
+
+@pytest.mark.only_platforms("android")
+@pytest.mark.asyncio
+@pytest.mark.without_interventions
+async def test_disabled(client):
+ assert not await splash_is_hidden(client)
diff --git a/testing/webcompat/interventions/tests/test_1829944_411_ca.py b/testing/webcompat/interventions/tests/test_1829944_411_ca.py
new file mode 100644
index 0000000000..8147063c7d
--- /dev/null
+++ b/testing/webcompat/interventions/tests/test_1829944_411_ca.py
@@ -0,0 +1,18 @@
+import pytest
+
+URL = "https://411.ca"
+UNSUPPORTED_CSS = "ngb-alert.header-outdated-alert"
+
+
+@pytest.mark.asyncio
+@pytest.mark.with_interventions
+async def test_enabled(client):
+ await client.navigate(URL)
+ assert not client.find_css(UNSUPPORTED_CSS)
+
+
+@pytest.mark.asyncio
+@pytest.mark.without_interventions
+async def test_disabled(client):
+ await client.navigate(URL)
+ assert client.find_css(UNSUPPORTED_CSS)
diff --git a/testing/webcompat/interventions/tests/test_1829947_torguard_net.py b/testing/webcompat/interventions/tests/test_1829947_torguard_net.py
new file mode 100644
index 0000000000..6e895c923d
--- /dev/null
+++ b/testing/webcompat/interventions/tests/test_1829947_torguard_net.py
@@ -0,0 +1,24 @@
+import pytest
+
+URL = "https://www.discountcoffee.co.uk/collections/wholesale-coffee-beans"
+SELECT_CSS = "#collection-filter-type select"
+
+
+async def is_fastclick_active(client):
+ async with client.ensure_fastclick_activates():
+ await client.navigate(URL)
+ return client.test_for_fastclick(client.await_css(SELECT_CSS))
+
+
+@pytest.mark.only_platforms("android")
+@pytest.mark.asyncio
+@pytest.mark.with_interventions
+async def test_enabled(client):
+ assert not await is_fastclick_active(client)
+
+
+@pytest.mark.only_platforms("android")
+@pytest.mark.asyncio
+@pytest.mark.without_interventions
+async def test_disabled(client):
+ assert await is_fastclick_active(client)
diff --git a/testing/webcompat/interventions/tests/test_1830739_casinoextreme_eu.py b/testing/webcompat/interventions/tests/test_1830739_casinoextreme_eu.py
new file mode 100644
index 0000000000..a895cc9d7a
--- /dev/null
+++ b/testing/webcompat/interventions/tests/test_1830739_casinoextreme_eu.py
@@ -0,0 +1,28 @@
+import pytest
+
+URL = "https://casinoextreme.eu/games"
+RUN_CSS = "a.playgame-demo[onclick^='playGame']"
+IFRAME_CSS = "#gameplay > iframe"
+GOOD_MSG = "GameViewModel"
+BAD_MSG = "UnsupportedDevice"
+
+
+async def check_for_message(client, message):
+ await client.navigate(URL)
+ client.soft_click(client.await_css(RUN_CSS))
+ client.switch_to_frame(client.await_css(IFRAME_CSS))
+ await (await client.promise_console_message_listener(message))
+
+
+@pytest.mark.only_platforms("android")
+@pytest.mark.asyncio
+@pytest.mark.with_interventions
+async def test_enabled(client):
+ await check_for_message(client, GOOD_MSG)
+
+
+@pytest.mark.only_platforms("android")
+@pytest.mark.asyncio
+@pytest.mark.without_interventions
+async def test_disabled(client):
+ await check_for_message(client, BAD_MSG)
diff --git a/testing/webcompat/interventions/tests/test_1830739_www_cryptoloko_com.py b/testing/webcompat/interventions/tests/test_1830739_www_cryptoloko_com.py
new file mode 100644
index 0000000000..d562ea4a91
--- /dev/null
+++ b/testing/webcompat/interventions/tests/test_1830739_www_cryptoloko_com.py
@@ -0,0 +1,26 @@
+import pytest
+
+URL = "https://www.cryptoloko.com/webplay/?play=sweet-16-blast"
+IFRAME_CSS = "#gameiframe"
+GOOD_MSG = "GameViewModel"
+BAD_MSG = "UnsupportedDevice"
+
+
+async def check_for_message(client, message):
+ await client.navigate(URL)
+ client.switch_to_frame(client.await_css(IFRAME_CSS))
+ await (await client.promise_console_message_listener(message))
+
+
+@pytest.mark.only_platforms("android")
+@pytest.mark.asyncio
+@pytest.mark.with_interventions
+async def test_enabled(client):
+ await check_for_message(client, GOOD_MSG)
+
+
+@pytest.mark.only_platforms("android")
+@pytest.mark.asyncio
+@pytest.mark.without_interventions
+async def test_disabled(client):
+ await check_for_message(client, BAD_MSG)
diff --git a/testing/webcompat/interventions/tests/test_1830739_www_heapsofwins_com.py b/testing/webcompat/interventions/tests/test_1830739_www_heapsofwins_com.py
new file mode 100644
index 0000000000..d2f44b4656
--- /dev/null
+++ b/testing/webcompat/interventions/tests/test_1830739_www_heapsofwins_com.py
@@ -0,0 +1,35 @@
+import time
+
+import pytest
+from webdriver.error import NoSuchElementException
+
+URL = "https://www.heapsofwins.com/games/all"
+RUN_CSS = "#pagehero.games a[href*='games?play']"
+IFRAME_CSS = "#gamemodal iframe"
+GOOD_MSG = "GameViewModel"
+BAD_MSG = "UnsupportedDevice"
+
+
+async def check_for_message(client, message):
+ await client.navigate(URL)
+ time.sleep(1)
+ try:
+ client.soft_click(client.await_css(RUN_CSS))
+ client.switch_to_frame(client.await_css(IFRAME_CSS))
+ await (await client.promise_console_message_listener(message, timeout=30))
+ except NoSuchElementException:
+ pytest.xfail("Cannot test; all games seem to require login right now")
+
+
+@pytest.mark.only_platforms("android")
+@pytest.mark.asyncio
+@pytest.mark.with_interventions
+async def test_enabled(client):
+ await check_for_message(client, GOOD_MSG)
+
+
+@pytest.mark.only_platforms("android")
+@pytest.mark.asyncio
+@pytest.mark.without_interventions
+async def test_disabled(client):
+ await check_for_message(client, BAD_MSG)
diff --git a/testing/webcompat/interventions/tests/test_1830739_www_planet7casino_com.py b/testing/webcompat/interventions/tests/test_1830739_www_planet7casino_com.py
new file mode 100644
index 0000000000..97901efe77
--- /dev/null
+++ b/testing/webcompat/interventions/tests/test_1830739_www_planet7casino_com.py
@@ -0,0 +1,34 @@
+import asyncio
+
+import pytest
+
+URL = "https://www.planet7casino.com/lobby/?play=alien-wins"
+IFRAME_CSS = "#gameiframe"
+GOOD_MSG = "GameViewModel"
+BAD_MSG = "UnsupportedDevice"
+DOWN_TEXT = "temporarily out of order"
+
+
+async def check_for_message(client, message):
+ await client.navigate(URL)
+ client.switch_to_frame(client.await_css(IFRAME_CSS))
+ down = client.async_await_element(client.text(DOWN_TEXT))
+ good = await client.promise_console_message_listener(message)
+ down, good = await asyncio.wait([down, good], return_when=asyncio.FIRST_COMPLETED)
+ if down:
+ pytest.skip("Cannot test: casino is 'temporarily out of order'")
+ return good
+
+
+@pytest.mark.only_platforms("android")
+@pytest.mark.asyncio
+@pytest.mark.with_interventions
+async def test_enabled(client):
+ assert check_for_message(client, GOOD_MSG)
+
+
+@pytest.mark.only_platforms("android")
+@pytest.mark.asyncio
+@pytest.mark.without_interventions
+async def test_disabled(client):
+ assert check_for_message(client, BAD_MSG)
diff --git a/testing/webcompat/interventions/tests/test_1830747_babbel_com.py b/testing/webcompat/interventions/tests/test_1830747_babbel_com.py
new file mode 100644
index 0000000000..cfefe31412
--- /dev/null
+++ b/testing/webcompat/interventions/tests/test_1830747_babbel_com.py
@@ -0,0 +1,35 @@
+import pytest
+from webdriver.error import NoSuchElementException
+
+URL = "https://my.babbel.com/en/welcome/1?bsc=engmag-rus&btp=default&learn_lang=RUS"
+COOKIES_CSS = "#onetrust-accept-btn-handler"
+BUTTON_CSS = "button.button[type=submit]"
+
+
+async def button_visible(client):
+ await client.navigate(URL)
+ try:
+ client.soft_click(client.await_css(COOKIES_CSS, timeout=3))
+ except NoSuchElementException:
+ pass
+ btn = client.await_css(BUTTON_CSS)
+ return client.execute_script(
+ """
+ return window.innerHeight > arguments[0].getBoundingClientRect().bottom;
+ """,
+ btn,
+ )
+
+
+@pytest.mark.only_platforms("android")
+@pytest.mark.asyncio
+@pytest.mark.with_interventions
+async def test_enabled(client):
+ assert await button_visible(client)
+
+
+@pytest.mark.only_platforms("android")
+@pytest.mark.asyncio
+@pytest.mark.without_interventions
+async def test_disabled(client):
+ assert not await button_visible(client)
diff --git a/testing/webcompat/interventions/tests/test_1830752_afisha_ru.py b/testing/webcompat/interventions/tests/test_1830752_afisha_ru.py
new file mode 100644
index 0000000000..5a2a51acc1
--- /dev/null
+++ b/testing/webcompat/interventions/tests/test_1830752_afisha_ru.py
@@ -0,0 +1,45 @@
+import time
+
+import pytest
+from webdriver.error import ElementClickInterceptedException, NoSuchElementException
+
+URL = "https://www.afisha.ru/msk/theatre/"
+COOKIES_CSS = "button.Txfw7"
+OPT_CSS = "button[data-name='price']"
+SLIDER_CSS = "input[type='range'][data-index='0']"
+
+
+async def slider_is_clickable(client):
+ await client.navigate(URL)
+
+ try:
+ client.soft_click(client.await_css(COOKIES_CSS))
+ client.await_element_hidden(client.css(COOKIES_CSS))
+ except NoSuchElementException:
+ pass
+
+ try:
+ client.soft_click(client.await_css(OPT_CSS, is_displayed=True))
+ except NoSuchElementException:
+ pytest.xfail("Site may have shown a captcha; please run this test again")
+
+ try:
+ slider = client.await_css(SLIDER_CSS, is_displayed=True)
+ time.sleep(2)
+ slider.click()
+ except ElementClickInterceptedException:
+ return False
+
+ return True
+
+
+@pytest.mark.asyncio
+@pytest.mark.with_interventions
+async def test_enabled(client):
+ assert await slider_is_clickable(client)
+
+
+@pytest.mark.asyncio
+@pytest.mark.without_interventions
+async def test_disabled(client):
+ assert not await slider_is_clickable(client)
diff --git a/testing/webcompat/interventions/tests/test_1830761_91mobiles_com.py b/testing/webcompat/interventions/tests/test_1830761_91mobiles_com.py
new file mode 100644
index 0000000000..4f83c371b7
--- /dev/null
+++ b/testing/webcompat/interventions/tests/test_1830761_91mobiles_com.py
@@ -0,0 +1,27 @@
+import pytest
+
+URL = "https://www.91mobiles.com/compare/realme/9+SE+5G/vs/Moto/G72-amp"
+CONTENT_CSS = "#fixed-table tr td .cmp-summary-box"
+
+
+async def content_has_height(client):
+ await client.navigate(URL)
+ elem = client.await_css(CONTENT_CSS)
+ return client.execute_script(
+ """
+ return arguments[0].getBoundingClientRect().height > 0;
+ """,
+ elem,
+ )
+
+
+@pytest.mark.asyncio
+@pytest.mark.with_interventions
+async def test_enabled(client):
+ assert await content_has_height(client)
+
+
+@pytest.mark.asyncio
+@pytest.mark.without_interventions
+async def test_disabled(client):
+ assert not await content_has_height(client)
diff --git a/testing/webcompat/interventions/tests/test_1830776_blueshieldca_com.py b/testing/webcompat/interventions/tests/test_1830776_blueshieldca_com.py
new file mode 100644
index 0000000000..cfe67c62bc
--- /dev/null
+++ b/testing/webcompat/interventions/tests/test_1830776_blueshieldca_com.py
@@ -0,0 +1,18 @@
+import pytest
+
+URL = "https://www.blueshieldca.com/provider"
+UNSUPPORTED_CSS = "#browserErrorModal"
+
+
+@pytest.mark.asyncio
+@pytest.mark.with_interventions
+async def test_enabled(client):
+ await client.navigate(URL)
+ assert client.find_css(UNSUPPORTED_CSS, is_displayed=False)
+
+
+@pytest.mark.asyncio
+@pytest.mark.without_interventions
+async def test_disabled(client):
+ await client.navigate(URL)
+ assert client.find_css(UNSUPPORTED_CSS, is_displayed=True)
diff --git a/testing/webcompat/interventions/tests/test_1830796_copyleaks_com.py b/testing/webcompat/interventions/tests/test_1830796_copyleaks_com.py
new file mode 100644
index 0000000000..56817c705b
--- /dev/null
+++ b/testing/webcompat/interventions/tests/test_1830796_copyleaks_com.py
@@ -0,0 +1,26 @@
+import time
+
+import pytest
+
+URL = "https://copyleaks.com/ai-content-detector"
+IFRAME_CSS = "#ai-content-detector"
+UNSUPPORTED_CSS = "#outdated"
+
+
+@pytest.mark.only_platforms("android")
+@pytest.mark.asyncio
+@pytest.mark.with_interventions
+async def test_enabled(client):
+ await client.navigate(URL)
+ client.switch_to_frame(client.await_css(IFRAME_CSS))
+ time.sleep(2)
+ assert not client.find_css(UNSUPPORTED_CSS, is_displayed=True)
+
+
+@pytest.mark.only_platforms("android")
+@pytest.mark.asyncio
+@pytest.mark.without_interventions
+async def test_disabled(client):
+ await client.navigate(URL)
+ client.switch_to_frame(client.await_css(IFRAME_CSS))
+ assert client.await_css(UNSUPPORTED_CSS, is_displayed=True)
diff --git a/testing/webcompat/interventions/tests/test_1830810_interceramic_com.py b/testing/webcompat/interventions/tests/test_1830810_interceramic_com.py
new file mode 100644
index 0000000000..78620fd343
--- /dev/null
+++ b/testing/webcompat/interventions/tests/test_1830810_interceramic_com.py
@@ -0,0 +1,21 @@
+import time
+
+import pytest
+
+URL = "https://interceramic.com/mx_202VWME/galeria/index"
+UNSUPPORTED_CSS = "#ff-modal"
+
+
+@pytest.mark.asyncio
+@pytest.mark.with_interventions
+async def test_enabled(client):
+ await client.navigate(URL)
+ time.sleep(3)
+ assert client.find_css(UNSUPPORTED_CSS, is_displayed=False)
+
+
+@pytest.mark.asyncio
+@pytest.mark.without_interventions
+async def test_disabled(client):
+ await client.navigate(URL)
+ assert client.await_css(UNSUPPORTED_CSS, is_displayed=True)
diff --git a/testing/webcompat/interventions/tests/test_1830813_onstove_com.py b/testing/webcompat/interventions/tests/test_1830813_onstove_com.py
new file mode 100644
index 0000000000..626f71f92b
--- /dev/null
+++ b/testing/webcompat/interventions/tests/test_1830813_onstove_com.py
@@ -0,0 +1,29 @@
+import pytest
+
+URL = "https://page.onstove.com/epicseven/global/"
+BANNER_CSS = ".gnb-alerts.gnb-old-browser"
+
+
+async def get_banner_height(client):
+ await client.navigate(URL)
+ banner = client.await_css(BANNER_CSS)
+ return client.execute_script(
+ """
+ return arguments[0].getBoundingClientRect().height;
+ """,
+ banner,
+ )
+
+
+@pytest.mark.skip_platforms("android")
+@pytest.mark.asyncio
+@pytest.mark.with_interventions
+async def test_enabled(client):
+ assert 0 == await get_banner_height(client)
+
+
+@pytest.mark.skip_platforms("android")
+@pytest.mark.asyncio
+@pytest.mark.without_interventions
+async def test_disabled(client):
+ assert 0 < await get_banner_height(client)
diff --git a/testing/webcompat/interventions/tests/test_1830821_enjoy_point_auone_jp.py b/testing/webcompat/interventions/tests/test_1830821_enjoy_point_auone_jp.py
new file mode 100644
index 0000000000..f8ccf39674
--- /dev/null
+++ b/testing/webcompat/interventions/tests/test_1830821_enjoy_point_auone_jp.py
@@ -0,0 +1,21 @@
+import pytest
+
+URL = "https://enjoy.point.auone.jp/"
+SUPPORTED_CSS = "main.tmr-page"
+UNSUPPORTED_TEXT = "Android:標準ブラウザ、Chrome"
+
+
+@pytest.mark.only_platforms("android")
+@pytest.mark.asyncio
+@pytest.mark.with_interventions
+async def test_enabled(client):
+ await client.navigate(URL)
+ assert client.await_css(SUPPORTED_CSS)
+
+
+@pytest.mark.only_platforms("android")
+@pytest.mark.asyncio
+@pytest.mark.without_interventions
+async def test_disabled(client):
+ await client.navigate(URL)
+ assert client.await_text(UNSUPPORTED_TEXT)
diff --git a/testing/webcompat/interventions/tests/test_1830821_webcartop_jp.py b/testing/webcompat/interventions/tests/test_1830821_webcartop_jp.py
new file mode 100644
index 0000000000..a1b105ed15
--- /dev/null
+++ b/testing/webcompat/interventions/tests/test_1830821_webcartop_jp.py
@@ -0,0 +1,23 @@
+import pytest
+
+URL = "https://www.webcartop.jp/"
+DESKTOP_CSS = "#page"
+MOBILE_CSS = "#my-page"
+
+
+@pytest.mark.only_platforms("android")
+@pytest.mark.asyncio
+@pytest.mark.with_interventions
+async def test_enabled(client):
+ await client.navigate(URL)
+ assert client.await_css(MOBILE_CSS)
+ assert not client.find_css(DESKTOP_CSS)
+
+
+@pytest.mark.only_platforms("android")
+@pytest.mark.asyncio
+@pytest.mark.without_interventions
+async def test_disabled(client):
+ await client.navigate(URL)
+ assert client.await_css(DESKTOP_CSS)
+ assert not client.find_css(MOBILE_CSS)
diff --git a/testing/webcompat/interventions/tests/test_1836103_autostar_novoross_ru.py b/testing/webcompat/interventions/tests/test_1836103_autostar_novoross_ru.py
new file mode 100644
index 0000000000..d63fd666fe
--- /dev/null
+++ b/testing/webcompat/interventions/tests/test_1836103_autostar_novoross_ru.py
@@ -0,0 +1,29 @@
+import pytest
+
+URL = "https://autostar-novoross.ru/"
+IFRAME_CSS = "iframe[src*='google.com/maps']"
+
+
+async def map_is_correct_height(client):
+ await client.navigate(URL)
+ iframe = client.await_css(IFRAME_CSS)
+ assert iframe
+ return client.execute_script(
+ """
+ const iframe = arguments[0];
+ return iframe.clientHeight == iframe.parentNode.clientHeight;
+ """,
+ iframe,
+ )
+
+
+@pytest.mark.asyncio
+@pytest.mark.with_interventions
+async def test_enabled(client):
+ assert await map_is_correct_height(client)
+
+
+@pytest.mark.asyncio
+@pytest.mark.without_interventions
+async def test_disabled(client):
+ assert not await map_is_correct_height(client)
diff --git a/testing/webcompat/interventions/tests/test_1836109_watch_tonton_com_my.py b/testing/webcompat/interventions/tests/test_1836109_watch_tonton_com_my.py
new file mode 100644
index 0000000000..e36eaaa53a
--- /dev/null
+++ b/testing/webcompat/interventions/tests/test_1836109_watch_tonton_com_my.py
@@ -0,0 +1,26 @@
+import pytest
+
+URL = "https://watch.tonton.com.my/#/"
+UNSUPPORTED_CSS = ".ua-barrier"
+LOGIN_CSS = ".login-page-container"
+
+
+# The site can take a little time to load, and this includes
+# interstitial ads, so for now we give it 10 seconds.
+
+
+# Skip Android as the site blocks many Android devices including the emulator
+@pytest.mark.skip_platforms("android")
+@pytest.mark.asyncio
+@pytest.mark.with_interventions
+async def test_enabled(client):
+ await client.navigate(URL)
+ assert client.await_css(LOGIN_CSS, timeout=10)
+
+
+@pytest.mark.skip_platforms("android")
+@pytest.mark.asyncio
+@pytest.mark.without_interventions
+async def test_disabled(client):
+ await client.navigate(URL)
+ assert client.await_css(UNSUPPORTED_CSS, timeout=10)
diff --git a/testing/webcompat/interventions/tests/test_1836112_www_capcut_cn.py b/testing/webcompat/interventions/tests/test_1836112_www_capcut_cn.py
new file mode 100644
index 0000000000..a57e678110
--- /dev/null
+++ b/testing/webcompat/interventions/tests/test_1836112_www_capcut_cn.py
@@ -0,0 +1,19 @@
+import pytest
+
+URL = "https://www.slushy.com/"
+UNSUPPORTED_TEXT = "If you wanna see this content"
+LOGIN_TEXT = "you must be at least 18"
+
+
+@pytest.mark.asyncio
+@pytest.mark.with_interventions
+async def test_enabled(client):
+ await client.navigate(URL)
+ assert client.await_text(LOGIN_TEXT)
+
+
+@pytest.mark.asyncio
+@pytest.mark.without_interventions
+async def test_disabled(client):
+ await client.navigate(URL)
+ assert client.await_text(UNSUPPORTED_TEXT)
diff --git a/testing/webcompat/interventions/tests/test_1836116_www_slushy_com.py b/testing/webcompat/interventions/tests/test_1836116_www_slushy_com.py
new file mode 100644
index 0000000000..69c5d1d1a3
--- /dev/null
+++ b/testing/webcompat/interventions/tests/test_1836116_www_slushy_com.py
@@ -0,0 +1,20 @@
+import pytest
+
+URL = "https://www.slushy.com/"
+
+
+# skip Android, as the site blocks low-res devices like the emulator
+@pytest.mark.skip_platforms("android")
+@pytest.mark.asyncio
+@pytest.mark.with_interventions
+async def test_enabled(client):
+ await client.navigate(URL, wait="load")
+ assert not client.execute_script("return location.href.includes('unsupported')")
+
+
+@pytest.mark.skip_platforms("android")
+@pytest.mark.asyncio
+@pytest.mark.without_interventions
+async def test_disabled(client):
+ await client.navigate(URL, wait="load")
+ assert client.execute_script("return location.href.includes('unsupported')")
diff --git a/testing/webcompat/interventions/tests/test_1836135_gts_pro_sdimedia_com.py b/testing/webcompat/interventions/tests/test_1836135_gts_pro_sdimedia_com.py
new file mode 100644
index 0000000000..67e1aa761b
--- /dev/null
+++ b/testing/webcompat/interventions/tests/test_1836135_gts_pro_sdimedia_com.py
@@ -0,0 +1,19 @@
+import pytest
+
+URL = "https://gts-pro.sdimedia.com/"
+UNSUPPORTED_CSS = "#invalid-browser"
+LOGIN_CSS = "button[data-qa='open-login-page']"
+
+
+@pytest.mark.asyncio
+@pytest.mark.with_interventions
+async def test_enabled(client):
+ await client.navigate(URL)
+ assert client.await_css(LOGIN_CSS, is_displayed=True)
+
+
+@pytest.mark.asyncio
+@pytest.mark.without_interventions
+async def test_disabled(client):
+ await client.navigate(URL)
+ assert client.await_css(UNSUPPORTED_CSS, is_displayed=True)
diff --git a/testing/webcompat/interventions/tests/test_1836140_indices_iriworldwide_com.py b/testing/webcompat/interventions/tests/test_1836140_indices_iriworldwide_com.py
new file mode 100644
index 0000000000..1ffaf5c09f
--- /dev/null
+++ b/testing/webcompat/interventions/tests/test_1836140_indices_iriworldwide_com.py
@@ -0,0 +1,18 @@
+import pytest
+
+URL = "https://indices.iriworldwide.com/covid19/register.html"
+BAD_CSS = ".hiddPage"
+
+
+@pytest.mark.asyncio
+@pytest.mark.with_interventions
+async def test_enabled(client):
+ await client.navigate(URL, wait="complete")
+ assert not client.find_css(BAD_CSS)
+
+
+@pytest.mark.asyncio
+@pytest.mark.without_interventions
+async def test_disabled(client):
+ await client.navigate(URL, wait="complete")
+ assert client.find_css(BAD_CSS)
diff --git a/testing/webcompat/interventions/tests/test_1836141_yabbycasino_com.py b/testing/webcompat/interventions/tests/test_1836141_yabbycasino_com.py
new file mode 100644
index 0000000000..bcf6de192d
--- /dev/null
+++ b/testing/webcompat/interventions/tests/test_1836141_yabbycasino_com.py
@@ -0,0 +1,24 @@
+import pytest
+
+URL = "https://yabbycasino.com/game/aces-and-eights"
+IFRAME_CSS = "iframe#game"
+GOOD_CSS = "#rotateDevice"
+BAD_CSS = ".unsupported-device-box"
+
+
+@pytest.mark.only_platforms("android")
+@pytest.mark.asyncio
+@pytest.mark.with_interventions
+async def test_enabled(client):
+ await client.navigate(URL)
+ client.switch_to_frame(client.await_css(IFRAME_CSS))
+ assert client.await_css(GOOD_CSS, is_displayed=True)
+
+
+@pytest.mark.only_platforms("android")
+@pytest.mark.asyncio
+@pytest.mark.without_interventions
+async def test_disabled(client):
+ await client.navigate(URL)
+ client.switch_to_frame(client.await_css(IFRAME_CSS))
+ assert client.await_css(BAD_CSS, is_displayed=True)
diff --git a/testing/webcompat/interventions/tests/test_1836157_thai_masszazs_net.py b/testing/webcompat/interventions/tests/test_1836157_thai_masszazs_net.py
new file mode 100644
index 0000000000..66f25283fd
--- /dev/null
+++ b/testing/webcompat/interventions/tests/test_1836157_thai_masszazs_net.py
@@ -0,0 +1,26 @@
+import pytest
+
+URL = "https://www.thai-masszazs.net/"
+
+
+async def is_scrolling_broken(client):
+ await client.navigate(URL)
+ return client.execute_script(
+ """
+ return document.querySelector("html").style.overflow == "hidden"
+ """
+ )
+
+
+@pytest.mark.only_platforms("android")
+@pytest.mark.asyncio
+@pytest.mark.with_interventions
+async def test_enabled(client):
+ assert not await is_scrolling_broken(client)
+
+
+@pytest.mark.only_platforms("android")
+@pytest.mark.asyncio
+@pytest.mark.without_interventions
+async def test_disabled(client):
+ assert await is_scrolling_broken(client)
diff --git a/testing/webcompat/interventions/tests/test_1836178_atracker_pro.py b/testing/webcompat/interventions/tests/test_1836178_atracker_pro.py
new file mode 100644
index 0000000000..78dbbaf1dd
--- /dev/null
+++ b/testing/webcompat/interventions/tests/test_1836178_atracker_pro.py
@@ -0,0 +1,23 @@
+import pytest
+
+URL = "https://atracker.pro/"
+LOGIN_CSS = "a[onclick='openWebVersionWindow()']"
+
+
+async def get_login_popup_url(client, popup_url):
+ popup = await client.await_popup(popup_url)
+ await client.navigate(URL, wait="load")
+ client.soft_click(client.await_css(LOGIN_CSS))
+ return await popup
+
+
+@pytest.mark.asyncio
+@pytest.mark.with_interventions
+async def test_enabled(client):
+ assert await get_login_popup_url(client, "index.html")
+
+
+@pytest.mark.asyncio
+@pytest.mark.without_interventions
+async def test_disabled(client):
+ assert await get_login_popup_url(client, "BrowserCheck")
diff --git a/testing/webcompat/interventions/tests/test_1836182_www_flatsatshadowglen_com.py b/testing/webcompat/interventions/tests/test_1836182_www_flatsatshadowglen_com.py
new file mode 100644
index 0000000000..069167092f
--- /dev/null
+++ b/testing/webcompat/interventions/tests/test_1836182_www_flatsatshadowglen_com.py
@@ -0,0 +1,18 @@
+import pytest
+
+URL = "https://apply.flatsatshadowglen.com/manor/the-flats-at-shadowglen/"
+UNSUPPORTED_CSS = "[aria-label='Your browser is not supported']"
+
+
+@pytest.mark.asyncio
+@pytest.mark.with_interventions
+async def test_enabled(client):
+ await client.navigate(URL)
+ assert not client.find_css(UNSUPPORTED_CSS)
+
+
+@pytest.mark.asyncio
+@pytest.mark.without_interventions
+async def test_disabled(client):
+ await client.navigate(URL)
+ assert client.find_css(UNSUPPORTED_CSS)
diff --git a/testing/webcompat/interventions/tests/test_1848711_vio_com.py b/testing/webcompat/interventions/tests/test_1848711_vio_com.py
new file mode 100644
index 0000000000..7d571887d8
--- /dev/null
+++ b/testing/webcompat/interventions/tests/test_1848711_vio_com.py
@@ -0,0 +1,43 @@
+import time
+from datetime import datetime, timedelta
+
+import pytest
+
+today = datetime.now()
+three_months = today + timedelta(days=90)
+plus_week = three_months + timedelta(days=7)
+formatted_start = three_months.strftime("%Y-%m-%d")
+formatted_end = plus_week.strftime("%Y-%m-%d")
+
+URL = f"https://www.vio.com/Hotel/Search?hotelId=17293575&checkIn={formatted_start}&checkOut={formatted_end}&rooms=2&homeSearch=1&userSearch=1&layout=map"
+MAP_CSS = ".mapboxgl-map"
+MIN_HEIGHT = 100
+
+
+def get_elem_height(client):
+ elem = client.await_css(MAP_CSS, is_displayed=True)
+ assert elem
+ return client.execute_script(
+ """
+ return arguments[0].getBoundingClientRect().height;
+ """,
+ elem,
+ )
+
+
+@pytest.mark.only_platforms("android")
+@pytest.mark.asyncio
+@pytest.mark.with_interventions
+async def test_enabled(client):
+ time.sleep(3)
+ await client.navigate(URL)
+ assert get_elem_height(client) > MIN_HEIGHT
+
+
+@pytest.mark.only_platforms("android")
+@pytest.mark.asyncio
+@pytest.mark.without_interventions
+async def test_disabled(client):
+ time.sleep(3)
+ await client.navigate(URL)
+ assert get_elem_height(client) < MIN_HEIGHT
diff --git a/testing/webcompat/interventions/tests/test_1848716_elal_com.py b/testing/webcompat/interventions/tests/test_1848716_elal_com.py
new file mode 100644
index 0000000000..835fb67b18
--- /dev/null
+++ b/testing/webcompat/interventions/tests/test_1848716_elal_com.py
@@ -0,0 +1,22 @@
+import pytest
+
+URL = "https://www.elal.com/he/Israel/Pages/default.aspx"
+UNSUPPORTED_CSS = "#upgrade-browser-wrap"
+
+
+@pytest.mark.skip_platforms("android")
+@pytest.mark.asyncio
+@pytest.mark.with_interventions
+async def test_enabled(client):
+ await client.navigate(URL)
+ unsupported = client.await_css(UNSUPPORTED_CSS)
+ assert not client.is_displayed(unsupported)
+
+
+@pytest.mark.skip_platforms("android")
+@pytest.mark.asyncio
+@pytest.mark.without_interventions
+async def test_disabled(client):
+ await client.navigate(URL)
+ unsupported = client.await_css(UNSUPPORTED_CSS)
+ assert client.is_displayed(unsupported)
diff --git a/testing/webcompat/interventions/tests/test_1849018_carefirst_com.py b/testing/webcompat/interventions/tests/test_1849018_carefirst_com.py
new file mode 100644
index 0000000000..9ac3131866
--- /dev/null
+++ b/testing/webcompat/interventions/tests/test_1849018_carefirst_com.py
@@ -0,0 +1,19 @@
+import pytest
+
+URL = "https://www.carefirst.com/myaccount"
+BLOCKED_TEXT = "Application Blocked"
+UNBLOCKED_CSS = "#maincontent"
+
+
+@pytest.mark.asyncio
+@pytest.mark.with_interventions
+async def test_enabled(client):
+ await client.navigate(URL)
+ assert client.await_css(UNBLOCKED_CSS)
+
+
+@pytest.mark.asyncio
+@pytest.mark.without_interventions
+async def test_disabled(client):
+ await client.navigate(URL)
+ assert client.await_text(BLOCKED_TEXT)
diff --git a/testing/webcompat/interventions/tests/test_1849019_123068_axa_assistance_pl.py b/testing/webcompat/interventions/tests/test_1849019_123068_axa_assistance_pl.py
new file mode 100644
index 0000000000..2caaf50cfc
--- /dev/null
+++ b/testing/webcompat/interventions/tests/test_1849019_123068_axa_assistance_pl.py
@@ -0,0 +1,39 @@
+import pytest
+from webdriver.error import NoSuchElementException
+
+URL = "https://www.axa-assistance.pl/ubezpieczenie-turystyczne/?externalpartner=13100009&gclid=Cj0KCQjw4NujBhC5ARIsAF4Iv6eUnfuIEl9YkvO6pXP-I8g0ImynOMqpS7eMdBhyhjOj7G4eZzfSr_oaAnEUEALw_wcB#"
+DATE_CSS = ".hiddenDate"
+COOKIES_CSS = "#onetrust-accept-btn-handler"
+MIN_WIDTH = 100
+
+
+def get_elem_width(client):
+ try:
+ client.soft_click(client.await_css(COOKIES_CSS))
+ client.await_element_hidden(client.css(COOKIES_CSS))
+ except NoSuchElementException:
+ pass
+
+ elem = client.await_css(DATE_CSS)
+ return client.execute_script(
+ """
+ return arguments[0].getBoundingClientRect().width;
+ """,
+ elem,
+ )
+
+
+@pytest.mark.only_platforms("android")
+@pytest.mark.asyncio
+@pytest.mark.with_interventions
+async def test_enabled(client):
+ await client.navigate(URL)
+ assert get_elem_width(client) > MIN_WIDTH
+
+
+@pytest.mark.only_platforms("android")
+@pytest.mark.asyncio
+@pytest.mark.without_interventions
+async def test_disabled(client):
+ await client.navigate(URL)
+ assert get_elem_width(client) < MIN_WIDTH
diff --git a/testing/webcompat/interventions/tests/test_1849029_publi24_ro.py b/testing/webcompat/interventions/tests/test_1849029_publi24_ro.py
new file mode 100644
index 0000000000..317f1bb0ff
--- /dev/null
+++ b/testing/webcompat/interventions/tests/test_1849029_publi24_ro.py
@@ -0,0 +1,28 @@
+import pytest
+
+URL = "https://www.publi24.ro/anunturi/imobiliare/"
+MOBILE_CSS = ".filter.mobile"
+DESKTOP_CSS = ".filter.desktop"
+BLOCKED_CSS = "[data-translate='block_headline']"
+
+
+async def load_page(client):
+ await client.navigate(URL, wait="complete")
+ if client.find_css(BLOCKED_CSS):
+ pytest.xfail("Site has blocked us, please test manually")
+
+
+@pytest.mark.only_platforms("android")
+@pytest.mark.asyncio
+@pytest.mark.with_interventions
+async def test_enabled(client):
+ await load_page(client)
+ assert client.await_css(MOBILE_CSS)
+
+
+@pytest.mark.only_platforms("android")
+@pytest.mark.asyncio
+@pytest.mark.without_interventions
+async def test_disabled(client):
+ await load_page(client)
+ assert client.await_css(DESKTOP_CSS)
diff --git a/testing/webcompat/interventions/tests/test_1849058_nicochannel_jp.py b/testing/webcompat/interventions/tests/test_1849058_nicochannel_jp.py
new file mode 100644
index 0000000000..ba66992c7a
--- /dev/null
+++ b/testing/webcompat/interventions/tests/test_1849058_nicochannel_jp.py
@@ -0,0 +1,19 @@
+import pytest
+
+URL = "https://nicochannel.jp/engage-kiss/video/smBAc4UhdTUivpbuzBexSa9d"
+BLOCKED_TEXT = "このブラウザはサポートされていません。"
+PLAY_BUTTON_CSS = ".nfcp-overlay-play-lg"
+
+
+@pytest.mark.asyncio
+@pytest.mark.with_interventions
+async def test_enabled(client):
+ await client.navigate(URL)
+ assert client.await_css(PLAY_BUTTON_CSS, timeout=30)
+
+
+@pytest.mark.asyncio
+@pytest.mark.without_interventions
+async def test_disabled(client):
+ await client.navigate(URL)
+ assert client.await_text(BLOCKED_TEXT, timeout=20)
diff --git a/testing/webcompat/interventions/tests/test_1855014_77221_eksiseyler_com.py b/testing/webcompat/interventions/tests/test_1855014_77221_eksiseyler_com.py
new file mode 100644
index 0000000000..9f615136bc
--- /dev/null
+++ b/testing/webcompat/interventions/tests/test_1855014_77221_eksiseyler_com.py
@@ -0,0 +1,21 @@
+import pytest
+
+URL = "https://eksiseyler.com/evrimin-kisa-surede-de-yasanabilecegini-kanitlayan-1971-hirvatistan-kertenkele-deneyi"
+IMAGE_CSS = ".content-heading .cover-img img"
+ERROR_MSG = "loggingEnabled is not defined"
+
+
+@pytest.mark.only_platforms("android")
+@pytest.mark.asyncio
+@pytest.mark.with_interventions
+async def test_enabled(client):
+ await client.navigate(URL)
+ client.await_css(IMAGE_CSS, condition="!elem.src.includes('placeholder')")
+
+
+@pytest.mark.only_platforms("android")
+@pytest.mark.asyncio
+@pytest.mark.without_interventions
+async def test_disabled(client):
+ await client.navigate(URL, await_console_message=ERROR_MSG)
+ client.await_css(IMAGE_CSS, condition="elem.src.includes('placeholder')")
diff --git a/testing/webcompat/interventions/tests/test_1855071_121197_www_meteoam_it.py b/testing/webcompat/interventions/tests/test_1855071_121197_www_meteoam_it.py
new file mode 100644
index 0000000000..1a5a3c37ff
--- /dev/null
+++ b/testing/webcompat/interventions/tests/test_1855071_121197_www_meteoam_it.py
@@ -0,0 +1,36 @@
+import time
+
+import pytest
+
+URL = "https://www.meteoam.it/it/home"
+IFRAME_CSS = "#iframe_map"
+SEARCH_CSS = "#mobile_rightpanel .barbtnsearch"
+INPUT_CSS = "#search_bar_txt"
+
+
+def getWindowHeight(client):
+ return client.execute_script("return window.outerHeight")
+
+
+async def doesHeightChange(client):
+ await client.navigate(URL)
+ client.switch_to_frame(client.await_css(IFRAME_CSS))
+ initialHeight = getWindowHeight(client)
+ client.await_css(SEARCH_CSS).click()
+ client.await_css(INPUT_CSS, is_displayed=True).click()
+ time.sleep(2)
+ return getWindowHeight(client) != initialHeight
+
+
+@pytest.mark.only_platforms("android")
+@pytest.mark.asyncio
+@pytest.mark.with_interventions
+async def test_enabled(client):
+ assert await doesHeightChange(client)
+
+
+@pytest.mark.only_platforms("android")
+@pytest.mark.asyncio
+@pytest.mark.without_interventions
+async def test_disabled(client):
+ assert not await doesHeightChange(client)
diff --git a/testing/webcompat/interventions/tests/test_1855088_125039_hrmis2_eghrmis_gov_my.py b/testing/webcompat/interventions/tests/test_1855088_125039_hrmis2_eghrmis_gov_my.py
new file mode 100644
index 0000000000..33f0193b9a
--- /dev/null
+++ b/testing/webcompat/interventions/tests/test_1855088_125039_hrmis2_eghrmis_gov_my.py
@@ -0,0 +1,25 @@
+import time
+
+import pytest
+
+URL = "https://hrmis2.eghrmis.gov.my/HRMISNET/Common/Main/Login.aspx"
+DIALOG_CSS = "#dialog4"
+
+
+async def is_dialog_shown(client):
+ await client.navigate(URL)
+ time.sleep(2)
+ dialog = client.await_css(DIALOG_CSS)
+ return client.is_displayed(dialog)
+
+
+@pytest.mark.asyncio
+@pytest.mark.with_interventions
+async def test_enabled(client):
+ assert not await is_dialog_shown(client)
+
+
+@pytest.mark.asyncio
+@pytest.mark.without_interventions
+async def test_disabled(client):
+ assert await is_dialog_shown(client)
diff --git a/testing/webcompat/interventions/tests/test_1855102_121877_my_southerncross_co_nz.py b/testing/webcompat/interventions/tests/test_1855102_121877_my_southerncross_co_nz.py
new file mode 100644
index 0000000000..00a17883f3
--- /dev/null
+++ b/testing/webcompat/interventions/tests/test_1855102_121877_my_southerncross_co_nz.py
@@ -0,0 +1,23 @@
+import pytest
+
+URL = "https://my.southerncross.co.nz/browsernotsupported"
+SUPPORTED_CSS = "svg.animated-success"
+UNSUPPORTED_CSS = "svg.animated-failure"
+
+
+@pytest.mark.only_platforms("android")
+@pytest.mark.asyncio
+@pytest.mark.with_interventions
+async def test_enabled(client):
+ await client.navigate(URL)
+ assert client.await_css(SUPPORTED_CSS)
+ assert not client.find_css(UNSUPPORTED_CSS)
+
+
+@pytest.mark.only_platforms("android")
+@pytest.mark.asyncio
+@pytest.mark.without_interventions
+async def test_disabled(client):
+ await client.navigate(URL)
+ assert client.await_css(SUPPORTED_CSS)
+ assert client.find_css(UNSUPPORTED_CSS)
diff --git a/testing/webcompat/interventions/tests/test_969844_mobile_de.py b/testing/webcompat/interventions/tests/test_969844_mobile_de.py
new file mode 100644
index 0000000000..ddde96e0d4
--- /dev/null
+++ b/testing/webcompat/interventions/tests/test_969844_mobile_de.py
@@ -0,0 +1,27 @@
+import pytest
+
+# The site serves a different mobile page to Firefox than Chrome
+
+URL = "https://www.mobile.de/"
+COOKIES_CSS = "button.mde-consent-accept-btn"
+MOBILE_MENU_CSS = "header [data-testid='mobile-navigation-menu']"
+
+
+@pytest.mark.only_platforms("android")
+@pytest.mark.asyncio
+@pytest.mark.with_interventions
+async def test_enabled(client):
+ await client.navigate(URL)
+ client.soft_click(client.await_css(COOKIES_CSS))
+ mobile_menu = client.await_css(MOBILE_MENU_CSS)
+ assert client.is_displayed(mobile_menu)
+
+
+@pytest.mark.only_platforms("android")
+@pytest.mark.asyncio
+@pytest.mark.without_interventions
+async def test_disabled(client):
+ await client.navigate(URL)
+ client.soft_click(client.await_css(COOKIES_CSS))
+ mobile_menu = client.await_css(MOBILE_MENU_CSS)
+ assert not client.is_displayed(mobile_menu)
diff --git a/testing/webcompat/mach_commands.py b/testing/webcompat/mach_commands.py
new file mode 100644
index 0000000000..d67caadaec
--- /dev/null
+++ b/testing/webcompat/mach_commands.py
@@ -0,0 +1,338 @@
+# 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 argparse
+import io
+import os
+import platform
+import sys
+
+from mach.decorators import Command
+from mozbuild.base import MozbuildObject
+
+here = os.path.abspath(os.path.dirname(__file__))
+
+GVE = "org.mozilla.geckoview_example"
+
+
+def get(url):
+ import requests
+
+ resp = requests.get(url)
+ resp.raise_for_status()
+ return resp
+
+
+def untar(fileobj, dest):
+ import tarfile
+
+ with tarfile.open(fileobj=fileobj, mode="r") as tar_data:
+ tar_data.extractall(path=dest)
+
+
+def unzip(fileobj, dest):
+ import zipfile
+
+ with zipfile.ZipFile(fileobj) as zip_data:
+ zip_data.extractall(path=dest)
+
+
+def create_parser_interventions():
+ from mozlog import commandline
+
+ parser = argparse.ArgumentParser()
+ parser.add_argument("--webdriver-binary", help="Path to webdriver binary")
+ parser.add_argument(
+ "--webdriver-port",
+ action="store",
+ default="4444",
+ help="Port on which to run WebDriver",
+ )
+ parser.add_argument(
+ "--webdriver-ws-port",
+ action="store",
+ default="9222",
+ help="Port on which to run WebDriver BiDi websocket",
+ )
+ parser.add_argument("--bug", help="Bug to run tests for")
+ parser.add_argument(
+ "--do2fa",
+ action="store_true",
+ default=False,
+ help="Do two-factor auth live in supporting tests",
+ )
+ parser.add_argument(
+ "--config", help="Path to JSON file containing logins and other settings"
+ )
+ parser.add_argument(
+ "--debug", action="store_true", default=False, help="Debug failing tests"
+ )
+ parser.add_argument(
+ "--headless",
+ action="store_true",
+ default=False,
+ help="Run firefox in headless mode",
+ )
+ parser.add_argument(
+ "--interventions",
+ action="store",
+ default="both",
+ choices=["enabled", "disabled", "both", "none"],
+ help="Enable webcompat interventions",
+ )
+ parser.add_argument(
+ "--shims",
+ action="store",
+ default="none",
+ choices=["enabled", "disabled", "both", "none"],
+ help="Enable SmartBlock shims",
+ )
+ parser.add_argument(
+ "--platform",
+ action="store",
+ choices=["android", "desktop"],
+ help="Platform to target",
+ )
+
+ desktop_group = parser.add_argument_group("Desktop-specific arguments")
+ desktop_group.add_argument("--binary", help="Path to browser binary")
+
+ android_group = parser.add_argument_group("Android-specific arguments")
+ android_group.add_argument(
+ "--device-serial",
+ action="store",
+ help="Running Android instances to connect to, if not emulator-5554",
+ )
+ android_group.add_argument(
+ "--package-name",
+ action="store",
+ default=GVE,
+ help="Android package name to use",
+ )
+
+ commandline.add_logging_group(parser)
+ return parser
+
+
+class InterventionTest(MozbuildObject):
+ def set_default_kwargs(self, logger, command_context, kwargs):
+ platform = kwargs["platform"]
+ binary = kwargs["binary"]
+ device_serial = kwargs["device_serial"]
+ is_gve_build = command_context.substs.get("MOZ_APP_NAME") == "fennec"
+
+ if platform == "android" or (
+ platform is None and binary is None and (device_serial or is_gve_build)
+ ):
+ kwargs["platform"] = "android"
+ else:
+ kwargs["platform"] = "desktop"
+
+ if kwargs["platform"] == "desktop" and kwargs["binary"] is None:
+ kwargs["binary"] = self.get_binary_path()
+
+ if kwargs["webdriver_binary"] is None:
+ webdriver_binary = self.get_binary_path(
+ "geckodriver", validate_exists=False
+ )
+
+ if not os.path.exists(webdriver_binary):
+ webdriver_binary = self.install_geckodriver(
+ logger, dest=os.path.dirname(webdriver_binary)
+ )
+
+ if not os.path.exists(webdriver_binary):
+ logger.error("Can't find geckodriver")
+ sys.exit(1)
+ kwargs["webdriver_binary"] = webdriver_binary
+
+ def platform_string_geckodriver(self):
+ uname = platform.uname()
+ platform_name = {"Linux": "linux", "Windows": "win", "Darwin": "macos"}.get(
+ uname[0]
+ )
+
+ if platform_name in ("linux", "win"):
+ bits = "64" if uname[4] == "x86_64" else "32"
+ elif platform_name == "macos":
+ bits = ""
+ else:
+ raise ValueError(f"No precompiled geckodriver for platform {uname}")
+
+ return f"{platform_name}{bits}"
+
+ def install_geckodriver(self, logger, dest):
+ """Install latest Geckodriver."""
+ if dest is None:
+ dest = os.path.join(self.distdir, "dist", "bin")
+
+ is_windows = platform.uname()[0] == "Windows"
+
+ release = get(
+ "https://api.github.com/repos/mozilla/geckodriver/releases/latest"
+ ).json()
+ ext = "zip" if is_windows else "tar.gz"
+ platform_name = self.platform_string_geckodriver()
+ name_suffix = f"-{platform_name}.{ext}"
+ for item in release["assets"]:
+ if item["name"].endswith(name_suffix):
+ url = item["browser_download_url"]
+ break
+ else:
+ raise ValueError(f"Failed to find geckodriver for platform {platform_name}")
+
+ logger.info(f"Installing geckodriver from {url}")
+
+ data = io.BytesIO(get(url).content)
+ data.seek(0)
+ decompress = unzip if ext == "zip" else untar
+ decompress(data, dest=dest)
+
+ exe_ext = ".exe" if is_windows else ""
+ path = os.path.join(dest, f"geckodriver{exe_ext}")
+
+ return path
+
+ def setup_device(self, command_context, kwargs):
+ if kwargs["platform"] != "android":
+ return
+
+ app = kwargs["package_name"]
+ device_serial = kwargs["device_serial"]
+
+ if not device_serial:
+ from mozrunner.devices.android_device import (
+ InstallIntent,
+ verify_android_device,
+ )
+
+ verify_android_device(
+ command_context, app=app, network=True, install=InstallIntent.YES
+ )
+
+ kwargs["device_serial"] = os.environ.get("DEVICE_SERIAL")
+
+ # GVE does not have the webcompat addon by default. Add it.
+ if app == GVE:
+ kwargs["addon"] = "/data/local/tmp/webcompat.xpi"
+ push_to_device(
+ command_context.substs["ADB"],
+ device_serial,
+ webcompat_addon(command_context),
+ kwargs["addon"],
+ )
+
+ def run(self, command_context, **kwargs):
+ import mozlog
+ import runner
+
+ mozlog.commandline.setup_logging(
+ "test-interventions", kwargs, {"mach": sys.stdout}
+ )
+ logger = mozlog.get_default_logger("test-interventions")
+ status_handler = mozlog.handlers.StatusHandler()
+ logger.add_handler(status_handler)
+
+ self.set_default_kwargs(logger, command_context, kwargs)
+
+ self.setup_device(command_context, kwargs)
+
+ if kwargs["interventions"] != "none":
+ interventions = (
+ ["enabled", "disabled"]
+ if kwargs["interventions"] == "both"
+ else [kwargs["interventions"]]
+ )
+
+ for interventions_setting in interventions:
+ runner.run(
+ logger,
+ os.path.join(here, "interventions"),
+ kwargs["webdriver_binary"],
+ kwargs["webdriver_port"],
+ kwargs["webdriver_ws_port"],
+ browser_binary=kwargs.get("binary"),
+ device_serial=kwargs.get("device_serial"),
+ package_name=kwargs.get("package_name"),
+ addon=kwargs.get("addon"),
+ bug=kwargs["bug"],
+ debug=kwargs["debug"],
+ interventions=interventions_setting,
+ config=kwargs["config"],
+ headless=kwargs["headless"],
+ do2fa=kwargs["do2fa"],
+ )
+
+ if kwargs["shims"] != "none":
+ shims = (
+ ["enabled", "disabled"]
+ if kwargs["shims"] == "both"
+ else [kwargs["shims"]]
+ )
+
+ for shims_setting in shims:
+ runner.run(
+ logger,
+ os.path.join(here, "shims"),
+ kwargs["webdriver_binary"],
+ kwargs["webdriver_port"],
+ kwargs["webdriver_ws_port"],
+ browser_binary=kwargs.get("binary"),
+ device_serial=kwargs.get("device_serial"),
+ package_name=kwargs.get("package_name"),
+ addon=kwargs.get("addon"),
+ bug=kwargs["bug"],
+ debug=kwargs["debug"],
+ shims=shims_setting,
+ config=kwargs["config"],
+ headless=kwargs["headless"],
+ do2fa=kwargs["do2fa"],
+ )
+
+ summary = status_handler.summarize()
+ passed = (
+ summary.unexpected_statuses == 0
+ and summary.log_level_counts.get("ERROR", 0) == 0
+ and summary.log_level_counts.get("CRITICAL", 0) == 0
+ )
+ return passed
+
+
+def webcompat_addon(command_context):
+ import shutil
+
+ src = os.path.join(command_context.topsrcdir, "browser", "extensions", "webcompat")
+ dst = os.path.join(
+ command_context.virtualenv_manager.virtualenv_root, "webcompat.xpi"
+ )
+ shutil.make_archive(dst, "zip", src)
+ shutil.move(f"{dst}.zip", dst)
+ return dst
+
+
+def push_to_device(adb_path, device_serial, local_path, remote_path):
+ from mozdevice import ADBDeviceFactory
+
+ device = ADBDeviceFactory(adb=adb_path, device=device_serial)
+ device.push(local_path, remote_path)
+ device.chmod(remote_path)
+
+
+@Command(
+ "test-interventions",
+ category="testing",
+ description="Test the webcompat interventions",
+ parser=create_parser_interventions,
+ virtualenv_name="webcompat",
+)
+def test_interventions(command_context, **params):
+ here = os.path.abspath(os.path.dirname(__file__))
+ command_context.virtualenv_manager.activate()
+ command_context.virtualenv_manager.install_pip_requirements(
+ os.path.join(here, "requirements.txt"),
+ require_hashes=False,
+ )
+
+ intervention_test = command_context._spawn(InterventionTest)
+ return 0 if intervention_test.run(command_context, **params) else 1
diff --git a/testing/webcompat/requirements.txt b/testing/webcompat/requirements.txt
new file mode 100644
index 0000000000..1e5f90af3c
--- /dev/null
+++ b/testing/webcompat/requirements.txt
@@ -0,0 +1,3 @@
+asyncio==3.4.3
+pytest-asyncio==0.16.0
+urllib3==1.26
diff --git a/testing/webcompat/runner.py b/testing/webcompat/runner.py
new file mode 100644
index 0000000000..fefef7bf70
--- /dev/null
+++ b/testing/webcompat/runner.py
@@ -0,0 +1,263 @@
+# 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 errno
+import os
+import shutil
+import tempfile
+
+import pytest
+
+GVE = "org.mozilla.geckoview_example"
+
+
+def run(
+ logger,
+ path,
+ webdriver_binary,
+ webdriver_port,
+ webdriver_ws_port,
+ browser_binary=None,
+ device_serial=None,
+ package_name=None,
+ environ=None,
+ bug=None,
+ debug=False,
+ interventions=None,
+ shims=None,
+ config=None,
+ headless=False,
+ addon=None,
+ do2fa=False,
+):
+ """"""
+ old_environ = os.environ.copy()
+ try:
+ with TemporaryDirectory() as cache:
+ if environ:
+ os.environ.update(environ)
+
+ config_plugin = WDConfig()
+ result_recorder = ResultRecorder(logger)
+
+ args = [
+ "--strict", # turn warnings into errors
+ "-vv", # show each individual subtest and full failure logs
+ "--capture",
+ "no", # enable stdout/stderr from tests
+ "--basetemp",
+ cache, # temporary directory
+ "--showlocals", # display contents of variables in local scope
+ "-p",
+ "no:mozlog", # use the WPT result recorder
+ "--disable-warnings",
+ "-rfEs",
+ "-p",
+ "no:cacheprovider", # disable state preservation across invocations
+ "-o=console_output_style=classic", # disable test progress bar
+ "--browser",
+ "firefox",
+ "--webdriver-binary",
+ webdriver_binary,
+ "--webdriver-port",
+ webdriver_port,
+ "--webdriver-ws-port",
+ webdriver_ws_port,
+ ]
+
+ if debug:
+ args.append("--pdb")
+
+ if headless:
+ args.append("--headless")
+
+ if browser_binary:
+ args.append("--browser-binary")
+ args.append(browser_binary)
+
+ if device_serial:
+ args.append("--device-serial")
+ args.append(device_serial)
+
+ if package_name:
+ args.append("--package-name")
+ args.append(package_name)
+
+ if addon:
+ args.append("--addon")
+ args.append(addon)
+
+ if bug:
+ args.append("--bug")
+ args.append(bug)
+
+ if do2fa:
+ args.append("--do2fa")
+
+ if config:
+ args.append("--config")
+ args.append(config)
+
+ if interventions is not None and shims is not None:
+ raise ValueError(
+ "Must provide only one of interventions or shims argument"
+ )
+ elif interventions is None and shims is None:
+ raise ValueError(
+ "Must provide either an interventions or shims argument"
+ )
+
+ name = "webcompat-interventions"
+ if interventions == "enabled":
+ args.extend(["-m", "with_interventions"])
+ elif interventions == "disabled":
+ args.extend(["-m", "without_interventions"])
+ elif interventions is not None:
+ raise ValueError(f"Invalid value for interventions {interventions}")
+ if shims == "enabled":
+ args.extend(["-m", "with_shims"])
+ name = "smartblock-shims"
+ elif shims == "disabled":
+ args.extend(["-m", "without_shims"])
+ name = "smartblock-shims"
+ elif shims is not None:
+ raise ValueError(f"Invalid value for shims {shims}")
+ else:
+ name = "smartblock-shims"
+
+ if bug is not None:
+ args.extend(["-k", bug])
+
+ args.append(path)
+ try:
+ logger.suite_start([], name=name)
+ pytest.main(args, plugins=[config_plugin, result_recorder])
+ except Exception as e:
+ logger.critical(str(e))
+ finally:
+ logger.suite_end()
+
+ finally:
+ os.environ = old_environ
+
+
+class WDConfig:
+ def pytest_addoption(self, parser):
+ parser.addoption(
+ "--browser-binary", action="store", help="Path to browser binary"
+ )
+ parser.addoption(
+ "--webdriver-binary", action="store", help="Path to webdriver binary"
+ )
+ parser.addoption(
+ "--webdriver-port",
+ action="store",
+ default="4444",
+ help="Port on which to run WebDriver",
+ )
+ parser.addoption(
+ "--webdriver-ws-port",
+ action="store",
+ default="9222",
+ help="Port on which to run WebDriver BiDi websocket",
+ )
+ parser.addoption(
+ "--browser", action="store", choices=["firefox"], help="Name of the browser"
+ )
+ parser.addoption("--bug", action="store", help="Bug number to run tests for")
+ parser.addoption(
+ "--do2fa",
+ action="store_true",
+ default=False,
+ help="Do two-factor auth live in supporting tests",
+ )
+ parser.addoption(
+ "--config",
+ action="store",
+ help="Path to JSON file containing logins and other settings",
+ )
+ parser.addoption(
+ "--addon",
+ action="store",
+ help="Path to the webcompat addon XPI to use",
+ )
+ parser.addoption(
+ "--device-serial",
+ action="store",
+ help="Emulator device serial number",
+ )
+ parser.addoption(
+ "--package-name",
+ action="store",
+ default=GVE,
+ help="Android package to run/connect to",
+ )
+ parser.addoption(
+ "--headless", action="store_true", help="Run browser in headless mode"
+ )
+
+
+class ResultRecorder(object):
+ def __init__(self, logger):
+ self.logger = logger
+
+ def pytest_runtest_logreport(self, report):
+ if report.passed and report.when == "call":
+ self.record_pass(report)
+ elif report.failed:
+ if report.when != "call":
+ self.record_error(report)
+ else:
+ self.record_fail(report)
+ elif report.skipped:
+ self.record_skip(report)
+
+ def record_pass(self, report):
+ self.record(report.nodeid, "PASS")
+
+ def record_fail(self, report):
+ # pytest outputs the stacktrace followed by an error message prefixed
+ # with "E ", e.g.
+ #
+ # def test_example():
+ # > assert "fuu" in "foobar"
+ # > E AssertionError: assert 'fuu' in 'foobar'
+ message = ""
+ for line in report.longreprtext.splitlines():
+ if line.startswith("E "):
+ message = line[1:].strip()
+ break
+
+ self.record(report.nodeid, "FAIL", message=message, stack=report.longrepr)
+
+ def record_error(self, report):
+ # error in setup/teardown
+ if report.when != "call":
+ message = "%s error" % report.when
+ self.record(report.nodeid, "ERROR", message, report.longrepr)
+
+ def record_skip(self, report):
+ self.record(report.nodeid, "SKIP")
+
+ def record(self, test, status, message=None, stack=None):
+ if stack is not None:
+ stack = str(stack)
+ self.logger.test_start(test)
+ self.logger.test_end(
+ test=test, status=status, expected="PASS", message=message, stack=stack
+ )
+
+
+class TemporaryDirectory(object):
+ def __enter__(self):
+ self.path = tempfile.mkdtemp(prefix="pytest-")
+ return self.path
+
+ def __exit__(self, *args):
+ try:
+ shutil.rmtree(self.path)
+ except OSError as e:
+ # no such file or directory
+ if e.errno != errno.ENOENT:
+ raise
diff --git a/testing/webcompat/shims/__init__.py b/testing/webcompat/shims/__init__.py
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/testing/webcompat/shims/__init__.py
diff --git a/testing/webcompat/shims/conftest.py b/testing/webcompat/shims/conftest.py
new file mode 100644
index 0000000000..30807a1188
--- /dev/null
+++ b/testing/webcompat/shims/conftest.py
@@ -0,0 +1,68 @@
+# 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/.
+
+
+from ..fixtures import * # noqa: F403
+
+
+def pytest_generate_tests(metafunc):
+ """Generate tests based on markers."""
+
+ if "session" not in metafunc.fixturenames:
+ return
+
+ marks = [mark.name for mark in metafunc.function.pytestmark]
+
+ otherargs = {}
+ argvalues = []
+ ids = []
+
+ if "only_platforms" in marks:
+ for mark in metafunc.function.pytestmark:
+ if mark.name == "only_platforms":
+ otherargs["only_platforms"] = mark.args
+
+ if "skip_platforms" in marks:
+ for mark in metafunc.function.pytestmark:
+ if mark.name == "skip_platforms":
+ otherargs["skip_platforms"] = mark.args
+
+ if "with_private_browsing" in marks:
+ otherargs["with_private_browsing"] = True
+ if "with_strict_etp" in marks:
+ otherargs["with_strict_etp"] = True
+ if "without_storage_partitioning" in marks:
+ otherargs["without_storage_partitioning"] = True
+ if "without_tcp " in marks:
+ otherargs["without_tcp "] = True
+
+ if "with_shims" in marks:
+ argvalues.append([dict({"shims": True}, **otherargs)])
+ ids.append("with_shims")
+
+ if "without_shims" in marks:
+ argvalues.append([dict({"shims": False}, **otherargs)])
+ ids.append("without_shims")
+
+ metafunc.parametrize(["session"], argvalues, ids=ids, indirect=True)
+
+
+@pytest.fixture(scope="function") # noqa: F405
+async def test_config(request, driver):
+ params = request.node.callspec.params.get("session")
+
+ use_shims = params.get("shims")
+ if use_shims is None:
+ raise ValueError(
+ "Missing shims marker in %s:%s"
+ % (request.fspath, request.function.__name__)
+ )
+
+ return {
+ "aps": not params.get("without_storage_partitioning", False),
+ "use_pbm": params.get("with_private_browsing", False),
+ "use_shims": use_shims,
+ "use_strict_etp": params.get("with_strict_etp", False),
+ "without_tcp": params.get("without_tcp", False),
+ }
diff --git a/testing/webcompat/shims/pytest.ini b/testing/webcompat/shims/pytest.ini
new file mode 100644
index 0000000000..6f1f05efd9
--- /dev/null
+++ b/testing/webcompat/shims/pytest.ini
@@ -0,0 +1,11 @@
+[pytest]
+console_output_style = classic
+markers =
+ only_platforms: only run tests on specific platforms (mac, linux, windows, android)
+ skip_platforms: skip tests on specific platforms (mac, linux, windows, android)
+ with_shims: enable web-compat shims
+ without_shims: disable web-compat shims
+ without_storage_partitioning: disable partitioning of non-cookie third-party web storage
+ with_private_browsing: run test in a private browsing window
+ with_strict_etp: enable strict ETP mode
+ without_tcp: disable Total Cookie Protection
diff --git a/testing/webcompat/shims/tests/__init__.py b/testing/webcompat/shims/tests/__init__.py
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/testing/webcompat/shims/tests/__init__.py
diff --git a/testing/webcompat/shims/tests/common.py b/testing/webcompat/shims/tests/common.py
new file mode 100644
index 0000000000..9a822e9e83
--- /dev/null
+++ b/testing/webcompat/shims/tests/common.py
@@ -0,0 +1,84 @@
+GPT_SHIM_MSG = "Google Publisher Tags is being shimmed by Firefox"
+GAGTA_SHIM_MSG = "Google Analytics and Tag Manager is being shimmed by Firefox"
+
+
+async def is_gtag_placeholder_displayed(client, url, finder, **kwargs):
+ await client.navigate(url, **kwargs)
+ client.execute_async_script(
+ """
+ const done = arguments[0];
+ if (window.dataLayer?.push?.toString() === [].push.toString()) {
+ return done();
+ }
+ setTimeout(() => {
+ dataLayer.push({
+ event: "datalayerReady",
+ eventTimeout: 1,
+ eventCallback: done,
+ });
+ }, 100);
+ """
+ )
+ return client.is_displayed(client.find_element(finder))
+
+
+async def clicking_link_navigates(client, url, finder, **kwargs):
+ await client.navigate(url, **kwargs)
+ elem = client.await_element(finder)
+ return client.session.execute_async_script(
+ """
+ const elem = arguments[0],
+ done = arguments[1];
+ window.onbeforeunload = function() {
+ done(true);
+ };
+ elem.click();
+ setTimeout(() => {
+ done(false);
+ }, 1000);
+ """,
+ args=[elem],
+ )
+
+
+async def verify_redirectors(client, urls, expected="REDIRECTED"):
+ await client.navigate(client.inline("<html>"))
+ for url, type in urls.items():
+ assert expected == client.execute_async_script(
+ """
+ const [url, type, resolve] = arguments;
+ fetch(url).then(async response => {
+ if (!response.ok) {
+ return resolve("FAILED");
+ }
+
+ try {
+ if (type === "image") {
+ const blob = await response.blob();
+ const url = URL.createObjectURL(blob);
+ const img = new Image(1, 1);
+ await new Promise((res, rej) => {
+ img.onerror = rej;
+ img.onload = res;
+ img.src = url;
+ });
+ } else if (type === "js") {
+ const text = await response.text();
+ if (!text.includes("This script is intentionally empty")) {
+ throw "";
+ }
+ } else {
+ return resolve("UNKNOWN TYPE");
+ }
+ } catch(_) {
+ return resolve("TYPE MISMATCH");
+ }
+
+ resolve(response.redirected ? "REDIRECTED" : "LOADED");
+ }).catch(_ => {
+ resolve("BLOCKED");
+ });
+ """,
+ url,
+ type,
+ )
diff --git a/testing/webcompat/shims/tests/test_1624914_google_trends.py b/testing/webcompat/shims/tests/test_1624914_google_trends.py
new file mode 100644
index 0000000000..1a11713cdc
--- /dev/null
+++ b/testing/webcompat/shims/tests/test_1624914_google_trends.py
@@ -0,0 +1,29 @@
+import pytest
+
+URL = "https://knowyourmeme.com/memes/awesome-face-epic-smiley"
+IFRAME = "iframe#trends-widget-1"
+
+
+@pytest.mark.skip_platforms("android")
+@pytest.mark.asyncio
+@pytest.mark.with_shims
+async def test_works_with_shims(client):
+ await client.load_page_and_wait_for_iframe(URL, client.css(IFRAME))
+ assert client.await_css("svg")
+
+
+@pytest.mark.skip_platforms("android")
+@pytest.mark.asyncio
+@pytest.mark.without_tcp
+@pytest.mark.without_shims
+async def test_works_without_etp(client):
+ await client.load_page_and_wait_for_iframe(URL, client.css(IFRAME))
+ assert client.await_css("body.neterror")
+
+
+@pytest.mark.skip_platforms("android")
+@pytest.mark.asyncio
+@pytest.mark.without_shims
+async def test_needs_shims(client):
+ await client.load_page_and_wait_for_iframe(URL, client.css(IFRAME))
+ assert client.await_css("body.neterror")
diff --git a/testing/webcompat/shims/tests/test_1694168_google_analytics_and_tag_manager.py b/testing/webcompat/shims/tests/test_1694168_google_analytics_and_tag_manager.py
new file mode 100644
index 0000000000..a00e617bdb
--- /dev/null
+++ b/testing/webcompat/shims/tests/test_1694168_google_analytics_and_tag_manager.py
@@ -0,0 +1,23 @@
+import pytest
+
+URL = "https://agspares.co.nz/category/Super-Store-Tractor-Linkage-Pins-Lynch-Pins-R-Clips"
+ITEM = ".productsListed.item a[onclick]"
+
+
+from .common import GAGTA_SHIM_MSG, clicking_link_navigates
+
+
+@pytest.mark.asyncio
+@pytest.mark.with_strict_etp
+@pytest.mark.with_shims
+async def test_enabled(client):
+ assert await clicking_link_navigates(
+ client, URL, client.css(ITEM), await_console_message=GAGTA_SHIM_MSG
+ )
+
+
+@pytest.mark.asyncio
+@pytest.mark.with_strict_etp
+@pytest.mark.without_shims
+async def test_disabled(client):
+ assert not await clicking_link_navigates(client, URL, client.css(ITEM))
diff --git a/testing/webcompat/shims/tests/test_1701685_advertising_com.py b/testing/webcompat/shims/tests/test_1701685_advertising_com.py
new file mode 100644
index 0000000000..b89cf51135
--- /dev/null
+++ b/testing/webcompat/shims/tests/test_1701685_advertising_com.py
@@ -0,0 +1,22 @@
+import pytest
+
+from .common import verify_redirectors
+
+URLS = {
+ "https://ads.advertising.com/x.js?1": "js",
+ "https://ads.advertising.com/x?1": "image",
+}
+
+
+@pytest.mark.asyncio
+@pytest.mark.with_strict_etp
+@pytest.mark.with_shims
+async def test_works_with_shims(client):
+ await verify_redirectors(client, URLS, "REDIRECTED")
+
+
+@pytest.mark.asyncio
+@pytest.mark.with_strict_etp
+@pytest.mark.without_shims
+async def test_works_without_etp(client):
+ await verify_redirectors(client, URLS, "BLOCKED")
diff --git a/testing/webcompat/shims/tests/test_1717806_adsafeprotected.py b/testing/webcompat/shims/tests/test_1717806_adsafeprotected.py
new file mode 100644
index 0000000000..e7a530e141
--- /dev/null
+++ b/testing/webcompat/shims/tests/test_1717806_adsafeprotected.py
@@ -0,0 +1,37 @@
+import pytest
+
+from .common import verify_redirectors
+
+URLS = {
+ "https://x.adsafeprotected.com/x.gif?1": "image",
+ "https://x.adsafeprotected.com/x.png?1": "image",
+ "https://x.adsafeprotected.com/x/x": "image",
+ "https://x.adsafeprotected.com/img": "image",
+ "https://x.adsafeprotected.com/x.js?1": "js",
+ "https://x.adsafeprotected.com/x/adj?1": "js",
+ "https://x.adsafeprotected.com/x/imp/1": "js",
+ "https://x.adsafeprotected.com/x/Serving/1": "js",
+ "https://x.adsafeprotected.com/x/unit/1": "js",
+ "https://x.adsafeprotected.com/jload": "js",
+ "https://x.adsafeprotected.com/jload?1": "js",
+ "https://x.adsafeprotected.com/jsvid": "js",
+ "https://x.adsafeprotected.com/jsvid?1": "js",
+ "https://x.adsafeprotected.com/mon?1": "js",
+ "https://x.adsafeprotected.com/tpl": "js",
+ "https://x.adsafeprotected.com/tpl?1": "js",
+ "https://x.adsafeprotected.com/services/pub?1": "js",
+}
+
+
+@pytest.mark.asyncio
+@pytest.mark.with_strict_etp
+@pytest.mark.with_shims
+async def test_works_with_shims(client):
+ await verify_redirectors(client, URLS, "REDIRECTED")
+
+
+@pytest.mark.asyncio
+@pytest.mark.with_strict_etp
+@pytest.mark.without_shims
+async def test_works_without_etp(client):
+ await verify_redirectors(client, URLS, "BLOCKED")
diff --git a/testing/webcompat/shims/tests/test_1717806_stickyadstv.py b/testing/webcompat/shims/tests/test_1717806_stickyadstv.py
new file mode 100644
index 0000000000..74a895aa51
--- /dev/null
+++ b/testing/webcompat/shims/tests/test_1717806_stickyadstv.py
@@ -0,0 +1,22 @@
+import pytest
+
+from .common import verify_redirectors
+
+URLS = {
+ "https://ads.stickyadstv.com/auto-user-sync?1": "image",
+ "https://ads.stickyadstv.com/user-matching?1": "image",
+}
+
+
+@pytest.mark.asyncio
+@pytest.mark.with_strict_etp
+@pytest.mark.with_shims
+async def test_works_with_shims(client):
+ await verify_redirectors(client, URLS, "REDIRECTED")
+
+
+@pytest.mark.asyncio
+@pytest.mark.with_strict_etp
+@pytest.mark.without_shims
+async def test_works_without_etp(client):
+ await verify_redirectors(client, URLS, "BLOCKED")
diff --git a/testing/webcompat/shims/tests/test_1762851_google_publisher_tags.py b/testing/webcompat/shims/tests/test_1762851_google_publisher_tags.py
new file mode 100644
index 0000000000..3b52ce8480
--- /dev/null
+++ b/testing/webcompat/shims/tests/test_1762851_google_publisher_tags.py
@@ -0,0 +1,23 @@
+import pytest
+
+URL = "https://www.carousell.sg/search/ps3/?searchId=DjgOQf"
+PLACEHOLDER = "[id*='Desktop_Search_FWB']"
+
+
+from .common import GPT_SHIM_MSG, is_gtag_placeholder_displayed
+
+
+@pytest.mark.asyncio
+@pytest.mark.with_strict_etp
+@pytest.mark.with_shims
+async def test_enabled(client):
+ assert not await is_gtag_placeholder_displayed(
+ client, URL, client.css(PLACEHOLDER), await_console_message=GPT_SHIM_MSG
+ )
+
+
+@pytest.mark.asyncio
+@pytest.mark.with_strict_etp
+@pytest.mark.without_shims
+async def test_disabled(client):
+ assert not await is_gtag_placeholder_displayed(client, URL, client.css(PLACEHOLDER))
diff --git a/testing/webcompat/shims/tests/test_1767270_rva311_com_pbm_fix.py b/testing/webcompat/shims/tests/test_1767270_rva311_com_pbm_fix.py
new file mode 100644
index 0000000000..2e30bae2cb
--- /dev/null
+++ b/testing/webcompat/shims/tests/test_1767270_rva311_com_pbm_fix.py
@@ -0,0 +1,26 @@
+import pytest
+
+URL = "https://www.rva311.com/rvaone"
+SHIM_ACTIVE_MSG = "Private Browsing Web APIs is being shimmed by Firefox"
+IDB_FAILURE_MSG = "InvalidStateError: A mutation operation was attempted on a database"
+
+
+@pytest.mark.asyncio
+@pytest.mark.with_private_browsing
+@pytest.mark.with_shims
+async def test_with_shim(client, platform):
+ msg = None if platform == "android" else SHIM_ACTIVE_MSG
+ await client.navigate(URL, await_console_message=msg)
+ desktop, mobile = client.await_first_element_of(
+ [client.css("#root nav"), client.css("#mobilePageTitle")], is_displayed=True
+ )
+ assert desktop or mobile
+
+
+@pytest.mark.asyncio
+@pytest.mark.with_private_browsing
+@pytest.mark.without_shims
+async def test_without_shim(client, platform):
+ msg = None if platform == "android" else IDB_FAILURE_MSG
+ await client.navigate(URL, await_console_message=msg)
+ assert client.find_css("#root [class*='loading-dot']", is_displayed=True)
diff --git a/testing/webcompat/shims/tests/test_1775099_google_publisher_tags.py b/testing/webcompat/shims/tests/test_1775099_google_publisher_tags.py
new file mode 100644
index 0000000000..1e4e595561
--- /dev/null
+++ b/testing/webcompat/shims/tests/test_1775099_google_publisher_tags.py
@@ -0,0 +1,23 @@
+import pytest
+
+URL = "https://themighty.com/topic/fibromyalgia/difficulties-sitting-chronic-pain-fibromyalgia/"
+PLACEHOLDER = ".tm-ads"
+
+
+from .common import GPT_SHIM_MSG, is_gtag_placeholder_displayed
+
+
+@pytest.mark.asyncio
+@pytest.mark.with_strict_etp
+@pytest.mark.with_shims
+async def test_enabled(client):
+ assert not await is_gtag_placeholder_displayed(
+ client, URL, client.css(PLACEHOLDER), await_console_message=GPT_SHIM_MSG
+ )
+
+
+@pytest.mark.asyncio
+@pytest.mark.with_strict_etp
+@pytest.mark.without_shims
+async def test_disabled(client):
+ assert await is_gtag_placeholder_displayed(client, URL, client.css(PLACEHOLDER))