""" Playwright E2E tests for StudyAgent. Requires: - Server running: cd ~/Projekte/StudyAgent && .venv/bin/uvicorn main:app --reload - Browsers installed: playwright install chromium """ import pytest pw = pytest.importorskip("playwright") from playwright.sync_api import Page, expect def _add_subject(page: Page, name: str) -> None: """Fill and submit the add-subject form via the visible input field.""" name_input = page.locator('input[name="name"]') name_input.fill(name) page.locator('button[type="submit"]:has-text("Fach hinzufuegen")').click() # Wait for htmx to swap the new card into the DOM page.wait_for_timeout(500) def test_dashboard_loads(page: Page, base_url: str) -> None: """Dashboard returns 200 and renders the subjects section heading.""" response = page.goto(base_url) assert response is not None and response.status == 200 # The section heading is always present on the page expect(page.locator("text=Aktive Faecher")).to_be_visible() def test_add_subject_flow(page: Page, base_url: str) -> None: """Adding a subject shows it on the page as a subject card.""" page.goto(base_url) _add_subject(page, "Mathematik") # The subject card renders the name inside .subject-name expect(page.locator(".subject-name", has_text="Mathematik")).to_be_visible() def test_wip_limit_enforced(page: Page, base_url: str) -> None: """A third subject is rejected with the WIP-limit buddy warning.""" page.goto(base_url) # Add first two subjects — the form is rendered while < 2 active subjects _add_subject(page, "Fach 1") # After first add, reload so the template re-evaluates the condition and # renders the form for the second subject page.reload() _add_subject(page, "Fach 2") # Now the form is hidden by the template (WIP=2/2). Post the third subject # directly via the API to trigger the server-side rejection message. page.evaluate( """async () => { const body = new URLSearchParams({ name: 'Fach 3', semester: '' }); const resp = await fetch('/subjects', { method: 'POST', headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, body: body.toString(), }); const html = await resp.text(); document.getElementById('wip-warning').innerHTML = html; }""" ) # The rejection message contains "Parkiere" expect(page.locator("#wip-warning")).to_contain_text("Parkiere")