import { type Page, type Request, type Response, expect } from "@playwright/test"; import { baseURL, targetHost, testUser } from "../playwright.config"; /** Resolve URL relative to the target host (bypasses localhost resolution). */ export function resolveUrl(path: string): string { const base = baseURL.replace("localhost", targetHost); return `${base}${path.startsWith("/") ? path : "/" + path}`; } /** Sign in as the test user via the API and return the session cookie. */ export async function getSessionCookie(page: Page): Promise { const res = await page.request.post(resolveUrl("/api/auth/sign-in/email"), { data: { email: testUser.email, password: testUser.password }, headers: { "Content-Type": "application/json" }, }); if (!res.ok()) { throw new Error(`Sign-in failed: ${res.status()} ${await res.text()}`); } const setCookie = res.headers()["set-cookie"] ?? ""; const match = setCookie.match(/session_token=([^;]+)/); if (!match) throw new Error("No session_token cookie in sign-in response"); return `session_token=${match[1]}`; } /** Parse SSE stream from a fetch response body. */ export async function parseSSEStream(body: string): Promise[]> { const events: Record[] = []; for (const line of body.split("\n")) { const trimmed = line.trim(); if (!trimmed || trimmed.startsWith("event:")) continue; if (trimmed.startsWith("data:")) { const dataStr = trimmed.slice(5).trim(); try { events.push(JSON.parse(dataStr)); } catch { // skip malformed } } } return events; } /** Extract the last SSE event of a given type. */ export function lastSSEOfType( events: Record[], type: "progress" | "done" ): Record | undefined { return events.reverse().find((e) => e[type] !== undefined); } // Re-export common helpers export { expect, resolveUrl as url, testUser }; export type { Page, Request, Response };