aboutsummaryrefslogtreecommitdiffstats
path: root/packages/meshbay-hub/tests/test_sidebar_scroll_measured.py
diff options
context:
space:
mode:
authorChristophe Besson <cbesson@gmail.com>2026-09-15 02:18:38 +0200
committerChristophe Besson <cbesson@gmail.com>2026-09-15 02:21:01 +0200
commitacb0d666540dacb092f596dada25822d1d0466f0 (patch)
tree7dc61234255c4f18a1bbc24754bcad53385c55d5 /packages/meshbay-hub/tests/test_sidebar_scroll_measured.py
parent73ad8e4eb566fe682107fa7e50ef624591199e99 (diff)
downloadmeshbay-acb0d666540dacb092f596dada25822d1d0466f0.tar.gz
fix(ui): sidebar stays above a phone's address bar and does not scroll the page
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XuNrwLf5EFWCMHzfoEvnpm
Diffstat (limited to 'packages/meshbay-hub/tests/test_sidebar_scroll_measured.py')
-rw-r--r--packages/meshbay-hub/tests/test_sidebar_scroll_measured.py68
1 files changed, 68 insertions, 0 deletions
diff --git a/packages/meshbay-hub/tests/test_sidebar_scroll_measured.py b/packages/meshbay-hub/tests/test_sidebar_scroll_measured.py
new file mode 100644
index 0000000..d2adaf1
--- /dev/null
+++ b/packages/meshbay-hub/tests/test_sidebar_scroll_measured.py
@@ -0,0 +1,68 @@
+"""
+A sidebar with many groups scrolls on its own, on a phone and on a desktop.
+
+Measured, not read: twelve groups beside a page long enough to scroll, in
+Chrome, at 390 and 1280 px. The list must scroll, its last group and the legal
+link must be reachable, and reaching an end must not hand the scroll on to the
+page behind — which it did, measured before the fix (`overscroll-behavior:
+auto`).
+
+What no headless measurement shows is a phone's address bar: an iframe has no
+dynamic toolbar, so `100vh` and `100dvh` agree here. That half is held by
+reading the stylesheet, the weaker evidence, and is the known behaviour of
+mobile browsers: `100vh` is the height with the bar hidden.
+"""
+
+import json
+import re
+import shutil
+import subprocess
+import sys
+from pathlib import Path
+
+import pytest
+
+HARNESS = Path(__file__).parent / "harness" / "sidebar_scroll_probe.py"
+STYLE = Path(__file__).resolve().parents[1] / "src" / "meshbay_hub" / "static" / "style.css"
+
+
+@pytest.fixture(scope="module")
+def measured():
+ if shutil.which("google-chrome") is None:
+ pytest.skip("Chrome is not available")
+ proc = subprocess.run([sys.executable, str(HARNESS)],
+ capture_output=True, text=True, timeout=90)
+ data = json.loads(proc.stdout.strip().splitlines()[-1])
+ assert "error" not in data, f"probe failed: {proc.stdout}{proc.stderr}"
+ return data
+
+
+@pytest.mark.parametrize("width", ["390", "1280"])
+def test_the_list_scrolls_inside_the_sidebar(measured, width):
+ m = measured[width]
+ assert m["overflowY"] == "auto"
+ assert m["scrollHeight"] > m["clientHeight"], "twelve groups should overflow"
+ assert m["sidebarBottom"] <= m["viewportHeight"], "the sidebar runs off the window"
+
+
+@pytest.mark.parametrize("width", ["390", "1280"])
+def test_the_end_of_the_list_can_be_reached(measured, width):
+ m = measured[width]
+ assert m["lastGroupBottom"] <= m["sidebarBottom"]
+ assert m["legalBottom"] <= m["sidebarBottom"] + 1
+
+
+@pytest.mark.parametrize("width", ["390", "1280"])
+def test_reaching_an_end_does_not_scroll_the_page(measured, width):
+ assert measured[width]["overscrollBehaviorY"] == "contain"
+
+
+def test_the_height_follows_the_visible_viewport_with_a_fallback():
+ css = STYLE.read_text(encoding="utf-8")
+ rules = [m.group(1) for m in re.finditer(r"\n\s*\.sidebar \{(.*?)\}", css, re.S)]
+ assert len(rules) == 2, "expected the desktop and the phone .sidebar rules"
+ for body in rules:
+ heights = re.findall(r"height:\s*calc\((100d?vh)", body)
+ assert heights == ["100vh", "100dvh"], (
+ f"a .sidebar rule sizes itself with {heights}: 100dvh is what keeps the "
+ "foot of the list above a phone's address bar, after a 100vh fallback")