Aufraeumarbeiten: ESLint, DOM-Verschachtelung, eigene Testwehr
ESLint hatte keine Konfiguration — das Skript "npm run lint" pruefte nichts. Jetzt Flat Config ohne Formatierungsregeln, dafuer mit den Regeln, die die Fehler dieses Projekts gemeldet haetten. Er hat gleich zwei gefunden, die niemand gesucht hatte. Das Badge rendert ein span statt eines div. Damit ist die Meldung "div cannot be a descendant of p" weg, die seit Phase 3 auf der Einsatzseite stand — sie ist der klassische Ausloeser fuer einen Hydration-Fehler. Die E2E-Tests laufen gegen eine eigene Wehr. Nach fuenf Phasen standen in der Beispielwehr 120 Atemschutz-Ueberwachungen statt einer und 115 Geraete statt 45; bei jedem Lauf aenderten sich alle Bildschirmfotos. Der Bestand ist einmal zurueckgesetzt, npm run db:test-reset baut die Testwehr neu auf. Behoben nebenbei: - Die Fehlerflaeche der Karte konnte nie etwas anzeigen. - Zwei useMemo der Lagekarte rechneten bei jedem Rendern neu. - tests/e2e war vom Typecheck ausgenommen und liess einen doppelt deklarierten Bezeichner durch. Closes #10 Closes #11 Closes #12 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
8081131eb7
commit
7196c7110c
@@ -1,4 +1,5 @@
|
||||
import { expect, test, type Locator, type Page } from "@playwright/test";
|
||||
import { anmelden } from "./wehr";
|
||||
|
||||
/**
|
||||
* E2E-Tests für das Geräteformular.
|
||||
@@ -18,16 +19,18 @@ import { expect, test, type Locator, type Page } from "@playwright/test";
|
||||
* werden deshalb getippt, so wie es ein Mensch auch täte.
|
||||
*/
|
||||
|
||||
const ADMIN_EMAIL = process.env.SEED_ADMIN_EMAIL ?? "admin@beispielwehr.de";
|
||||
const ADMIN_PASSWORT = process.env.SEED_ADMIN_PASSWORT ?? "Feuerwehr2026";
|
||||
|
||||
/** Eindeutig je Testlauf, damit sich parallele Läufe nicht ins Gehege kommen. */
|
||||
function testnummer(kennzeichen: string): string {
|
||||
return `E2E-${kennzeichen}-${Date.now().toString().slice(-6)}`;
|
||||
}
|
||||
|
||||
/** Tippt ein Datum in ein date-Feld. Reihenfolge nach deutscher Locale. */
|
||||
async function datumTippen(feld: Locator, tag: string, monat: string, jahr: string) {
|
||||
async function datumTippen(
|
||||
feld: Locator,
|
||||
tag: string,
|
||||
monat: string,
|
||||
jahr: string,
|
||||
) {
|
||||
await feld.click();
|
||||
await feld.pressSequentially(`${tag}${monat}${jahr}`, { delay: 30 });
|
||||
}
|
||||
@@ -44,16 +47,12 @@ function auswahl(page: Page, name: string): Locator {
|
||||
return page.getByRole("combobox", { name, exact: true });
|
||||
}
|
||||
|
||||
async function anmelden(page: Page): Promise<void> {
|
||||
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("/");
|
||||
}
|
||||
|
||||
/** Füllt die beiden Pflichtfelder und wählt eine Lagerung. */
|
||||
async function grunddatenAusfuellen(page: Page, nummer: string, bezeichnung: string) {
|
||||
async function grunddatenAusfuellen(
|
||||
page: Page,
|
||||
nummer: string,
|
||||
bezeichnung: string,
|
||||
) {
|
||||
await feld(page, "Inventarnummer").fill(nummer);
|
||||
await feld(page, "Bezeichnung").fill(bezeichnung);
|
||||
}
|
||||
@@ -63,11 +62,15 @@ test.beforeEach(async ({ page }) => {
|
||||
});
|
||||
|
||||
test.describe("Gerät anlegen", () => {
|
||||
test("legt ein Gerät an einem Standort an und öffnet es danach", async ({ page }) => {
|
||||
test("legt ein Gerät an einem Standort an und öffnet es danach", async ({
|
||||
page,
|
||||
}) => {
|
||||
const nummer = testnummer("STANDORT");
|
||||
|
||||
await page.goto("/geraete/neu");
|
||||
await expect(page.getByRole("heading", { name: "Neues Gerät" })).toBeVisible();
|
||||
await expect(
|
||||
page.getByRole("heading", { name: "Neues Gerät" }),
|
||||
).toBeVisible();
|
||||
|
||||
await grunddatenAusfuellen(page, nummer, "Testgerät Standort");
|
||||
await feld(page, "Hersteller").fill("Prüffirma");
|
||||
@@ -78,11 +81,15 @@ test.describe("Gerät anlegen", () => {
|
||||
await page.getByRole("button", { name: "Gerät anlegen" }).click();
|
||||
|
||||
await page.waitForURL(/\/geraete\/[0-9a-f-]{36}$/);
|
||||
await expect(page.getByRole("heading", { name: "Testgerät Standort" })).toBeVisible();
|
||||
await expect(
|
||||
page.getByRole("heading", { name: "Testgerät Standort" }),
|
||||
).toBeVisible();
|
||||
await expect(page.getByText(nummer).first()).toBeVisible();
|
||||
});
|
||||
|
||||
test("legt ein Gerät auf einem Fahrzeug samt Geräteraum an", async ({ page }) => {
|
||||
test("legt ein Gerät auf einem Fahrzeug samt Geräteraum an", async ({
|
||||
page,
|
||||
}) => {
|
||||
const nummer = testnummer("FAHRZEUG");
|
||||
|
||||
await page.goto("/geraete/neu");
|
||||
@@ -91,7 +98,9 @@ test.describe("Gerät anlegen", () => {
|
||||
await page.getByRole("radio", { name: "Auf einem Fahrzeug" }).click();
|
||||
|
||||
await auswahl(page, "Fahrzeug").click();
|
||||
await page.getByRole("option", { name: /Florian Musterhausen 40\/1/ }).click();
|
||||
await page
|
||||
.getByRole("option", { name: /Florian Musterhausen 40\/1/ })
|
||||
.click();
|
||||
|
||||
await auswahl(page, "Geräteraum").click();
|
||||
await page.getByRole("option", { name: /^G1/ }).click();
|
||||
@@ -113,12 +122,16 @@ test.describe("Gerät anlegen", () => {
|
||||
await page.getByRole("radio", { name: "Auf einem Fahrzeug" }).click();
|
||||
await page.getByRole("button", { name: "Gerät anlegen" }).click();
|
||||
|
||||
await expect(page.getByTestId("formularfehler")).toContainText("Fahrzeug auswählen");
|
||||
await expect(page.getByTestId("formularfehler")).toContainText(
|
||||
"Fahrzeug auswählen",
|
||||
);
|
||||
// Nichts gespeichert: die Adresse hat sich nicht geändert.
|
||||
await expect(page).toHaveURL(/\/geraete\/neu$/);
|
||||
});
|
||||
|
||||
test("rechnet die Fristen schon beim Ausfüllen vor", async ({ page }, angaben) => {
|
||||
test("rechnet die Fristen schon beim Ausfüllen vor", async ({
|
||||
page,
|
||||
}, angaben) => {
|
||||
// Unter Touch-Emulation nimmt Chromium keine zeichenweise Eingabe in
|
||||
// Datumsfelder an — dort öffnet sich der Auswähler des Betriebssystems.
|
||||
// Die Rechnung selbst prüfen die Unit-Tests in qr-und-fristen.test.ts;
|
||||
@@ -142,7 +155,9 @@ test.describe("Gerät anlegen", () => {
|
||||
await expect(page.getByText("01.03.2036")).toBeVisible();
|
||||
});
|
||||
|
||||
test("übernimmt Prüfintervall und Lebensdauer aus dem Gerätetyp", async ({ page }, angaben) => {
|
||||
test("übernimmt Prüfintervall und Lebensdauer aus dem Gerätetyp", async ({
|
||||
page,
|
||||
}, angaben) => {
|
||||
test.skip(
|
||||
angaben.project.name === "tablet",
|
||||
"Datumsfelder lassen sich unter Touch-Emulation nicht tippen.",
|
||||
@@ -166,7 +181,9 @@ test.describe("Gerät anlegen", () => {
|
||||
});
|
||||
|
||||
test.describe("Gerät bearbeiten", () => {
|
||||
test("ändert ein vorhandenes Gerät und protokolliert es", async ({ page }) => {
|
||||
test("ändert ein vorhandenes Gerät und protokolliert es", async ({
|
||||
page,
|
||||
}) => {
|
||||
await page.goto("/geraete");
|
||||
await page.getByRole("link", { name: "INV-0002" }).click();
|
||||
await page.getByRole("link", { name: "Bearbeiten" }).click();
|
||||
@@ -182,10 +199,14 @@ test.describe("Gerät bearbeiten", () => {
|
||||
|
||||
// Die Änderung steht im Verlauf. Bei wiederholten Läufen sammeln sich dort
|
||||
// mehrere Einträge — der Log ist append-only, das ist so gewollt.
|
||||
await expect(page.getByText(/Gerät INV-0002 geändert/).first()).toBeVisible();
|
||||
await expect(
|
||||
page.getByText(/Gerät INV-0002 geändert/).first(),
|
||||
).toBeVisible();
|
||||
});
|
||||
|
||||
test("schaltet die Lagerung von Fahrzeug auf Standort um", async ({ page }) => {
|
||||
test("schaltet die Lagerung von Fahrzeug auf Standort um", async ({
|
||||
page,
|
||||
}) => {
|
||||
await page.goto("/geraete");
|
||||
await page.getByRole("link", { name: "INV-0003" }).click();
|
||||
await page.getByRole("link", { name: "Bearbeiten" }).click();
|
||||
|
||||
Reference in New Issue
Block a user