aboutsummaryrefslogtreecommitdiffstats
path: root/packages/meshbay-hub/tests/test_layout_measured.py
diff options
context:
space:
mode:
authorChristophe Besson <cbesson@gmail.com>2026-09-09 14:28:40 +0200
committerChristophe Besson <cbesson@gmail.com>2026-09-09 14:28:40 +0200
commit7e2d078fe1870d256ae47781bee6ac4f454edf24 (patch)
tree05689049fd48f01ebbdb9995d5189cba15cee052 /packages/meshbay-hub/tests/test_layout_measured.py
parent813d18424ec57963bb56e6f40824a2db0ccce50d (diff)
parente6f895c473a0b19e7b186889c1836d3945bc880b (diff)
downloadmeshbay-7e2d078fe1870d256ae47781bee6ac4f454edf24.tar.gz
Merge branch 'fix/large-download-paths'
Concurrent-transfer limits, with the queue, the pause and the flag day. A node now caps how many transfers it runs at once (8 downloads, 8 uploads, node-wide) and how many one member may run in one group (2 by default, operator-signed). Beyond that the node answers "queued" and the client waits its turn, visibly, in the transfers panel — and a slot that frees starts whatever is next, skipping past a member who is at their own cap rather than letting them stall everyone behind them. Browsing is never subject to a slot: not the poster grid, not the covers, not opening a photo to look at it. That is structural — a transfer is what the transfers widget shows — and the exemption is bounded rather than open, at two files in flight per session, because an exemption with no bound is a leaseless branch under another name. Transfers can be cancelled, and now paused and resumed. A paused one holds nothing: its slot goes back at once and resuming rejoins the queue at the tail. Uploads survive the connection that started them and resume where the node stopped, asked for inside the seal rather than on a clear message. What they leave behind when they are abandoned is reaped, which closes a disk leak that predates this work. MNP 3.0 makes the lease compulsory and refuses 2.x at the handshake, with the desktop client checking `client.minimum` before connecting so an un-updated one says "update" instead of failing every connection in a protocol vocabulary. Fourteen defects were found on the way, eight of them by a person clicking Download and pasting a console — none of which 2075 tests could reach. Section 12 of ~/next/improve-downloads.md is that report, including the three this work introduced itself and the one that turned out to be caused by an instruction to hard-reload after each deployment. Node suite 1209 passed, hub suite 866 passed. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HCGdheDLxGReuKHga3BtST
Diffstat (limited to 'packages/meshbay-hub/tests/test_layout_measured.py')
-rw-r--r--packages/meshbay-hub/tests/test_layout_measured.py119
1 files changed, 118 insertions, 1 deletions
diff --git a/packages/meshbay-hub/tests/test_layout_measured.py b/packages/meshbay-hub/tests/test_layout_measured.py
index 91f1ed0..a71b6b9 100644
--- a/packages/meshbay-hub/tests/test_layout_measured.py
+++ b/packages/meshbay-hub/tests/test_layout_measured.py
@@ -54,7 +54,7 @@ NAV = textwrap.dedent("""
<div class="transfer-item">
<div class="transfer-line">
<span class="transfer-kind">&#8595;</span>
- <span class="transfer-name">S03E01. Salt and Sea, Fire and Blood.mp4</span>
+ <span class="transfer-name">Some Saga S03E01 - A Long Enough Title.mp4</span>
<button class="transfer-cancel">&#10005;</button>
</div>
<div class="dl-progress"><div class="dl-fill" style="width:42%"></div></div>
@@ -144,3 +144,120 @@ def test_the_page_does_not_scroll_sideways(measured, width):
r = measured[str(width)]
assert r["docScrollW"] <= r["viewport"]["w"], (
f"the document scrolls to {r['docScrollW']} px on a {width} px screen")
+
+
+# ── The grouped panel (§8.2) ────────────────────────────────────────────────
+#
+# The panel gained groups, a header summary and a waiting row. Every one of
+# those can push something off a 320 px screen, and none of it can be seen by
+# reading the stylesheet: what decides where the panel lands is the button it
+# hangs from, which is not at the right edge. That is the defect this file was
+# written for, and it comes back with any change to the header's width.
+
+GROUPED = textwrap.dedent("""
+ <nav class="nav">
+ <div class="nav-left">
+ <button class="nav-hamburger">&#9776;</button>
+ <a class="nav-brand" href="#/">MeshBay</a>
+ </div>
+ <div class="nav-right">
+ <div class="transfer-wrap">
+ <button class="nav-notif transfer-btn">&#8595;</button>
+ <div class="transfer-panel">
+ <div class="transfer-head">
+ <span class="transfer-head-title">Transfers</span>
+ <span class="transfer-head-summary">2 running &middot; 3 waiting</span>
+ <button class="btn-secondary">Clear finished</button>
+ </div>
+ <div class="transfer-group">
+ <div class="transfer-group-head">Running</div>
+ <div class="transfer-item transfer-running">
+ <div class="transfer-line">
+ <span class="transfer-kind">&#8595;</span>
+ <span class="transfer-name">Some Saga S03E01 - A Long Enough Title.mp4</span>
+ <button class="transfer-cancel">&#10005;</button>
+ </div>
+ <div class="dl-progress"><div class="dl-fill" style="width:42%"></div></div>
+ <div class="transfer-meta"><span>210 MB / 493 MB</span><span>3.1 MB/s &middot; 4 min left</span></div>
+ </div>
+ </div>
+ <div class="transfer-group">
+ <div class="transfer-group-head">Waiting</div>
+ <div class="transfer-item transfer-queued">
+ <div class="transfer-line">
+ <span class="transfer-kind">&#8595;</span>
+ <span class="transfer-name">Another File With A Long Name.mkv</span>
+ <button class="transfer-cancel">&#10005;</button>
+ </div>
+ <div class="dl-progress dl-waiting"></div>
+ <div class="transfer-meta"><span>Waiting &mdash; your slots are busy</span><span>1.2 GB</span></div>
+ </div>
+ </div>
+ </div>
+ </div>
+ <a class="nav-notif" href="#/">&#128276;</a>
+ <div class="user-menu"><button class="nav-btn">someone</button></div>
+ </div>
+ </nav>
+""")
+
+GROUPED_SELECTORS = [".transfer-panel", ".transfer-head", ".transfer-head-summary",
+ ".transfer-group-head", ".transfer-name",
+ ".transfer-item.transfer-queued .dl-progress"]
+
+
+@pytest.fixture(scope="module")
+def grouped(tmp_path_factory):
+ fragment = tmp_path_factory.mktemp("grouped") / "fragment.html"
+ fragment.write_text(GROUPED)
+ proc = subprocess.run(
+ ["python3", str(HARNESS), ",".join(str(w) for w in WIDTHS),
+ str(fragment), *GROUPED_SELECTORS],
+ 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
+
+
+def test_the_grouped_panel_stays_on_a_phone_screen(grouped):
+ for width in WIDTHS:
+ box = grouped[str(width)]["boxes"][".transfer-panel"]
+ assert box["offLeft"] == 0, (
+ f"at {width} px the panel hangs {box['offLeft']} px off the left — "
+ "which is where the file names are")
+ assert box["offRight"] == 0, (
+ f"at {width} px the panel hangs {box['offRight']} px off the right")
+
+
+def test_the_file_name_is_on_screen_in_every_group(grouped):
+ for width in WIDTHS:
+ box = grouped[str(width)]["boxes"][".transfer-name"]
+ assert box["offLeft"] == 0 and box["offRight"] == 0, (
+ f"at {width} px a file name is cut off: {box}")
+ assert box["width"] > 40, "the name column collapsed to nothing"
+
+
+def test_the_header_summary_does_not_push_the_header_taller(grouped):
+ """It is the one part of the header that grows with what is happening. If
+ it wraps, the header changes height as transfers come and go and every row
+ below it moves — on the narrowest screen, repeatedly."""
+ for width in WIDTHS:
+ head = grouped[str(width)]["boxes"][".transfer-head"]
+ summary = grouped[str(width)]["boxes"][".transfer-head-summary"]
+ assert head["height"] <= 48, (
+ f"at {width} px the header is {head['height']} px tall — it wrapped")
+ assert summary["height"] <= 24, (
+ f"at {width} px the summary wrapped to {summary['height']} px")
+
+
+def test_the_waiting_bar_is_as_wide_as_a_progress_bar(grouped):
+ """A waiting row has no inner fill element — the stripes are on the track
+ itself. Getting that wrong renders a zero-width bar, which reads as a
+ transfer stuck at 0% rather than one that has not started."""
+ for width in WIDTHS:
+ bar = grouped[str(width)]["boxes"][
+ ".transfer-item.transfer-queued .dl-progress"]
+ 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"