Files
HOA_Financial_Platform/playwright.config.ts
JoeBot dfd1bccb89 feat: add Playwright E2E and API regression test suite
Production-ready test infrastructure with Page Object Model pattern,
reusable fixtures for auth/DB/test-data, and example tests covering
login flow, dashboard, accounts CRUD API, and visual regression.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-05 12:40:05 -04:00

128 lines
3.2 KiB
TypeScript

import { defineConfig, devices } from '@playwright/test';
import path from 'path';
/**
* Playwright configuration for HOA LedgerIQ E2E + API regression tests.
*
* Architecture: Docker Compose (nginx :80 -> backend :3000 + frontend :5173)
* - Local dev: `docker-compose up` then `npm run test:e2e`
* - CI: starts Docker services automatically
* - Production: set BASE_URL to skip webServer start
*/
// Load test-specific env from .env.test (falls back to .env)
require('dotenv').config({ path: path.resolve(__dirname, '.env.test') });
const BASE_URL = process.env.BASE_URL || 'http://localhost';
const IS_CI = !!process.env.CI;
// Skip auto-starting services when pointing at an external URL
const isExternalTarget =
BASE_URL !== 'http://localhost' && BASE_URL !== 'http://localhost:80';
export default defineConfig({
testDir: './tests',
testMatch: ['**/*.spec.ts'],
/* Run tests in parallel where safe */
fullyParallel: true,
/* Fail CI builds if test.only was left in */
forbidOnly: IS_CI,
/* Retry on CI to handle transient failures */
retries: IS_CI ? 2 : 0,
/* Limit parallel workers on CI */
workers: IS_CI ? 1 : undefined,
/* Reporter configuration */
reporter: IS_CI
? [['github'], ['html', { open: 'never' }]]
: [['list'], ['html', { open: 'on-failure' }]],
/* Shared settings for all projects */
use: {
baseURL: BASE_URL,
/* Collect trace on first retry for debugging */
trace: 'on-first-retry',
/* Screenshot on failure */
screenshot: 'only-on-failure',
/* Video on failure in CI */
video: IS_CI ? 'on-first-retry' : 'off',
/* Default timeout for actions (click, fill, etc.) */
actionTimeout: 10_000,
/* Navigation timeout */
navigationTimeout: 30_000,
},
/* Global test timeout */
timeout: 60_000,
/* Assertion timeout */
expect: {
timeout: 10_000,
toHaveScreenshot: {
maxDiffPixelRatio: 0.02,
},
},
/* Browser projects */
projects: [
/* Auth setup — runs once, stores auth state for other tests */
{
name: 'auth-setup',
testMatch: /auth\.setup\.ts/,
use: { ...devices['Desktop Chrome'] },
},
{
name: 'chromium',
use: {
...devices['Desktop Chrome'],
storageState: 'tests/.auth/user.json',
},
dependencies: ['auth-setup'],
},
{
name: 'firefox',
use: {
...devices['Desktop Firefox'],
storageState: 'tests/.auth/user.json',
},
dependencies: ['auth-setup'],
},
{
name: 'webkit',
use: {
...devices['Desktop Safari'],
storageState: 'tests/.auth/user.json',
},
dependencies: ['auth-setup'],
},
/* API tests — no browser needed, runs in chromium for request context */
{
name: 'api',
testMatch: ['**/api/**/*.spec.ts'],
use: { ...devices['Desktop Chrome'] },
dependencies: [],
},
],
/* Start Docker services before tests when running locally */
...(!isExternalTarget && {
webServer: {
command: 'docker-compose up -d && sleep 5 && docker-compose exec backend echo "ready"',
url: BASE_URL,
reuseExistingServer: !IS_CI,
timeout: 120_000,
stdout: 'pipe',
stderr: 'pipe',
},
}),
});