test: add accessibility coverage for the status-recommendation panel

Adds aria-live="polite" to the status-conflict panel (matching the existing
resolved-issue success-panel convention) so the applied-status confirmation is
announced to screen readers, and a Playwright test covering: keyboard-only
activation of both the "Review recommendation" and "Change status to X" actions,
reduced-motion emulation, and that status is never conveyed by colour alone (the
badge always carries its own localized text).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
NuklearRabbit
2026-08-03 23:08:21 +02:00
co-authored by Claude Sonnet 5
parent 1fdd2b3ccf
commit 1e407754e6
2 changed files with 36 additions and 1 deletions
+35
View File
@@ -85,3 +85,38 @@ test("key demo pages load without console errors", async ({ page }) => {
expect(errors, `Unexpected console errors: ${errors.join("\n")}`).toEqual([]);
});
test("status-recommendation panel is fully keyboard operable, respects reduced motion, and never signals status by colour alone", async ({
page,
request,
}) => {
await request.post("/api/v1/demo/login", { data: { role: "operations_manager" } });
await request.post("/api/v1/demo/reset");
await request.post("/api/v1/demo/login", { data: { role: "operations_manager" } });
await page.emulateMedia({ reducedMotion: "reduce" });
await page.goto("/login");
await page.getByRole("button", { name: "Verken als Operations Manager" }).click();
await expect(page).toHaveURL(/\/dashboard$/);
await page.goto("/data-quality/DQ-DEMO-STATUS");
const reviewButton = page.getByRole("button", { name: "Aanbeveling bekijken" });
await reviewButton.focus();
await expect(reviewButton).toBeFocused();
await page.keyboard.press("Enter");
const confirmButton = page.getByRole("button", { name: /^Status wijzigen naar/ });
await expect(confirmButton).toBeVisible();
// Status is never conveyed by colour alone: the badge always carries its own text.
const badge = page.locator(".status-decision .badge").first();
await expect(badge).not.toHaveText("");
// The confirm action itself is a real, focusable, keyboard-activatable button (the
// previous "Aanbeveling bekijken" button is unmounted once the decision panel
// replaces it, so focus is verified directly rather than via a Tab chain from it).
await confirmButton.focus();
await expect(confirmButton).toBeFocused();
await page.keyboard.press("Enter");
await expect(page.getByText("Toegepast", { exact: false })).toBeVisible();
});
@@ -566,7 +566,7 @@ function VehicleStatusConflictPanel({ issue, onResolved }: { issue: IssueDetail;
recommendation.safe_to_apply;
return (
<section className="panel" aria-labelledby="status-conflict-heading">
<section className="panel" aria-labelledby="status-conflict-heading" aria-live="polite">
<SectionHeading
headingId="status-conflict-heading"
title={t("detail.vehicleStatusConflict.heading")}