1
0
Fork 0
polaris-task-force/tests/e2e/tours.e2e.spec.ts
Z8MB1E 8b79c1379d feat(tours): add permission-aware guided tours with replay and device opt-out
Pages declare brief tours in code, keyed to stable data-tour anchors. The layout resolves each visitor's access signals (intelligence and logistics qualifications, admin-panel access, feature flags) and step filtering hides what a user cannot reach, mirroring the sidebar. A tour auto-starts the first time a browser visits its page; dismissals are remembered per browser in localStorage. The header gains a replay control for on-demand runs on toured routes, the tour card gains Skip all tours on this device to opt a browser out of auto-starts, and the Account page holds the re-enable toggle. The card and spotlight get a distinct overlay treatment (glow, accent strip, outlined target).
2026-09-30 18:43:21 -04:00

95 lines
4.2 KiB
TypeScript

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<void> {
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);
});
});