import { test as base, expect, type Page } from "@playwright/test"; /** Matches services/control_api/auth/models.py's ManagementRole group names, * and tests/e2e/harness.py's ROLE_GROUPS -- these are the only group names * the real per-endpoint RBAC matrix (services/control_api/authz.py) maps to * a role at all; anything else is denied by default. */ export type Persona = "hub-viewer" | "hub-auditor" | "hub-operator" | "hub-admin"; /** Drives the real PKCE/BFF login flow through a real browser navigation: * GET /auth/login (real 307 to the fake OIDC provider's real HTTPS * /authorize), rewriting only the *outbound request URL* to add a * `persona` hint the fake provider reads (see fake_provider.py) -- no * production auth code is touched or bypassed, only the request this test * harness itself sends to a fake dependency. */ export async function loginAs(page: Page, persona: Persona): Promise { // Rewriting the *navigation request itself* to /authorize (page.route // continue({url})) does not reliably apply to a same-navigation redirect // hop in Chromium -- verified by hand: the outbound request to // /authorize still carried no persona param, and the fake provider fell // back to its default group. Instead, intercept the /auth/login response // (a 307), fetch it with redirects disabled, and rewrite the outgoing // Location header before letting the browser follow it. await page.route("**/auth/login", async (route) => { const response = await route.fetch({ maxRedirects: 0 }); const headers = response.headers(); if (!headers.location) throw new Error("expected /auth/login to redirect with a Location header"); const location = new URL(headers.location); location.searchParams.set("persona", persona); await route.fulfill({ status: response.status(), headers: { ...headers, location: location.toString() } }); }); await page.goto("/auth/login"); await page.waitForLoadState("networkidle"); await page.unroute("**/auth/login"); } export const test = base; export { expect };