This repository has been archived on 2026-02-15. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
breakpilot-pwa/backend/tests/test_design_system.py
Benjamin Admin bfdaf63ba9 fix: Restore all files lost during destructive rebase
A previous `git pull --rebase origin main` dropped 177 local commits,
losing 3400+ files across admin-v2, backend, studio-v2, website,
klausur-service, and many other services. The partial restore attempt
(660295e2) only recovered some files.

This commit restores all missing files from pre-rebase ref 98933f5e
while preserving post-rebase additions (night-scheduler, night-mode UI,
NightModeWidget dashboard integration).

Restored features include:
- AI Module Sidebar (FAB), OCR Labeling, OCR Compare
- GPU Dashboard, RAG Pipeline, Magic Help
- Klausur-Korrektur (8 files), Abitur-Archiv (5+ files)
- Companion, Zeugnisse-Crawler, Screen Flow
- Full backend, studio-v2, website, klausur-service
- All compliance SDKs, agent-core, voice-service
- CI/CD configs, documentation, scripts

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-09 09:51:32 +01:00

254 lines
10 KiB
Python

"""
Tests fuer das Design-System (Light/Dark Mode, CSS-Variablen).
Testet:
- CSS-Variablen-Definitionen
- Theme-Switching (Light/Dark Mode)
- Footer-Struktur mit Legal-Links
- Design-Konsistenz
"""
import pytest
import re
from pathlib import Path
class TestCSSVariables:
"""Tests fuer CSS-Variablen im Design-System."""
@pytest.fixture
def studio_css(self):
"""Laedt CSS-Variablen fuer Tests.
Nach dem Refactoring sind die Variablen in modules/base/variables.css.
Fallback auf studio.css fuer Abwaertskompatibilitaet.
"""
# Primaer: Modularisierte Variablen-Datei
variables_path = Path(__file__).parent.parent / "frontend" / "static" / "css" / "modules" / "base" / "variables.css"
if variables_path.exists():
return variables_path.read_text()
# Fallback: Legacy studio.css
css_path = Path(__file__).parent.parent / "frontend" / "static" / "css" / "studio.css"
if css_path.exists():
return css_path.read_text()
return None
@pytest.fixture
def base_py(self):
"""Laedt base.py fuer Tests."""
base_path = Path(__file__).parent.parent / "frontend" / "components" / "base.py"
if base_path.exists():
return base_path.read_text()
return None
def test_dark_mode_primary_color_is_teal(self, studio_css):
"""Test: Dark Mode Primary ist Teal (#0f766e)."""
if studio_css is None:
pytest.skip("studio.css nicht gefunden")
# Suche nach --bp-primary in :root (Dark Mode)
root_match = re.search(r':root\s*\{([^}]+)\}', studio_css, re.DOTALL)
assert root_match is not None, ":root Block nicht gefunden"
root_content = root_match.group(1)
assert "--bp-primary: #0f766e" in root_content, "Dark Mode Primary sollte Teal (#0f766e) sein"
def test_dark_mode_accent_color_is_lime_green(self, studio_css):
"""Test: Dark Mode Accent ist Lime Green (#22c55e)."""
if studio_css is None:
pytest.skip("studio.css nicht gefunden")
root_match = re.search(r':root\s*\{([^}]+)\}', studio_css, re.DOTALL)
assert root_match is not None, ":root Block nicht gefunden"
root_content = root_match.group(1)
assert "--bp-accent: #22c55e" in root_content, "Dark Mode Accent sollte Lime Green (#22c55e) sein"
def test_light_mode_primary_color_is_sky_blue(self, studio_css):
"""Test: Light Mode Primary ist Sky Blue (#0ea5e9)."""
if studio_css is None:
pytest.skip("studio.css nicht gefunden")
# Suche nach [data-theme="light"] Block
light_match = re.search(r'\[data-theme="light"\]\s*\{([^}]+)\}', studio_css, re.DOTALL)
assert light_match is not None, "[data-theme='light'] Block nicht gefunden"
light_content = light_match.group(1)
assert "--bp-primary: #0ea5e9" in light_content, "Light Mode Primary sollte Sky Blue (#0ea5e9) sein"
def test_light_mode_accent_color_is_fuchsia(self, studio_css):
"""Test: Light Mode Accent ist Fuchsia (#d946ef)."""
if studio_css is None:
pytest.skip("studio.css nicht gefunden")
light_match = re.search(r'\[data-theme="light"\]\s*\{([^}]+)\}', studio_css, re.DOTALL)
assert light_match is not None, "[data-theme='light'] Block nicht gefunden"
light_content = light_match.group(1)
assert "--bp-accent: #d946ef" in light_content, "Light Mode Accent sollte Fuchsia (#d946ef) sein"
def test_no_hardcoded_material_design_grays(self):
"""Test: Keine hardcodierten Material Design Grays (#E0E0E0, #F5F5F5, #F8F8F8)."""
# Pruefe alle CSS-Dateien im modules-Verzeichnis
css_base = Path(__file__).parent.parent / "frontend" / "static" / "css"
modules_dir = css_base / "modules"
if not modules_dir.exists():
# Fallback: Pruefe nur studio.css
css_path = css_base / "studio.css"
if not css_path.exists():
pytest.skip("Keine CSS-Dateien gefunden")
css_files = [css_path]
else:
css_files = list(modules_dir.rglob("*.css"))
# Diese sollten durch CSS-Variablen ersetzt sein
# Ausnahme: In Kommentaren oder Variable-Definitionen
problem_colors = ['#E0E0E0', '#F5F5F5', '#F8F8F8']
for css_file in css_files:
lines = css_file.read_text().split('\n')
for line_num, line in enumerate(lines, 1):
# Ueberspringe Kommentare
if line.strip().startswith('/*') or line.strip().startswith('*') or line.strip().startswith('//'):
continue
# Ueberspringe Variable-Definitionen im Light-Mode Block
if '--bp-' in line:
continue
for color in problem_colors:
if color in line.upper():
pytest.fail(f"Hardcodierte Farbe {color} gefunden in {css_file.name}:{line_num}: {line.strip()}")
def test_light_mode_uses_slate_colors(self, studio_css):
"""Test: Light Mode verwendet Slate-Farbpalette."""
if studio_css is None:
pytest.skip("studio.css nicht gefunden")
light_match = re.search(r'\[data-theme="light"\]\s*\{([^}]+)\}', studio_css, re.DOTALL)
assert light_match is not None, "[data-theme='light'] Block nicht gefunden"
light_content = light_match.group(1)
# Slate-50 fuer Background
assert "#f8fafc" in light_content.lower(), "Light Mode sollte Slate-50 (#f8fafc) verwenden"
# Slate-200 fuer Border
assert "#e2e8f0" in light_content.lower(), "Light Mode sollte Slate-200 (#e2e8f0) fuer Border verwenden"
def test_base_py_has_website_design_light_mode(self, base_py):
"""Test: base.py verwendet Website Design fuer Light Mode."""
if base_py is None:
pytest.skip("base.py nicht gefunden")
# Pruefe auf Website Design Kommentar
assert "Website Design" in base_py, "base.py sollte Website Design verwenden"
assert "Sky Blue" in base_py or "#0ea5e9" in base_py, "base.py sollte Sky Blue fuer Light Mode verwenden"
assert "Fuchsia" in base_py or "#d946ef" in base_py, "base.py sollte Fuchsia fuer Light Mode verwenden"
class TestFooterStructure:
"""Tests fuer Footer-Struktur mit Legal-Links."""
@pytest.fixture
def studio_html(self):
"""Laedt studio.html fuer Tests."""
html_path = Path(__file__).parent.parent / "frontend" / "templates" / "studio.html"
if html_path.exists():
return html_path.read_text()
return None
@pytest.fixture
def base_py(self):
"""Laedt base.py fuer Tests."""
base_path = Path(__file__).parent.parent / "frontend" / "components" / "base.py"
if base_path.exists():
return base_path.read_text()
return None
def test_footer_has_impressum_link(self, studio_html):
"""Test: Footer enthaelt Impressum-Link."""
if studio_html is None:
pytest.skip("studio.html nicht gefunden")
assert "Impressum" in studio_html, "Footer sollte Impressum-Link enthalten"
def test_footer_has_agb_link(self, studio_html):
"""Test: Footer enthaelt AGB-Link."""
if studio_html is None:
pytest.skip("studio.html nicht gefunden")
assert "AGB" in studio_html, "Footer sollte AGB-Link enthalten"
def test_footer_has_datenschutz_link(self, studio_html):
"""Test: Footer enthaelt Datenschutz-Link."""
if studio_html is None:
pytest.skip("studio.html nicht gefunden")
assert "Datenschutz" in studio_html, "Footer sollte Datenschutz-Link enthalten"
def test_footer_has_cookies_link(self, studio_html):
"""Test: Footer enthaelt Cookies-Link."""
if studio_html is None:
pytest.skip("studio.html nicht gefunden")
assert "Cookies" in studio_html, "Footer sollte Cookies-Link enthalten"
def test_footer_has_deine_rechte_link(self, studio_html):
"""Test: Footer enthaelt Deine Rechte (GDPR)-Link."""
if studio_html is None:
pytest.skip("studio.html nicht gefunden")
assert "Deine Rechte" in studio_html, "Footer sollte 'Deine Rechte' (GDPR)-Link enthalten"
def test_footer_has_einstellungen_link(self, studio_html):
"""Test: Footer enthaelt Einstellungen-Link."""
if studio_html is None:
pytest.skip("studio.html nicht gefunden")
assert "Einstellungen" in studio_html, "Footer sollte Einstellungen-Link enthalten"
def test_base_py_footer_has_all_links(self, base_py):
"""Test: base.py Footer enthaelt alle erforderlichen Links."""
if base_py is None:
pytest.skip("base.py nicht gefunden")
required_links = ["Impressum", "AGB", "Datenschutz", "Cookies", "Deine Rechte", "Einstellungen"]
for link in required_links:
assert link in base_py, f"base.py Footer sollte '{link}'-Link enthalten"
class TestThemeSwitching:
"""Tests fuer Theme-Switching Funktionalitaet."""
@pytest.fixture
def studio_js(self):
"""Laedt studio.js fuer Tests."""
js_path = Path(__file__).parent.parent / "frontend" / "static" / "js" / "studio.js"
if js_path.exists():
return js_path.read_text()
return None
def test_theme_toggle_function_exists(self, studio_js):
"""Test: Theme-Toggle Funktion existiert."""
if studio_js is None:
pytest.skip("studio.js nicht gefunden")
assert "initThemeToggle" in studio_js or "theme-toggle" in studio_js, \
"Theme-Toggle Funktionalitaet sollte existieren"
def test_theme_saved_to_localstorage(self, studio_js):
"""Test: Theme wird in localStorage gespeichert."""
if studio_js is None:
pytest.skip("studio.js nicht gefunden")
assert "localStorage" in studio_js, "Theme sollte in localStorage gespeichert werden"
assert "bp-theme" in studio_js or "bp_theme" in studio_js, \
"Theme-Key sollte 'bp-theme' oder 'bp_theme' sein"
def test_data_theme_attribute_used(self, studio_js):
"""Test: data-theme Attribut wird verwendet."""
if studio_js is None:
pytest.skip("studio.js nicht gefunden")
assert "data-theme" in studio_js, "data-theme Attribut sollte fuer Theme-Switching verwendet werden"