""" A group always opens on a tab that exists. The landing tab comes from a preference and is chosen at mount; which applications the group runs comes from the node, several awaits later. When the two disagree — Chat disabled here while 'chat' is the default, or a preferred app this group does not offer — the page used to render no panel at all, with no tab shown active and nothing on screen to explain it. A string comparison against a list that arrives later is not something reading `group-page.js` makes obvious, so this renders the real `GroupPage` against a stub node and reads the tab bar back. """ import json import shutil import subprocess from pathlib import Path import pytest HARNESS = Path(__file__).parent / "harness" / "group_tab_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 / "group-page.js").exists(), reason="Chrome or the SPA sources are not available") @pytest.fixture(scope="module") def cases(): run = subprocess.run(["python3", str(HARNESS)], capture_output=True, timeout=180) assert run.returncode == 0, run.stderr.decode()[-2000:] return {c["name"]: c for c in json.loads(run.stdout.decode())} # Asserted by position, not by label: the harness renders in whatever language # the browser resolves to. def test_every_case_lands_somewhere(cases): """The bug, stated once: a tab bar with nothing selected and nothing under it.""" for name, case in cases.items(): assert case["active"] >= 0, f"{name}: no tab is active" assert case["panelDrawn"], f"{name}: a tab is active but no panel was drawn" def test_the_default_is_the_first_app(cases): """Nothing changes for a group running everything.""" case = cases["every app, default chat"] assert case["active"] == 0 def test_a_disabled_default_falls_back(cases): """'chat' is the default for everyone, and a group may not run it.""" case = cases["chat disabled"] assert case["active"] == 0, "should have fallen back to the group's first app" assert len(case["tabs"]) == 3, "Files, Videos and Settings, with no Chat tab" def test_a_preference_for_an_absent_app_falls_back(cases): """The preference is global; the app list is per group.""" assert cases["preferred app absent"]["active"] == 0 def test_a_preference_that_names_nothing_falls_back(cases): """A stale or hand-edited preference must not strand anyone either.""" assert cases["preference names nothing real"]["active"] == 0 def test_a_preference_that_arrives_after_the_group_opened_applies(cases): """The reported bug: Videos chosen in Settings, and a group opened directly (a reload, a link) still landed on Chat. app.js fetches the preferences after sign-in; GroupPage chose its tab at mount and never looked again. """ case = cases["preference arrives after the group opened"] assert case["active"] == 2, ( f"the preference arrived and was ignored: landed on tab {case['active']}") def test_a_late_preference_does_not_undo_a_tab_the_reader_chose(cases): """Arriving late must not yank someone off a tab they just clicked.""" case = cases["tab chosen before the preference arrives"] assert case["active"] == 1, ( f"the reader clicked Files and was moved to tab {case['active']}") def test_a_preference_the_group_offers_is_kept(cases): """The fallback must not become 'always the first app'.""" case = cases["preferred app present"] assert case["active"] == 2, ( "Videos was enabled and preferred; the reader should have landed on it, " f"not on tab {case['active']}")