import type { Page, BrowserContext } from '@playwright/test'; import type { RouteAccess } from './websiteRouteInventory'; export type WebsiteAuthContext = 'public' | 'auth' | 'admin' | 'sponsor'; export type WebsiteSessionDriftMode = 'invalid-cookie' | 'expired' | 'missing-sponsor-id'; export type WebsiteFaultMode = 'null-array' | 'missing-field' | 'invalid-date'; export function authContextForAccess(access: RouteAccess): WebsiteAuthContext { if (access === 'public') return 'public'; if (access === 'auth') return 'auth'; if (access === 'admin') return 'admin'; return 'sponsor'; } export async function setWebsiteAuthContext( context: BrowserContext, auth: WebsiteAuthContext, options: { sessionDrift?: WebsiteSessionDriftMode; faultMode?: WebsiteFaultMode } = {}, ): Promise { const domain = 'localhost'; const base = { domain, path: '/' }; // The website uses `gridpilot_demo_mode` cookie to switch identity modes without OAuth. // We keep these cookies consistent across smoke tests. const cookies = auth === 'public' ? [ { ...base, name: 'gridpilot_demo_mode', value: 'none' }, { ...base, name: 'gridpilot_sponsor_id', value: '' }, { ...base, name: 'gridpilot_sponsor_name', value: '' }, ] : auth === 'sponsor' ? [ { ...base, name: 'gridpilot_demo_mode', value: 'sponsor' }, { ...base, name: 'gridpilot_sponsor_id', value: 'demo-sponsor-1' }, { ...base, name: 'gridpilot_sponsor_name', value: 'Demo Sponsor' }, ] : auth === 'admin' ? [ { ...base, name: 'gridpilot_demo_mode', value: 'admin' }, { ...base, name: 'gridpilot_sponsor_id', value: '' }, { ...base, name: 'gridpilot_sponsor_name', value: '' }, ] : [ { ...base, name: 'gridpilot_demo_mode', value: 'driver' }, { ...base, name: 'gridpilot_sponsor_id', value: '' }, { ...base, name: 'gridpilot_sponsor_name', value: '' }, ]; const driftCookie = options.sessionDrift != null ? [{ ...base, name: 'gridpilot_session_drift', value: String(options.sessionDrift) }] : []; const faultCookie = options.faultMode != null ? [{ ...base, name: 'gridpilot_fault_mode', value: String(options.faultMode) }] : []; await context.clearCookies(); await context.addCookies([...cookies, ...driftCookie, ...faultCookie]); } export type ConsoleCapture = { consoleErrors: string[]; pageErrors: string[]; }; export function attachConsoleErrorCapture(page: Page): ConsoleCapture { const consoleErrors: string[] = []; const pageErrors: string[] = []; page.on('pageerror', (err) => { pageErrors.push(String(err)); }); page.on('console', (msg) => { const type = msg.type(); if (type !== 'error') return; const text = msg.text(); // Filter known benign warnings (keep small + generic). if (text.includes('Download the React DevTools')) return; // Next/Image accessibility warning (not a runtime failure for smoke coverage). if (text.includes('Image is missing required "alt" property')) return; // React controlled instead of setting `selected` on