117 lines
3.7 KiB
TypeScript
117 lines
3.7 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;
|
|
const request = typeof requestOrRole === 'string' ? null : requestOrRole;
|
|
|
|
const context = await browser.newContext({ baseURL });
|
|
|
|
if (request) {
|
|
const token = await WebsiteAuthManager.loginViaApi(request, apiBaseUrl, role);
|
|
|
|
// Critical: the website must receive `gp_session` so middleware can forward it.
|
|
// Playwright runs in its own container and accesses website via PLAYWRIGHT_BASE_URL
|
|
// The cookie domain must match the hostname in the URL that Playwright uses
|
|
const url = new URL(baseURL);
|
|
const domain = url.hostname; // "website" in Docker, "localhost" locally
|
|
|
|
await context.addCookies([
|
|
{
|
|
name: 'gp_session',
|
|
value: token,
|
|
domain: domain,
|
|
path: '/',
|
|
httpOnly: true,
|
|
sameSite: 'Lax',
|
|
},
|
|
]);
|
|
}
|
|
|
|
const page = await context.newPage();
|
|
|
|
if (!request) {
|
|
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!' };
|
|
}
|
|
} |