"""Static checks on static/app.js — voice-message UI + vault panel. Asserts the plugin's frontend code declares the right hooks and URLs without needing a browser. Runtime behavior tested manually + by upstream-drift smoke. """ import re from pathlib import Path APP_JS = Path(__file__).resolve().parents[2] / "static" / "app.js" def test_app_js_exists_and_nonempty(): assert APP_JS.is_file() assert APP_JS.stat().st_size > 0 def test_app_js_idempotent_guard(): """Loading twice must not re-execute — protocol Rule 4 (no surprise side effects).""" src = APP_JS.read_text() assert "__svrntyExtLoaded" in src assert "if (window.__svrntyExtLoaded) return" in src def test_app_js_voice_message_filename_prefix(): """voice-message-* prefix is the contract that the audio processor recognizes.""" src = APP_JS.read_text() assert "voice-message-" in src, ( "static/app.js must produce voice-message-* filenames so " "routes/transcribe.py:_transcribe_audio_attachments triggers" ) def test_app_js_uses_data_transfer_for_file_attach(): """DataTransfer is the only cross-browser way to set programmatically.""" src = APP_JS.read_text() assert "new DataTransfer()" in src def test_app_js_vault_status_url_pinned(): """Frontend → /api/vault/status URL must match the plugin route.""" src = APP_JS.read_text() assert re.search(r"['\"`]/api/vault/status['\"`]", src), \ "vault panel must call /api/vault/status (served by routes/vault_status.py)" def test_app_js_overrides_mic_button(): """Plugin owns #btnMic. Must use idempotent dataset flag to avoid double-install.""" src = APP_JS.read_text() assert "btnMic" in src assert "svrntyVm" in src or "dataset" in src