// Shared E2E helpers — the sign-up journey (SD-0001 §5.1–§5.4) and products-page // navigation (SD-0002 §5.2). Selectors are role/label-based against the real screens // (Landing.tsx, SignIn.tsx, CreateStorefront.tsx, Admin.tsx, ProductsPage.tsx). import { expect, type Page } from "@playwright/test"; import { readFile } from "node:fs/promises"; import { join } from "node:path"; const LOG = join(__dirname, ".backend.log"); let seq = 0; // The storefront name every helper-created merchant uses; tests assert against it. export const STOREFRONT_NAME = "E2E Test Goods"; export function freshEmail(): string { return `merchant${Date.now()}-${seq++}@example.com`; } // LogMailer's exact line (backend/app/platform/mailer.py): // INFO:ecomm.mailer: LogMailer -> | Your ecomm code: <6 digits>\n // The first 6-digit group after the marker is the code. async function codeFor(email: string): Promise { for (let i = 0; i < 50; i++) { const log = await readFile(LOG, "utf8").catch(() => ""); const at = log.lastIndexOf(`LogMailer -> ${email}`); if (at >= 0) { const m = log.slice(at).match(/\b(\d{6})\b/); if (m) return m[1]; } await new Promise((r) => setTimeout(r, 200)); } throw new Error(`no code logged for ${email}`); } export async function signUpWithStorefront(page: Page, email = freshEmail()): Promise { // Landing → the sign-up door. await page.goto("/"); await page.getByRole("button", { name: "Create your storefront →" }).click(); // Sign-in step 1: request the one-time code. await page.getByLabel("Email").fill(email); await page.getByRole("button", { name: "Send code" }).click(); // Sign-in step 2: read the code from the backend log and verify it. await expect(page.getByRole("heading", { name: "Check your email" })).toBeVisible(); const code = await codeFor(email); await page.getByLabel("One-time code").fill(code); await page.getByRole("button", { name: "Continue" }).click(); // Create-storefront screen (a new account has none yet). await expect(page.getByRole("heading", { name: "Create your storefront" })).toBeVisible(); await page.getByLabel("Storefront name").fill(STOREFRONT_NAME); await page.getByRole("button", { name: "Create storefront", exact: true }).click(); // Admin shell: nav strip + the storefront identity in the topbar. await expect(page.getByRole("navigation", { name: "Admin sections" })).toBeVisible(); await expect(page.locator(".storeid__name")).toHaveText(STOREFRONT_NAME); return email; } export async function gotoProducts(page: Page) { await page .getByRole("navigation", { name: "Admin sections" }) .getByRole("link", { name: "Products" }) .click(); await expect(page.getByRole("heading", { level: 1, name: "Products" })).toBeVisible(); } export async function uploadFixture(page: Page, fixture: string) { // Two "Import products" links render on the empty products page (header + empty // state) — same destination, so take the first. await page.getByRole("link", { name: "Import products" }).first().click(); await expect(page.getByRole("heading", { name: "Import products" })).toBeVisible(); await page.locator('input[type="file"]').setInputFiles(join(__dirname, "fixtures", fixture)); }