""" The page cannot end up silently blank, and there is a way out of it. A reader spent an evening on a white screen. The shell arrived on every reload, every module was served from the browser's own store so not one request reached the hub, and nothing rendered: no message, no error, nothing in the server log to look at. The same account worked in a private window and in another browser — the signature of something wrong in this origin's stored state rather than in what was deployed. Clearing the site's data fixed it; clearing the *cache*, three times, had not, because a service worker and IndexedDB are not the cache. What is measured here is the silence, not its cause — that evidence was destroyed by the fix, necessarily. A blank page is a bug report nobody can write, and on a phone there is no console to open. `boot-guard.js` is a classic script, loaded before the module graph, because the failure it guards against includes the graph never linking: one bad module and no module code runs at all, so a guard inside `app.js` would be part of what failed. """ import json import shutil import subprocess import sys from pathlib import Path import pytest HARNESS = Path(__file__).parent / "harness" / "boot_guard_probe.py" @pytest.fixture(scope="module") def cases(): if shutil.which("google-chrome") is None: pytest.skip("Chrome is not available") proc = subprocess.run([sys.executable, str(HARNESS)], capture_output=True, text=True, timeout=300) data = json.loads(proc.stdout) assert "error" not in data, f"probe failed: {proc.stdout}{proc.stderr}" return {c["case"]: c for c in data["cases"]} def test_the_guard_says_nothing_when_the_application_mounts(cases): # The ordinary case, and the one that matters most: a guard that draws over # a working application would be a worse bug than the one it is for. c = cases["the application mounted"] assert c["buttons"] == [], f"the guard drew over a mounted application: {c}" assert "application" in c["text"] def test_a_graph_that_does_not_link_shows_a_message_not_a_blank_page(cases): c = cases["the module graph did not link"] assert len(c["text"]) > 40, f"still effectively blank: {c['text']!r}" def test_the_failure_is_named_on_screen(cases): # Verbatim, on screen, because a phone has no console and this is the first # thing anybody diagnosing it will ask for. c = cases["the module graph did not link"] assert "__case.js" in c["text"], ( f"the module that failed is not named anywhere: {c['text']!r}") def test_a_way_out_is_offered(cases): c = cases["the module graph did not link"] assert len(c["buttons"]) == 2, c["buttons"] joined = " ".join(c["buttons"]).lower() assert "essayer" in joined or "try" in joined assert "initialis" in joined or "reset" in joined def test_the_reset_button_really_empties_this_origin(cases): """A button that claims to clear and does not would be worse than none. So it is clicked, for real, against a seeded `localStorage` and a seeded database — and what it leaves behind is what is read back. """ c = cases["the reset button empties this origin"] assert c["clicked"], "no reset button to click" assert c["after"]["auth"] is None, ( f"localStorage survived the reset: {c['after']}") assert c["after"]["dbs"] == [], ( f"a database survived the reset: {c['after']}") def test_a_reset_another_tab_is_blocking_says_so(cases): """Deleting a database waits for every other connection to close — silently. Unwatched, `deleteDatabase` neither fails nor completes, so a reset that reloads regardless comes back to exactly the state it claimed to clear. The reader would then have tried the one thing that works, watched it appear to work, and still be staring at the same page. """ c = cases["a reset another tab is blocking says so"] assert c["clicked"], "no reset button to click" assert c["told"] == "meshbay", ( f"the blocked database was not named on screen: {c}") assert not c["reloadedAnyway"], "it reloaded into the state it had not cleared" assert c["stillOffersReset"], "the reader is left with no way to try again" def test_a_blocked_database_upgrade_gives_up_instead_of_hanging(cases): """`indexedDB.open` fires neither `success` nor `error` when an upgrade is blocked by another connection — it fires `blocked`, and unhandled that leaves the promise unsettled for ever. Version 2 arrived with the playlists, so every browser that had used this site before it has a version 1 to upgrade, and a second tab holding one open is all it takes. """ c = cases["a blocked database upgrade gives up instead of hanging"] assert c["result"] != "never settled", c assert c["result"]["held"], "the fixture did not hold a connection open" assert c["result"]["outcome"].startswith("rejected:"), c["result"] # `blocked` fires the moment the upgrade is attempted, so this is immediate. # The deadline behind it is a backstop for the case where even `blocked` # never arrives — tolerating five seconds here would let the backstop stand # in for the handler and the handler could be deleted unnoticed. assert "blocked" in c["result"]["outcome"], ( f"gave up on a timeout rather than on the blocked event: {c['result']}") assert c["result"]["ms"] < 1000, f"it took {c['result']['ms']}ms to give up"