Some checks failed
Tests / Go Tests (push) Has been cancelled
Tests / Python Tests (push) Has been cancelled
Tests / Integration Tests (push) Has been cancelled
Tests / Go Lint (push) Has been cancelled
Tests / Python Lint (push) Has been cancelled
Tests / Security Scan (push) Has been cancelled
Tests / All Checks Passed (push) Has been cancelled
Security Scanning / Secret Scanning (push) Has been cancelled
Security Scanning / Dependency Vulnerability Scan (push) Has been cancelled
Security Scanning / Go Security Scan (push) Has been cancelled
Security Scanning / Python Security Scan (push) Has been cancelled
Security Scanning / Node.js Security Scan (push) Has been cancelled
Security Scanning / Docker Image Security (push) Has been cancelled
Security Scanning / Security Summary (push) Has been cancelled
CI/CD Pipeline / Go Tests (push) Has been cancelled
CI/CD Pipeline / Python Tests (push) Has been cancelled
CI/CD Pipeline / Website Tests (push) Has been cancelled
CI/CD Pipeline / Linting (push) Has been cancelled
CI/CD Pipeline / Security Scan (push) Has been cancelled
CI/CD Pipeline / Docker Build & Push (push) Has been cancelled
CI/CD Pipeline / Integration Tests (push) Has been cancelled
CI/CD Pipeline / Deploy to Staging (push) Has been cancelled
CI/CD Pipeline / Deploy to Production (push) Has been cancelled
CI/CD Pipeline / CI Summary (push) Has been cancelled
ci/woodpecker/manual/build-ci-image Pipeline was successful
ci/woodpecker/manual/main Pipeline failed
All services: admin-v2, studio-v2, website, ai-compliance-sdk, consent-service, klausur-service, voice-service, and infrastructure. Large PDFs and compiled binaries excluded via .gitignore.
254 lines
10 KiB
Python
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"
|