feat: Grundgerüst mit Anmeldung, Layout und Design-Tokens
Next.js 16, Prisma 7, Tailwind 4 und Vitest/Playwright, Konfiguration aus Laufzettel übernommen, weil dort derselbe Stack erprobt ist. Die Farben der Branding-Vorgabe stehen als Tokens in globals.css, das helle Theme setzt dieselben Namen neu. Amber und Rot sind dort abgedunkelt, sonst reicht der Kontrast nicht – ein Test prüft beide Themes gegen 4,5:1. Anmeldung mit lokalem Benutzer, Rolle je Anfrage aus der Datenbank, Audit-Log per Trigger nur anhängbar. Geprüft im gebauten Stand auf 1440 px und 390 px. Closes #001 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,65 @@
|
||||
import { expect, test, type Page } from "@playwright/test";
|
||||
|
||||
const BENUTZER = process.env["E2E_BENUTZER"] ?? "admin";
|
||||
const PASSWORT = process.env["E2E_PASSWORT"] ?? "proxdeploy-dev-admin";
|
||||
|
||||
async function anmelden(page: Page) {
|
||||
await page.goto("/");
|
||||
await expect(page).toHaveURL(/\/anmelden/);
|
||||
await page.getByLabel("Benutzername").fill(BENUTZER);
|
||||
await page.getByLabel("Passwort").fill(PASSWORT);
|
||||
await page.getByRole("button", { name: "Anmelden" }).click();
|
||||
await expect(page.getByRole("heading", { name: "Übersicht" })).toBeVisible();
|
||||
}
|
||||
|
||||
async function keinQuerscrollen(page: Page) {
|
||||
const breiten = await page.evaluate(() => ({
|
||||
inhalt: document.documentElement.scrollWidth,
|
||||
fenster: window.innerWidth,
|
||||
}));
|
||||
expect(breiten.inhalt).toBeLessThanOrEqual(breiten.fenster);
|
||||
}
|
||||
|
||||
test("falsches Passwort zeigt eine Meldung mit Symbol und Text", async ({ page }) => {
|
||||
await page.goto("/anmelden");
|
||||
// Eigener Name: Fehlversuche zählen je Benutzer, der echte Admin soll nie gesperrt werden.
|
||||
await page.getByLabel("Benutzername").fill("e2e-unbekannt");
|
||||
await page.getByLabel("Passwort").fill("falsch-falsch");
|
||||
await page.getByRole("button", { name: "Anmelden" }).click();
|
||||
// Wenn die Seite nicht hydriert, passiert hier nichts – das ist die eigentliche Prüfung.
|
||||
await expect(page.getByText("Benutzername oder Passwort ist falsch.")).toBeVisible();
|
||||
await expect(page.getByText("✗")).toBeVisible();
|
||||
});
|
||||
|
||||
test("Anmelden, Übersicht, Abmelden", async ({ page }) => {
|
||||
await anmelden(page);
|
||||
await keinQuerscrollen(page);
|
||||
await page.screenshot({ path: `test-results/uebersicht-${test.info().project.name}.png`, fullPage: true });
|
||||
|
||||
if (test.info().project.name === "handy") {
|
||||
await page.getByRole("button", { name: "Navigation öffnen" }).click();
|
||||
}
|
||||
await page.getByRole("button", { name: "Abmelden" }).click();
|
||||
await expect(page).toHaveURL(/\/anmelden/);
|
||||
});
|
||||
|
||||
test("Theme-Wechsel bleibt nach dem Neuladen erhalten", async ({ page }) => {
|
||||
await anmelden(page);
|
||||
await expect(page.locator("html")).toHaveAttribute("data-theme", "dunkel");
|
||||
if (test.info().project.name === "handy") {
|
||||
await page.getByRole("button", { name: "Navigation öffnen" }).click();
|
||||
}
|
||||
await page.getByRole("button", { name: "Helles Theme einschalten" }).click();
|
||||
await expect(page.locator("html")).toHaveAttribute("data-theme", "hell");
|
||||
await page.reload();
|
||||
await expect(page.locator("html")).toHaveAttribute("data-theme", "hell");
|
||||
await page.screenshot({
|
||||
path: `test-results/uebersicht-hell-${test.info().project.name}.png`,
|
||||
fullPage: true,
|
||||
});
|
||||
});
|
||||
|
||||
test("Anmeldeseite ohne Querscrollen", async ({ page }) => {
|
||||
await page.goto("/anmelden");
|
||||
await keinQuerscrollen(page);
|
||||
});
|
||||
Reference in New Issue
Block a user