mirror of
https://github.com/smittix/intercept.git
synced 2026-06-18 18:39:47 -07:00
test: mode registry consistency checks; fail fast if registry missing
Also documents the registry-driven mode integration in CLAUDE.md. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -161,7 +161,13 @@ Each signal type has its own Flask blueprint:
|
||||
- **Templates**: `templates/index.html` (main SPA), `templates/partials/modes/*.html` (sidebar panels), `templates/partials/nav.html` (global nav)
|
||||
- **JS Modules**: `static/js/modes/*.js` - IIFE pattern per mode (e.g., `WeatherSat`, `SSTV`, `Meshtastic`)
|
||||
- **CSS**: `static/css/modes/*.css` - scoped styles per mode, CSS variables for theming (`--bg-card`, `--accent-cyan`, `--font-mono`)
|
||||
- **Mode Integration**: Each mode needs entries in `index.html` at ~12 points: CSS include, welcome card, partial include, visuals container, JS include, `validModes` set, `modeGroups` map, classList toggle, `modeNames`, visuals display toggle, titles, and init call in `switchMode()`
|
||||
- **Mode Integration**: Each mode is declared once in `static/js/mode-registry.js`
|
||||
(label, group, elementId, module, init/destroy hooks, visuals flag). The
|
||||
catalog, sidebar toggles, destroy map, visuals list, and init dispatch in
|
||||
`templates/index.html` are all derived from it. A new mode additionally needs:
|
||||
its partial in `templates/partials/modes/`, entries in the CSS/JS lazy-load
|
||||
asset maps in `index.html`, and its include in the partials block.
|
||||
`tests/test_mode_registry.py` enforces registry/asset consistency.
|
||||
|
||||
### Docker
|
||||
- `Dockerfile` - Single-stage build with all SDR tools compiled from source (dump1090, AIS-catcher, slowrx, SatDump, etc.). CMD runs `start.sh` (gunicorn + gevent)
|
||||
|
||||
@@ -3815,6 +3815,9 @@
|
||||
}
|
||||
|
||||
// Mode from query string (e.g., /?mode=wifi)
|
||||
if (!window.INTERCEPT_MODES) {
|
||||
throw new Error('mode-registry.js failed to load — the SPA cannot start');
|
||||
}
|
||||
let pendingStartMode = null;
|
||||
const modeCatalog = {};
|
||||
for (const [mode, def] of Object.entries(window.INTERCEPT_MODES)) {
|
||||
@@ -4918,7 +4921,7 @@
|
||||
refreshDroneDevices();
|
||||
}
|
||||
|
||||
// Module destroy is now handled by moduleDestroyMap above.
|
||||
// Module destroy is now handled by the mode registry (static/js/mode-registry.js).
|
||||
|
||||
// Show/hide Device Intelligence for modes that use it (not for satellite/aircraft/tscm)
|
||||
const reconBtn = document.getElementById('reconBtn');
|
||||
@@ -4999,7 +5002,7 @@
|
||||
}
|
||||
if (requestId !== modeSwitchRequestId) return;
|
||||
|
||||
// Waterfall destroy is now handled by moduleDestroyMap above.
|
||||
// Waterfall destroy is now handled by the mode registry (static/js/mode-registry.js).
|
||||
|
||||
const totalMs = Math.round(performance.now() - switchStartMs);
|
||||
console.info(
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
"""Consistency checks between the mode registry and the template/assets."""
|
||||
|
||||
import re
|
||||
from pathlib import Path
|
||||
|
||||
ROOT = Path(__file__).resolve().parent.parent
|
||||
REGISTRY = ROOT / "static" / "js" / "mode-registry.js"
|
||||
INDEX = ROOT / "templates" / "index.html"
|
||||
|
||||
|
||||
def _registry_modes() -> set[str]:
|
||||
src = REGISTRY.read_text()
|
||||
return set(re.findall(r"^\s{4}([a-z_]+):\s*\{", src, re.M))
|
||||
|
||||
|
||||
def test_registry_has_all_modes():
|
||||
"""The registry must declare a sane number of modes (28 at creation)."""
|
||||
modes = _registry_modes()
|
||||
assert len(modes) >= 28, f"registry lost modes: {sorted(modes)}"
|
||||
|
||||
|
||||
def test_registry_modes_have_partials():
|
||||
"""Every partial included by index.html must exist on disk."""
|
||||
html = INDEX.read_text()
|
||||
partials = set(re.findall(r"partials/modes/([\w.-]+)\.html", html))
|
||||
for partial in partials:
|
||||
assert (ROOT / "templates" / "partials" / "modes" / f"{partial}.html").exists(), (
|
||||
f"index.html includes missing partial: {partial}"
|
||||
)
|
||||
|
||||
|
||||
def test_no_orphan_mode_assets():
|
||||
"""Every modes/*.js and modes/*.css file is referenced somewhere."""
|
||||
referenced = INDEX.read_text() + REGISTRY.read_text()
|
||||
# ground_station_waterfall.js belongs to the satellite dashboard
|
||||
referenced += (ROOT / "templates" / "satellite_dashboard.html").read_text()
|
||||
for asset_dir, ext in [("static/js/modes", ".js"), ("static/css/modes", ".css")]:
|
||||
for f in (ROOT / asset_dir).glob(f"*{ext}"):
|
||||
assert f.name in referenced, f"orphaned mode asset: {f}"
|
||||
Reference in New Issue
Block a user