From c3d4ab186f779e7d45a3552f06b2c6a994cb01ba Mon Sep 17 00:00:00 2001 From: Dotty Dotter Date: Wed, 6 May 2026 17:35:59 +0200 Subject: [PATCH] fix: icon()-Macro mit ignore-missing + Coverage-Test MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Folge zum scales.svg-Vorfall (commit 01ea766): 1. icon.html: `{% include … ignore missing %}` — fehlende SVG-Files rendern jetzt leeren Span statt einen 500 auszuloesen. data-icon- Attribut zeigt den angefragten Namen, hilft im DevTools-Inspector. 2. tests/test_icons.py: scannt alle templates/-Files nach icon("name")-Aufrufen und prueft, dass jedes referenzierte Icon als SVG-File existiert. 4 Tests, alle gruen — verhindert dass solche Aufrufe in Zukunft unentdeckt durchrutschen. Co-Authored-By: Claude Opus 4.7 (1M context) --- app/templates/v2/components/icon.html | 3 +- tests/test_icons.py | 63 +++++++++++++++++++++++++++ 2 files changed, 65 insertions(+), 1 deletion(-) create mode 100644 tests/test_icons.py diff --git a/app/templates/v2/components/icon.html b/app/templates/v2/components/icon.html index 3f1ec60..86eaa51 100644 --- a/app/templates/v2/components/icon.html +++ b/app/templates/v2/components/icon.html @@ -1 +1,2 @@ -{% macro icon(name, size=16, cls="") %}{% endmacro %} +{# `ignore missing`: fehlendes Icon-File rendert leeren Span statt 500. #} +{% macro icon(name, size=16, cls="") %}{% endmacro %} diff --git a/tests/test_icons.py b/tests/test_icons.py new file mode 100644 index 0000000..c6ac2c6 --- /dev/null +++ b/tests/test_icons.py @@ -0,0 +1,63 @@ +"""Verify dass jeder im Template-Code referenzierte Icon-Name als +phosphor/.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"): + 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)}" + )