import { expect, test } from "@playwright/test"; import { anmelden } from "./wehr"; /** * E2E-Tests für Phase 3. * * Geprüft wird der Weg vom Alarm bis zur Auswertung: schnell erfassen, * Zeitstempel setzen, Mannschaft eintragen, melden — und am Ende die Zahlen, * die die Gemeinde sehen will. * * Dazu der Portal-Link: er ist der einzige Weg in FLORIAN hinein, der ohne * Anmeldung funktioniert, und wird deshalb ausdrücklich in einem Browser * **ohne** Sitzung geprüft. * * Voraussetzung: Stack läuft, Datenbank ist migriert und geseedet. */ test.beforeEach(async ({ page }) => { await anmelden(page); }); test.describe("Einsatzliste", () => { test("zeigt die Einsätze der Wehr", async ({ page }) => { await page.goto("/einsaetze"); await expect(page.getByRole("heading", { name: "Einsätze" })).toBeVisible(); // Aus dem Seed: acht Einsätze des laufenden Jahres. await expect( page.getByRole("link", { name: /\d{4}-\d{4}/ }).first(), ).toBeVisible(); // Die Zelle enthaelt Stichwort UND Objekt ("B 3 Wohnhausbrand" + // "Reihenhaus, zwei Vollgeschosse"). Deshalb der Anker am Anfang statt // einer Pruefung auf Gleichheit — die ging bis Phase 8 nur deshalb durch, // weil in der Beispielwehr Einsaetze aus frueheren Testlaeufen ohne Objekt // standen. await expect( page.getByRole("cell", { name: /^B 3 Wohnhausbrand/ }).first(), ).toBeVisible(); }); test("filtert über die Suche", async ({ page }) => { await page.goto("/einsaetze"); await page.getByLabel("Einsätze durchsuchen").fill("Ölspur"); await expect(page.getByText("THL 1 Ölspur")).toBeVisible(); await expect(page.getByText("B 3 Wohnhausbrand")).toHaveCount(0); }); }); /** * Nacheinander: die Tests bauen aufeinander auf — erst wird ein Einsatz * erfasst, dann daran weitergearbeitet. */ test.describe.configure({ mode: "serial" }); test.describe("Einsatz erfassen und bearbeiten", () => { /** Merkt sich die Adresse des in diesem Durchlauf angelegten Einsatzes. */ let einsatzAdresse = ""; test("legt einen Einsatz über die Schnellerfassung an", async ({ page }) => { await page.goto("/einsaetze"); await page.getByRole("button", { name: "Einsatz erfassen" }).click(); await page .getByLabel("Einsatzstichwort") .fill(`THL 1 Testeinsatz ${test.info().project.name}`); await page.getByLabel("Straße").fill("Teststraße"); await page.getByLabel("Ort", { exact: true }).fill("Musterhausen"); await page.getByRole("button", { name: "Einsatz anlegen" }).click(); // Danach steht die Detailseite offen. await page.waitForURL(/\/einsaetze\/[0-9a-f-]{36}$/); einsatzAdresse = page.url(); await expect( page.getByRole("heading", { name: /THL 1 Testeinsatz/ }), ).toBeVisible(); await expect(page.getByText("läuft", { exact: true })).toBeVisible(); }); test("setzt Zeitstempel und rechnet die Kennzahlen", async ({ page }) => { await page.goto(einsatzAdresse); await expect(page.getByRole("heading", { name: "Zeiten" })).toBeVisible(); await page.getByRole("button", { name: "ausgerückt jetzt" }).click(); await expect( page.getByRole("button", { name: "ausgerückt zurücknehmen" }), ).toBeVisible(); await page.getByRole("button", { name: "eingetroffen jetzt" }).click(); await expect( page.getByRole("button", { name: "eingetroffen zurücknehmen" }), ).toBeVisible(); // Die Hilfsfrist steht danach da — auch wenn sie null Minuten beträgt. await expect(page.getByText("Hilfsfrist")).toBeVisible(); }); test("erfasst Mannschaft über den Vorschlag und zeigt die Stärke", async ({ page, }) => { await page.goto(einsatzAdresse); await page.getByRole("button", { name: "Mannschaft erfassen" }).click(); await expect( page.getByText(/sortiert nach Einsätzen im letzten Jahr/), ).toBeVisible(); // Die ersten drei Vorschläge übernehmen. const vorschlaege = page.locator("button", { hasText: /\d+ Einsätze/ }); await vorschlaege.nth(0).click(); await expect(page.getByText(/Kräfte erfasst/)).toBeVisible(); // Die Stärke wird sofort angezeigt. await expect(page.getByText(/^\d+\/\d+\/\d+\/\d+$/)).toBeVisible(); }); test("nimmt eine Lagemeldung auf", async ({ page }) => { await page.goto(einsatzAdresse); await page .getByLabel("Neue Lagemeldung") .fill("Lage erkundet, keine Gefahr für Personen."); await page.getByRole("button", { name: "Melden" }).click(); await expect( page.getByText("Lage erkundet, keine Gefahr für Personen."), ).toBeVisible(); }); test("gibt einen Portal-Link aus, der ohne Anmeldung funktioniert", async ({ page, browser, }) => { await page.goto(einsatzAdresse); await page.getByLabel("Für wen ist der Link?").fill("FF Nachbarhausen"); await page.getByRole("button", { name: "Erzeugen" }).click(); const linkfeld = page.locator("input[readonly]"); await expect(linkfeld).toBeVisible(); const adresse = await linkfeld.inputValue(); expect(adresse).toContain("/portal/"); // Jetzt ein Browser ohne Sitzung — genau so, wie ihn eine Fremdwehr hätte. const fremd = await browser.newContext(); const fremdeSeite = await fremd.newPage(); await fremdeSeite.goto(adresse); await expect( fremdeSeite.getByRole("heading", { name: /Testeinsatz/ }), ).toBeVisible(); await expect( fremdeSeite.getByText("Zugang für FF Nachbarhausen"), ).toBeVisible(); // Und eine Meldung absetzen. await fremdeSeite.getByLabel("Wer meldet?").fill("Gruppenführer Huber"); await fremdeSeite .getByLabel("Meldung") .fill("Wir sind mit einer Staffel eingetroffen."); await fremdeSeite.getByRole("button", { name: "Melden" }).click(); await expect( fremdeSeite.getByText("Wir sind mit einer Staffel eingetroffen."), ).toBeVisible(); // Kein Weg in den Rest der Anwendung. await expect(fremdeSeite.getByRole("link", { name: "Geräte" })).toHaveCount( 0, ); await fremd.close(); }); test("weist einen erfundenen Portal-Link ab", async ({ browser }) => { const fremd = await browser.newContext(); const seite = await fremd.newPage(); await seite.goto( "/portal/3f2504e0-4f89-11d3-9a0c-0305e82c3301.abcdefghijklmnopqrstuvwxyz01", ); await expect(seite.getByTestId("portalfehler")).toBeVisible(); await expect(seite.getByText(/nicht gültig/)).toBeVisible(); await fremd.close(); }); }); test.describe("Statistik", () => { test("zeigt Hilfsfrist, Kategorien und Beteiligung", async ({ page }) => { await page.goto("/einsaetze/statistik"); await expect( page.getByRole("heading", { name: "Einsatzstatistik" }), ).toBeVisible(); await expect(page.getByText("Hilfsfrist (80 %)")).toBeVisible(); await expect( page.getByRole("heading", { name: "Nach Kategorie" }), ).toBeVisible(); await expect( page.getByRole("heading", { name: "Beteiligung" }), ).toBeVisible(); // Aus dem Seed: die Kategorien der acht Einsätze. await expect(page.getByText("Brand groß").first()).toBeVisible(); }); }); test.describe("Übungen", () => { test("zeigt die Übungen und ihre Teilnehmerzahl", async ({ page }) => { await page.goto("/uebungen"); await expect(page.getByRole("heading", { name: "Übungen" })).toBeVisible(); await expect(page.getByText("Innenangriff mit Atemschutz")).toBeVisible(); }); test("öffnet eine Übung und zeigt die Anwesenheitsliste", async ({ page, }) => { await page.goto("/uebungen"); await page .getByRole("link", { name: /\d{4}-\d{4}/ }) .first() .click(); await expect( page.getByRole("heading", { name: "Anwesenheit" }), ).toBeVisible(); await expect(page.getByText(/von \d+ aktiven Mitgliedern/)).toBeVisible(); }); });