Files
comparaison/e2e/helpers.ts
Hermes Agent 26c7ad4d7b Add Playwright E2E test suite (25 tests across 4 specs)
- playwright.config.ts: headless CI setup with JSON/HTML reporters
- e2e/global-setup.ts: app reachability check before tests
- e2e/auth.spec.ts: 6 auth tests (sign-in, session, protected routes)
- e2e/compare.spec.ts: 8 API tests (validation, SSE flow, DB persistence)
- e2e/comparisons.spec.ts: 6 CRUD tests (list, detail, 404, view counts)
- e2e/user.spec.ts: 5 user tests (stats, pagination, session binding)
- e2e/helpers.ts: shared SSE parsing, auth, URL resolution utilities
- e2e/README.md: setup and run instructions
- AGENTS.md: updated with LLM provider notes and testing docs
- .gitignore: added playwright report/result directories
- package.json: added @playwright/test and test:e2e scripts
2026-04-27 20:48:05 -07:00

54 lines
2.0 KiB
TypeScript

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<string> {
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<Record<string, unknown>[]> {
const events: Record<string, unknown>[] = [];
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<string, unknown>[],
type: "progress" | "done"
): Record<string, unknown> | undefined {
return events.reverse().find((e) => e[type] !== undefined);
}
// Re-export common helpers
export { expect, resolveUrl as url, testUser };
export type { Page, Request, Response };