Redesign Sentinel UI: test_frontend_contract.py
This commit is contained in:
parent
ff46387a43
commit
630f980906
1 changed files with 77 additions and 0 deletions
77
backup-monitor/tests/test_frontend_contract.py
Normal file
77
backup-monitor/tests/test_frontend_contract.py
Normal file
|
|
@ -0,0 +1,77 @@
|
||||||
|
import re
|
||||||
|
import subprocess
|
||||||
|
import unittest
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
|
||||||
|
ROOT = Path(__file__).resolve().parents[1]
|
||||||
|
HTML = (ROOT / "templates" / "index.html").read_text(encoding="utf-8")
|
||||||
|
JS = (ROOT / "static" / "js" / "app.js").read_text(encoding="utf-8")
|
||||||
|
CSS = (ROOT / "static" / "css" / "style.css").read_text(encoding="utf-8")
|
||||||
|
|
||||||
|
|
||||||
|
class FrontendContractTests(unittest.TestCase):
|
||||||
|
def test_stable_dom_ids_exist_once(self):
|
||||||
|
required = {
|
||||||
|
"nav-dashboard", "nav-alerts", "nav-hosts", "nav-config",
|
||||||
|
"sysStatus", "lastScan", "mOk", "mSize", "mLatest",
|
||||||
|
"mLatestHost", "mWarn", "mWarnCard", "clusterList",
|
||||||
|
"liveStream", "chartSvg", "hostGrid", "alertList",
|
||||||
|
"drawer", "drawerBg", "drawerTitle", "drawerBody",
|
||||||
|
"modal", "modalBg", "hostForm", "formMode", "formName",
|
||||||
|
"formKumaUrl", "formEnabled", "toasts",
|
||||||
|
}
|
||||||
|
for element_id in required:
|
||||||
|
count = len(re.findall(rf'id=["\']{re.escape(element_id)}["\']', HTML))
|
||||||
|
self.assertEqual(count, 1, f"#{element_id} must exist exactly once")
|
||||||
|
|
||||||
|
def test_uses_local_styles_without_runtime_css_framework(self):
|
||||||
|
self.assertIn('/static/css/style.css', HTML)
|
||||||
|
self.assertNotIn('cdn.tailwindcss.com', HTML)
|
||||||
|
self.assertNotIn('Material+Symbols', HTML)
|
||||||
|
self.assertNotIn('backdrop-blur', HTML)
|
||||||
|
self.assertNotIn('pulse-', HTML)
|
||||||
|
|
||||||
|
def test_monitor_surface_has_semantic_landmarks_and_accessible_actions(self):
|
||||||
|
self.assertRegex(HTML, r'<nav[^>]+aria-label="Hauptnavigation"')
|
||||||
|
self.assertIn('<main', HTML)
|
||||||
|
self.assertIn('aria-label="Daten aktualisieren"', HTML)
|
||||||
|
self.assertIn('aria-label="Host hinzufügen"', HTML)
|
||||||
|
self.assertIn('aria-live="polite"', HTML)
|
||||||
|
|
||||||
|
def test_copy_is_plain_german(self):
|
||||||
|
expected = [
|
||||||
|
'Backup-Übersicht', 'Letzte Aktualisierung', 'Heute gesichert',
|
||||||
|
'Letzte Sicherung', 'Auffälligkeiten', 'Letzte Meldungen',
|
||||||
|
'Backup-Hosts', 'Einstellungen',
|
||||||
|
]
|
||||||
|
for text in expected:
|
||||||
|
self.assertIn(text, HTML)
|
||||||
|
forbidden = ['Operational command', 'Real-time surveillance', 'LIVE', 'Vault Overview']
|
||||||
|
for text in forbidden:
|
||||||
|
self.assertNotIn(text, HTML)
|
||||||
|
|
||||||
|
def test_css_contains_responsive_and_motion_contracts(self):
|
||||||
|
self.assertIn('@media (max-width: 720px)', CSS)
|
||||||
|
self.assertIn('@media (prefers-reduced-motion: reduce)', CSS)
|
||||||
|
self.assertIn(':focus-visible', CSS)
|
||||||
|
self.assertIn('--status-ok', CSS)
|
||||||
|
|
||||||
|
def test_javascript_uses_semantic_status_classes_and_german_labels(self):
|
||||||
|
self.assertIn('status--ok', JS)
|
||||||
|
self.assertIn('Betriebsbereit', JS)
|
||||||
|
self.assertIn('vor ', JS)
|
||||||
|
self.assertNotIn('material-symbols-outlined', JS)
|
||||||
|
self.assertNotIn('bg-${color}', JS)
|
||||||
|
|
||||||
|
def test_javascript_syntax(self):
|
||||||
|
result = subprocess.run(
|
||||||
|
["node", "--check", str(ROOT / "static" / "js" / "app.js")],
|
||||||
|
capture_output=True,
|
||||||
|
text=True,
|
||||||
|
)
|
||||||
|
self.assertEqual(result.returncode, 0, result.stderr)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
unittest.main()
|
||||||
Loading…
Add table
Add a link
Reference in a new issue