E2E-Tests

Playwright-Konfiguration, Smoke-Tests vs. vollständige Suite, Test-Fixtures und CI/CD-Integration für End-to-End-Testing

Das Kit enthält 17 E2E-Testdateien, die 50+ Szenarien in den Bereichen Authentifizierung, AI-Chat, Abrechnung, Dashboard, Pricing und Security abdecken. Alle Tests laufen gegen MSW-gemockte APIs — keine Datenbank, keine externen Services und keine Clerk-Authentifizierung in CI erforderlich.

Konfiguration

Die Playwright-Konfiguration definiert Browser, Timeouts, Retry-Strategie und den Test-Webserver:
playwright.config.ts
import { defineConfig, devices } from '@playwright/test'
import dotenv from 'dotenv'
import path from 'path'

// Load test environment variables (quietly)
dotenv.config({
  path: path.resolve(process.cwd(), '.env.test'),
  debug: false, // Suppress dotenv debug output that clutters CI logs
})

export default defineConfig({
  testDir: './e2e',
  // PHASE 2: Parallel execution re-enabled with robust safeguards
  // - User IDs synchronized between auth system and seed data
  // - Clerk API calls guarded by shouldUseClerk()
  // - Fail-fast logic prevents user creation in test mode
  fullyParallel: true,
  // MONOREPO: Global setup is at repository root, not within boilerplate app
  globalSetup: require.resolve('../../playwright/global-setup.ts'),
  forbidOnly: !!process.env.CI,
  retries: process.env.CI ? 2 : 0,
  // PHASE 3: Single worker in CI for maximum stability and predictable execution
  // Reduces resource contention and ensures consistent test timing
  workers: (() => {
    const ciWorkers = process.env.CI ? 1 : undefined
    if (process.env.CI) {
      console.log('[Playwright Config] CI mode detected')
      console.log('[Playwright Config] Workers: 1 (optimized for stability)')
      console.log(
        '[Playwright Config] Test timeout: 90s (increased for slow dashboard prefetch)'
      )
    }
    return ciWorkers
  })(),
  reporter: process.env.CI ? [['html'], ['github']] : 'html',
  // PHASE 4: Unified timeouts for CI and local (environment parity)
  // Dashboard prefetch takes 7-9s + page render time = 9-10s total
  // Generous timeouts prevent false failures on slow routes
  // Both CI and local now use same values for consistent behavior
  timeout: 90 * 1000, // 90s global timeout (was: 30s local, 90s CI)
  expect: {
    timeout: 20 * 1000, // 20s for assertions (was: 10s local, 20s CI)
  },
  use: {
    baseURL: process.env.BASE_URL || 'http://localhost:3000',
    trace: 'on-first-retry',
    screenshot: 'only-on-failure',
    // CRITICAL: Navigation timeout must exceed dashboard prefetch (7-9s) + render time
    // Local dev server adds compilation overhead (~2s), requiring 30s total
    // CI production server is faster but uses same timeout for consistency
    navigationTimeout: 30 * 1000, // 30s navigation (was: 10s local, 30s CI)
    actionTimeout: 15 * 1000, // 15s for actions (was: 5s local, 15s CI)
    // Disable animations for faster tests
    video: 'retain-on-failure',
    launchOptions: {
      args: ['--disable-blink-features=AutomationControlled'],
    },
  },
  projects: [
    // OPTIMIZATION: Primary browser for CI
    {
      name: 'chromium',
      use: { ...devices['Desktop Chrome'] },
    },
    // OPTIMIZATION: Other browsers only for main branch or local development
    ...(process.env.FULL_TEST_SUITE === 'true' || !process.env.CI
      ? [
          {
            name: 'firefox',
            use: { ...devices['Desktop Firefox'] },
          },
          {
            name: 'webkit',
            use: { ...devices['Desktop Safari'] },
          },
          // Mobile Testing - only when explicitly needed
          {
            name: 'mobile-chrome',
            use: { ...devices['Pixel 5'] },
          },
          {
            name: 'mobile-safari',
            use: { ...devices['iPhone 12'] },
          },
        ]
      : []),
  ],
  webServer: {
    // Use dedicated script to ensure clean test environment
    command: './scripts/start-test-server.sh',
    url: 'http://localhost:3000',
    reuseExistingServer: false, // Always start fresh for tests
    timeout: 2 * 60 * 1000, // 2 minutes to start
    stdout: 'pipe',
    stderr: 'pipe',
  },
})
Wichtige Designentscheidungen:
  • Einheitliche Timeouts — CI und lokal verwenden identische Werte (90 Sek. global, 20 Sek. expect, 30 Sek. Navigation), um Überraschungen à la „funktioniert lokal, schlägt in CI fehl" zu vermeiden
  • Navigations-Timeout von 30 Sek. — Dashboard-Seiten laden beim Start Daten vor (7–9 Sek.), plus Kompilierungs-Overhead während der Entwicklung (~2 Sek.). Ein zu knapper Timeout hier verursacht falsche Fehler
  • Einzelner Worker in CI — Reduziert Ressourcenkonflikte und sorgt für vorhersehbare Ausführungsreihenfolge
  • fullyParallel: true lokal für schnelles Feedback, mit korrekter User-ID-Synchronisation zwischen Auth-System und Seed-Daten
  • Frischer Server pro DurchlaufreuseExistingServer: false stellt sicher, dass Tests mit einem sauberen Zustand beginnen

