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>
103 lines
5.3 KiB
TypeScript
103 lines
5.3 KiB
TypeScript
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();
|
||
});
|