From 76724252d08162d4df39090af19796054bf4add8 Mon Sep 17 00:00:00 2001 From: Christophe Besson Date: Sat, 15 Aug 2026 00:49:54 +0200 Subject: fix(members): restore the member list and invite form, and retire the pairing form MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Moving the invite form above the member list cut both out of MembersPanel and pasted them into AdminPage, where `doInvite`, `members`, `adminId` and `inviteCode` do not exist. A standard member saw an empty Members tab, the group owner saw only a pairing form, and the hub's own Users tab referenced four undefined names. The pairing form outstaying its welcome is a second bug and an older one. `is_node_admin` compares the connecting account with the account that owns the node — it says nothing about whether *this browser's key* was ever paired, which is the thing pairing changes and the thing that lets you sign an invite. So the form showed for an operator who paired months ago, accepted a fresh code, reported success, and stayed exactly where it was. The node already reports the roster role in `join_result`; the transport keeps it, and the form appears only when this identity is not an operator key yet. Also dropped a clause from the pairing hint: the code never passing through the hub is worth saying, the theory behind it is not. test_spa_ordering.py gets three checks for this class of bug — a cut-and-paste between components is invisible to every other test we have. Co-Authored-By: Claude Opus 5 --- packages/meshbay-hub/tests/test_spa_ordering.py | 42 +++++++++++++++++++++++++ 1 file changed, 42 insertions(+) (limited to 'packages/meshbay-hub/tests/test_spa_ordering.py') diff --git a/packages/meshbay-hub/tests/test_spa_ordering.py b/packages/meshbay-hub/tests/test_spa_ordering.py index 0ef34fc..9301fcc 100644 --- a/packages/meshbay-hub/tests/test_spa_ordering.py +++ b/packages/meshbay-hub/tests/test_spa_ordering.py @@ -101,3 +101,45 @@ def test_the_ack_still_verifies_the_announced_node_key(): "the challenge's node_pk must be checked against the ack's") assert "verifyNodeSignature" in source, ( "the ack's signature over the handshake transcript must still be verified") + + +# ── Component boundaries ──────────────────────────────────────────────────── +# +# A second class of bug this file exists for. Moving a block between components +# is a plain cut and paste, and nothing checks that the paste landed somewhere +# the names it uses exist: the invite form and the member list were cut out of +# MembersPanel and pasted into AdminPage, which left a standard member seeing an +# empty Members tab, the group owner seeing only a pairing form, and the hub's +# Users tab referencing `doInvite`, `members` and `adminId` — none of which are +# defined there. + +APP = STATIC / "app.js" + + +def _component(name: str) -> str: + """The source of one top-level `function Name(...)`, up to the next one.""" + source = APP.read_text() + start = source.find(f"\nfunction {name}(") + assert start != -1, f"{name} is gone from app.js — update this test" + end = source.find("\nfunction ", start + 1) + return source[start:end if end != -1 else len(source)] + + +def test_members_panel_renders_what_it_owns(): + panel = _component("MembersPanel") + assert "members.map(" in panel, "the member list is not rendered" + assert "onSubmit=${doInvite}" in panel, "the invite form is not rendered" + assert "onSubmit=${doPair}" in panel, "the pairing form is not rendered" + + +def test_the_invite_form_comes_before_the_list(): + panel = _component("MembersPanel") + assert panel.index("onSubmit=${doInvite}") < panel.index("members.map("), \ + "the invite form belongs above the member list" + + +def test_admin_page_does_not_borrow_the_members_panel_state(): + admin = _component("AdminPage") + for name in ("doInvite", "adminId", "inviteCode", "setInviteUser"): + assert name not in admin, \ + f"AdminPage references {name}, which only exists in MembersPanel" -- cgit v1.2.3