Files
gridpilot.gg/tests/shared/website/WebsiteAuthManager.ts
2026-01-17 18:28:10 +01:00

132 lines
4.1 KiB
TypeScript

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<AuthContext>;
static async createAuthContext(browser: Browser, request: APIRequestContext, role: AuthRole): Promise<AuthContext>;
static async createAuthContext(
browser: Browser,
requestOrRole: APIRequestContext | AuthRole,
maybeRole?: AuthRole,
): Promise<AuthContext> {
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;
// 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<string> {
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<void> {
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!' };
}
}