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_studio_frontend.py
BreakPilot Dev 19855efacc
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
feat: BreakPilot PWA - Full codebase (clean push without large binaries)
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.
2026-02-11 13:25:58 +01:00

255 lines
10 KiB
Python

"""
Tests fuer das BreakPilot Studio Frontend (studio.py)
Testet CSS-Regeln und HTML-Struktur des Frontends.
Nach dem Refactoring (2024-12-16) werden CSS und JS aus separaten Dateien geladen:
- CSS: frontend/static/css/studio.css
- JS: frontend/static/js/studio.js
- HTML: frontend/templates/studio.html
"""
import pytest
import re
import sys
import os
from pathlib import Path
# Add parent directory to path for imports
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
# Pfade zu den statischen Dateien
FRONTEND_DIR = Path(__file__).parent.parent / "frontend"
STATIC_CSS = FRONTEND_DIR / "static" / "css" / "studio.css"
STATIC_JS = FRONTEND_DIR / "static" / "js" / "studio.js"
TEMPLATE_HTML = FRONTEND_DIR / "templates" / "studio.html"
class TestStudioRefactoringStructure:
"""Tests fuer die refactored Dateistruktur."""
def test_css_file_exists(self):
"""Testet, dass die CSS-Datei existiert."""
assert STATIC_CSS.exists(), f"CSS-Datei nicht gefunden: {STATIC_CSS}"
def test_js_file_exists(self):
"""Testet, dass die JS-Datei existiert."""
assert STATIC_JS.exists(), f"JS-Datei nicht gefunden: {STATIC_JS}"
def test_html_template_exists(self):
"""Testet, dass das HTML-Template existiert."""
assert TEMPLATE_HTML.exists(), f"HTML-Template nicht gefunden: {TEMPLATE_HTML}"
def test_html_references_css(self):
"""Testet, dass das HTML-Template die CSS-Datei referenziert."""
html_content = TEMPLATE_HTML.read_text(encoding="utf-8")
assert '/static/css/studio.css' in html_content, \
"HTML-Template muss CSS-Datei referenzieren"
def test_html_references_js(self):
"""Testet, dass das HTML-Template die JS-Datei referenziert."""
html_content = TEMPLATE_HTML.read_text(encoding="utf-8")
assert '/static/js/studio.js' in html_content, \
"HTML-Template muss JS-Datei referenzieren"
def test_studio_py_loads_template(self):
"""Testet, dass studio.py das Template laedt."""
from frontend.studio import app_ui
html = app_ui()
# Nach Refactoring sollte HTML aus Template kommen
assert '<!DOCTYPE html>' in html or 'BreakPilot' in html, \
"studio.py muss HTML-Inhalt zurueckgeben"
class TestStudioSidebarCSS:
"""Tests fuer die Sidebar CSS-Eigenschaften."""
@pytest.fixture
def studio_css(self):
"""Laedt den CSS-Inhalt aus der separaten CSS-Datei.
Nach dem CSS-Refactoring sind die Styles modularisiert.
Sidebar-Styles befinden sich in modules/admin/sidebar.css.
"""
# Primaer: Modularisierte Sidebar-Datei
sidebar_css_path = FRONTEND_DIR / "static" / "css" / "modules" / "admin" / "sidebar.css"
if sidebar_css_path.exists():
return sidebar_css_path.read_text(encoding="utf-8")
# Fallback: Legacy studio.css
return STATIC_CSS.read_text(encoding="utf-8")
@pytest.fixture
def studio_html(self):
"""Laedt den HTML-Inhalt aus dem Template."""
return TEMPLATE_HTML.read_text(encoding="utf-8")
def test_sidebar_has_overflow_y_auto(self, studio_css):
"""
Testet, dass die Sidebar vertikal scrollbar ist.
Regression Test: Die Sidebar muss overflow-y: auto haben,
damit GPU Start/Stop Buttons sichtbar sind wenn viele
Kostenzeilen vorhanden sind.
"""
# Suche nach .sidebar CSS-Block
sidebar_css_pattern = r'\.sidebar\s*\{[^}]*\}'
sidebar_css_match = re.search(sidebar_css_pattern, studio_css, re.DOTALL)
assert sidebar_css_match is not None, "Sidebar CSS-Block nicht gefunden"
sidebar_css = sidebar_css_match.group(0)
# Pruefe dass overflow-y: auto vorhanden ist
assert 'overflow-y: auto' in sidebar_css or 'overflow-y:auto' in sidebar_css, \
"Sidebar muss overflow-y: auto haben fuer Scrollbarkeit"
def test_sidebar_has_overflow_x_hidden(self, studio_css):
"""
Testet, dass die Sidebar horizontal nicht scrollbar ist.
"""
sidebar_css_pattern = r'\.sidebar\s*\{[^}]*\}'
sidebar_css_match = re.search(sidebar_css_pattern, studio_css, re.DOTALL)
assert sidebar_css_match is not None, "Sidebar CSS-Block nicht gefunden"
sidebar_css_block = sidebar_css_match.group(0)
# Pruefe dass overflow-x: hidden vorhanden ist
assert 'overflow-x: hidden' in sidebar_css_block or 'overflow-x:hidden' in sidebar_css_block, \
"Sidebar muss overflow-x: hidden haben"
def test_sidebar_does_not_have_overflow_hidden_only(self, studio_css):
"""
Testet, dass die Sidebar NICHT nur overflow: hidden hat.
Regression Test: overflow: hidden wuerde verhindern,
dass Benutzer zu den GPU-Buttons scrollen koennen.
"""
sidebar_css_pattern = r'\.sidebar\s*\{[^}]*\}'
sidebar_css_match = re.search(sidebar_css_pattern, studio_css, re.DOTALL)
assert sidebar_css_match is not None, "Sidebar CSS-Block nicht gefunden"
sidebar_css_block = sidebar_css_match.group(0)
# Pruefe dass nicht einfach "overflow: hidden" steht (ohne x/y Spezifikation)
# Erlaubt sind: overflow-x: hidden, overflow-y: hidden, aber nicht nur "overflow: hidden"
has_overflow_hidden_only = re.search(r'overflow\s*:\s*hidden', sidebar_css_block) and \
not re.search(r'overflow-[xy]\s*:', sidebar_css_block)
assert not has_overflow_hidden_only, \
"Sidebar darf nicht nur 'overflow: hidden' haben - GPU Buttons waeren nicht erreichbar"
@pytest.mark.skip(reason="vast.ai GPU UI not yet implemented in frontend template")
class TestStudioGPUControls:
"""Tests fuer die GPU-Kontrollelemente.
HINWEIS: Diese Tests sind fuer zukuenftige vast.ai GPU-UI-Elemente.
Die Backend-API existiert bereits (infra/vast_power.py), aber die
Frontend-UI-Elemente wurden noch nicht in studio.html implementiert.
"""
@pytest.fixture
def studio_html(self):
"""Laedt den HTML-Inhalt aus dem Template."""
return TEMPLATE_HTML.read_text(encoding="utf-8")
def test_gpu_start_button_exists(self, studio_html):
"""Testet, dass der GPU Start-Button existiert."""
assert 'id="btn-vast-start"' in studio_html, \
"GPU Start-Button (btn-vast-start) nicht gefunden"
def test_gpu_stop_button_exists(self, studio_html):
"""Testet, dass der GPU Stop-Button existiert."""
assert 'id="btn-vast-stop"' in studio_html, \
"GPU Stop-Button (btn-vast-stop) nicht gefunden"
def test_gpu_status_badge_exists(self, studio_html):
"""Testet, dass der GPU Status-Badge existiert."""
assert 'id="vast-status-badge"' in studio_html, \
"GPU Status-Badge nicht gefunden"
def test_gpu_buttons_are_in_sidebar(self, studio_html):
"""
Testet, dass die GPU-Buttons innerhalb der Sidebar sind.
"""
# Finde die Sidebar-Sektion
sidebar_pattern = r'<aside[^>]*class="[^"]*sidebar[^"]*"[^>]*>.*?</aside>'
sidebar_match = re.search(sidebar_pattern, studio_html, re.DOTALL)
assert sidebar_match is not None, "Sidebar nicht gefunden"
sidebar_content = sidebar_match.group(0)
# Pruefe dass GPU-Buttons in der Sidebar sind
assert 'btn-vast-start' in sidebar_content, \
"GPU Start-Button muss in der Sidebar sein"
assert 'btn-vast-stop' in sidebar_content, \
"GPU Stop-Button muss in der Sidebar sein"
def test_gpu_cost_elements_exist(self, studio_html):
"""Testet, dass die Kostenanzeige-Elemente existieren."""
required_elements = [
'vast-cost-hour', # Kosten pro Stunde
'vast-credit', # Budget/Credit
'vast-session-cost', # Session-Kosten
]
for element_id in required_elements:
assert f'id="{element_id}"' in studio_html, \
f"Kostenelement {element_id} nicht gefunden"
@pytest.mark.skip(reason="vast.ai GPU UI not yet implemented in frontend template")
class TestStudioVastButtons:
"""Tests fuer die vast.ai Button-Styles.
HINWEIS: vast.ai UI-Elemente wurden noch nicht implementiert.
"""
@pytest.fixture
def studio_html(self):
"""Laedt den HTML-Inhalt aus dem Template."""
return TEMPLATE_HTML.read_text(encoding="utf-8")
@pytest.fixture
def studio_css(self):
"""Laedt den CSS-Inhalt aus der separaten CSS-Datei."""
return STATIC_CSS.read_text(encoding="utf-8")
def test_vast_buttons_container_exists(self, studio_html):
"""Testet, dass der vast-buttons Container existiert."""
assert 'class="vast-buttons"' in studio_html, \
"vast-buttons Container nicht gefunden"
def test_vast_buttons_css_exists(self, studio_css):
"""Testet, dass CSS fuer .vast-buttons existiert."""
assert '.vast-buttons' in studio_css, \
"CSS fuer .vast-buttons nicht gefunden"
class TestStudioStaticFilesIntegration:
"""Integration Tests fuer Static Files Serving."""
def test_main_py_mounts_static_files(self):
"""Testet, dass main.py die Static Files korrekt mountet."""
main_py = Path(__file__).parent.parent / "main.py"
main_content = main_py.read_text(encoding="utf-8")
assert 'StaticFiles' in main_content, \
"main.py muss StaticFiles importieren"
assert 'app.mount("/static"' in main_content or "app.mount('/static'" in main_content, \
"main.py muss /static mounten"
def test_static_directory_structure(self):
"""Testet, dass die Static-Verzeichnisstruktur korrekt ist."""
static_dir = FRONTEND_DIR / "static"
assert static_dir.exists(), "static/ Verzeichnis muss existieren"
assert (static_dir / "css").exists(), "static/css/ Verzeichnis muss existieren"
assert (static_dir / "js").exists(), "static/js/ Verzeichnis muss existieren"
if __name__ == '__main__':
pytest.main([__file__, '-v'])