Files
GerbilManager/gerbil-manager-web/e2e/fixtures.ts
Gulum a2f5e848d1
Some checks failed
CI / Backend Tests (.NET) (push) Successful in 52s
CI / Docker Build & Push (push) Has been cancelled
CI / Frontend Tests (Node/Vite) (push) Has been cancelled
Merge feature/qa-2: Verbatim-few-shots (POLISH-2), CI-Flake-Fix, skipUnlessLive+Live-Smoke, Abgabe-Wizard-e2e [god-QA validated]
2026-06-06 15:34:38 +02:00

92 lines
2.9 KiB
TypeScript

/**
* QA-1: Test-Fixtures.
*
* - MOCK-Modus (Standard): installiert vor jedem Test den In-Memory-Mock
* (frischer Datenbestand pro Test).
* - LIVE-Modus (E2E_BASE_URL gesetzt): kein Mock; fixture-gebundene Tests
* überspringen sich über `skipUnlessMock()`.
*
* Deutsche Oberflächen-Texte werden DIREKT aus src/strings/de.ts importiert —
* die Suite bleibt damit automatisch synchron zur App.
*/
import { test as base, expect, type Page } from '@playwright/test'
import { de } from '../src/strings/de'
import { installMockApi } from './mock-api'
import type { MockDb } from './mock-data'
export const LIVE = Boolean(process.env.E2E_BASE_URL)
export { expect }
export { de }
interface Fixtures {
/** Im Mock-Modus der pro Test frische Datenbestand, sonst null. */
mockDb: MockDb | null
}
export const test = base.extend<Fixtures>({
mockDb: [
async ({ page }, use) => {
const db = LIVE ? null : await installMockApi(page)
await use(db)
},
{ auto: true },
],
})
/** Test überspringen, wenn er Seed-Daten des Mocks voraussetzt. */
export function skipUnlessMock() {
test.skip(LIVE, 'benötigt die Mock-Seed-Daten (läuft nicht im LIVE-Modus)')
}
/**
* Test überspringen im Mock-Modus — nur im LIVE-Modus sinnvoll.
* Live-Tests MÜSSEN Ergebnisse assertieren (z. B. Liste nicht leer,
* konkreter Wert sichtbar), nicht nur HTTP-200. Lesson: der Mock spiegelt
* den Frontend-Code, nicht das echte Backend — ein Bug bleibt unsichtbar,
* wenn wir nur testen, ob die Seite lädt.
*/
export function skipUnlessLive() {
test.skip(!LIVE, 'nur im LIVE-Modus sinnvoll (E2E_BASE_URL setzen)')
}
/**
* Zu einem Navigationsziel wechseln — auf dem Smartphone liegen Becken,
* Kontakte und Statistik hinter dem „Mehr“-Blatt.
*/
export async function gotoSection(page: Page, label: string) {
const nav = page.getByRole('navigation')
// App noch nicht geladen (about:blank) -> erst zur Startseite
if (!(await nav.isVisible())) {
await page.goto('/')
await expect(nav).toBeVisible()
}
const link = nav.getByRole('link', { name: label })
if (!(await link.isVisible())) {
await nav.getByRole('button', { name: de.nav.more }).click()
await expect(link).toBeVisible()
}
await link.click()
}
/** window.confirm des nächsten Dialogs annehmen. */
export function acceptNextDialog(page: Page) {
page.once('dialog', (d) => void d.accept())
}
/**
* UX-MOBILE-1: Filter-Drawer öffnen, falls der Toggle-Button sichtbar ist
* (= Smartphone-Ansicht). Auf Desktop-Ansicht ist er per CSS versteckt, dann
* kein Klick nötig — Controls sind direkt sichtbar.
*/
export async function openFilterPanel(page: Page) {
const toggle = page.locator('.filter-panel__toggle')
if (await toggle.isVisible()) {
await toggle.click()
}
}
/** Eindeutiger Name für LIVE-taugliche Create-Flows. */
export const uniqueName = (prefix: string) =>
`${prefix} E2E ${Date.now().toString(36)}${Math.random().toString(36).slice(2, 6)}`