Spaces:
Running
Running
| import { chromium } from "playwright"; | |
| import fs from "node:fs"; | |
| const baseUrl = process.env.E2E_BASE_URL ?? "http://localhost:3000"; | |
| const memberApiKey = process.env.MEMBER_API_KEY; | |
| if (!memberApiKey) { | |
| throw new Error("MEMBER_API_KEY is required"); | |
| } | |
| fs.mkdirSync("artifacts", { recursive: true }); | |
| const browser = await chromium.launch({ headless: true }); | |
| try { | |
| // Flow A: free user signup | |
| const freeContext = await browser.newContext(); | |
| const freePage = await freeContext.newPage(); | |
| const freeSignupResp = await freeContext.request.post(`${baseUrl}/api/v1/signup/free`, { | |
| data: { display_name: "E2E Free Proof" }, | |
| }); | |
| if (freeSignupResp.status() !== 201) { | |
| throw new Error(`Free signup failed with status ${freeSignupResp.status()}`); | |
| } | |
| const freeData = await freeSignupResp.json(); | |
| const freeSetCookie = freeSignupResp.headers()["set-cookie"] ?? ""; | |
| const freeMatch = freeSetCookie.match(/tcg_session=([^;]+)/); | |
| if (!freeMatch) { | |
| throw new Error("Free signup did not return tcg_session cookie"); | |
| } | |
| await freeContext.addCookies([ | |
| { | |
| name: "tcg_session", | |
| value: decodeURIComponent(freeMatch[1]), | |
| domain: "localhost", | |
| path: "/", | |
| httpOnly: true, | |
| sameSite: "Lax", | |
| secure: false, | |
| }, | |
| ]); | |
| await freePage.goto(baseUrl, { waitUntil: "domcontentloaded" }); | |
| await freePage.waitForTimeout(1200); | |
| const freeTenantLabel = freePage.locator(`text=${freeData.tenant.id}`).first(); | |
| await freeTenantLabel.waitFor({ timeout: 15000 }); | |
| await freePage.screenshot({ path: "artifacts/e2e-free-user-success.png", fullPage: true }); | |
| await freeContext.close(); | |
| // Flow B: member login (API) -> UI session restore -> checkout redirect | |
| const memberContext = await browser.newContext(); | |
| const memberPage = await memberContext.newPage(); | |
| const loginResp = await memberContext.request.post(`${baseUrl}/api/v1/auth/session`, { | |
| data: { api_key: memberApiKey }, | |
| }); | |
| if (loginResp.status() !== 200) { | |
| throw new Error(`Member login failed with status ${loginResp.status()}`); | |
| } | |
| const setCookie = loginResp.headers()["set-cookie"] ?? ""; | |
| const match = setCookie.match(/tcg_session=([^;]+)/); | |
| if (!match) { | |
| throw new Error("Member login did not return tcg_session cookie"); | |
| } | |
| await memberContext.addCookies([ | |
| { | |
| name: "tcg_session", | |
| value: decodeURIComponent(match[1]), | |
| domain: "localhost", | |
| path: "/", | |
| httpOnly: true, | |
| sameSite: "Lax", | |
| secure: false, | |
| }, | |
| ]); | |
| await memberPage.goto(baseUrl, { waitUntil: "domcontentloaded" }); | |
| await memberPage.waitForTimeout(1200); | |
| const memberTenantLabel = memberPage.locator("text=/tenant_staging_admin/i").first(); | |
| await memberTenantLabel.waitFor({ timeout: 15000 }); | |
| await memberPage.waitForTimeout(500); | |
| await memberPage.getByRole("button", { name: /pricing \/ upgrade/i }).click(); | |
| await memberPage.waitForTimeout(800); | |
| await memberPage.screenshot({ path: "artifacts/e2e-member-signedin-pricing.png", fullPage: true }); | |
| const checkoutResp = await memberContext.request.post(`${baseUrl}/api/v1/billing/checkout/session`, { | |
| data: { | |
| plan: "pro_guardian", | |
| mode: "subscription", | |
| success_url: `${baseUrl}/?checkout=success`, | |
| cancel_url: `${baseUrl}/?checkout=cancel`, | |
| }, | |
| }); | |
| if (checkoutResp.status() !== 200) { | |
| throw new Error(`Checkout create failed with status ${checkoutResp.status()}`); | |
| } | |
| const checkoutData = await checkoutResp.json(); | |
| if (!checkoutData?.checkout_url) { | |
| throw new Error("Checkout URL missing from checkout session response"); | |
| } | |
| await memberPage.goto(checkoutData.checkout_url, { waitUntil: "domcontentloaded" }); | |
| await memberPage.waitForURL(/checkout\.stripe\.com/, { timeout: 30000 }); | |
| await memberPage.screenshot({ path: "artifacts/e2e-member-checkout-success.png", fullPage: true }); | |
| await memberContext.close(); | |
| } finally { | |
| await browser.close(); | |
| } | |
| console.log("Saved screenshots:"); | |
| console.log("- artifacts/e2e-free-user-success.png"); | |
| console.log("- artifacts/e2e-member-checkout-success.png"); | |