feat(doku): Anleitung, PDF und Ende-zu-Ende-Abnahme (#36)
Prüfung / pruefung (push) Canceled after 0s
Prüfung / e2e (push) Canceled after 0s

This commit was merged in pull request #36.
This commit is contained in:
2026-09-26 09:04:15 +00:00
parent 9bd198a531
commit fbbb634e56
104 changed files with 4527 additions and 299 deletions
+103
View File
@@ -0,0 +1,103 @@
import { chromium } from "@playwright/test";
import "dotenv/config";
import { prisma } from "@/lib/db";
import { anmelden, SEED_GERAET, SEED_TAG, SEED_VORLAGE } from "./helfer";
/**
* Was vor dem ersten Ablauf stimmen muss (#018).
*
* Bisher standen die Voraussetzungen als Satz im Kopfkommentar jeder Datei
* („Voraussetzung: ein gebauter Stand laeuft und `pnpm beispiel:vorlage` hat
* …“). Ein Kommentar prueft nichts. Stimmte etwas davon nicht, fiel das erst
* mitten im Lauf auf, und die Meldung sprach von einem Zeitablauf oder von null
* Treffern — nie von der Ursache.
*
* Hier wird es geprueft, einmal, bevor irgendein Ablauf startet. Jede Meldung
* sagt, **was** fehlt und **was zu tun ist**.
*/
const APP_URL = process.env["E2E_APP_URL"] ?? "http://127.0.0.1:3000";
function fehlen(was: string, abhilfe: string): never {
throw new Error(`Ende-zu-Ende-Tests: ${was}\n → ${abhilfe}`);
}
export default async function vorbereiten(): Promise<void> {
if (!process.env["SEED_ADMIN_EMAIL"] || !process.env["SEED_ADMIN_PASSWORD"]) {
fehlen(
"SEED_ADMIN_EMAIL oder SEED_ADMIN_PASSWORD fehlt.",
"Beide stehen in der .env und müssen zu dem Administrator passen, den der Seed angelegt hat.",
);
}
// 1. Laeuft ueberhaupt etwas an dieser Adresse?
const antwort = await fetch(`${APP_URL}/api/gesundheit`, { signal: AbortSignal.timeout(5000) }).catch(
() => undefined,
);
if (!antwort?.ok) {
fehlen(
`Unter ${APP_URL} antwortet keine Anwendung.`,
"Erst bauen und starten: pnpm build && PORT=3300 APP_URL=http://127.0.0.1:3300 pnpm start — " +
"und die Adresse über E2E_APP_URL mitgeben. Gegen `pnpm dev` laufen die Abläufe nicht " +
"(siehe CLAUDE.md, „Oberfläche prüfen“).",
);
}
// 2. Steht der Seed-Bestand, mit dem die Ablaeufe arbeiten?
const vorlage = await prisma.vorlage.findFirst({
where: { name: SEED_VORLAGE },
select: { id: true, versionen: { where: { status: "VEROEFFENTLICHT" }, select: { id: true } } },
});
if (!vorlage || vorlage.versionen.length === 0) {
fehlen(
`Die Vorlage „${SEED_VORLAGE}“ fehlt oder ist nicht veröffentlicht.`,
"pnpm db:seed laufen lassen.",
);
}
const geraet = await prisma.geraet.findUnique({ where: { tagCode: SEED_TAG }, select: { id: true } });
if (!geraet) {
fehlen(`Das Beispielgerät ${SEED_GERAET} (${SEED_TAG}) fehlt.`, "pnpm db:seed laufen lassen.");
}
/*
* 2b. In welchem Betriebsmodus laeuft die Installation?
*
* Ein Ablauf soll das nicht selbst aus der Datenbank holen muessen — und zwei
* Quellen fuer dieselbe Auskunft liefen frueher oder spaeter auseinander. Der
* Modus steht danach in `E2E_BETRIEBSMODUS`, und ein Ablauf, der nur in einem
* der beiden Sinn ergibt, ueberspringt sich selbst mit einem Grund.
*/
const einstellung = await prisma.einstellung.findUnique({
where: { id: "global" },
select: { modus: true },
});
process.env["E2E_BETRIEBSMODUS"] = einstellung?.modus ?? "INTERN";
/*
* 3. Liest der Server **dieselbe** Datenbank?
*
* Der Testprozess schreibt über DATABASE_URL aus der .env, der Server bekommt
* seine eigene mit. Zeigen beide woandershin, legt ein Ablauf seine Daten in
* die eine und sucht sie in der anderen. Geprüft wird an einer Vorlage, die
* hier entsteht und gleich wieder verschwindet.
*/
const probe = `ZZ Datenbankprüfung ${Date.now()}`;
const angelegt = await prisma.vorlage.create({ data: { name: probe, kategorie: "ZZ Test" } });
const browser = await chromium.launch();
try {
const tab = await browser.newPage({ baseURL: APP_URL });
await anmelden(tab);
await tab.goto(`/vorlagen?suche=${encodeURIComponent(probe)}`);
if ((await tab.getByRole("link", { name: probe }).count()) === 0) {
fehlen(
"Der Server unter Test liest eine andere Datenbank als dieser Lauf.",
"DATABASE_URL des Testprozesses (aus der .env) und die des Servers müssen auf dieselbe " +
"Datenbank zeigen — sonst schreibt der Ablauf in die eine und sucht in der anderen.",
);
}
} finally {
await browser.close();
await prisma.vorlage.delete({ where: { id: angelegt.id } });
await prisma.$disconnect();
}
}