import { test as base, expect } from '@playwright/test'; import { request } from '@playwright/test'; import { exec as cp_exec } from 'child_process'; import { promisify } from 'util'; const exec = promisify(cp_exec); // Define the type for our custom fixtures type MyFixtures = { createUniqueUser: () => Promise<{ email: string, password: string, id: string, token: string }>; createAdminUser: () => Promise<{ email: string, password: string, id: string, token: string }>; }; // Extend the base test with our custom fixture export const test = base.extend({ createUniqueUser: async ({ }, use) => { // We use a new API context for setup to avoid polluting request history, // although setup requests are usually separate anyway. const apiContext = await request.newContext({ baseURL: 'http://127.0.0.1:3001' // Direct access to backend }); // Setup: Helper function to create a user const createUser = async () => { const uniqueId = Math.random().toString(36).substring(7); const email = `test.user.${uniqueId}@example.com`; const password = 'StrongPassword123!'; const response = await apiContext.post('/api/auth/register', { data: { email, password } }); const body = await response.json(); // If registration fails because we hit a collision (unlikely) or other error, fail the test if (!response.ok()) { console.error(`REGISTRATION FAILED: ${response.status()} ${response.statusText()}`); console.error(`RESPONSE BODY: ${JSON.stringify(body, null, 2)}`); throw new Error(`Failed to register user: ${JSON.stringify(body)}`); } return { email, password, id: body.user.id, token: body.token }; }; // Use the fixture await use(createUser); // Cleanup: In a real "test:full" env with ephemeral db, cleanup might not be needed. // But if we want to be clean, we can delete the user. // Requires admin privileges usually, or specific delete-me endpoint. // Given the requirements "delete own account" exists (5.6), we could theoretically login and delete. // For now we skip auto-cleanup to keep it simple, assuming test DB is reset periodically. }, createAdminUser: async ({ createUniqueUser }, use) => { // Setup: Helper function to create an admin user (create regular -> promote) const createAdmin = async () => { const user = await createUniqueUser(); // Create a regular user first console.log(`Promoting user ${user.email} to ADMIN...`); try { const { stdout, stderr } = await exec(`npx ts-node promote_admin.ts ${user.email}`, { cwd: 'server', env: { ...process.env, APP_MODE: 'test', DATABASE_URL: 'file:./prisma/test.db', DATABASE_URL_TEST: 'file:./prisma/test.db' } }); if (stderr) { console.error(`Promote Admin Stderr: ${stderr}`); } console.log(`Promote Admin Stdout: ${stdout}`); if (!stdout.includes(`User ${user.email} promoted to ADMIN`)) { throw new Error('Admin promotion failed or unexpected output.'); } } catch (error) { console.error(`Error promoting user ${user.email} to ADMIN:`, error); throw error; } return user; }; await use(createAdmin); }, }); export { expect };