import { chromium, FullConfig } from '@playwright/test'; import { TEST_CONFIG, TEST_USERS } from './utils/test-helpers'; // Load test user credentials from environment or use defaults const getTestUser = () => { const email = process.env.TEST_EMAIL || 'e2e@test.com'; const password = process.env.TEST_PASSWORD || 'Xk9$mP2#vL7@nQ4!wR8'; return { email, password }; }; /** * Global Setup for Playwright E2E Tests * * This setup runs ONCE before all tests to: * 1. Log in as a test user * 2. Save the authenticated session state to storageState.json * 3. All subsequent tests will use this saved state (no need to login again) * * This eliminates: * - Rate limiting issues (only 1 login instead of N logins) * - Test execution time (no login overhead per test) * - Flaky authentication failures */ async function globalSetup(config: FullConfig) { console.log('🔧 [GLOBAL SETUP] Starting global setup...'); const testUser = getTestUser(); console.log(`🔧 [GLOBAL SETUP] Using test user: ${testUser.email}`); // Use the first project's browser (usually chromium) const project = config.projects[0]; const browser = await chromium.launch({ headless: true, }); const context = await browser.newContext(); const page = await context.newPage(); try { // Navigate to frontend root (not /login to avoid routing issues) console.log('🔧 [GLOBAL SETUP] Navigating to frontend...'); await page.goto(TEST_CONFIG.FRONTEND_URL, { waitUntil: 'domcontentloaded', timeout: 30000, }); // Login via API directly in the browser context console.log('🔧 [GLOBAL SETUP] Attempting API login via browser...'); console.log(`🔧 [GLOBAL SETUP] API URL: ${TEST_CONFIG.API_URL}`); const loginResult = await page.evaluate(async ({ apiUrl, email, password }) => { try { console.log(`[BROWSER] Attempting login to: ${apiUrl}/auth/login`); const response = await fetch(`${apiUrl}/auth/login`, { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ email, password, }), }); if (!response.ok) { const errorText = await response.text(); return { success: false, error: `HTTP ${response.status}: ${errorText}` }; } const data = await response.json(); const accessToken = data?.token?.access_token || data?.data?.token?.access_token || data?.access_token; const refreshToken = data?.token?.refresh_token || data?.data?.token?.refresh_token || data?.refresh_token; if (!accessToken) { return { success: false, error: 'No access token in response', data }; } // Store tokens in localStorage localStorage.setItem('veza_access_token', accessToken); if (refreshToken) { localStorage.setItem('veza_refresh_token', refreshToken); } // Also set auth-storage for Zustand const authStorage = { state: { isAuthenticated: true, accessToken: accessToken, refreshToken: refreshToken, }, }; localStorage.setItem('auth-storage', JSON.stringify(authStorage)); return { success: true, accessToken, refreshToken }; } catch (error) { return { success: false, error: error instanceof Error ? error.message : String(error) }; } }, { apiUrl: TEST_CONFIG.API_URL, email: testUser.email, password: testUser.password }); if (!loginResult.success) { throw new Error(`API login failed: ${loginResult.error}`); } console.log('✅ [GLOBAL SETUP] API login successful!'); console.log(`✅ [GLOBAL SETUP] Access token: ${loginResult.accessToken?.substring(0, 20)}...`); // Verify tokens are stored const storedToken = await page.evaluate(() => localStorage.getItem('veza_access_token')); if (!storedToken) { throw new Error('Token not stored in localStorage'); } // Save the authenticated state const storageStatePath = config.projects[0]?.use?.storageState as string || 'e2e/.auth/user.json'; console.log(`💾 [GLOBAL SETUP] Saving authenticated state to: ${storageStatePath}`); await context.storageState({ path: storageStatePath }); console.log('✅ [GLOBAL SETUP] Global setup completed successfully!'); } catch (error) { console.error('❌ [GLOBAL SETUP] Global setup failed:', error); throw error; } finally { await browser.close(); } } export default globalSetup;