1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
|
"""
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"
|