diff options
Diffstat (limited to 'packages/meshbay-hub/tests/test_hover_scoped.py')
| -rw-r--r-- | packages/meshbay-hub/tests/test_hover_scoped.py | 62 |
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)] |