Files
MobilityOps/frontend/e2e/ui-redesign.spec.ts
T
NuklearRabbit f5212959b4 feat(returns): add authoritative return preview
The return-review step predicted operational consequences independently in
the frontend, and got it wrong: damage or a technical warning was described
as routing to "maintenance" when the actual domain rule (returns.py) routes
it to "blocked", and the no-contradiction case was described as becoming
"available" when the vehicle actually always goes to "cleaning" first
(only reaching "maintenance" if the service threshold was crossed).

Extract the evaluation returns.py already performed inline into a pure
evaluate_return() function with no writes -- resulting status (with an
explanation), odometer regression, would-create-quality-issue,
next-booking-risk -- and share it between a new non-mutating
POST /bookings/{ref}/return-preview endpoint and the existing commit path,
so preview and commit can never drift apart again. The result screen also
now distinguishes local commit success from n8n delivery (still queued/
unconfirmed) instead of implying both succeeded, and links to any created
quality issue for Operations Manager.
2026-08-02 05:33:12 +02:00

68 lines
3.2 KiB
TypeScript

import { expect, test, type APIRequestContext } from "@playwright/test";
async function resetDemoData(request: APIRequestContext) {
await request.post("/api/v1/demo/login", { data: { role: "operations_manager" } });
await request.post("/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("global search supports its keyboard shortcut and public references", async ({ page }) => {
await page.keyboard.press("Control+k");
const search = page.getByRole("searchbox", { name: "Search MobilityOps" });
await expect(search).toBeFocused();
await search.fill("MO-024");
await search.press("Enter");
await expect(page).toHaveURL(/\/vehicles\/MO-024$/);
await expect(page.getByRole("heading", { name: "MO-024" })).toBeVisible();
});
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 commitRequests = 0;
let previewRequests = 0;
page.on("request", (request) => {
if (request.method() !== "POST") return;
if (request.url().endsWith("/return-preview")) previewRequests += 1;
else if (request.url().endsWith("/return")) commitRequests += 1;
});
await page.getByRole("button", { name: "Review return" }).click();
await expect(page.getByText(/Expected fleet state/)).toBeVisible();
expect(commitRequests).toBe(0);
// The review step is server-evaluated (not client-guessed), so exactly one non-mutating
// preview call is expected before any commit.
expect(previewRequests).toBe(1);
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();
});
}