diff options
| author | Christophe Besson <cbesson@gmail.com> | 2026-09-11 13:57:34 +0200 |
|---|---|---|
| committer | Christophe Besson <cbesson@gmail.com> | 2026-09-11 13:57:34 +0200 |
| commit | 4b74ff3fc54f35fbc41d9c0f7d2b4ec89475154a (patch) | |
| tree | 6a0ab44c40602415015cbacda30e5615e0863d8f /packages/meshbay-hub/tests | |
| parent | cab9ba28cfba9d90d0a6c4c00dbba852825f66c2 (diff) | |
| download | meshbay-4b74ff3fc54f35fbc41d9c0f7d2b4ec89475154a.tar.gz | |
feat(hub): legal information link at the foot of the sidebar
A small link to the hub's legal pages, pinned to the bottom of the
sidebar once signed in. It opens in a new tab: the desktop application
refuses to navigate away from its interface and hands a new window to
the system browser, and in a browser it keeps the session on screen. The
address comes from hubBase(), so it is the legal pages of the hub in use.
The sidebar now sticks under the navigation bar at the window's height;
otherwise, on a long file list, the link would sit at the bottom of the
page. The music bar, pinned to the bottom of the window as well,
publishes its height through useStickyBand as --music-bar-h and the
sidebar stops above it. On a phone the slide-out panel does the same.
test_sidebar_legal_measured.py measures the real stylesheet: the link at
the bottom of the window on a 3000px page, and above the music bar, at
phone and desktop widths. Checked signed in on a local hub in Chrome and
Firefox 155.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01D9MCBBWSm9GhBESmqzJxNy
Diffstat (limited to 'packages/meshbay-hub/tests')
| -rw-r--r-- | packages/meshbay-hub/tests/test_sidebar_legal_measured.py | 101 |
1 files changed, 101 insertions, 0 deletions
diff --git a/packages/meshbay-hub/tests/test_sidebar_legal_measured.py b/packages/meshbay-hub/tests/test_sidebar_legal_measured.py new file mode 100644 index 0000000..faaaec7 --- /dev/null +++ b/packages/meshbay-hub/tests/test_sidebar_legal_measured.py @@ -0,0 +1,101 @@ +""" +The legal link at the foot of the sidebar, measured in a browser. + +It is pinned to the bottom of the window, not of the page: the sidebar sticks +under the navigation bar at the window's height, so on a long file list the +link is still on screen. And the music bar, pinned to the bottom of the window +as well, must not cover it — the bar publishes its height as `--music-bar-h` +(sticky.js, as the toolbars do), and the sidebar stops above it. That value +is set by hand here; what is under test is the stylesheet's arithmetic. + +On a phone the sidebar is the slide-out panel, measured open. +""" + +import json +import shutil +import subprocess +import textwrap +from pathlib import Path + +import pytest + +HARNESS = Path(__file__).parent / "harness" / "layout_probe.py" +STATIC = Path(__file__).resolve().parents[1] / "src" / "meshbay_hub" / "static" + +pytestmark = pytest.mark.skipif( + shutil.which("google-chrome") is None or not (STATIC / "style.css").exists(), + reason="Chrome or the SPA stylesheet is not available") + +# The probe's frames are this tall. +WINDOW_H = 740 +MUSIC_BAR_H = 64 +WIDTHS = [390, 1024, 1440] + + +def _page(music: bool) -> str: + groups = "".join( + f'<a class="sidebar-item sidebar-group" href="#/"><span class="si-head">' + f'<span class="sidebar-item-name">Some group {i}</span></span></a>' for i in range(4)) + bar = (f'<div class="music-player-bar" style="height:{MUSIC_BAR_H}px;' + f'box-sizing:border-box">a track</div>') if music else "" + style = f' style="--music-bar-h:{MUSIC_BAR_H}px"' if music else "" + return textwrap.dedent(f""" + <div id="app"{style}> + <nav class="nav"><div class="nav-left"><a class="nav-brand" href="#/">MeshBay</a></div> + <div class="nav-right"><button class="nav-btn">someone</button></div></nav> + <div class="layout"> + <aside class="sidebar open"> + <div class="sidebar-section"><div class="sidebar-heading">My groups</div>{groups}</div> + <a class="sidebar-legal" href="/legal/">Legal information</a> + </aside> + <main class="main"><h2>A long list</h2><div style="height:3000px"></div></main> + </div> + {bar} + </div> + """) + + +def _measure(tmp_path_factory, music: bool) -> dict: + fragment = tmp_path_factory.mktemp("sidebar") / "fragment.html" + fragment.write_text(_page(music), encoding="utf-8") + proc = subprocess.run( + ["python3", str(HARNESS), ",".join(str(w) for w in WIDTHS), + str(fragment), ".sidebar-legal", ".sidebar", ".music-player-bar"], + capture_output=True, text=True, timeout=180) + assert proc.returncode == 0, f"probe failed: {proc.stdout}{proc.stderr}" + out = json.loads(proc.stdout) + assert "error" not in out, f"no measurement: {out}" + return out + + +@pytest.fixture(scope="module") +def plain(tmp_path_factory): + return _measure(tmp_path_factory, music=False) + + +@pytest.fixture(scope="module") +def playing(tmp_path_factory): + return _measure(tmp_path_factory, music=True) + + +@pytest.mark.parametrize("width", WIDTHS) +def test_the_link_is_at_the_bottom_of_the_window(plain, width): + """Not at the bottom of a 3000px page, where nobody would find it.""" + link = plain[str(width)]["boxes"][".sidebar-legal"] + assert link is not None, "no legal link" + bottom = link["top"] + link["height"] + assert WINDOW_H - 8 <= bottom <= WINDOW_H, ( + f"at {width} px the link ends at {bottom}, the window at {WINDOW_H}") + assert link["offLeft"] == 0 and link["offRight"] == 0 + + +@pytest.mark.parametrize("width", WIDTHS) +def test_the_music_bar_does_not_cover_it(playing, width): + link = playing[str(width)]["boxes"][".sidebar-legal"] + bar = playing[str(width)]["boxes"][".music-player-bar"] + assert link is not None and bar is not None + bottom = link["top"] + link["height"] + assert bottom <= bar["top"] + 1, ( + f"at {width} px the link ends at {bottom} and the music bar starts at {bar['top']}") + assert bottom >= bar["top"] - 8, ( + f"at {width} px the link floats {bar['top'] - bottom} px above the music bar") |