import { expect, test } from "@playwright/test"; import { anmelden } from "./wehr"; /** * E2E-Tests für Phase 1. * * Geprüft werden die Wege, die eine Wehr täglich geht: in der Geräteliste * suchen, ein Gerät öffnen, die Beladung eines Fahrzeugs ansehen, und über * einen gescannten QR-Code auf der richtigen Seite landen. * * Voraussetzung: Stack läuft, Datenbank ist migriert und geseedet. */ test.beforeEach(async ({ page }) => { await anmelden(page); }); test.describe("Geräteliste", () => { test("zeigt die Geräte der Wehr", async ({ page }) => { await page.goto("/geraete"); await expect(page.getByRole("heading", { name: "Geräte" })).toBeVisible(); // Aus dem Seed: die Inventarnummern beginnen bei INV-0001. await expect(page.getByRole("link", { name: "INV-0001" })).toBeVisible(); await expect(page.getByText(/\d+ Geräte/)).toBeVisible(); }); test("filtert über die Suche", async ({ page }) => { await page.goto("/geraete"); await expect(page.getByRole("link", { name: "INV-0001" })).toBeVisible(); await page.getByLabel("Geräte durchsuchen").fill("Pressluftatmer"); // Die Suche ist entprellt — deshalb auf das Ergebnis warten, nicht auf die Zeit. await expect( page.getByRole("cell", { name: "Pressluftatmer PA 94" }).first(), ).toBeVisible(); await expect( page.getByRole("cell", { name: "Feuerwehrhelm F220" }), ).toHaveCount(0); }); test("meldet verständlich, wenn die Suche nichts findet", async ({ page, }) => { await page.goto("/geraete"); await page.getByLabel("Geräte durchsuchen").fill("gibtesnichtxyz"); await expect(page.getByText("Kein Gerät gefunden")).toBeVisible(); }); test("blendet Spalten aus und wieder ein", async ({ page }) => { await page.goto("/geraete"); await expect( page.getByRole("columnheader", { name: /Hersteller/i }), ).toBeVisible(); await page.getByRole("button", { name: "Spalten" }).click(); await page.getByRole("checkbox", { name: "Hersteller" }).click(); await expect( page.getByRole("columnheader", { name: /Hersteller/i }), ).toHaveCount(0); }); test("zeigt Sammelaktionen, sobald etwas ausgewählt ist", async ({ page, }) => { await page.goto("/geraete"); await expect(page.getByRole("link", { name: "INV-0001" })).toBeVisible(); await page.getByRole("checkbox", { name: "INV-0001 auswählen" }).click(); await expect(page.getByText("1 Gerät ausgewählt")).toBeVisible(); await expect( page.getByRole("button", { name: "Etiketten drucken" }), ).toBeVisible(); }); }); test.describe("Gerätedetail", () => { test("zeigt Angaben, QR-Code und Lagerung", async ({ page }) => { await page.goto("/geraete"); await page.getByRole("link", { name: "INV-0001" }).click(); await expect(page.getByRole("heading", { name: "Angaben" })).toBeVisible(); await expect(page.getByRole("heading", { name: "QR-Code" })).toBeVisible(); await expect(page.getByAltText(/QR-Code für INV-0001/)).toBeVisible(); await expect(page.getByText("Lagerung")).toBeVisible(); }); test("übernimmt das Prüfintervall aus dem Gerätetyp, wenn am Gerät keines steht", async ({ page, }) => { await page.goto("/geraete"); await page.getByRole("link", { name: "INV-0001" }).click(); await expect(page.getByText(/aus dem Gerätetyp/)).toBeVisible(); }); }); test.describe("QR-Route", () => { test("leitet von /g/ auf das Gerät weiter", async ({ page, }) => { await page.goto("/g/INV-0001"); await expect(page).toHaveURL(/\/geraete\/[0-9a-f-]{36}/); await expect(page.getByText("INV-0001").first()).toBeVisible(); }); test("erklärt verständlich, wenn es die Nummer nicht gibt", async ({ page, }) => { await page.goto("/g/INV-GIBTESNICHT"); await expect(page.getByText("Kein Gerät gefunden")).toBeVisible(); await expect(page.getByText("INV-GIBTESNICHT")).toBeVisible(); }); test("schickt nicht Angemeldete erst zur Anmeldung", async ({ browser }) => { // Frischer Kontext ohne Sitzungscookie. const kontext = await browser.newContext(); const seite = await kontext.newPage(); await seite.goto("/g/INV-0001"); await expect(seite).toHaveURL(/\/anmelden/); await kontext.close(); }); }); test.describe("Fahrzeuge", () => { test("zeigt die Fahrzeuge als Kacheln", async ({ page }) => { await page.goto("/fahrzeuge"); await expect( page.getByRole("heading", { name: "Fahrzeuge" }), ).toBeVisible(); await expect( page.getByRole("link", { name: /Florian Musterhausen 40\/1/ }), ).toBeVisible(); }); test("zeigt die Beladung nach Geräteräumen", async ({ page }) => { await page.goto("/fahrzeuge"); await page .getByRole("link", { name: /Florian Musterhausen 40\/1/ }) .click(); await expect(page.getByRole("heading", { name: "Beladung" })).toBeVisible(); // Die Beladeordnung aus dem Seed: G1 bis G6, Heck, Dach, Mannschaftsraum. await expect(page.getByText("G1", { exact: true })).toBeVisible(); await expect(page.getByText("Dach", { exact: true })).toBeVisible(); }); }); test.describe("Qualifikationen", () => { test("zeigt Ablaufwarnungen und den Katalog", async ({ page }) => { await page.goto("/qualifikationen"); await expect( page.getByRole("heading", { name: "Qualifikationen" }), ).toBeVisible(); await expect(page.getByRole("heading", { name: "Läuft ab" })).toBeVisible(); await expect(page.getByRole("heading", { name: "Katalog" })).toBeVisible(); await expect( page.getByText("Atemschutzgeräteträger").first(), ).toBeVisible(); }); }); test.describe("Mitglieder", () => { test("gruppiert nach Abteilung", async ({ page }) => { await page.goto("/mitglieder"); await expect( page.getByRole("heading", { name: "Mitglieder" }), ).toBeVisible(); await expect( page.getByRole("heading", { name: /^Aktive \(/ }), ).toBeVisible(); await expect( page.getByRole("link", { name: "Huber, Andreas" }), ).toBeVisible(); }); test("zeigt Qualifikationen am Mitglied", async ({ page }) => { await page.goto("/mitglieder"); await page.getByRole("link", { name: "Huber, Andreas" }).click(); await expect( page.getByRole("heading", { name: "Qualifikationen" }), ).toBeVisible(); await expect( page.getByRole("heading", { name: "Terminal-Zugang" }), ).toBeVisible(); }); }); test.describe("Import", () => { test("führt durch die drei Schritte und zeigt die Vorschau", async ({ page, }) => { await page.goto("/geraete/importieren"); await expect( page.getByRole("heading", { name: "1. Datei wählen" }), ).toBeVisible(); const csv = [ "Inventarnummer;Bezeichnung;Hersteller;Indienststellung", "E2E-001;Testgerät A;Beispiel;01.03.2024", "E2E-002;Testgerät B;Beispiel;kein Datum", ].join("\n"); await page.setInputFiles("#datei", { name: "test-geraete.csv", mimeType: "text/csv", buffer: Buffer.from(csv, "utf8"), }); // Schritt 2 erscheint erst, wenn der Server die Datei gelesen hat. await expect( page.getByRole("heading", { name: "2. Spalten zuordnen" }), ).toBeVisible(); await expect(page.getByRole("heading", { name: "Vorschau" })).toBeVisible(); // Eine Zeile ist gut, eine hat ein unlesbares Datum. await expect( page.getByText("1 von 2 Zeilen sind übernehmbar"), ).toBeVisible(); await expect( page.getByRole("button", { name: /1 Zeilen übernehmen/ }), ).toBeEnabled(); }); test("verweigert die Übernahme, wenn ein Pflichtfeld nicht zugeordnet ist", async ({ page, }) => { await page.goto("/geraete/importieren"); await page.setInputFiles("#datei", { name: "unvollstaendig.csv", mimeType: "text/csv", buffer: Buffer.from("Inventarnummer\nE2E-100\n", "utf8"), }); await expect( page.getByText(/Diesen Pflichtfeldern ist keine Spalte zugeordnet/), ).toBeVisible(); }); });