/** * Authentication fixture for E2E tests. * * Provides helpers to authenticate via the API and manage JWT tokens. * The auth.setup.ts project uses this to create persistent auth state * that other tests reuse (avoiding login in every test). */ import { type APIRequestContext, type BrowserContext } from '@playwright/test'; import { TEST_USERS } from './test-data'; const API_BASE = process.env.API_BASE_URL || 'http://localhost/api'; export interface AuthTokens { accessToken: string; user: Record; organizations: Array>; } /** * Login via the API and return tokens. * Uses Playwright's request context (no browser needed). */ export async function apiLogin( request: APIRequestContext, credentials: { email: string; password: string } = TEST_USERS.treasurer, ): Promise { const response = await request.post(`${API_BASE}/auth/login`, { data: { email: credentials.email, password: credentials.password, }, }); if (!response.ok()) { const body = await response.text(); throw new Error(`Login failed (${response.status()}): ${body}`); } return response.json(); } /** * Switch to a specific organization after login. */ export async function apiSwitchOrg( request: APIRequestContext, accessToken: string, organizationId: string, ): Promise { const response = await request.post(`${API_BASE}/auth/switch-org`, { data: { organizationId }, headers: { Authorization: `Bearer ${accessToken}` }, }); if (!response.ok()) { const body = await response.text(); throw new Error(`Switch org failed (${response.status()}): ${body}`); } return response.json(); } /** * Create an authenticated API request context with a Bearer token. * Useful for API-level tests that don't need a browser. */ export function authHeaders(accessToken: string) { return { Authorization: `Bearer ${accessToken}`, 'Content-Type': 'application/json', }; } /** * Inject auth state into a browser context's localStorage. * Matches the frontend's Zustand authStore shape. */ export async function injectAuthState( context: BrowserContext, tokens: AuthTokens, orgId?: string, ): Promise { const baseURL = process.env.BASE_URL || 'http://localhost'; // The frontend uses Zustand with persist middleware in localStorage // under key 'auth-storage'. We inject it so the frontend thinks // the user is already logged in. const selectedOrg = orgId ? tokens.organizations.find((o: any) => o.id === orgId) : tokens.organizations[0]; const authState = { state: { token: tokens.accessToken, user: tokens.user, organizations: tokens.organizations, currentOrg: selectedOrg ? { id: (selectedOrg as any).id, name: (selectedOrg as any).name, role: (selectedOrg as any).role } : null, }, version: 0, }; await context.addInitScript((authData) => { window.localStorage.setItem('auth-storage', JSON.stringify(authData)); }, authState); }