import { readFileSync } from "node:fs"; import { expect, test } from "@playwright/test"; import { E2E_CONTEXT_PATH_ENV, type PersistedE2eContext } from "../global-setup"; const loadContext = (): PersistedE2eContext => { const path = process.env[E2E_CONTEXT_PATH_ENV]; if (!path) throw new Error(`${E2E_CONTEXT_PATH_ENV} is not set; global-setup didn't run.`); return JSON.parse(readFileSync(path, "utf8")) as PersistedE2eContext; }; const loadSetupState = () => ({ invite: loadContext().invite }); test.describe("invite → signup → sign-out → sign-in", () => { // Sign-out lands on the landing page; the header pill flip is the // confirmation. There's no dedicated /sign-out route any more. test("walks a new user through the invite-driven signup and back through sign-in", async ({ page }) => { const { invite } = loadSetupState(); // 1. Land on the invite acceptance page. Email is locked because the // invite was minted with a specific address — UI guards against // sign-up under a different email than the one invited. await page.goto(`/invite/${encodeURIComponent(invite.token)}`); await expect(page.getByRole("heading", { name: /accept your invite/i })).toBeVisible(); const emailInput = page.getByLabel("Email"); await expect(emailInput).toHaveValue(invite.email); await expect(emailInput).toBeDisabled(); await page.getByLabel("Name").fill("E2E Tester"); await page.getByLabel("Password").fill(invite.password); // Turnstile test keys (1x00…AA) always pass, but the widget still has // to mount before the submit button becomes enabled. Wait on the // button instead of polling the iframe state. const submit = page.getByRole("button", { name: /accept invite/i }); await expect(submit).toBeEnabled({ timeout: 15_000 }); await submit.click(); // 2. Successful acceptance lands on /account. invite-accept calls // `session.refetch()` before navigating so the nanostore is fresh — // don't poll networkidle here, Turnstile keeps that signal busy. await page.waitForURL(/\/account$/); await expect(page.getByRole("heading", { name: /your account/i })).toBeVisible({ timeout: 15_000 }); await expect(page.getByText(invite.email)).toBeVisible(); // 3. Sign out via the header button, then verify we're not signed in. // The control is a