aboutsummaryrefslogtreecommitdiffstats
path: root/packages/meshbay-hub/tests
diff options
context:
space:
mode:
Diffstat (limited to 'packages/meshbay-hub/tests')
-rw-r--r--packages/meshbay-hub/tests/harness/sidebar_scroll_probe.py4
-rw-r--r--packages/meshbay-hub/tests/test_layout_measured.py83
-rw-r--r--packages/meshbay-hub/tests/test_layout_responsive.py33
-rw-r--r--packages/meshbay-hub/tests/test_sidebar_node_section.py5
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="#/">&#128276;</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="#/">&#128276;</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