/** * Playwright global setup for dev-system tests. * * 1. Logs in once per user role and saves browser storage state to disk. * Tests use these cached states via `test.use({ storageState })` to avoid * hitting the auth rate limiter (5 attempts / 15 min / email). * * 2. Ensures reset-test@planarchy.dev exists for the password-reset E2E tests. * If the user is missing, creates them directly in the DB via argon2id hash * + docker exec psql (no tRPC roundtrip needed in setup). */ import { execSync } from "node:child_process"; import { chromium, type FullConfig } from "@playwright/test"; import * as fs from "fs"; import * as path from "path"; const USERS = { admin: { email: "admin@planarchy.dev", password: "admin123" }, manager: { email: "manager@planarchy.dev", password: "manager123" }, viewer: { email: "viewer@planarchy.dev", password: "viewer123" }, } as const; const RESET_TEST_USER = { email: "reset-test@planarchy.dev", password: "Dev123456!", }; const DB_CONTAINER = "capakraken-postgres-1"; const DB_USER = "capakraken"; const DB_NAME = "capakraken"; function psqlExec(sql: string): string { return execSync( `docker exec -i ${DB_CONTAINER} psql -U ${DB_USER} -d ${DB_NAME} -t -v ON_ERROR_STOP=1`, { input: sql, encoding: "utf8" }, ).trim(); } /** * Ensure reset-test@planarchy.dev exists in the DB. * Creates the user directly via SQL + argon2id hash — no email / tRPC roundtrip. */ async function ensureResetTestUser(): Promise { const count = psqlExec(`SELECT COUNT(*) FROM users WHERE email='${RESET_TEST_USER.email}';`); if (parseInt(count, 10) > 0) { console.log(`[global-setup] reset-test user already exists — skipping`); return; } console.log(`[global-setup] Creating reset-test user…`); const { hash } = await import("@node-rs/argon2"); const passwordHash = await hash(RESET_TEST_USER.password); // argon2id hashes use base64 chars only — safe inside a SQL single-quoted string // Column names are camelCase (Prisma default) — must be double-quoted in SQL const sql = ` INSERT INTO users (id, email, name, "systemRole", "passwordHash", "createdAt", "updatedAt") VALUES ( gen_random_uuid()::text, '${RESET_TEST_USER.email}', 'reset-test', 'USER', '${passwordHash}', NOW(), NOW() ) ON CONFLICT (email) DO NOTHING; `; psqlExec(sql); console.log(`[global-setup] Created reset-test user (${RESET_TEST_USER.email})`); } async function globalSetup(config: FullConfig) { const baseURL = config.projects[0]?.use?.baseURL ?? "http://localhost:3100"; const authDir = path.join(__dirname, ".auth"); fs.mkdirSync(authDir, { recursive: true }); const browser = await chromium.launch(); for (const [role, creds] of Object.entries(USERS)) { const context = await browser.newContext(); const page = await context.newPage(); await page.goto(`${baseURL}/auth/signin`); await page.fill('input[type="email"]', creds.email); await page.fill('input[type="password"]', creds.password); await page.click('button[type="submit"]'); await page.waitForURL(/\/(dashboard|resources)/, { timeout: 15000 }); await context.storageState({ path: path.join(authDir, `${role}.json`) }); await context.close(); console.log(`[global-setup] Saved auth state for ${role}`); } await browser.close(); // Ensure the dedicated password-reset test user exists await ensureResetTestUser(); } export default globalSetup;