Files
MobilityOps/frontend/e2e/recruiter-polish.spec.ts
NuklearRabbit 81e3fd63bd
MobilityOps acceptance / backend (push) Failing after 19s
MobilityOps acceptance / frontend (push) Successful in 25s
MobilityOps acceptance / e2e (push) Skipped
M54: harden operations and demo resilience
2026-08-24 03:31:03 +02:00

143 lines
6.4 KiB
TypeScript

import { expect, test, type Page } from "@playwright/test";
test.describe.configure({ mode: "serial" });
async function loginAsOperationsManager(page: Page) {
await page.goto("/login");
await page.getByRole("button", { name: "Verken als Operationsmanager" }).click();
await expect(page).toHaveURL(/\/dashboard$/);
}
test("90-second recruiter entry exposes three verifiable engineering highlights", async ({ page }) => {
await page.goto("/login");
await page.getByRole("button", { name: "Bekijk de highlights in 90 seconden" }).click();
await expect(page).toHaveURL(/\/highlights$/);
await expect(page.getByRole("heading", { name: "De sterkste keuzes in 90 seconden" })).toBeVisible();
await expect(page.locator(".highlight-card")).toHaveCount(3);
await expect(page.getByText("Transactie · idempotency · outbox · degradatie")).toBeVisible();
await expect(page.getByText("RAGcore · citations · veilige degradatie")).toBeVisible();
});
test("Engineering Story exposes architecture, reliability and honest integration evidence", async ({ page }) => {
await loginAsOperationsManager(page);
await page.goto("/about");
await expect(page.getByRole("heading", { name: "Controle, betrouwbaarheid en uitlegbaarheid" }).first()).toBeVisible();
await expect(page.getByText("Commit eerst, orkestreer daarna")).toBeVisible();
await expect(page.getByText("AI moet bewijs tonen")).toBeVisible();
await expect(page.locator(".architecture-step", { hasText: "PostgreSQL + audittrail" }).getByRole("button")).toBeVisible();
const outboxStep = page.locator(".architecture-step", { hasText: "Betrouwbare outbox" }).getByRole("button");
await outboxStep.click();
await expect(outboxStep).toHaveAttribute("aria-pressed", "true");
await expect(page.locator("#architecture-active-detail")).toContainText("Post-commit aflevering");
const architectureTypeMinimums = [
[".architecture-zones > span", 12],
[".architecture-step button > small", 13],
[".architecture-zone-tag", 12],
[".architecture-commit-note", 12],
[".architecture-interaction-hint", 13],
[".architecture-active-copy p", 13],
[".architecture-active-evidence dt", 12],
[".architecture-active-evidence dd", 13],
] as const;
for (const [selector, minimumPixels] of architectureTypeMinimums) {
const fontSize = await page.locator(selector).first().evaluate((element) =>
Number.parseFloat(window.getComputedStyle(element).fontSize));
expect(fontSize, `${selector} font-size`).toBeGreaterThanOrEqual(minimumPixels);
}
await expect(page.getByText(/nog niet live gekoppeld/)).toHaveCount(0);
await expect(page.getByText(/unknown procedures/)).toHaveCount(0);
});
test("dashboard load failure offers an in-place retry and recovers", async ({ page }) => {
await loginAsOperationsManager(page);
let dashboardAttempts = 0;
let allowDashboardRecovery = false;
await page.route("**/api/v1/dashboard", async (route) => {
dashboardAttempts += 1;
if (!allowDashboardRecovery) {
await route.fulfill({ status: 503, contentType: "application/json", body: "{}" });
return;
}
await route.continue();
});
await page.reload();
const retryButton = page.getByRole("button", { name: "Opnieuw proberen" });
await expect(retryButton).toBeVisible();
allowDashboardRecovery = true;
await retryButton.click();
await expect(page.getByRole("heading", { name: "Wagenparkstatus" })).toBeVisible();
expect(dashboardAttempts).toBeGreaterThanOrEqual(2);
});
test("dashboard header stacks a full-width touch target on mobile", async ({ page }) => {
await page.setViewportSize({ width: 390, height: 844 });
await loginAsOperationsManager(page);
const header = page.locator(".page-header");
const action = page.locator(".page-actions .button");
const [headerBox, actionBox] = await Promise.all([header.boundingBox(), action.boundingBox()]);
expect(headerBox).not.toBeNull();
expect(actionBox).not.toBeNull();
expect(actionBox!.width).toBeGreaterThanOrEqual(headerBox!.width - 1);
expect(Math.round(actionBox!.height)).toBeGreaterThanOrEqual(44);
expect(await page.evaluate(() => document.documentElement.scrollWidth)).toBeLessThanOrEqual(
await page.evaluate(() => document.documentElement.clientWidth + 1),
);
});
test("mobile recruiter surfaces remain readable without horizontal overflow", async ({ page }) => {
await page.setViewportSize({ width: 390, height: 844 });
await page.goto("/login");
await page.getByRole("button", { name: "Bekijk de highlights in 90 seconden" }).click();
await expect(page).toHaveURL(/\/highlights$/);
const overflow = await page.evaluate(() => document.documentElement.scrollWidth - document.documentElement.clientWidth);
expect(overflow).toBeLessThanOrEqual(1);
await expect(page.locator(".highlight-card").first()).toBeVisible();
});
test("remaining attention items use a compact, legible queue action", async ({ page, request }) => {
await request.post("/api/v1/demo/login", { data: { role: "operations_manager" } });
await request.post("/api/v1/demo/reset");
await loginAsOperationsManager(page);
const queueAction = page.locator(".queue-more a");
await expect(queueAction).toContainText("Nog 2 aandachtspunten");
await expect(queueAction).toContainText("Open de volledige werklijst");
const actionBox = await queueAction.boundingBox();
const arrowBox = await queueAction.locator(".queue-more-arrow svg").boundingBox();
expect(actionBox?.height).toBeLessThanOrEqual(70);
expect(arrowBox?.width).toBeLessThanOrEqual(16);
});
test("Knowledge Hub renders independently verified RAGcore index evidence", async ({ page }) => {
await loginAsOperationsManager(page);
await page.route("**/api/v1/knowledge/status**", async (route) => {
await route.fulfill({
contentType: "application/json",
body: JSON.stringify({
provider: "ragcore",
available: true,
detail: "11/11 managed sources verified",
tenant: "northstar-mobility-demo",
workspace: "mobilityops",
collection: "internal-procedures",
document_count: 11,
source_document_count: 11,
reported_synced_document_count: 33,
reported_failed_document_count: 0,
last_sync_at: "2026-08-10T16:00:00Z",
statistics_state: "verified",
}),
});
});
await page.goto("/knowledge");
await expect(page.getByText("11 procedures geïndexeerd")).toBeVisible();
await expect(page.getByText("exact gevonden en actief gepubliceerd")).toBeVisible();
});