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
+10 -28
View File
@@ -1,6 +1,6 @@
import { expect, test, type Page } from "@playwright/test";
import { expect, test } from "@playwright/test";
import "dotenv/config";
import { prisma } from "@/lib/db";
import { anmelden, SEED_TAG } from "./helfer";
/**
* Der Etikettendruck am gebauten Stand (#014).
@@ -14,34 +14,21 @@ import { prisma } from "@/lib/db";
* Die Aufloesung: 1 mm sind 96/25,4 px, ein `deviceScaleFactor` von 300/96 =
* 3,125 macht daraus 300 dpi. 62 mm werden so zu 62 · 96/25,4 · 3,125 ≈ 732
* Geraetepixeln — genau die Aufloesung, mit der ein Etikettendrucker arbeitet.
*
* Gedruckt wird `SEED_TAG` und nicht irgendein Code aus der Datenbank: dass es
* ihn gibt, prueft `tests/e2e/global-setup.ts` vor dem ersten Ablauf. Ein
* `findFirst` haette gegen eine frische Installation „kein Geraet“ gemeldet und
* gegen eine benutzte einen beliebigen Aufkleber gedruckt (#018).
*/
const DPI = 300;
const PX_JE_MM = 96 / 25.4;
async function anmelden(seite: Page) {
const email = process.env["SEED_ADMIN_EMAIL"];
const passwort = process.env["SEED_ADMIN_PASSWORD"];
if (!email || !passwort) throw new Error("SEED_ADMIN_EMAIL und SEED_ADMIN_PASSWORD fehlen.");
await seite.goto("/anmelden");
await seite.fill('input[name="email"]', email);
await seite.fill('input[name="passwort"]', passwort);
await Promise.all([seite.waitForURL("**/"), seite.click('button[type="submit"]')]);
}
/** Irgendein Tag-Code, den es in dieser Datenbank wirklich gibt. */
async function einTagCode(): Promise<string> {
const geraet = await prisma.geraet.findFirst({ select: { tagCode: true } });
if (!geraet) throw new Error("Kein Gerät in der Datenbank — erst pnpm db:seed.");
return geraet.tagCode;
}
test.describe("Etikettendruck", () => {
test("druckt ein Einzeletikett auf 62 × 29 mm", async ({ page }) => {
test.skip(test.info().project.name !== "desktop", "Das Seitenformat hängt nicht am Gerät.");
await anmelden(page);
const code = await einTagCode();
await page.goto(`/drucken/etiketten?layout=etikett&codes=${code}`);
await page.goto(`/drucken/etiketten?layout=etikett&codes=${SEED_TAG}`);
/*
* `page.pdf()` gilt in Chromium und beachtet `@page`. Gemessen wird die
@@ -69,7 +56,6 @@ test.describe("Etikettendruck", () => {
test("ein bei 300 dpi gerastertes Etikett lässt sich zurücklesen", async ({ browser }) => {
test.skip(test.info().project.name !== "desktop", "Der Decoder hängt nicht am Gerät.");
const code = await einTagCode();
/*
* Eigener Kontext: der `deviceScaleFactor` ist die Aufloesung, und die
@@ -84,7 +70,7 @@ test.describe("Etikettendruck", () => {
try {
const tab = await kontext.newPage();
await anmelden(tab);
await tab.goto(`/drucken/etiketten?layout=etikett&codes=${code}`);
await tab.goto(`/drucken/etiketten?layout=etikett&codes=${SEED_TAG}`);
const etikett = tab.locator("[data-etikett]").first();
await expect(etikett).toBeVisible();
const bild = await etikett.screenshot();
@@ -100,7 +86,7 @@ test.describe("Etikettendruck", () => {
* dazulegen koennte.
*/
const adresse = new URL(gelesen[0] ?? "");
expect(adresse.pathname).toBe(`/t/${code}`);
expect(adresse.pathname).toBe(`/t/${SEED_TAG}`);
expect(adresse.search).toBe("");
expect(adresse.hash).toBe("");
} finally {
@@ -157,7 +143,3 @@ function seitenmasseAusPdf(pdf: Buffer): { breite: number; hoehe: number } {
hoehe: Number(treffer[4]) - Number(treffer[2]),
};
}
test.afterAll(async () => {
await prisma.$disconnect();
});