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
|
"""
`window.prompt` does not exist in the desktop client.
The same `static/` tree is the web page and the application (CLAUDE.md's "one
UI source"), and Electron does not implement `prompt` — Chromium leaves it to
the embedder and Electron declines. It does not return null: it **throws**,
`Error: prompt() is not supported.`
That made the Files toolbar's New folder button do nothing whatsoever. The call
sat above its own try, so the click produced no folder, no error, and nothing
on screen to react to — the failure looks exactly like a dead button, which is
what it was reported as.
Measured rather than assumed, against this repo's own Electron 44:
prompt('name?') -> Error: prompt() is not supported.
confirm('sure?') -> opens a real modal
alert('hi') -> opens a real modal
So `confirm` and `alert` stay allowed and are used in a dozen places; only
`prompt` is banned. Anything that needs typed input needs a field.
"""
import re
from pathlib import Path
import pytest
STATIC = Path(__file__).resolve().parents[1] / "src" / "meshbay_hub" / "static"
pytestmark = pytest.mark.skipif(not STATIC.exists(),
reason="SPA sources unavailable")
# `prompt(` as a call, not `window.prompt` inside a comment or a longer
# identifier like `mkdir_prompt` / `promptForName`.
CALL = re.compile(r"(?<![\w.$])(?:window\.)?prompt\s*\(")
def _code_only(source: str) -> str:
source = re.sub(r"/\*.*?\*/", "", source, flags=re.S)
return re.sub(r"^\s*//.*$", "", source, flags=re.M)
def test_nothing_calls_prompt():
offenders = []
for path in sorted(STATIC.glob("*.js")):
if path.name == "sw.js":
continue
for i, line in enumerate(_code_only(
path.read_text(encoding="utf-8")).splitlines(), 1):
if CALL.search(line):
offenders.append(f"{path.name}:{i}: {line.strip()}")
assert not offenders, (
"prompt() throws in the desktop client, and the click that reaches it "
"does nothing at all:\n " + "\n ".join(offenders))
def test_the_pattern_would_catch_a_real_call():
"""
A guard that matches nothing passes over an empty set, which looks exactly
like success. Both spellings, and the near-misses it must not flag.
"""
assert CALL.search("const n = prompt('x');")
assert CALL.search("const n = window.prompt('x');")
assert not CALL.search("t('group.mkdir_prompt')")
assert not CALL.search("promptForName();")
assert not CALL.search("this.prompt(1);")
def test_creating_a_folder_uses_a_field():
"""
The control the ban is about. A typed name needs somewhere to type it, and
an inline field can show the node's refusal beside the input rather than
after a dialog has closed.
"""
source = (STATIC / "files-app.js").read_text(encoding="utf-8")
assert "tb-mkdir-input" in source
assert "newDirName" in source
assert "group.mkdir_prompt" in source, "the field has no label or placeholder"
def test_leaving_the_folder_drops_a_half_typed_name():
"""
Otherwise the folder is created where the person is no longer looking —
they navigated away, the draft came along, and the name lands in a
directory they were not thinking about.
"""
source = (STATIC / "files-app.js").read_text(encoding="utf-8")
assert "useEffect(() => { setNewDirName(null); }, [currentPath]);" in source
|