Tests ausführen

BefehlBeschreibung
pnpm test:e2eAlle E2E-Tests ausführen (Playwright, nur Chromium in CI)
pnpm test:e2e:smokeNur Smoke-Tests — schnelles Feedback während der Entwicklung
pnpm test:e2e:fullVollständige Suite mit FULL_TEST_SUITE=true (alle Browser)
pnpm test:e2e:uiVisueller UI-Modus — Tests interaktiv im Browser ausführen
pnpm test:e2e:debugDebug-Modus — öffnet Playwright Inspector mit Schritt-für-Schritt-Ausführung
pnpm test:e2e:prodProduction-Bundle bauen, dann Smoke-Tests ausführen
pnpm test:e2e:prod:fullProduction-Bundle bauen, dann vollständige Suite ausführen

Verzeichnisstruktur der Tests

apps/boilerplate/e2e/
  smoke/                          # Schnelle Tests für kritische Pfade
    auth.smoke.spec.ts            # Login, Registrierung, Logout
    ai-chat.smoke.spec.ts         # Grundlegender AI-Chat-Ablauf
    ai-support.smoke.spec.ts      # Support-Bot-Interaktion
    billing.smoke.spec.ts         # Abrechnungsseite lädt
    dashboard.smoke.spec.ts       # Dashboard-Rendering
    pricing-checkout.smoke.spec.ts # Pricing-Seite und Checkout
    public.smoke.spec.ts          # Öffentliche Seiten (Home, About)
    showcase.smoke.spec.ts        # Feature-Showcase
  demo/
    demo-flow.spec.ts             # Demo-Modus End-to-End-Ablauf
  examples/
    test-data-usage.spec.ts       # Beispiel-Patterns für neue Tests
  helpers/
    auth.helper.ts                # Login/Registrierung/Logout-Utilities
    test-config.ts                # Selektoren, Timeouts, Umgebungserkennung
  ai-chat.spec.ts                 # Vollständige AI-Chat-Test-Suite
  ai-support.spec.ts              # Vollständige AI-Support-Test-Suite
  auth-flow.spec.ts               # Vollständige Authentifizierungsabläufe
  credit-display.spec.ts          # Credit-UI und Anzeigelogik
  security.spec.ts                # Validierung von Security-Features
  subscription-checkout.spec.ts   # Vollständiger Subscription- und Checkout-Ablauf
  smoke-tests.spec.ts             # Legacy-Smoke-Tests

Smoke-Tests vs. vollständige Tests

