import { expect, test, type Page } from "@playwright/test"; /** * Guided tour smoke: auto-start on first visit, per-browser dismissal via * localStorage, and the header replay control. Each test gets a fresh browser * context, so localStorage starts empty: exactly the "first visit" state. */ /** Login and wait for the redirect to land (cold dev-server compiles are slow). */ async function login(page: Page): Promise { await page.goto("/login"); await page.getByLabel("Username").fill("dev"); await page.getByLabel("Password").fill("Test123"); await page.getByRole("button", { name: "Log in" }).click(); await page.waitForURL((url) => !url.pathname.startsWith("/login"), { timeout: 90_000 }); } test.describe("Guided tours", () => { test("auto-starts on first visit, dismisses, and never auto-starts again", async ({ page }) => { await login(page); // Landing on the dashboard starts its tour (first visit for this browser). await expect(page.locator("[data-tour-card]")).toBeVisible(); await expect(page.locator("[data-tour-card]")).toContainText("1 / 3"); // Walk the tour to the end: dismissal is remembered. await page.getByRole("button", { name: "Next", exact: true }).click(); await expect(page.locator("[data-tour-card]")).toContainText("2 / 3"); await page.getByRole("button", { name: "Next", exact: true }).click(); await expect(page.locator("[data-tour-card]")).toContainText("3 / 3"); await page.getByRole("button", { name: "Done" }).click(); await expect(page.locator("[data-tour-card]")).toHaveCount(0); const dismissals = await page.evaluate(() => window.localStorage.getItem("ptf:tour:dismissals"), ); expect(dismissals).not.toBeNull(); expect(JSON.parse(dismissals ?? "{}")).toHaveProperty("dashboard"); // Reload: the tour does NOT auto-start again. await page.reload(); await expect(page.locator("[data-tour-card]")).toHaveCount(0); }); test("replay control re-runs the tour after dismissal", async ({ page }) => { await login(page); // Dismiss the auto-started tour immediately. await expect(page.locator("[data-tour-card]")).toBeVisible(); await page.getByRole("button", { name: "Skip", exact: true }).click(); await expect(page.locator("[data-tour-card]")).toHaveCount(0); // The header replay control brings it back on demand. await page.getByRole("button", { name: "Replay page tour" }).click(); await expect(page.locator("[data-tour-card]")).toBeVisible(); await expect(page.locator("[data-tour-card]")).toContainText("Dashboard"); }); test("skip all opts the browser out; the account toggle re-enables", async ({ page }) => { await login(page); // Opt out from the tour card itself. await expect(page.locator("[data-tour-card]")).toBeVisible(); await page.getByRole("button", { name: "Skip all tours on this device" }).click(); await expect(page.locator("[data-tour-card]")).toHaveCount(0); expect(await page.evaluate(() => window.localStorage.getItem("ptf:tours:opted-out"))).toBe("1"); // Another toured page no longer auto-starts... await page.goto("/wiki"); await expect(page.locator("[data-tour-card]")).toHaveCount(0); // ...but replay stays available as the explicit opt-in. await page.getByRole("button", { name: "Replay page tour" }).click(); await expect(page.locator("[data-tour-card]")).toBeVisible(); await page.getByRole("button", { name: "Skip", exact: true }).click(); await expect(page.locator("[data-tour-card]")).toHaveCount(0); // Re-enable from the account page (the switch reads off after the opt-out), // then a never-visited toured page auto-starts again. await page.goto("/account"); const toursSwitch = page.getByRole("switch", { name: "Auto-start page tours" }); await expect(toursSwitch).toBeVisible(); await toursSwitch.click(); await page.goto("/awards"); await expect(page.locator("[data-tour-card]")).toBeVisible(); }); test("pages without tours show no replay control and no overlay", async ({ page }) => { await login(page); await page.goto("/account"); await expect(page.locator("[data-tour-card]")).toHaveCount(0); await expect(page.getByRole("button", { name: "Replay page tour" })).toHaveCount(0); }); });