import path from 'node:path'; import { defineConfig, devices } from '@playwright/test'; import { config } from 'dotenv'; // Load .env.test so webServer processes and tests use the test database if (!process.env.CI) { config({ path: path.resolve(__dirname, '.env.test'), override: true }); } // Server ports are configurable via env to avoid conflicts with dev containers. // GitHub Actions loads .env.test before invoking Playwright. const API_PORT = process.env.API_PORT ?? '3001'; const WEB_PORT = process.env.WEB_PORT ?? '3000'; const SERVER_STARTUP_TIMEOUT_MS = process.env.CI ? 300_000 : 60_000; const VIETNAMESE_BROWSER_CONTEXT = { locale: 'vi-VN', extraHTTPHeaders: { 'Accept-Language': 'vi-VN,vi;q=0.9,en;q=0.8', }, }; /** * Playwright E2E configuration for Goodgo Platform. * * Projects: * - "api" — tests against the NestJS API (port 3011 local CI / 3001 GH Actions) * - "web" — tests against the Next.js frontend (port 3010 local CI / 3000 GH Actions) * * Database isolation: * - globalSetup runs migrations + seed on the test DB * - globalTeardown cleans up test-generated data after all tests */ export default defineConfig({ testDir: './e2e', globalSetup: './e2e/global-setup.ts', globalTeardown: './e2e/global-teardown.ts', fullyParallel: true, forbidOnly: !!process.env.CI, retries: process.env.CI ? 2 : 0, workers: process.env.CI ? 1 : undefined, reporter: process.env.CI ? [['html', { open: 'never' }], ['github']] : [['html', { open: 'on-failure' }]], use: { trace: 'on-first-retry', screenshot: 'only-on-failure', }, projects: [ // API E2E tests — no browser needed, uses APIRequestContext { name: 'api', testDir: './e2e/api', use: { baseURL: process.env.API_BASE_URL ?? `http://localhost:${API_PORT}/api/v1/`, }, }, // Web E2E tests — Chromium browser { name: 'web', testDir: './e2e/web', use: { ...devices['Desktop Chrome'], ...VIETNAMESE_BROWSER_CONTEXT, baseURL: process.env.WEB_BASE_URL ?? `http://localhost:${WEB_PORT}`, }, }, // Smoke projects — subsets of api/web tagged @smoke for post-deploy checks { name: 'smoke-api', testDir: './e2e/api', grep: /@smoke/, use: { baseURL: process.env.API_BASE_URL ?? `http://localhost:${API_PORT}/api/v1/`, }, }, { name: 'smoke-web', testDir: './e2e/web', grep: /@smoke/, use: { ...devices['Desktop Chrome'], ...VIETNAMESE_BROWSER_CONTEXT, baseURL: process.env.WEB_BASE_URL ?? `http://localhost:${WEB_PORT}`, }, }, // Accessibility scorecard — axe-core audit of 10 key routes { name: 'a11y', testDir: './e2e/a11y', use: { ...devices['Desktop Chrome'], ...VIETNAMESE_BROWSER_CONTEXT, baseURL: process.env.WEB_BASE_URL ?? `http://localhost:${WEB_PORT}`, }, }, ], webServer: [ { name: 'GoodGo API', command: `pnpm --filter @goodgo/mcp-servers build && PORT=${API_PORT} pnpm --filter @goodgo/api run dev`, port: Number(API_PORT), reuseExistingServer: !process.env.CI, timeout: SERVER_STARTUP_TIMEOUT_MS, stdout: process.env.CI ? 'pipe' : 'ignore', env: { ...process.env as Record, NODE_ENV: 'test', PORT: API_PORT, DATABASE_URL: process.env.DATABASE_URL ?? '', }, }, { name: 'GoodGo Web', command: `rm -rf .next && pnpm exec next dev --port ${WEB_PORT}`, cwd: './apps/web', port: Number(WEB_PORT), reuseExistingServer: !process.env.CI, timeout: SERVER_STARTUP_TIMEOUT_MS, stdout: process.env.CI ? 'pipe' : 'ignore', env: { ...process.env as Record, PORT: WEB_PORT, NODE_ENV: 'test', NEXT_PUBLIC_API_URL: `http://localhost:${API_PORT}/api/v1`, }, }, ], });