Finance-Accounts/ar-aging-app/backend/tests/test_frontend_guards.py

55 lines
2.1 KiB
Python

"""
Source guards for frontend pitfalls that unit tests cannot catch.
Native browser dialogs (window.confirm / alert / prompt) are silently suppressed in
embedded and sandboxed browser contexts: confirm() returns false without ever showing a
dialog, so any action gated behind it becomes a no-op with no error. This actually
happened — the Dashboard delete button did nothing because of it. Use ConfirmDialog
(components/ui.tsx) instead.
"""
from __future__ import annotations
import re
from pathlib import Path
SRC = Path(__file__).resolve().parents[2] / "frontend" / "src"
# `confirm(`, `alert(`, `prompt(` as bare calls or on window.
_NATIVE_DIALOG = re.compile(r"(?:\bwindow\s*\.\s*)?\b(confirm|alert|prompt)\s*\(")
_ALLOWED = {"ConfirmDialog"} # our own component, not the native dialog
def _sources() -> list[Path]:
return [p for p in SRC.rglob("*.ts*") if p.is_file()]
def test_no_native_browser_dialogs():
offenders: list[str] = []
for path in _sources():
for i, line in enumerate(path.read_text().splitlines(), 1):
stripped = line.strip()
if stripped.startswith("//") or stripped.startswith("*"):
continue
m = _NATIVE_DIALOG.search(line)
if m and not any(a in line for a in _ALLOWED):
offenders.append(f"{path.relative_to(SRC)}:{i}: {stripped[:90]}")
assert not offenders, (
"Native browser dialogs are suppressed in embedded contexts and silently "
"cancel the action. Use <ConfirmDialog> instead:\n " + "\n ".join(offenders)
)
def test_confirm_dialog_component_exists():
ui = (SRC / "components" / "ui.tsx").read_text()
assert "export function ConfirmDialog" in ui
def test_destructive_actions_use_confirm_dialog():
"""Anything calling deleteSession must route through the in-app dialog."""
for path in _sources():
text = path.read_text()
if "deleteSession" in text and "api.ts" not in path.name and "client.ts" not in path.name:
assert "ConfirmDialog" in text, (
f"{path.relative_to(SRC)} deletes a closing without <ConfirmDialog>"
)