diff options
Diffstat (limited to 'packages/meshbay-hub/tests')
4 files changed, 107 insertions, 18 deletions
diff --git a/packages/meshbay-hub/tests/harness/sidebar_scroll_probe.py b/packages/meshbay-hub/tests/harness/sidebar_scroll_probe.py index 0324367..c70688b 100644 --- a/packages/meshbay-hub/tests/harness/sidebar_scroll_probe.py +++ b/packages/meshbay-hub/tests/harness/sidebar_scroll_probe.py @@ -41,9 +41,7 @@ FRAGMENT = f""" <div class="layout"> <aside class="sidebar open"> <div class="sidebar-section"><div class="sidebar-heading">Discover</div> - <a class="sidebar-item" href="#">Search</a></div> - <div class="sidebar-section"><div class="sidebar-heading">Node</div> - <a class="sidebar-item" href="#">Node</a><a class="sidebar-item" href="#">Create</a></div> + <a class="sidebar-item" href="#">Public groups</a></div> <div class="sidebar-section"><div class="sidebar-heading">My groups</div>{GROUPS}</div> <a class="sidebar-legal" href="#">Legal</a> </aside> diff --git a/packages/meshbay-hub/tests/test_layout_measured.py b/packages/meshbay-hub/tests/test_layout_measured.py index 57d325a..298a754 100644 --- a/packages/meshbay-hub/tests/test_layout_measured.py +++ b/packages/meshbay-hub/tests/test_layout_measured.py @@ -62,8 +62,12 @@ NAV = textwrap.dedent(""" </div> </div> </div> + <a class="nav-notif nav-search" href="#/search"> + <svg class="icon" viewBox="0 0 24 24"></svg></a> <a class="nav-notif" href="#/">🔔</a> - <div class="user-menu"><button class="nav-btn">someone</button></div> + <div class="user-menu-wrap"><button class="user-menu-trigger"> + <span class="user-menu-avatar">S</span><span class="user-menu-name">someone</span> + </button></div> </div> </nav> """) @@ -206,8 +210,12 @@ GROUPED = textwrap.dedent(""" </div> </div> </div> + <a class="nav-notif nav-search" href="#/search"> + <svg class="icon" viewBox="0 0 24 24"></svg></a> <a class="nav-notif" href="#/">🔔</a> - <div class="user-menu"><button class="nav-btn">someone</button></div> + <div class="user-menu-wrap"><button class="user-menu-trigger"> + <span class="user-menu-avatar">S</span><span class="user-menu-name">someone</span> + </button></div> </div> </nav> """) @@ -283,3 +291,74 @@ def test_the_waiting_bar_is_as_wide_as_a_progress_bar(grouped): assert bar["width"] > 100, ( f"at {width} px the waiting bar is {bar['width']} px wide") assert bar["height"] >= 3, "the waiting bar has no height" + + +# ── The whole nav on a phone ──────────────────────────────────────────────── +# +# Search moved from the sidebar to the nav, beside the bell. With the user's +# name in the menu trigger, a 20-character handle already put the trigger 79 px +# off a 320 px screen, and 159 px once search and a transfer were there too. +# The fixture is the real markup of `Nav()` and `UserMenu()`, with the icons as +# the 1em boxes `Icon()` draws. + +_ICON = '<svg class="icon" viewBox="0 0 24 24"></svg>' +FULL_NAV = textwrap.dedent(f""" + <nav class="nav"> + <div class="nav-left"> + <button class="nav-hamburger">{_ICON}</button> + <a class="nav-brand" href="#/"><img class="nav-brand-m" alt="M">eshBay</a> + </div> + <div class="nav-right"> + <div class="transfer-wrap"> + <button class="nav-notif transfer-btn">{_ICON}<span class="notif-badge">2</span></button> + </div> + <a class="nav-notif nav-search" href="#/search">{_ICON}</a> + <a class="nav-notif" href="#/">{_ICON}<span class="notif-badge">3</span></a> + <div class="user-menu-wrap"> + <button class="user-menu-trigger"> + <span class="user-menu-avatar">A</span> + <span class="user-menu-name">a_rather_long_handle</span> + <svg class="icon user-menu-caret" viewBox="0 0 24 24"></svg> + </button> + </div> + </div> + </nav> +""") +FULL_NAV_WIDTHS = [320, 360, 412, 521, 768, 1024] + + +@pytest.fixture(scope="module") +def full_nav(tmp_path_factory): + fragment = tmp_path_factory.mktemp("fullnav") / "fragment.html" + fragment.write_text(FULL_NAV, encoding="utf-8") + proc = subprocess.run( + ["python3", str(HARNESS), ",".join(str(w) for w in FULL_NAV_WIDTHS), + str(fragment), ".nav-brand", ".nav-right", ".user-menu-trigger", + ".user-menu-name"], + 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.mark.parametrize("width", FULL_NAV_WIDTHS) +def test_the_user_menu_stays_inside_the_nav(full_nav, width): + """Off the right edge is where nobody can open it — and it holds sign-out.""" + boxes = full_nav[str(width)]["boxes"] + trigger = boxes[".user-menu-trigger"] + assert trigger["right"] <= width - 16, ( + f"at {width} px the user menu ends at {trigger['right']} px, past the " + "nav's own padding") + assert boxes[".nav-right"]["left"] >= boxes[".nav-brand"]["right"], ( + f"at {width} px the nav's icons slide under the wordmark") + + +@pytest.mark.parametrize("width", FULL_NAV_WIDTHS) +def test_the_name_leaves_the_trigger_on_mobile_only(full_nav, width): + """Avatar alone up to the mobile breakpoint; the name beside it on a desktop.""" + name = full_nav[str(width)]["boxes"][".user-menu-name"] + shown = bool(name and name["width"] > 0) + assert shown == (width > 768), ( + f"at {width} px the name in the user menu is " + f"{'shown' if shown else 'hidden'}") diff --git a/packages/meshbay-hub/tests/test_layout_responsive.py b/packages/meshbay-hub/tests/test_layout_responsive.py index bb73557..a44df1a 100644 --- a/packages/meshbay-hub/tests/test_layout_responsive.py +++ b/packages/meshbay-hub/tests/test_layout_responsive.py @@ -50,17 +50,28 @@ def _rule(css: str, selector: str) -> str: @pytest.fixture(scope="module") def mobile(css): - """The body of the max-width: 768px block.""" - i = css.index("@media (max-width: 768px)") - depth, j = 0, css.index("{", i) - for k in range(j, len(css)): - if css[k] == "{": - depth += 1 - elif css[k] == "}": - depth -= 1 - if depth == 0: - return css[j:k] - pytest.fail("the mobile media query is not closed") + """The bodies of every block that applies at 768 px and below, joined. + + Every one, not the first: a second block whose condition also names the + breakpoint (`(max-width: 768px), (pointer: coarse)`) came first in the file + and hid the rules these tests look for. + """ + bodies = [] + for m in re.finditer(r"@media[^{]*\(max-width: 768px\)[^{]*\{", css): + depth, j = 0, m.end() - 1 + for k in range(j, len(css)): + if css[k] == "{": + depth += 1 + elif css[k] == "}": + depth -= 1 + if depth == 0: + bodies.append(css[j:k]) + break + else: + pytest.fail("a mobile media query is not closed") + if not bodies: + pytest.fail("no max-width: 768px media query") + return "\n".join(bodies) def test_a_toolbar_group_can_break(css): diff --git a/packages/meshbay-hub/tests/test_sidebar_node_section.py b/packages/meshbay-hub/tests/test_sidebar_node_section.py index 371a018..57c9e9e 100644 --- a/packages/meshbay-hub/tests/test_sidebar_node_section.py +++ b/packages/meshbay-hub/tests/test_sidebar_node_section.py @@ -1,6 +1,7 @@ """ -The sidebar's Node section follows the account's node link, and must follow it -when it changes -- not only at sign-in. +The Node and Create group entries -- in the sidebar when this was written, in +the user menu since -- follow the account's node link, and must follow it when +it changes -- not only at sign-in. Reported on a real install: after the first click on Create group, the Node section (Node, Create group) was gone from the sidebar until a reload, while |