import AxeBuilder from "@axe-core/playwright"; import { expect, test } from "@playwright/test"; /** * Voraussetzung: Stripe-Testschluessel und `pnpm db:seed` (simulierte * Early-Bird-Phase, Testgutschein TEST-STAMMMODEL). Die Zahlung selbst wird * nicht ausgeloest — der Test endet vor der Weiterleitung zu Stripe. */ test("Onepage: Aufbau, kein horizontales Scrollen, Barrierefreiheit", async ({ page }, testInfo) => { await page.goto("/"); await expect(page.getByRole("heading", { level: 1 })).toContainText("ARTISTA"); for (const id of ["camp", "leistungen", "unterkunft", "team", "artista", "preis", "fragen"]) { await expect(page.locator(`#${id}`)).toBeAttached(); } // Keine oeffentliche Platzzahl und kein Restplatz-Zaehler. await expect(page.locator("body")).not.toContainText(/Restpl|noch \d+ Pl|Plätze frei/); const overflow = await page.evaluate(() => document.documentElement.scrollWidth - window.innerWidth); expect(overflow).toBeLessThanOrEqual(0); await page.waitForTimeout(1600); // Hero-Eroeffnung abwarten await page.screenshot({ path: testInfo.outputPath(`onepage-${testInfo.project.name}.png`), fullPage: true }); const results = await new AxeBuilder({ page }).withTags(["wcag2a", "wcag2aa", "wcag21aa"]).analyze(); expect(results.violations.map((v) => `${v.id}: ${v.nodes.length}`)).toEqual([]); }); test("Buchungsdialog: Daten, Gutschein, Ratenplan, Erklaerungen, Fokus", async ({ page }, testInfo) => { await page.goto("/"); const trigger = page.locator("#top").getByRole("button", { name: "Platz buchen" }); await trigger.click(); const dialog = page.getByRole("dialog"); await expect(dialog.getByRole("heading", { name: "Deine Daten" })).toBeFocused(); // Pflichtfelder werden verstaendlich gemeldet. await dialog.getByRole("button", { name: "Weiter" }).click(); await expect(dialog.getByText("Bitte gib deinen Vornamen an.")).toBeVisible(); await dialog.getByLabel("Vorname").fill("Test"); await dialog.getByLabel("Nachname").fill("Person"); await dialog.getByLabel("E-Mail").fill("test@example.org"); await dialog.getByLabel("Telefon").fill("+49 30 123456"); await dialog.getByRole("button", { name: "Weiter" }).click(); await expect(dialog.getByRole("heading", { name: "Zahlungsweise" })).toBeFocused(); await dialog.getByRole("radio", { name: /Drei Raten/ }).check(); await dialog.getByLabel("Gutscheincode (optional)").fill("test-stammmodel"); await dialog.getByRole("button", { name: "Anwenden" }).click(); await expect(dialog.getByText("Gutschein angewendet.")).toBeVisible(); await expect(dialog.getByText("1.980,00 €").first()).toBeVisible(); await dialog.getByRole("button", { name: "Weiter" }).click(); await expect(dialog.getByText("Heute fällig: 660,00 €", { exact: false })).toBeVisible(); await expect(dialog.getByText("Nicht enthalten:", { exact: false })).toBeVisible(); // Ohne Erklaerungen keine Buchung. await dialog.getByRole("button", { name: "Zahlungspflichtig buchen" }).click(); await expect(dialog.getByText("Bitte stimme dem Einzug der weiteren Raten zu.")).toBeVisible(); await dialog.screenshot({ path: testInfo.outputPath(`dialog-${testInfo.project.name}.png`) }); // Escape schliesst, Fokus kehrt zum ausloesenden Button zurueck. await page.keyboard.press("Escape"); await expect(dialog).toBeHidden(); await expect(trigger).toBeFocused(); }); test("Verwaltung und Buchungsstatus sind geschuetzt und nicht indexierbar", async ({ page, request }) => { await page.goto("/admin/buchungen"); await expect(page).toHaveURL(/\/admin\/anmelden$/); const exportResponse = await request.get("/admin/export", { maxRedirects: 0, headers: { cookie: "better-auth.session_token=gefaelscht" } }); expect(exportResponse.status()).toBe(401); const status = await request.get("/buchung/status/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"); expect(status.status()).toBe(404); expect(status.headers()["x-robots-tag"]).toContain("noindex"); });