import { APIRequestContext, Browser, BrowserContext, Page } from '@playwright/test'; type AuthRole = 'auth' | 'admin' | 'sponsor'; type Credentials = { email: string; password: string; }; export interface AuthContext { context: BrowserContext; page: Page; role: AuthRole; } export class WebsiteAuthManager { static async createAuthContext(browser: Browser, role: AuthRole): Promise; static async createAuthContext(browser: Browser, request: APIRequestContext, role: AuthRole): Promise; static async createAuthContext( browser: Browser, requestOrRole: APIRequestContext | AuthRole, maybeRole?: AuthRole, ): Promise { const baseURL = process.env.PLAYWRIGHT_BASE_URL || 'http://localhost:3000'; const apiBaseUrl = process.env.API_BASE_URL || 'http://localhost:3101'; const role = (typeof requestOrRole === 'string' ? requestOrRole : maybeRole) as AuthRole; const request = typeof requestOrRole === 'string' ? null : requestOrRole; // If using API login, create context with cookies pre-set if (typeof requestOrRole !== 'string') { const token = await WebsiteAuthManager.loginViaApi(requestOrRole, apiBaseUrl, role); console.log(`[WebsiteAuthManager] Creating context with pre-set cookie, baseURL: ${baseURL}, token length: ${token.length}`); // Create context with storage state that includes the cookie // This is more reliable than adding cookies after context creation const contextWithCookies = await browser.newContext({ baseURL, storageState: { cookies: [ { name: 'gp_session', value: token, domain: new URL(baseURL).hostname, path: '/', expires: -1, httpOnly: true, secure: false, sameSite: 'Lax', }, ], origins: [], }, }); const page = await contextWithCookies.newPage(); // Verify cookies const cookies = await contextWithCookies.cookies(); console.log(`[WebsiteAuthManager] Cookies in context:`, cookies); return { context: contextWithCookies, page, role, }; } // UI login path const context = await browser.newContext({ baseURL }); const page = await context.newPage(); await WebsiteAuthManager.loginViaUi(page, role); return { context, page, role, }; } private static async loginViaApi( request: APIRequestContext, apiBaseUrl: string, role: AuthRole, ): Promise { const credentials = WebsiteAuthManager.getCredentials(role); // In Docker, the API is at http://api:3000, but the website needs to receive cookies // that will be forwarded to the API. The cookie domain should match the website. const res = await request.post(`${apiBaseUrl}/auth/login`, { data: { email: credentials.email, password: credentials.password, }, }); const setCookie = res.headers()['set-cookie'] ?? ''; const cookiePart = setCookie.split(';')[0] ?? ''; const token = cookiePart.startsWith('gp_session=') ? cookiePart.slice('gp_session='.length) : ''; if (!token) { throw new Error(`Expected gp_session cookie from ${apiBaseUrl}/auth/login`); } return token; } private static async loginViaUi(page: Page, role: AuthRole): Promise { const credentials = WebsiteAuthManager.getCredentials(role); await page.goto('/auth/login'); await page.getByLabel('Email Address').fill(credentials.email); await page.getByLabel('Password').fill(credentials.password); await Promise.all([ page.getByRole('button', { name: 'Sign In' }).click(), page.waitForURL((url) => !url.pathname.startsWith('/auth/login'), { timeout: 15_000 }), ]); } private static getCredentials(role: AuthRole): Credentials { if (role === 'admin') { return { email: 'demo.admin@example.com', password: 'Demo1234!' }; } if (role === 'sponsor') { return { email: 'demo.sponsor@example.com', password: 'Demo1234!' }; } return { email: 'demo.driver@example.com', password: 'Demo1234!' }; } }