AspektSmoke-TestsVollständige Tests
Ortapps/boilerplate/e2e/smoke/*.smoke.spec.tsapps/boilerplate/e2e/*.spec.ts
UmfangNur kritische Pfade (Login, Navigation, grundlegende Interaktionen)Tiefes Feature-Testing (Grenzfälle, Fehlerbehandlung, Responsiveness)
BrowserNur ChromiumChromium + Firefox + WebKit + Mobile (mit FULL_TEST_SUITE)
Dauer~1–2 Minuten~10–15 Minuten (alle Browser)
Wann ausführenBei jeder Code-Änderung, jedem PRVor dem Merge in main, Release-Validierung
CI-TriggerJeder PushFULL_TEST_SUITE=true oder main-Branch
Die Browser-Matrix wird durch die Umgebungsvariable FULL_TEST_SUITE gesteuert:
  • Standard (CI): Nur Chromium — schnelles Feedback für jeden PR
  • FULL_TEST_SUITE=true: Fügt Firefox, WebKit, Mobile Chrome (Pixel 5) und Mobile Safari (iPhone 12) hinzu
  • Lokale Entwicklung: Standardmäßig alle Browser für gründliches Testen aktiviert

Testumgebung

Umgebungsvariablen

E2E-Tests laden apps/boilerplate/.env.test, das folgendes konfiguriert:
  • NODE_ENV=test — Aktiviert Test-Modus-Verhalten in der gesamten App
  • NEXT_PUBLIC_MSW_ENABLED=true — Aktiviert MSW im Browser für API-Mocking
  • NEXT_PUBLIC_CLERK_ENABLED=false — Deaktiviert Clerk-Authentifizierung (alle Auth-Keys geleert)
  • NEXT_PUBLIC_PAYMENTS_ENABLED=false — Deaktiviert Lemon Squeezy-Zahlungsverarbeitung
  • NEXT_PUBLIC_EMAIL_ENABLED=false — Deaktiviert den Resend-E-Mail-Service
  • Alle Service-API-Keys auf Dummy-Werte gesetzt — MSW fängt jeden externen Aufruf ab

Globales Setup

Die globale Setup-Datei im Monorepo-Root startet den MSW-Server, bevor Tests ausgeführt werden:
typescript
// playwright/global-setup.ts
export default async function globalSetup() {
  // Testumgebung setzen
  process.env.NODE_ENV = 'test'
  process.env.NEXT_PUBLIC_CLERK_ENABLED = 'false'

  // MSW-Server für API-Mocking starten
  const { server } = await import('./msw-setup')
  server.listen({ onUnhandledRequest: 'warn' })

  // Teardown-Funktion zurückgeben
  return () => server.close()
}
Das bedeutet: Für E2E-Tests werden weder Datenbank noch externe Services benötigt — MSW ersetzt alles.

Test-Server-Start

Playwright startet für jeden Testdurchlauf einen frischen Next.js-Server über scripts/start-test-server.sh. Der Server hat ein 2-Minuten-Startup-Timeout, um Cold Builds zu berücksichtigen.

Test-Helpers & Fixtures

Testkonfiguration

e2e/helpers/test-config.ts
/**
 * Test Configuration Utilities
 * Provides environment-specific test configuration
 */

import { isTestEnvironment } from '@/lib/test-utils'

/**
 * Determines if we're running in CI environment with Clerk bypass
 * Uses the centralized test-utils for consistency
 */
export function isClerkBypassed(): boolean {
  return isTestEnvironment()
}

/**
 * Skip test helper for Clerk-specific tests
 */
export function skipIfClerkBypassed(testTitle: string) {
  return isClerkBypassed()
    ? `${testTitle} (SKIPPED: Clerk bypassed in CI)`
    : testTitle
}

/**
 * Test environment configuration
 */
