import { type Browser, type BrowserContext, request } from '@playwright/test'; export const FIXTURE_PASSWORD = 'fixture passwort 123'; /** * Signs a fixture user in through the api and returns a browser context * that already carries the session cookie — UI tests skip the login form * unless the form itself is under test. */ export async function contextForUser( browser: Browser, baseURL: string, username: string, password: string = FIXTURE_PASSWORD, ): Promise { const api = await request.newContext({ baseURL }); const response = await api.post('/api/v1/auth/login', { data: { usernameOrEmail: username, password }, }); if (!response.ok()) { throw new Error(`fixture login for ${username} failed: ${response.status()}`); } const storageState = await api.storageState(); await api.dispose(); // The CSRF check fails closed (#189): cookie mutations must carry a // matching Origin. The browser sends it on its own fetches; this default // covers the specs' manual `context.request.*` seeding calls too. return browser.newContext({ baseURL, storageState, extraHTTPHeaders: { origin: new URL(baseURL).origin }, }); } /** Latest mail for an address from the Mailpit REST api. */ export async function latestMailFor( mailpitUrl: string, address: string, ): Promise<{ subject: string; text: string }> { const api = await request.newContext({ baseURL: mailpitUrl }); for (let attempt = 0; attempt < 30; attempt += 1) { const list = await api.get('/api/v1/search', { params: { query: `to:${address}`, limit: 1 }, }); const body = (await list.json()) as { messages?: { ID: string; Subject: string }[] }; const found = body.messages?.[0]; if (found) { const message = await api.get(`/api/v1/message/${found.ID}`); const details = (await message.json()) as { Text: string }; await api.dispose(); return { subject: found.Subject, text: details.Text }; } // The outbox worker delivers every 15s — poll until it does. await new Promise((resolve) => setTimeout(resolve, 2000)); } await api.dispose(); throw new Error(`no mail arrived for ${address}`); } export function tokenFromMail(text: string): string { const match = text.match(/token=([A-Za-z0-9_-]+)/); if (!match) throw new Error('mail contains no token link'); return match[1]!; }