diff --git a/tests/e2e/helpers.ts b/tests/e2e/helpers.ts index 887d10ced..7e66317f4 100644 --- a/tests/e2e/helpers.ts +++ b/tests/e2e/helpers.ts @@ -95,16 +95,13 @@ export async function loginViaUI( }); } -/** - * Cache for login tokens to avoid triggering rate limits. - * Key: email, Value: { token, cookies, expiry } - */ -const loginCache = new Map(); - /** * Login via l'API directement (plus rapide, pour les tests qui n'ont pas besoin de tester le login). * STRICT: echoue si l'API retourne une erreur. - * Uses a token cache to avoid hitting rate limits on repeated logins. + * + * Auth uses httpOnly cookies set by backend. Each test needs to call the login API + * so that the browser context receives the auth cookies. We cannot skip this — the + * cookie cannot be copied across browser contexts from JavaScript. */ export async function loginViaAPI( page: Page, @@ -114,57 +111,24 @@ export async function loginViaAPI( const base = CONFIG.baseURL; await page.goto(`${base}/`, { waitUntil: 'commit', timeout: CONFIG.timeouts.navigation }); - const cached = loginCache.get(email); - let accessToken: string; + const response = await page.request.post(`${base}/api/v1/auth/login`, { + data: { email, password, remember_me: false }, + }); - if (cached && cached.expiry > Date.now()) { - // Reuse cached token - accessToken = cached.token; - if (cached.cookies) { - const cookieParts = cached.cookies.split(';')[0]?.split('='); - if (cookieParts && cookieParts.length === 2) { - await page.context().addCookies([{ - name: cookieParts[0].trim(), - value: cookieParts[1].trim(), - domain: '127.0.0.1', - path: '/', - }]); - } - } - } else { - // Call login API - const response = await page.request.post(`${base}/api/v1/auth/login`, { - data: { email, password, remember_me: false }, - }); + // STRICT: login must succeed + expect(response.ok(), `Login API failed: ${response.status()} for ${email}`).toBeTruthy(); - // STRICT: login must succeed - expect(response.ok(), `Login API failed: ${response.status()} for ${email}`).toBeTruthy(); - - const body = await response.json(); - accessToken = body?.data?.token?.access_token || ''; - const setCookie = response.headers()['set-cookie'] || ''; - - // Cache the token for 4 minutes (tokens expire in 5min) - loginCache.set(email, { - token: accessToken, - cookies: setCookie, - expiry: Date.now() + 4 * 60 * 1000, - }); - } - - await page.evaluate((token) => { + // Set the auth-storage flag so React knows user is authenticated. + // The actual token is in an httpOnly cookie set automatically by the backend response. + await page.evaluate(() => { const authState = { state: { isAuthenticated: true, isLoading: false, error: null }, version: 1, }; localStorage.setItem('auth-storage', JSON.stringify(authState)); - if (token) { - localStorage.setItem('access_token', token); - } - }, accessToken); + }); await page.goto(`${CONFIG.baseURL}/dashboard`, { waitUntil: 'domcontentloaded', timeout: 30_000 }); - // Wait for auth initialization to complete await page.locator('main, [role="main"]').first().waitFor({ state: 'visible', timeout: 20_000, @@ -182,17 +146,14 @@ export async function loginViaAPI( export async function navigateTo(page: Page, path: string): Promise { const url = path.startsWith('http') ? path : `${CONFIG.baseURL}${path}`; await page.goto(url, { waitUntil: 'domcontentloaded', timeout: 30_000 }); + await page.waitForLoadState('networkidle', { timeout: 5_000 }).catch(() => { + // networkidle can legitimately timeout on pages with websockets/polling — not a test failure + }); // App must render a main content area await page.locator('main, [role="main"]').first().waitFor({ state: 'visible', timeout: 20_000, }); - // Wait for React hydration + initial data fetches to settle - await page.waitForLoadState('networkidle', { timeout: 10_000 }).catch(() => { - // networkidle can legitimately timeout on pages with websockets/polling — not a test failure - }); - // Extra settle time for React Query cache + state updates to render - await page.waitForTimeout(300); } /**