import { expect, test } from "@playwright/test"; import { base32Dekodieren, totpCode, totpSchritt } from "../../src/lib/totp"; import { anmelden, oidcBestaetigen } from "./hilfen"; /** * Lokale Konten (#035) im gebauten Stand: Admin legt ein Konto an, die Person meldet sich zum * ersten Mal an (eigenes Passwort, TOTP), der Admin sperrt sie – die Sitzung endet sofort. */ test.setTimeout(120_000); test("lokales Konto anlegen, erste Anmeldung, sperren", async ({ page, browser }) => { const anmeldename = `zz-e2e-${Date.now().toString(36)}`; const passwort = "ein-eigenes-e2e-passwort"; await anmelden(page); await page.goto("/einstellungen"); await page.getByRole("link", { name: "Konten verwalten" }).click(); await expect(page.getByRole("heading", { name: "Benutzer", exact: true })).toBeVisible(); // Frische Bestaetigung immer selbst holen – ob ein frueherer Test sie schon gesetzt hat, // haengt von der Reihenfolge ab. Dass das Recht sie verlangt, pruefen die Unit-Tests. await page.goto("/bestaetigen?ziel=%2Feinstellungen%2Fbenutzer"); await oidcBestaetigen(page); await expect(page).toHaveURL(/\/einstellungen\/benutzer$/); await page.getByLabel("Anmeldename").fill(anmeldename); await page.getByLabel("Name", { exact: true }).fill("E2E Lokal"); await page.getByLabel("Rolle", { exact: true }).selectOption("OPERATOR"); await page.getByRole("button", { name: "Konto anlegen" }).click(); const anzeige = page.getByRole("region", { name: `Einmal-Passwort für ${anmeldename}` }); const einmal = ((await anzeige.locator("code").textContent()) ?? "").trim(); expect(einmal).toMatch(/^[a-zA-Z2-9]{5}(-[a-zA-Z2-9]{5}){3}$/); await page.screenshot({ path: "docs/anleitung/bilder/benutzer-desktop.png", fullPage: true }); // Zweiter Browser: die Person meldet sich zum ersten Mal an. const kontext = await browser.newContext(); const person = await kontext.newPage(); await person.goto("/anmelden"); await person.getByText("Lokales Konto oder Notfallzugang", { exact: true }).click(); await person.getByLabel("Anmeldename").fill(anmeldename); await person.getByLabel("Passwort").fill(einmal); await person.getByRole("button", { name: "Mit Benutzername anmelden" }).click(); await expect(person).toHaveURL(/\/anmelden\/passwort/); await person.getByLabel("Neues Passwort", { exact: true }).fill("zu-kurz"); await person.getByLabel("Neues Passwort wiederholen").fill("zu-kurz"); await person.getByRole("button", { name: "Passwort setzen" }).click(); await expect(person.getByText(/mindestens 12 Zeichen/).last()).toBeVisible(); await person.getByLabel("Neues Passwort", { exact: true }).fill(passwort); await person.getByLabel("Neues Passwort wiederholen").fill(passwort); await person.getByRole("button", { name: "Passwort setzen" }).click(); await expect(person).toHaveURL(/\/anmelden\/totp/); const geheimnis = ((await person.locator("code").first().textContent()) ?? "").trim(); await person .getByLabel("Code zur Bestätigung") .fill(totpCode(base32Dekodieren(geheimnis), totpSchritt(Date.now()))); await person.getByRole("button", { name: /TOTP einrichten/ }).click(); await expect(person.getByRole("heading", { name: "Übersicht" })).toBeVisible(); await expect(person.getByText("Operator · über lokales Konto")).toBeVisible(); // Ein Operator verwaltet keine Benutzer. await person.goto("/einstellungen/benutzer"); await expect(person).toHaveURL(/\/kein-zugriff/); // Admin sperrt das Konto – die Sitzung der Person endet sofort. await page.reload(); const zeile = page.getByRole("row").filter({ hasText: anmeldename }); await expect(zeile.getByText("Einrichtung offen")).toHaveCount(0); await zeile.getByRole("button", { name: /Sperren/ }).click(); await expect(zeile.getByText("gesperrt", { exact: true })).toBeVisible(); await person.goto("/"); await expect(person).toHaveURL(/\/anmelden/); await kontext.close(); });