export const testConfig = {
  // Longer timeouts for CI environment
  waitTimeout: process.env.CI ? 15000 : 10000,
  expectTimeout: process.env.CI ? 20000 : 15000,

  // Environment flags
  isCI: Boolean(process.env.CI),
  isTestEnv: process.env.NODE_ENV === 'test',
  clerkBypassed: isClerkBypassed(),

  // Test selectors
  selectors: {
    signInButton:
      '[data-testid="signin-button"], nav a[href="/login"], nav button:has-text("Sign in")',
    getStartedButton:
      '[data-testid="get-started-button"], nav a[href="/register"], nav button:has-text("Get started")',
    mobileMenuButton:
      '[data-testid="mobile-menu-button"], button:has(.lucide-menu), button:has(svg)',
    mobileSignInButton:
      '[data-testid="mobile-signin-button"], a[href="/login"]',
    mobileGetStartedButton:
      '[data-testid="mobile-get-started-button"], a[href="/register"]',
  },
}
Das testConfig-Objekt bietet:
  • Umgebungsabhängige Timeouts — Längere Wartezeiten in CI, wo Ressourcen begrenzt sind
  • Flexible Selektoren — Jeder Selektor ist eine CSS-Selektorliste, die mehrere mögliche DOM-Strukturen abgleicht. Dies berücksichtigt Clerks dynamische UI-Komponenten und Test-ID-Variationen
  • Clerk-Bypass-ErkennungisClerkBypassed() gibt in Testumgebungen true zurück, sodass Tests echte Authentifizierungsabläufe überspringen können

Authentifizierungs-Helpers

Die Datei auth.helper.ts stellt Utilities für Authentifizierungsabläufe in E2E-Tests bereit:
HelperZweck
waitForClerkAuth()Wartet, bis Clerk-Komponenten fertig geladen sind
registerWithClerk()Registriert einen neuen Benutzer über das Clerk SignUp-Formular
loginWithClerk()Meldet einen bestehenden Benutzer über das Clerk SignIn-Formular an
logoutUser()Meldet den aktuellen Benutzer ab
isLoggedIn()Prüft, ob ein Benutzer aktuell authentifiziert ist
createTestUser()Generiert einen eindeutigen Testbenutzer mit zufälliger E-Mail
getSubscribedTestUser()Gibt einen vorkonfigurierten Testbenutzer mit aktiver Subscription zurück
Im Test-Modus (Clerk umgangen) verwenden diese Helpers vereinfachte Abläufe, die nicht mit echten Clerk-Formularen interagieren. Das macht Tests schneller und zuverlässiger in CI.

E2E-Tests schreiben

Einfacher Navigationstest

typescript
import { test, expect } from '@playwright/test'

test('homepage loads and shows hero section', async ({ page }) => {
  await page.goto('/')

  await expect(page.locator('h1')).toBeVisible()
  await expect(page.locator('[data-testid="hero-section"]')).toBeVisible()
})

Responsiveness testen

Desktop- und Mobile-Viewports in derselben Datei testen:
typescript
import { test, expect } from '@playwright/test'
import { testConfig } from './helpers/test-config'

test('mobile menu opens and closes', async ({ page }) => {
  // Mobile Viewport setzen
  await page.setViewportSize({ width: 375, height: 667 })
  await page.goto('/')

  // Mobilmenü öffnen
  const menuButton = page.locator(testConfig.selectors.mobileMenuButton)
  await menuButton.click()

  // Prüfen, ob Menüeinträge sichtbar sind
  await expect(
    page.locator(testConfig.selectors.mobileSignInButton)
  ).toBeVisible()

  // Schließen durch Klick außerhalb
  await page.locator('body').click()
  await expect(
    page.locator(testConfig.selectors.mobileSignInButton)
  ).not.toBeVisible()
})

Routen-Interception

API-Responses für bestimmte Testszenarien mit Playwrights Routen-Interception überschreiben:
typescript
test('shows error state when API fails', async ({ page }) => {
  // Dashboard-API abfangen und Fehler zurückgeben
  await page.route('/api/dashboard', (route) => {
    route.fulfill({
      status: 500,
      contentType: 'application/json',
      body: JSON.stringify({ error: 'Internal Server Error' }),
    })
  })

  await page.goto('/dashboard')

  await expect(page.getByText(/something went wrong/i)).toBeVisible()
})

