mirror of
https://github.com/diegosouzapw/OmniRoute.git
synced 2026-09-15 19:32:20 +03:00
The gamification anomalies page had hard-coded English for its loading state, Status column header and Suspicious badge, and was the only standalone non-redirect dashboard page without a sidebar entry. Both are fixed: the strings come from the common catalog, and the page joins the Gamification sidebar group as a hideable section item shown only by the "all" preset, like its siblings. The loading and empty states also become role="status" aria-live="polite" live regions with aria-busy, matching profile/page.tsx and health/page.tsx. Three new keys in en.json, propagated to the other 42 locales with the __MISSING__ sentinel. Validated in a combined worktree with all 25 PRs of this batch boarded together: typecheck:core clean, 443/443 node-runner tests plus 14/14 vitest across every test file the batch touches, and check-changelog-integrity, check:cycles (418 files), check:provider-consistency (272 REGISTRY entries, 355 canonical providers), check:docs-counts, check:docs-sync (42 locales) and check-file-size all green. Thanks @pacocartones.
82 lines
3.1 KiB
TypeScript
82 lines
3.1 KiB
TypeScript
// @vitest-environment jsdom
|
|
import React from "react";
|
|
import { cleanup, render, screen, waitFor } from "@testing-library/react";
|
|
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
|
|
|
// Namespace-prefixed keys instead of the global en.json-backed mock: any English
|
|
// string still hard-coded in the page would surface verbatim in the rendered text.
|
|
vi.mock("next-intl", () => ({
|
|
useTranslations: (namespace: string) => (key: string) => `${namespace}.${key}`,
|
|
}));
|
|
|
|
vi.mock("@/shared/components", () => ({
|
|
Card: ({ children }: { children: React.ReactNode }) => <div>{children}</div>,
|
|
}));
|
|
|
|
const RAW_ENGLISH = ["Loading...", "Status", "Suspicious"];
|
|
const originalFetch = globalThis.fetch;
|
|
|
|
function mockFetch(payload: unknown) {
|
|
globalThis.fetch = vi.fn().mockResolvedValue({
|
|
ok: true,
|
|
json: async () => payload,
|
|
}) as unknown as typeof fetch;
|
|
}
|
|
|
|
async function renderPage() {
|
|
const { default: GamificationAdminPage } =
|
|
await import("../../../src/app/(dashboard)/dashboard/gamification/admin/page");
|
|
return render(<GamificationAdminPage />);
|
|
}
|
|
|
|
describe("GamificationAdminPage (anomalies)", () => {
|
|
beforeEach(() => {
|
|
(
|
|
globalThis as typeof globalThis & { IS_REACT_ACT_ENVIRONMENT?: boolean }
|
|
).IS_REACT_ACT_ENVIRONMENT = true;
|
|
});
|
|
|
|
afterEach(() => {
|
|
cleanup();
|
|
globalThis.fetch = originalFetch;
|
|
vi.restoreAllMocks();
|
|
});
|
|
|
|
it("announces the loading state through a busy polite live region", async () => {
|
|
globalThis.fetch = vi.fn().mockReturnValue(new Promise(() => {})) as unknown as typeof fetch;
|
|
const { container } = await renderPage();
|
|
|
|
const status = screen.getByRole("status");
|
|
expect(status.getAttribute("aria-live")).toBe("polite");
|
|
expect(status.getAttribute("aria-busy")).toBe("true");
|
|
expect(status.textContent).toBe("common.loading");
|
|
for (const raw of RAW_ENGLISH) expect(container.textContent).not.toContain(raw);
|
|
});
|
|
|
|
it("announces the empty result through a polite live region", async () => {
|
|
mockFetch({ anomalies: [] });
|
|
const { container } = await renderPage();
|
|
|
|
const status = await screen.findByText("common.noAnomaliesDetected");
|
|
expect(status.getAttribute("role")).toBe("status");
|
|
expect(status.getAttribute("aria-live")).toBe("polite");
|
|
expect(status.hasAttribute("aria-busy")).toBe(false);
|
|
for (const raw of RAW_ENGLISH) expect(container.textContent).not.toContain(raw);
|
|
});
|
|
|
|
it("renders the flagged table with translated column headers and badge", async () => {
|
|
mockFetch({
|
|
anomalies: [{ apiKeyId: "sk-0123456789abcdef0123", xpLastHour: 12345, zScore: 4.2 }],
|
|
});
|
|
const { container } = await renderPage();
|
|
|
|
await waitFor(() => expect(screen.getByText("common.suspicious")).toBeTruthy());
|
|
for (const key of ["common.apiKey", "common.xpLastHour", "common.zScore", "common.status"]) {
|
|
expect(screen.getByText(key)).toBeTruthy();
|
|
}
|
|
expect(screen.getByText("4.20")).toBeTruthy();
|
|
expect(screen.queryByRole("status")).toBeNull();
|
|
for (const raw of RAW_ENGLISH) expect(container.textContent).not.toContain(raw);
|
|
});
|
|
});
|