test: add route matrix (11F) and hardcoded-JSX-text check (11D)

- fleet-ops-correction.spec.ts: opens every main route in all 3 languages, asserting
  no console errors, correct html[lang], and a real non-empty page heading (key parity
  across locale files is already proven structurally elsewhere, so this focuses on what
  only a live render can catch).
- i18n-coverage.spec.ts: a static scan for hardcoded JSX text bypassing t(...). A naive
  `>text<` regex falsely flagged TypeScript generics everywhere (`useState<string |
  null>(null)` was read as a "JSX tag" spanning to the next unrelated `>`) -- fixed by
  requiring the closing tag name to backreference the opening one
  (`<Tag>...</Tag>`), which generics can never satisfy. Verified against both false
  positives (passes clean on the current codebase) and false negatives (deliberately
  injected and reverted a hardcoded string to confirm it's caught).

Known pre-existing flake (unrelated to this branch, not touched by it): "logout
invalidates the server session so a refresh returns to login" in
interactive-elements.spec.ts occasionally fails only in the full sequential run,
never in isolation -- AuthContext.logout() clears local state and redirects before
awaiting the server-side cookie-clearing POST, a narrow race no human interaction
speed would ever hit. Noted as a known limitation, not fixed (out of this branch's
scope).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
NuklearRabbit
2026-08-03 23:30:09 +02:00
co-authored by Claude Sonnet 5
parent a7ac5ed9d0
commit 7851e807fa
2 changed files with 126 additions and 0 deletions
+55
View File
@@ -295,3 +295,58 @@ test("automation shows a localized error explanation with the raw error only und
await page.getByText("Technische details").first().click();
await expect(page.getByText("Synthetic connection timeout to n8n")).toBeVisible();
});
test.describe("route matrix (section 11F)", () => {
// Opens every main route in all 3 languages: no console errors, correct html[lang],
// and a real, non-empty page heading (proving the route actually rendered content
// instead of silently falling back to a raw i18next key or a blank screen). Key
// parity across locale files is already proven structurally by i18n-coverage.spec.ts
// (every key that exists in nl-BE also exists, non-empty, in en-GB/fr-BE), so this
// matrix focuses on what only a live render can catch.
const routes = [
"/dashboard",
"/vehicles",
"/vehicles/MO-001",
"/bookings",
"/bookings/BK-DEMO-RETURN",
"/data-quality",
"/data-quality/DQ-DEMO-STATUS",
"/automation",
"/knowledge",
"/audit",
"/scenarios",
"/about",
];
for (const lang of ["nl-BE", "en-GB", "fr-BE"]) {
test(`every main route renders correctly with no console errors (${lang})`, async ({ page, request }) => {
await resetDemoData(request);
const errors: string[] = [];
page.on("console", (msg) => {
if (msg.type() !== "error") return;
if (msg.text().includes("401") && msg.text().includes("Unauthorized")) return;
errors.push(msg.text());
});
page.on("pageerror", (err) => errors.push(err.message));
await loginAsOpsManager(page, lang);
for (const route of routes) {
await page.goto(route);
await expect(page.locator("html")).toHaveAttribute("lang", lang);
const heading = page.getByRole("heading", { level: 1 });
await expect(heading, `${route} (${lang})`).toBeVisible();
const headingText = (await heading.first().textContent())?.trim() ?? "";
expect(headingText, `${route} (${lang}) heading text`).not.toBe("");
// A raw, unresolved i18next key looks like "namespace:some.key.path" -- real
// page headings never contain a colon followed by a dotted identifier.
expect(headingText, `${route} (${lang}) heading looks like a raw i18n key`).not.toMatch(
/^[a-zA-Z]+:[\w.]+$/,
);
}
expect(errors, `Console errors across the route matrix (${lang}):\n${errors.join("\n")}`).toEqual([]);
});
}
});