ouroboros/tests/test_web_dialogs_static.py
Ouroboros c5a705bfd8 release v6.90.3: every native browser dialog is replaced by the in-app dialog
All 21 window.prompt/confirm/alert sites across web/modules migrate to the
in-app openConfirmDialog (owner decision: the full class, not just the broken
sites), with a new single-button alert mode in confirm_dialog.js. This fixes
the two DEAD window.prompt sites on the macOS desktop shell — the cocoa shell
has no prompt delegate, so marketplace version updates and evolution campaign
objectives silently returned null there — while preserving each site's
contract: empty update version = latest, evolution cancel = do NOT start a
campaign, and the /panic critical control's complete confirm-and-send flow is
the injectable, node-tested chat.js::confirmAndSendPanic. Superseding an
INPUT dialog resolves the documented {confirmed:false, value:''} instead of a
bare false. A static quick-CI gate bans the native dialog trio from
web/modules so the class stays closed, and the canonical rule lives in
DEVELOPMENT.md with the confirm_dialog.js authority mapped in ARCHITECTURE.

Harness Accounts (#125) fetches immediately on page show, re-checks on
tab/page activation, and login polling backs off 6→30s and honestly gives up
into the existing unconfirmed verdict after 10 consecutive failures. The
login lifecycle is fully serialized (plan decision C7): ONE transition lock
covers start and dismiss, a new login starts only after the previous job is
terminal or provably cancelled (gone only on ok/404/410 — a 5xx or network
death refuses the restart instead of orphaning a live job server-side), an
unproven cancel keeps the card and the job id with a transient note that the
next successful poll read clears, the Retry button leaves the poll armed as
the recovery path, and both cancel continuations re-check the terminal
snapshot after their await so a settled job is never frozen behind a stale
cancel error — with the lost-contact verdict explicitly NOT counting as
proof of settlement (loginSettleProven). Six real-Chromium behavioral tests
pin the races, mutation-checked. Deleting every reviewer-slot row and saving
(#126) now surfaces the backend's 400 instead of pretending success. The
desktop pywebview-bridge branches in settings.js stay untouched by design.

Co-authored-by: Ouroboros <311266734+ouroboros-agent@users.noreply.github.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-08 17:43:11 +03:00

83 lines
3.6 KiB
Python

"""Static class ban: no native browser dialogs in ``web/modules``.
pywebview's WKWebView implements no ``window.prompt`` (it answers ``null``
silently — a dead control on the macOS desktop app), and native
``confirm()``/``alert()`` render OS-modal chrome outside the design system.
The whole class is therefore banned in favor of the in-house
``openConfirmDialog`` (input mode replaces prompt, ``alert: true`` replaces
alert) — owner decision Б2-2, v6.90.3 full-class migration.
Pattern follows ``tests/test_mcp_ui_static.py``: read the sources and assert
the structural fact — no browser needed, so quick CI enforces the ban
automatically.
"""
from __future__ import annotations
import pathlib
import re
REPO_ROOT = pathlib.Path(__file__).resolve().parents[1]
WEB_MODULES = REPO_ROOT / "web" / "modules"
# A real native-dialog CALL:
# * ``window.prompt(`` / ``window . confirm(`` / ``window.alert(``, or
# * bare ``prompt(`` / ``confirm(`` / ``alert(`` NOT preceded by an
# identifier character or ``.``.
# Deliberate non-matches:
# * ``openConfirmDialog(`` — the name continues past ``confirm``;
# * ``promptProfileName(`` etc. — same;
# * ``foo.confirm(`` — a method on our own object (``.`` excluded);
# * ``alert: true`` — an option, not a call.
NATIVE_DIALOG_CALL = re.compile(
r"window\s*\.\s*(?:prompt|confirm|alert)\s*\("
r"|(?<![\w$.])(?:prompt|confirm|alert)\s*\("
)
def _code_lines(source: str) -> list[tuple[int, str]]:
"""Source lines in code position: full-line ``//``/``*`` comment lines are
skipped, nothing else is stripped.
Deliberately NO block-comment blanking: a ``/*...*/`` DOTALL sweep cannot
tell a comment opener from the same two characters inside a string, and
``accept="*/*"`` in chat.js opened a pseudo comment that blanked 403 lines
of real code — a hole in the only automated enforcement of this class.
Trailing ``//`` comments are likewise not stripped (a ``//`` inside a URL
string would truncate real code). Both choices over-approximate in the
safe direction: a call smuggled into a comment is reported, which for a
class ban is a nuisance, while a blind region is a defect."""
lines: list[tuple[int, str]] = []
for lineno, line in enumerate(source.splitlines(), 1):
stripped = line.strip()
if stripped.startswith("//") or stripped.startswith("*"):
continue
lines.append((lineno, line))
return lines
def test_no_native_dialog_calls_in_web_modules() -> None:
sources = sorted(WEB_MODULES.glob("**/*.js"))
assert sources, f"no JS modules found under {WEB_MODULES}"
violations: list[str] = []
for path in sources:
for lineno, line in _code_lines(path.read_text(encoding="utf-8")):
if NATIVE_DIALOG_CALL.search(line):
violations.append(
f"{path.relative_to(REPO_ROOT)}:{lineno}: {line.strip()}"
)
assert not violations, (
"Native browser dialogs are banned in web/modules: window.prompt is a "
"silent no-op under pywebview (macOS desktop), and confirm()/alert() "
"bypass the design system. Use openConfirmDialog from "
"web/modules/confirm_dialog.js (input mode for prompt, alert:true for "
"alert).\n" + "\n".join(violations)
)
def test_confirm_dialog_offers_the_alert_mode() -> None:
"""The replacement the ban points at must actually exist: the in-house
dialog exposes the alert option the migrated alert() sites rely on."""
source = (WEB_MODULES / "confirm_dialog.js").read_text(encoding="utf-8")
assert "alert = false" in source
assert "openConfirmDialog" in source