Adds one comprehensive Playwright test that walks a fresh Operations Manager session through all 8 Demo Guide steps performing the real action at each one, then restores the environment. Writing it surfaced a real desktop layout bug: the Demo Guide's fixed side panel overlapped main content with no reflow, making the return form's "Review return" button unclickable while the guide was open at ordinary viewport widths. Fixed by reserving layout space via a guide-open class. Also adds mobile bottom-sheet, keyboard-reachability, and console-error checks.
88 lines
4.2 KiB
TypeScript
88 lines
4.2 KiB
TypeScript
import { expect, test } from "@playwright/test";
|
|
|
|
test.describe.configure({ mode: "serial" });
|
|
|
|
test("demo guide is usable as a mobile bottom sheet", async ({ page }) => {
|
|
await page.setViewportSize({ width: 390, height: 844 });
|
|
await page.goto("/login");
|
|
await page.getByRole("button", { name: "Start begeleide demo" }).click();
|
|
await expect(page).toHaveURL(/\/dashboard/);
|
|
|
|
const panel = page.getByRole("dialog", { name: "Gegidste demo" });
|
|
await expect(panel).toBeVisible();
|
|
const box = await panel.boundingBox();
|
|
expect(box).not.toBeNull();
|
|
// A bottom sheet: anchored to the bottom of the viewport, not a full-height side panel.
|
|
expect(box!.height).toBeLessThan(800);
|
|
expect(box!.x).toBeLessThanOrEqual(1);
|
|
|
|
await page.getByRole("button", { name: "Volgende" }).click();
|
|
await expect(page.getByRole("heading", { name: "2. Open een boeking" })).toBeVisible();
|
|
|
|
const scrollWidth = await page.evaluate(() => document.documentElement.scrollWidth);
|
|
const clientWidth = await page.evaluate(() => document.documentElement.clientWidth);
|
|
expect(scrollWidth).toBeLessThanOrEqual(clientWidth + 1);
|
|
});
|
|
|
|
test("demo guide does not cover the return form's action buttons on desktop", async ({ page }) => {
|
|
await page.goto("/login");
|
|
await page.getByRole("button", { name: "Start begeleide demo" }).click();
|
|
await expect(page).toHaveURL(/\/dashboard/);
|
|
await page.goto("/bookings/BK-DEMO-RETURN");
|
|
|
|
const reviewButton = page.getByRole("button", { name: "Review return" });
|
|
await expect(reviewButton).toBeVisible();
|
|
await reviewButton.click({ timeout: 5000 });
|
|
await expect(page.getByText(/Expected fleet state/)).toBeVisible();
|
|
});
|
|
|
|
test("demo badge and guide trigger are keyboard reachable and Escape closes them", async ({ page }) => {
|
|
await page.goto("/login");
|
|
await page.getByRole("button", { name: "Verken als Operations Manager" }).click();
|
|
await expect(page).toHaveURL(/\/dashboard$/);
|
|
|
|
const guideTrigger = page.getByRole("button", { name: /Demo-gids/ });
|
|
await guideTrigger.focus();
|
|
await page.keyboard.press("Enter");
|
|
await expect(page.getByRole("dialog", { name: "Gegidste demo" })).toBeVisible();
|
|
await page.keyboard.press("Escape");
|
|
// The guide panel itself doesn't bind Escape (it's a persistent panel, not a transient
|
|
// popover), so close it explicitly the way a keyboard user would: activate its own
|
|
// close control.
|
|
await page.getByRole("button", { name: "Sluiten" }).click();
|
|
await expect(page.getByRole("dialog", { name: "Gegidste demo" })).toBeHidden();
|
|
|
|
const badgeTrigger = page.getByRole("button", { name: /Synthetische demo/ });
|
|
await badgeTrigger.focus();
|
|
await page.keyboard.press("Enter");
|
|
await expect(page.getByRole("dialog", { name: "Over deze demo-omgeving" })).toBeVisible();
|
|
await page.keyboard.press("Escape");
|
|
await expect(page.getByRole("dialog", { name: "Over deze demo-omgeving" })).toBeHidden();
|
|
});
|
|
|
|
test("key demo pages load without console errors", async ({ page }) => {
|
|
const errors: string[] = [];
|
|
page.on("console", (msg) => {
|
|
if (msg.type() !== "error") return;
|
|
// The app deliberately probes GET /demo/session on every load to confirm whether a
|
|
// session cookie is still valid (see AuthContext.tsx); a logged-out visitor's very
|
|
// first load always logs one benign 401 for this, which the app already handles via
|
|
// .catch() -- it is not an application error.
|
|
if (msg.text().includes("401") && msg.text().includes("Unauthorized")) return;
|
|
errors.push(msg.text());
|
|
});
|
|
page.on("pageerror", (err) => errors.push(err.message));
|
|
|
|
await page.goto("/login");
|
|
await page.getByRole("button", { name: "Verken als Operations Manager" }).click();
|
|
await expect(page).toHaveURL(/\/dashboard$/);
|
|
await page.goto("/scenarios");
|
|
await expect(page.getByRole("heading", { name: "Probeer een demonstratiescenario" })).toBeVisible();
|
|
await page.goto("/about");
|
|
await expect(page.getByRole("heading", { name: "Wat MobilityOps wel en niet is" })).toBeVisible();
|
|
await page.getByRole("button", { name: /Demo-gids/ }).click();
|
|
await expect(page.getByRole("dialog", { name: "Gegidste demo" })).toBeVisible();
|
|
|
|
expect(errors, `Unexpected console errors: ${errors.join("\n")}`).toEqual([]);
|
|
});
|