"""Frontend-Smoke-Tests: verify all 13 HTML pages exist and contain the expected keywords/structure. These tests read the HTML files directly from `app/webui/` (rather than hitting the FastAPI server) because the static-file mount is added in a later phase by the orchestrator. Once that mount is in place, the same checks could be re-implemented as `httpx.AsyncClient.get("/…")`. Each test loads exactly one page and asserts: - file exists and is non-empty - contains the page's title text (so a typo / removal is caught) - has Tailwind-CDN, Alpine-CDN, and the app.css link - has no x-html attribute (R-5) """ from __future__ import annotations from pathlib import Path import pytest WEBUI_DIR = Path(__file__).resolve().parent.parent / "app" / "webui" def _read(name: str) -> str: """Read a file from the webui directory.""" p = WEBUI_DIR / name assert p.exists(), f"Expected frontend file {p} to exist" return p.read_text(encoding="utf-8") # --------------------------------------------------------------------------- # Each page: (filename, [required substrings]) # --------------------------------------------------------------------------- PAGES = [ ("index.html", ["Login", "Registrieren", "Anmelden", "Konto erstellen"]), ("app.html", ["Dashboard", "Logout", "Pipeline"]), ("accounts.html", ["Accounts", "Suche (Name)", "Branche", "+ New Account"]), ("accounts-detail.html", ["Account-Detail", "Bearbeiten", "Löschen", "Info"]), ("contacts.html", ["Contacts", "Vorname", "Nachname", "+ New Contact"]), ("contacts-detail.html", ["Contact-Detail", "Bearbeiten", "Löschen"]), ("pipeline.html", ["Pipeline", "Owner-ID (optional)", "+ New Deal", "Ziehe Karten"]), ("activities.html", ["Activities", "+ New Activity", "Fällig"]), ("settings-profile.html", ["Mein Profil", "Avatar-URL", "E-Mail-Benachrichtigungen"]), ("settings-users.html", ["User-Verwaltung", "+ New User", "Rolle"]), ("settings-org.html", ["Org-Einstellungen", "Aktuelle Organisation", "v1.1"]), ("404.html", ["404", "Seite nicht gefunden", "Zum Dashboard"]), # Dashboard: ships inside app.html. We test the marker text directly there. ] @pytest.mark.parametrize("filename,required", PAGES) def test_frontend_page_loads(filename, required): """Each HTML page must exist and contain its identifying substrings.""" content = _read(filename) assert len(content) > 200, f"{filename} seems empty or too small" for needle in required: assert needle in content, f"{filename} missing required string {needle!r}" def test_index_html_contains_login_and_register(): """Index page (auth screen) must show both Login and Register tabs.""" content = _read("index.html") assert "Login" in content assert "Registrieren" in content # Alpine-CDN required assert "alpinejs" in content def test_app_html_contains_dashboard_and_logout(): """app.html is the post-login shell — must show Dashboard + Logout.""" content = _read("app.html") assert "Dashboard" in content assert "Logout" in content def test_13_pages_exist(): """Exactly 13 HTML pages must be present (index + app + 10 + 404).""" pages = sorted(p.name for p in WEBUI_DIR.glob("*.html")) assert len(pages) == 13, f"Expected 13 pages, found {len(pages)}: {pages}" def test_dashboard_section_inside_app_html(): """The dashboard content is rendered inside app.html (default landing).""" content = _read("app.html") # The 4 KPI labels from the dashboard section assert "Open Deals" in content assert "Pipeline Value" in content assert "Won this Month" in content assert "Conversion Rate" in content def test_each_page_links_app_css(): """Every page must reference /css/app.css.""" for filename, _ in PAGES: content = _read(filename) assert "/css/app.css" in content, f"{filename} missing /css/app.css link" def test_each_page_includes_alpine_cdn(): """Every page must include the Alpine.js CDN script.""" for filename, _ in PAGES: content = _read(filename) assert "alpinejs" in content, f"{filename} missing Alpine.js CDN" def test_each_page_includes_tailwind_cdn(): """Every page must include the Tailwind-CDN script (dev-mode).""" for filename, _ in PAGES: content = _read(filename) assert "cdn.tailwindcss.com" in content, f"{filename} missing Tailwind CDN"