"""Anti-Regression-Wache gegen neue Inline-Styles in Templates (#184). Die Baseline wurde am Tag der Einführung dieser Wache eingefroren — ``tools/inline_styles_baseline.json``. Der Test prüft pro Datei: **aktuelle Anzahl <= Baseline**. Damit verhindert der Test neue Inline-Styles, ohne existierende sofort migrieren zu müssen. Bei der Migration eines Files (Inline-Styles → benannte Klassen) sinkt die Anzahl. Der Test bleibt grün, aber die Baseline driftet — sie kann einfach mit ``python3 tools/audit_inline_styles.py --baseline > tools/inline_styles_baseline.json`` neu eingefroren werden, damit die Wache nicht versehentlich auf den alten Stand zurückgesetzt wird. """ import json import re from pathlib import Path import pytest ROOT = Path(__file__).resolve().parent.parent TEMPLATES = ROOT / "app" / "templates" BASELINE = ROOT / "tools" / "inline_styles_baseline.json" PAT = re.compile(r'style="([^"]+)"') def _count_per_file() -> dict[str, int]: out: dict[str, int] = {} for f in sorted(set(TEMPLATES.rglob("*.html"))): rel = str(f.relative_to(ROOT)) n = sum(1 for _ in PAT.finditer(f.read_text(errors="replace"))) if n > 0: out[rel] = n return out @pytest.fixture(scope="module") def baseline() -> dict[str, int]: return json.loads(BASELINE.read_text()) @pytest.fixture(scope="module") def current() -> dict[str, int]: return _count_per_file() def test_baseline_file_exists(): assert BASELINE.exists(), ( f"Baseline {BASELINE} fehlt. Erst-Erzeugung: " "python3 tools/audit_inline_styles.py --baseline > tools/inline_styles_baseline.json" ) def test_no_new_inline_styles_per_file(current: dict[str, int], baseline: dict[str, int]): """Pro Datei darf die aktuelle Anzahl die Baseline nicht überschreiten.""" over = [] for path, current_count in current.items(): base = baseline.get(path) if base is None: # Neue Datei mit Inline-Styles: nicht erlaubt — keine neuen Inline-Styles. over.append(f" {path}: NEU mit {current_count} Inline-Styles, " "Baseline kennt diese Datei noch nicht") continue if current_count > base: over.append(f" {path}: {current_count} > Baseline {base}") assert not over, ( "Inline-Styles-Regression in folgenden Templates:\n" + "\n".join(over) + "\n\nFix: Inline-Styles in benannte Klassen überführen, oder " "(falls Migration in einer separaten PR läuft) Baseline aktualisieren:\n" " python3 tools/audit_inline_styles.py --baseline > tools/inline_styles_baseline.json" ) def test_baseline_total_does_not_grow(current: dict[str, int], baseline: dict[str, int]): """Globale Wache: Gesamtsumme darf nicht steigen.""" cur_total = sum(current.values()) base_total = sum(baseline.values()) assert cur_total <= base_total, ( f"Inline-Styles total: {cur_total} > Baseline {base_total}. " "Pro Datei prüfen via test_no_new_inline_styles_per_file." )