Files
sshkeyvault/tests/e2e/einstellungen.spec.ts
florian.wildeandClaude Opus 5.5 3e19b11a80 test(einstellungen): E2E, Screenshots und Issue-Abschluss (#028)
Der E2E-Lauf belegt im gebauten Stand, was die Unit- und DB-Tests nur
einzeln zeigen: ein Admin speichert SMTP, die Testnachricht kommt ueber
den Worker in Mailpit an, das Passwort kehrt nie in die Seite zurueck,
und ein Leser sieht keine Aendern-Knoepfe. Die Screenshots (1440/390 px)
halten das Layout der neuen Seite fuer die Anleitung fest.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 16:54:56 +02:00

103 lines
5.3 KiB
TypeScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { expect, test, type Page } from "@playwright/test";
import "dotenv/config";
/**
* Einstellungen und Benachrichtigungen im gebauten Stand (#028). Braucht neben Web und Worker
* ein Mailpit (siehe tests/integration/benachrichtigung.test.ts) unter SSHKV_TEST_SMTP und
* SSHKV_TEST_MAILPIT_API.
*/
const SMTP = process.env["SSHKV_TEST_SMTP"]?.trim() || "127.0.0.1:1026";
const MAILPIT = (process.env["SSHKV_TEST_MAILPIT_API"]?.trim() || "http://127.0.0.1:8026").replace(
/\/+$/,
"",
);
const [SMTP_HOST, SMTP_PORT] = [SMTP.slice(0, SMTP.lastIndexOf(":")), SMTP.slice(SMTP.lastIndexOf(":") + 1)];
const PASSWORT = "e2e-smtp-passwort-nie-sichtbar";
async function oidcAnmelden(page: Page, rolle: "admin" | "reader") {
await page.goto("/anmelden");
await page.getByRole("link", { name: /Mit Mock-OIDC anmelden/ }).click();
await page.locator(`button[data-rolle="${rolle}"]`).click();
await page.getByRole("button", { name: "Anmelden", exact: true }).click();
await expect(page.getByRole("heading", { name: "Übersicht" })).toBeVisible();
}
function emailKarte(page: Page) {
// Die innerste Section mit der Ueberschrift – die aeussere ist der Abschnitt „Benachrichtigungen“.
return page
.locator("section")
.filter({ has: page.getByRole("heading", { name: "E-Mail (SMTP)" }) })
.last();
}
async function mailpitSuchen(empfaenger: string): Promise<{ Subject: string }[]> {
for (let versuch = 0; versuch < 40; versuch += 1) {
const antwort = await fetch(`${MAILPIT}/api/v1/search?query=${encodeURIComponent(`to:${empfaenger}`)}`);
const daten = (await antwort.json()) as { messages: { Subject: string }[] };
if (daten.messages.length > 0) return daten.messages;
await new Promise((weiter) => setTimeout(weiter, 250));
}
return [];
}
test("Admin speichert SMTP, sendet eine Testnachricht, sie kommt in Mailpit an", async ({ page }) => {
const empfaenger = `e2e-${Date.now().toString(36)}@sshkv.test`;
await oidcAnmelden(page, "admin");
await page.goto("/einstellungen");
await expect(page.getByRole("heading", { name: "Einstellungen", level: 1 })).toBeVisible();
const karte = emailKarte(page);
await karte.getByRole("checkbox", { name: "Per E-Mail benachrichtigen" }).check();
await karte.getByLabel("Server").fill(SMTP_HOST);
await karte.getByRole("textbox", { name: "Port" }).fill(SMTP_PORT);
await karte.getByRole("radio", { name: /Unverschlüsselt/ }).check();
await expect(karte.getByRole("note")).toContainText("Klartext");
await karte.getByLabel("Absender").fill("sshkv@sshkv.test");
await karte.getByLabel("Benutzer (optional)").fill("");
await karte.getByLabel("Passwort", { exact: true }).fill(PASSWORT);
await karte.getByLabel("Empfänger").fill(empfaenger);
await page.getByRole("button", { name: "Benachrichtigungen speichern" }).click();
await expect(page.getByText("Einstellungen gespeichert.")).toBeVisible();
// Das Passwort kommt nie zurueck: Feld leer, nur „gesetzt“.
const passwort = emailKarte(page).getByLabel("Passwort", { exact: true });
await expect(passwort).toHaveValue("");
await expect(passwort).toHaveAttribute("placeholder", /gesetzt/);
await expect(emailKarte(page).getByText("gesetzt", { exact: true })).toBeVisible();
await page.reload();
expect(await page.content()).not.toContain(PASSWORT);
await expect(emailKarte(page).getByLabel("Server")).toHaveValue(SMTP_HOST);
await emailKarte(page).getByRole("button", { name: "Testnachricht senden" }).click();
await expect(emailKarte(page).getByText("Erledigt.")).toBeVisible({ timeout: 30_000 });
const mails = await mailpitSuchen(empfaenger);
expect(mails.map((m) => m.Subject)).toEqual(["[sshkv] Testnachricht"]);
const zustellungen = page
.locator("section")
.filter({ has: page.getByRole("heading", { name: "Letzte Zustellungen" }) })
.last();
await expect(zustellungen.getByRole("row").nth(1)).toContainText("[sshkv] Testnachricht");
await expect(zustellungen.getByRole("row").nth(1)).toContainText("gesendet");
// Webhook nur mit https – http ohne Haekchen ergibt eine Feldmeldung.
await page.getByRole("textbox", { name: "Adresse", exact: true }).fill("http://10.0.0.9/hook");
await page.getByRole("button", { name: "Benachrichtigungen speichern" }).click();
await expect(page.locator("#feld-webhookUrl-fehler")).toContainText("Nur https://");
});
test("Leser sieht die Einstellungen ohne Ändern-Knöpfe", async ({ page }) => {
await oidcAnmelden(page, "reader");
await page.goto("/einstellungen");
await expect(page.getByRole("heading", { name: "Einstellungen", level: 1 })).toBeVisible();
await expect(page.getByText("Nur Ansicht – ändern dürfen Administratoren.")).toBeVisible();
await expect(page.getByRole("button", { name: "Benachrichtigungen speichern" })).toHaveCount(0);
await expect(page.getByRole("button", { name: "Testnachricht senden" })).toHaveCount(0);
await expect(emailKarte(page).getByLabel("Server")).toBeDisabled();
// Statt eines Passwortfelds nur der Hinweis, ob eins gesetzt ist.
await expect(page.locator('input[type="password"]')).toHaveCount(0);
await expect(emailKarte(page).getByText("gesetzt", { exact: true })).toBeVisible();
await expect(page.getByRole("heading", { name: "Instanz" })).toBeVisible();
await expect(page.getByRole("button", { name: /Deployment-Keys kopieren/ })).toBeVisible();
});