Barrierefreiheit testen

Grundlegende Barrierefreiheitsanforderungen in E2E-Tests prüfen:
typescript
test('pricing page meets accessibility requirements', async ({ page }) => {
  await page.goto('/pricing')

  // Prüfen, ob alle Bilder Alt-Text haben
  const images = page.locator('img')
  for (const image of await images.all()) {
    await expect(image).toHaveAttribute('alt')
  }

  // Überschriften-Hierarchie prüfen
  const h1Count = await page.locator('h1').count()
  expect(h1Count).toBe(1)
})

CI/CD-Integration

CI-Konfiguration

E2E-Tests laufen in CI mit optimierten Einstellungen:
EinstellungWertGrund
Worker1Einzelner Worker verhindert Ressourcenkonflikte
Retries2Retries fangen flüchtige Fehler ab (Netzwerk, Timing)
ScreenshotBei FehlerErfasst visuellen Zustand zum Debuggen
TraceBeim ersten RetryVollständige Ausführungs-Trace für flaky Test-Analyse
VideoBei Fehler behaltenVideo-Playback für komplexes Interaktions-Debugging
ReporterHTML + GitHubHTML für lokale Überprüfung, GitHub für PR-Annotationen

Timeout-Strategie

TimeoutWertWas abgedeckt wird
Global90 Sek.Gesamtzeit pro Test (inkl. aller Navigationen und Assertions)
Expect20 Sek.Einzelner Assertion-Timeout (z.B. „warte bis Element sichtbar")
Navigation30 Sek.Seitennavigation inkl. Prefetch und Render
Aktion15 Sek.Benutzeraktionen (Klick, Tippen, Auswählen)
Web-Server120 Sek.Next.js-Build- und Server-Startzeit
Diese Werte sind bewusst großzügig. Schnelle Tests werden weit unter diesen Grenzen abschließen, während langsame Operationen (Dashboard-Prefetch, Cold Builds) keine falschen Fehler erzeugen.

Debugging

Playwright Inspector

bash
pnpm test:e2e:debug
Öffnet den Playwright Inspector mit Schritt-für-Schritt-Ausführung. Du kannst:
  • An jeder Zeile pausieren und die Seite untersuchen
  • Den Selektor-Picker verwenden, um Elemente zu finden
  • Aktionen einzeln durchgehen
  • Konsolen- und Netzwerk-Logs ansehen

Visueller UI-Modus

bash
pnpm test:e2e:ui
Öffnet eine browserbasierte Oberfläche mit:
  • Allen Tests mit Bestanden/Nicht-Bestanden-Status
  • Live-Seitenvorschau während der Testausführung
  • Zeitstrahl der Aktionen und Assertions
  • Screenshots bei jedem Schritt

CI-Fehler-Artefakte

Wenn Tests in CI fehlschlagen, generiert Playwright:
  • Screenshots — Werden automatisch bei Fehlern erfasst
  • Traces — Werden beim ersten Retry aufgezeichnet, ansehbar mit npx playwright show-trace trace.zip
  • Videos — Für fehlgeschlagene Tests aufbewahrt, zeigen die gesamte Testausführung
  • HTML-Bericht — Nach jedem CI-Durchlauf generiert unter playwright-report/index.html

Wichtige Dateien

DateiZweck
apps/boilerplate/playwright.config.tsBrowser-Projekte, Timeouts, Retries, Webserver-Konfiguration
apps/boilerplate/e2e/helpers/test-config.tsSelektoren, Timeouts, Umgebungserkennung
apps/boilerplate/e2e/helpers/auth.helper.tsClerk-Authentifizierungs-Utilities für E2E
apps/boilerplate/e2e/smoke/Kritische Pfad-Smoke-Tests (schnell, nur Chromium)
apps/boilerplate/scripts/start-test-server.shSkript für sauberen Test-Server-Start
apps/boilerplate/.env.testTest-Umgebungsvariablen (MSW aktiviert, Clerk deaktiviert)