2026-05-06 17:35:59 +02:00
|
|
|
"""Verify dass jeder im Template-Code referenzierte Icon-Name als
|
|
|
|
|
phosphor/<name>.svg-File existiert.
|
|
|
|
|
|
|
|
|
|
Hintergrund: Vor diesem Test gab es einen 500 nach Login, weil
|
|
|
|
|
`base.html` `icon("scales")` aufrief, das SVG aber nicht im Repo lag
|
|
|
|
|
(commit 741faae). Mit `ignore missing` rendert das Macro jetzt leer
|
|
|
|
|
statt zu crashen — aber wir wollen trotzdem in CI sehen, dass kein
|
|
|
|
|
Aufruf ins Leere läuft.
|
|
|
|
|
"""
|
|
|
|
|
from __future__ import annotations
|
|
|
|
|
|
|
|
|
|
import re
|
|
|
|
|
from pathlib import Path
|
|
|
|
|
|
|
|
|
|
import pytest
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
REPO_ROOT = Path(__file__).resolve().parent.parent
|
|
|
|
|
TEMPLATES_DIR = REPO_ROOT / "app" / "templates"
|
|
|
|
|
ICONS_DIR = TEMPLATES_DIR / "v2" / "icons" / "phosphor"
|
|
|
|
|
|
|
|
|
|
# Matcht: icon("name", …) oder icon('name', …) — Default-Quote +/-.
|
|
|
|
|
ICON_CALL_PATTERN = re.compile(r"""\bicon\(\s*['"]([a-z0-9-]+)['"]""")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def _collect_referenced_icons() -> set[str]:
|
|
|
|
|
"""Sammle alle Icon-Namen, die irgendwo in templates/ aufgerufen werden."""
|
|
|
|
|
referenced: set[str] = set()
|
|
|
|
|
for path in TEMPLATES_DIR.rglob("*.html"):
|
2026-05-09 22:29:37 +02:00
|
|
|
# macOS-AppleDouble-Metadaten (._foo.html) ueberspringen — keine Templates.
|
|
|
|
|
if path.name.startswith("._"):
|
|
|
|
|
continue
|
2026-05-06 17:35:59 +02:00
|
|
|
text = path.read_text(encoding="utf-8")
|
|
|
|
|
for m in ICON_CALL_PATTERN.finditer(text):
|
|
|
|
|
referenced.add(m.group(1))
|
|
|
|
|
return referenced
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def _existing_icons() -> set[str]:
|
|
|
|
|
"""Set der existierenden phosphor-Icon-Namen (ohne .svg)."""
|
|
|
|
|
if not ICONS_DIR.exists():
|
|
|
|
|
return set()
|
|
|
|
|
return {p.stem for p in ICONS_DIR.glob("*.svg")}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class TestIconCoverage:
|
|
|
|
|
def test_phosphor_dir_exists(self):
|
|
|
|
|
assert ICONS_DIR.is_dir(), f"erwartetes Verzeichnis fehlt: {ICONS_DIR}"
|
|
|
|
|
|
|
|
|
|
def test_at_least_one_icon_present(self):
|
|
|
|
|
icons = _existing_icons()
|
|
|
|
|
assert icons, "keine Icon-Files im phosphor/-Verzeichnis"
|
|
|
|
|
|
|
|
|
|
def test_at_least_one_call_referenced(self):
|
|
|
|
|
refs = _collect_referenced_icons()
|
|
|
|
|
assert refs, "keine icon('name')-Aufrufe in Templates gefunden"
|
|
|
|
|
|
|
|
|
|
def test_every_referenced_icon_has_file(self):
|
|
|
|
|
"""Jeder im Template referenzierte Icon-Name muss als SVG-File existieren."""
|
|
|
|
|
referenced = _collect_referenced_icons()
|
|
|
|
|
existing = _existing_icons()
|
|
|
|
|
missing = referenced - existing
|
|
|
|
|
assert not missing, (
|
|
|
|
|
f"{len(missing)} referenzierte Icons fehlen in "
|
|
|
|
|
f"{ICONS_DIR.relative_to(REPO_ROOT)}: {sorted(missing)}"
|
|
|
|
|
)
|