import { expect, test, type Page } from "@playwright/test"; // P5 exit criterion (spec §17): the full admin + billing loop — credit // decrement matches token usage, and promo redemption grants credits. const OWNER_EMAIL = "owner@acme.test"; const OWNER_PASSWORD = "e2e-password"; async function signIn(page: Page) { await page.goto("/login"); await page.getByLabel("Email address").fill(OWNER_EMAIL); await page.getByLabel("Password").fill(OWNER_PASSWORD); await page.getByRole("button", { name: "Sign in" }).click(); await page.waitForURL((url) => !url.pathname.endsWith("/login"), { timeout: 15000 }); } async function readBalance(page: Page): Promise { await page.getByRole("link", { name: "Credits" }).click(); await expect(page.getByText("Available credits")).toBeVisible(); const text = await page.getByTestId("credit-balance").innerText(); return Number(text.replace(/,/g, "")); } test("chat usage decrements credits and shows in the 7-day meter", async ({ page, }) => { await signIn(page); const before = await readBalance(page); await page.getByRole("link", { name: /Scout/ }).click(); await expect(page).toHaveURL(/\/claws\/.+\/chat\//); // Fresh session: older sessions carry scenario markers in history that // would steer the scripted provider away from the plain echo. const before_url = page.url(); await page.getByRole("button", { name: "New" }).click(); await page.waitForURL((url) => url.toString() !== before_url); const box = page.getByLabel("Message Scout"); await box.fill("bill me for this message"); await box.press("Enter"); await expect( page.getByText("I received: bill me for this message"), ).toBeVisible(); await expect .poll(async () => readBalance(page), { timeout: 15000 }) .toBeLessThan(before); await expect(page.getByText(/Usage · last 7 days/i)).toBeVisible(); await expect(page.getByText(/tokens \(/)).toBeVisible(); }); test("a promo code redeems exactly once", async ({ page }) => { await signIn(page); const before = await readBalance(page); await page.getByLabel("Promo code").fill("WELCOME500"); await page.getByRole("button", { name: "Redeem" }).click(); await expect(page.getByText("Added 500 credits.")).toBeVisible(); await expect .poll(async () => readBalance(page)) .toBe(before + 500); // Second redemption refuses. await page.getByLabel("Promo code").fill("WELCOME500"); await page.getByRole("button", { name: "Redeem" }).click(); await expect( page.getByText("That code is invalid or already redeemed."), ).toBeVisible(); });