import { expect, test } from "@playwright/test"; /** * Beispiel-E2E-Test für Phase 0. * * Geprüft wird der Weg, ohne den nichts anderes funktioniert: jemand ruft die * Anwendung auf, wird zur Anmeldung geschickt, meldet sich an und landet auf * der Übersicht seiner Wehr. * * Voraussetzung: Stack läuft, Datenbank ist migriert und geseedet. * docker compose up -d && npm run db:migrate && npm run db:seed */ const ADMIN_EMAIL = process.env.SEED_ADMIN_EMAIL ?? "admin@beispielwehr.de"; const ADMIN_PASSWORT = process.env.SEED_ADMIN_PASSWORT ?? "Feuerwehr2026"; test.describe("Anmeldung", () => { test("schickt nicht angemeldete Aufrufe zur Anmeldeseite", async ({ page }) => { await page.goto("/"); await expect(page).toHaveURL(/\/anmelden/); await expect(page.getByRole("heading", { name: "FLORIAN" })).toBeVisible(); }); test("weist eine falsche Kombination ab und nennt den Grund auf Deutsch", async ({ page }) => { await page.goto("/anmelden"); await page.getByLabel("E-Mail-Adresse").fill(ADMIN_EMAIL); await page.getByLabel("Passwort").fill("definitiv-das-falsche-passwort"); await page.getByRole("button", { name: "Anmelden" }).click(); // Nicht ueber die Rolle "alert" suchen: Next.js haengt einen eigenen // Routen-Ansager mit derselben Rolle in die Seite. const meldung = page.getByTestId("anmeldefehler"); await expect(meldung).toBeVisible(); await expect(meldung).toContainText("stimmt nicht"); // Und niemand ist hineingekommen. await expect(page).toHaveURL(/\/anmelden/); }); test("lässt den Administrator hinein und zeigt die Übersicht seiner Wehr", async ({ page }) => { await page.goto("/anmelden"); await page.getByLabel("E-Mail-Adresse").fill(ADMIN_EMAIL); await page.getByLabel("Passwort").fill(ADMIN_PASSWORT); await page.getByRole("button", { name: "Anmelden" }).click(); await page.waitForURL("/"); // Die Wehr aus dem Seed. await expect( page.getByRole("heading", { name: "Freiwillige Feuerwehr Musterhausen" }), ).toBeVisible(); // Die Kennzahlen aus dem Seed: 15 Mitglieder, 3 Fahrzeuge, 40 Geräte. await expect(page.getByText("Aktive Mitglieder")).toBeVisible(); await expect(page.getByText("Fahrzeuge", { exact: true }).first()).toBeVisible(); await expect(page.getByText("Geräte", { exact: true }).first()).toBeVisible(); // Der Sicherheitshinweis muss stehen, solange nichts freigegeben ist. await expect(page.getByRole("note")).toContainText("analoge Überwachung"); }); test("zeigt die Navigation und markiert künftige Module als noch nicht verfügbar", async ({ page, }) => { await page.goto("/anmelden"); await page.getByLabel("E-Mail-Adresse").fill(ADMIN_EMAIL); await page.getByLabel("Passwort").fill(ADMIN_PASSWORT); await page.getByRole("button", { name: "Anmelden" }).click(); await page.waitForURL("/"); const navigation = page.getByRole("navigation", { name: "Hauptnavigation" }); await expect(navigation.getByRole("link", { name: "Übersicht" })).toBeVisible(); // Die Karte steht seit Phase 6 zur Verfügung und ist ein Verweis. await expect(navigation.getByRole("link", { name: "Karte" })).toBeVisible(); // Die Termine kommen erst in Phase 7 — der Eintrag ist sichtbar, aber kein // Verweis. Beim Abschluss einer Phase wandert diese Zusicherung eine Zeile // weiter nach unten. await expect(navigation.getByText("Termine")).toBeVisible(); await expect(navigation.getByRole("link", { name: "Termine" })).toHaveCount(0); }); }); test.describe("Erreichbarkeit", () => { test("meldet den Zustand der Randsysteme unter /api/health", async ({ request }) => { const antwort = await request.get("/api/health"); expect(antwort.status()).toBe(200); const daten = (await antwort.json()) as { status: string; dienste: { datenbank: boolean; speicher: boolean; warteschlange: boolean }; }; expect(daten.status).toBe("ok"); expect(daten.dienste.datenbank).toBe(true); expect(daten.dienste.speicher).toBe(true); expect(daten.dienste.warteschlange).toBe(true); }); });