From b3946af4e0e14f2a3acecd212e9f0079e3028fca Mon Sep 17 00:00:00 2001 From: NuklearRabbit <145918611+NuklearRabbit@users.noreply.github.com> Date: Sun, 2 Aug 2026 03:27:30 +0200 Subject: [PATCH] test(ui): cover redesigned journeys and responsive shell --- frontend/e2e/demo.spec.ts | 16 ++++--- frontend/e2e/interactive-elements.spec.ts | 17 ++++---- frontend/e2e/ui-redesign.spec.ts | 53 +++++++++++++++++++++++ 3 files changed, 71 insertions(+), 15 deletions(-) create mode 100644 frontend/e2e/ui-redesign.spec.ts diff --git a/frontend/e2e/demo.spec.ts b/frontend/e2e/demo.spec.ts index fcb31ca..8c5c546 100644 --- a/frontend/e2e/demo.spec.ts +++ b/frontend/e2e/demo.spec.ts @@ -20,14 +20,14 @@ test("five-minute demo script end to end", async ({ page, request }) => { await test.step("1. login as Operations Manager", async () => { await page.goto("/login"); - await expect(page.getByText("Synthetic demo environment")).toBeVisible(); + await expect(page.getByText(/Synthetic proof of concept/)).toBeVisible(); await page.getByRole("button", { name: "Open as Operations Manager" }).click(); await expect(page).toHaveURL(/\/dashboard$/); }); await test.step("2. verify dashboard metrics are loaded", async () => { - await expect(page.getByRole("heading", { name: "Operational metrics" })).toBeVisible(); - const metricValues = page.locator(".metric-value"); + await expect(page.getByRole("heading", { name: "Fleet readiness" })).toBeVisible(); + const metricValues = page.locator(".metric-cell dd"); await expect(metricValues.first()).toBeVisible(); const values = await metricValues.allTextContents(); expect(values.length).toBeGreaterThan(0); @@ -50,7 +50,9 @@ test("five-minute demo script end to end", async ({ page, request }) => { await page.getByLabel("End odometer (km)").fill(String(lowReading)); await page.getByLabel("Fuel level (%)").fill("55"); - await page.getByRole("button", { name: "Register return" }).click(); + await page.getByRole("button", { name: "Review return" }).click(); + await expect(page.getByRole("heading", { name: "Review return impact" })).toBeVisible(); + await page.getByRole("button", { name: "Confirm return" }).click(); await expect(page.getByRole("heading", { name: "Return registered" })).toBeVisible(); }); @@ -83,14 +85,14 @@ test("five-minute demo script end to end", async ({ page, request }) => { await page.goto("/audit"); await page.getByLabel("Action").fill("return_registered"); await expect(page.locator(".data-table tbody tr").first()).toBeVisible(); - await expect(page.getByText("return_registered").first()).toBeVisible(); + await expect(page.getByText("return registered").first()).toBeVisible(); }); await test.step("9. verify responsive navigation at mobile width", async () => { await page.setViewportSize({ width: 360, height: 800 }); await page.goto("/dashboard"); - await expect(page.getByText("Synthetic demo environment")).toBeVisible(); - await expect(page.getByRole("link", { name: "Dashboard" })).toBeVisible(); + await expect(page.getByText(/Synthetic demo data/).first()).toBeVisible(); + await expect(page.getByRole("link", { name: "Overview" }).first()).toBeVisible(); const scrollWidth = await page.evaluate(() => document.documentElement.scrollWidth); const clientWidth = await page.evaluate(() => document.documentElement.clientWidth); expect(scrollWidth).toBeLessThanOrEqual(clientWidth + 1); diff --git a/frontend/e2e/interactive-elements.spec.ts b/frontend/e2e/interactive-elements.spec.ts index 93d9944..f32eb29 100644 --- a/frontend/e2e/interactive-elements.spec.ts +++ b/frontend/e2e/interactive-elements.spec.ts @@ -17,16 +17,17 @@ test.beforeEach(async ({ page }) => { test("all seven nav items navigate correctly", async ({ page }) => { const items: [string, RegExp][] = [ - ["Dashboard", /\/dashboard$/], - ["Vehicles", /\/vehicles$/], + ["Overview", /\/dashboard$/], + ["Fleet", /\/vehicles$/], ["Bookings", /\/bookings$/], - ["Data Quality", /\/data-quality$/], + ["Data quality", /\/data-quality$/], ["Knowledge", /\/knowledge$/], - ["Automation", /\/automation$/], - ["Audit", /\/audit$/], + ["Integrations", /\/automation$/], + ["Audit trail", /\/audit$/], ]; + const primaryNavigation = page.getByRole("navigation", { name: "Primary navigation" }); for (const [label, urlPattern] of items) { - await page.getByRole("link", { name: label }).click(); + await primaryNavigation.getByRole("link", { name: label, exact: true }).click(); await expect(page).toHaveURL(urlPattern); } }); @@ -123,7 +124,7 @@ test("audit page: action filter works", async ({ page }) => { await page.getByLabel("Action").fill("demo_login"); await expect(page.locator(".data-table tbody tr").first()).toBeVisible(); const actions = await page.locator(".data-table tbody tr td:nth-child(3)").allTextContents(); - expect(actions.every((a) => a.includes("demo_login"))).toBeTruthy(); + expect(actions.every((a) => a.includes("demo login"))).toBeTruthy(); }); test("knowledge page: form submits and clears input", async ({ page }) => { @@ -146,7 +147,7 @@ test("rental employee role sees restricted automation page and cannot access res await page.getByRole("button", { name: "Switch role" }).click(); await page.getByRole("button", { name: "Open as Rental Employee" }).click(); await expect(page).toHaveURL(/\/dashboard$/); - await page.getByRole("link", { name: "Automation" }).click(); + await page.getByRole("link", { name: "Integrations" }).click(); await expect( page.getByText("Automation delivery status is visible to Operations Managers only."), ).toBeVisible(); diff --git a/frontend/e2e/ui-redesign.spec.ts b/frontend/e2e/ui-redesign.spec.ts new file mode 100644 index 0000000..12a4ff6 --- /dev/null +++ b/frontend/e2e/ui-redesign.spec.ts @@ -0,0 +1,53 @@ +import { expect, test, type APIRequestContext } from "@playwright/test"; + +const API_BASE = process.env.MOBILITYOPS_API_URL ?? "http://localhost:8128"; + +async function resetDemoData(request: APIRequestContext) { + await request.post(`${API_BASE}/api/v1/demo/login`, { data: { role: "operations_manager" } }); + await request.post(`${API_BASE}/api/v1/demo/reset`); +} + +test.beforeEach(async ({ page, request }) => { + await resetDemoData(request); + await page.goto("/login"); + await page.getByRole("button", { name: "Open as Operations Manager" }).click(); + await expect(page).toHaveURL(/\/dashboard$/); +}); + +test("control-centre shell exposes landmarks, persisted readiness and active navigation", async ({ page }) => { + await expect(page.getByRole("navigation", { name: "Primary navigation" })).toBeVisible(); + await expect(page.getByRole("main")).toBeVisible(); + await expect(page.getByRole("heading", { name: "Fleet readiness" })).toBeVisible(); + await expect(page.locator(".metric-cell dd").first()).not.toHaveText(""); + await expect(page.getByRole("link", { name: "Overview" }).first()).toHaveAttribute("aria-current", "page"); +}); + +test("return review separates capture from irreversible commit", async ({ page }) => { + await page.goto("/bookings/BK-DEMO-RETURN"); + await page.getByLabel("End odometer (km)").fill("60000"); + await page.getByLabel("Fuel level (%)").fill("65"); + + let returnRequests = 0; + page.on("request", (request) => { + if (request.url().includes("/return") && request.method() === "POST") returnRequests += 1; + }); + + await page.getByRole("button", { name: "Review return" }).click(); + expect(returnRequests).toBe(0); + await expect(page.getByText("Expected fleet state")).toBeVisible(); + await expect(page.getByText("Queue n8n delivery after the local commit")).toBeVisible(); + + await page.getByRole("button", { name: "Edit details" }).click(); + await expect(page.getByLabel("End odometer (km)")).toHaveValue("60000"); +}); + +for (const width of [390, 768, 1280, 1440]) { + test(`responsive shell has no horizontal overflow at ${width}px`, async ({ page }) => { + await page.setViewportSize({ width, height: width < 700 ? 844 : 900 }); + await page.goto("/dashboard"); + await expect(page.getByRole("heading", { name: "Attention queue" })).toBeVisible(); + const dimensions = await page.evaluate(() => ({ scroll: document.documentElement.scrollWidth, client: document.documentElement.clientWidth })); + expect(dimensions.scroll).toBeLessThanOrEqual(dimensions.client + 1); + if (width < 960) await expect(page.getByRole("navigation", { name: "Mobile navigation" })).toBeVisible(); + }); +}