aboutsummaryrefslogtreecommitdiffstats
path: root/packages/meshbay-hub/tests/test_hover_scoped.py
diff options
context:
space:
mode:
Diffstat (limited to 'packages/meshbay-hub/tests/test_hover_scoped.py')
-rw-r--r--packages/meshbay-hub/tests/test_hover_scoped.py62
1 files changed, 62 insertions, 0 deletions
diff --git a/packages/meshbay-hub/tests/test_hover_scoped.py b/packages/meshbay-hub/tests/test_hover_scoped.py
new file mode 100644
index 0000000..f28dacd
--- /dev/null
+++ b/packages/meshbay-hub/tests/test_hover_scoped.py
@@ -0,0 +1,62 @@
+"""
+Every `:hover` rule in style.css is inside `@media (hover: hover)`.
+
+A tap on a touchscreen puts the element in `:hover` and nothing takes it out
+until another element is tapped, so a button tapped once kept its hover colour
+as if still pressed. It was fixed for the music player's buttons alone, then
+reported again on the pager arrows, the nav hamburger and the rest: a rule per
+button only moves the bug to the next button. This holds the whole file.
+"""
+
+from pathlib import Path
+
+import pytest
+
+STYLE = (Path(__file__).resolve().parents[1] / "src" / "meshbay_hub"
+ / "static" / "style.css")
+
+pytestmark = pytest.mark.skipif(not STYLE.exists(),
+ reason="the stylesheet is not in this checkout")
+
+
+def _hover_rules(source: str) -> list[tuple[int, list[str], str]]:
+ """(line, enclosing at-rule preludes, selector) for each rule naming :hover."""
+ found = []
+ stack: list[str] = []
+ start = 0
+ i = 0
+ while i < len(source):
+ if source.startswith("/*", i):
+ i = source.index("*/", i) + 2
+ start = i
+ continue
+ c = source[i]
+ if c == "{":
+ prelude = source[start:i].strip()
+ if ":hover" in prelude and not prelude.startswith("@"):
+ found.append((source.count("\n", 0, i) + 1, list(stack), prelude))
+ stack.append(prelude)
+ start = i + 1
+ elif c == "}":
+ stack.pop()
+ start = i + 1
+ elif c == ";":
+ start = i + 1
+ i += 1
+ return found
+
+
+def test_every_hover_rule_is_scoped_to_devices_that_hover():
+ rules = _hover_rules(STYLE.read_text(encoding="utf-8"))
+ assert rules, "no :hover rule found, did the parser break?"
+ loose = [f"line {line}: {sel}" for line, stack, sel in rules
+ if not any("(hover: hover)" in at for at in stack)]
+ assert not loose, ("wrap these in @media (hover: hover), a tap leaves them "
+ "stuck on a touchscreen:\n" + "\n".join(loose))
+
+
+def test_the_parser_catches_a_loose_rule():
+ rules = _hover_rules("/* a { } */ .a:hover { color: red; }\n"
+ "@media (hover: hover) { .b:hover { color: red; } }")
+ assert [(sel, bool(stack)) for _, stack, sel in rules] == [
+ (".a:hover", False), (".b:hover", True)]