Files
kandev/apps/web/components/settings/system/system-invisible-copy.test.tsx
T
2026-09-04 00:37:41 +00:00

195 lines
7.0 KiB
TypeScript

import { cleanup, render, screen } from "@testing-library/react";
import { afterAll, afterEach, beforeAll, describe, expect, it, vi } from "vitest";
import { activateLocale, t } from "@/lib/i18n";
import type { HealthCheckSummary, HealthIssue } from "@/lib/types/health";
import { HealthIssuesCard } from "./health-issues-card";
import { JobProgressIndicator } from "./job-progress-indicator";
import { SETTINGS_MENU_SECTIONS } from "@/components/app-sidebar/sections/settings/settings-tree";
const SYSTEM_MENU_ITEMS =
SETTINGS_MENU_SECTIONS.find((section) => section.id === "system")?.items ?? [];
const PSEUDO_ACCENTED = /[À-ɏ]/;
afterEach(cleanup);
const jobs = vi.hoisted(() => ({ current: [] as unknown[] }));
vi.mock("@/hooks/domains/system/use-system-jobs", () => ({
useSystemJob: () => null,
useSystemJobs: () => jobs.current,
}));
const health = vi.hoisted(() => ({
issues: [] as HealthIssue[],
checks: [] as HealthCheckSummary[],
loaded: true,
}));
vi.mock("@/hooks/domains/settings/use-system-health", () => ({
useSystemHealth: () => health,
}));
vi.mock("@/components/state-provider", () => ({
useAppStore: (selector: (s: unknown) => unknown) =>
selector({ workspaces: { activeId: "ws-1" }, auth: { user: { role: "admin" } } }),
}));
vi.mock("@/hooks/domains/features/use-feature", () => ({ useFeature: () => true }));
/**
* `stateLabel()` returns its four labels from a plain function, so
* `mode: "jsx-only"` reported this file as clean while every label was a
* hardcoded literal. It renders on four cards — including
* `storage-quarantine-card.tsx`, which ships on a route this PR does not
* otherwise touch — so a regression here is not visible from the System pages.
*/
describe("JobProgressIndicator state labels", () => {
const job = (state: string) => ({
id: "j1",
kind: "vacuum",
state,
started_at: "2026-08-03T10:00:00Z",
message: "",
});
it.each([
["queued", "Queued"],
["running", "Running"],
["succeeded", "Done"],
["failed", "Failed"],
])("renders %s as %s", (state, label) => {
jobs.current = [job(state)];
render(<JobProgressIndicator kind="vacuum" />);
expect(screen.getByTestId("system-job-vacuum").textContent).toContain(label);
});
// The wire enum is echoed rather than rendering blank, so a state from a
// newer backend still shows something.
it("echoes an unknown state token", () => {
jobs.current = [job("throttled")];
render(<JobProgressIndicator kind="vacuum" />);
expect(screen.getByTestId("system-job-vacuum").textContent).toContain("throttled");
});
});
/**
* This badge used to build its plural by hand — `issue${n === 1 ? "" : "s"}` —
* which renders correctly in English and is untranslatable everywhere else.
*/
describe("HealthIssuesCard issue count", () => {
it("agrees the noun with the number", () => {
health.issues = [
{ id: "a", severity: "error", title: "Disk full", message: "", fix_url: "", fix_label: "" },
] as HealthIssue[];
render(<HealthIssuesCard />);
expect(screen.getByTestId("system-health-card").textContent).toContain("1 issue");
cleanup();
health.issues = [
{ id: "a", severity: "error", title: "Disk full", message: "", fix_url: "", fix_label: "" },
{ id: "b", severity: "warning", title: "Slow", message: "", fix_url: "", fix_label: "" },
] as HealthIssue[];
render(<HealthIssuesCard />);
expect(screen.getByTestId("system-health-card").textContent).toContain("2 issues");
});
// `severity` is a wire enum. Title-casing the raw token was English-shaped by
// accident; unknown severities still echo rather than render blank.
it("labels a known severity and echoes an unknown one", () => {
health.issues = [
{ id: "a", severity: "warning", title: "Slow", message: "", fix_url: "", fix_label: "" },
{
id: "b",
severity: "critical" as HealthIssue["severity"],
title: "Boom",
message: "",
fix_url: "",
fix_label: "",
},
] as HealthIssue[];
render(<HealthIssuesCard />);
const card = screen.getByTestId("system-health-card").textContent ?? "";
expect(card).toContain("Warning");
expect(card).toContain("critical");
});
});
/**
* `BASE_ITEMS` / `AUTH_ITEMS` were SCREAMING_CASE, which the guard skips
* entirely. The menu config still is, so the same check applies: every System
* row label must resolve through the catalog.
*/
describe("System menu labels", () => {
it("renders every System row label through the catalog", () => {
const labels = SYSTEM_MENU_ITEMS.map((item) => t(item.labelKey));
expect(labels).toEqual(
expect.arrayContaining([
"Status",
"Data & Logs",
"Storage",
"Feature Toggles",
"Updates",
"About",
]),
);
});
it("looks up the Storage title through the pseudo-locale", async () => {
await activateLocale("pseudo");
try {
expect(t("system:storageTitle")).toMatch(PSEUDO_ACCENTED);
} finally {
await activateLocale("en");
}
});
});
/**
* The English assertions above pin the copy, but they cannot tell a `t()` call
* from a literal that happens to read the same — reverting
* `t("system:jobStateQueued")` to `"Queued"` passes every one of them. Only a
* non-English locale separates the two, and both surfaces here were reported as
* clean by lint, so this is the check that keeps them migrated.
*/
describe("copy the guard cannot see, under the pseudo-locale", () => {
beforeAll(async () => {
await activateLocale("pseudo");
});
afterAll(async () => {
await activateLocale("en");
});
it("accents every job state label", () => {
for (const state of ["queued", "running", "succeeded", "failed"]) {
jobs.current = [
{ id: "j1", kind: "vacuum", state, started_at: "2026-08-03T10:00:00Z", message: "" },
];
render(<JobProgressIndicator kind="vacuum" />);
expect(screen.getByTestId("system-job-vacuum").textContent).toMatch(PSEUDO_ACCENTED);
cleanup();
}
});
/**
* The Backups description carries a SQL statement and a filesystem path. Baked
* into the message they render `VÀĆŨŨḾ ĨŃŢŌ` and `<ďàţà-ďĩŕ>/ƀàćķũƥś/` — a
* command the user runs and a directory they have to find, both turned into
* dead pointers. They are interpolated as values instead, so the frame
* accents and they do not.
*/
it("keeps the backup SQL command and path literal inside a translated frame", () => {
const path = "/var/lib/kandev/backups";
const description = t("system:backupsPageDescription", {
command: "VACUUM INTO",
path,
});
expect(description).toContain("VACUUM INTO");
expect(description).toContain(path);
// The surrounding sentence is still translated.
expect(description).toMatch(PSEUDO_ACCENTED);
});
it("accents every System menu label", () => {
expect(SYSTEM_MENU_ITEMS.length).toBeGreaterThan(0);
for (const item of SYSTEM_MENU_ITEMS) {
expect(t(item.labelKey)).toMatch(PSEUDO_ACCENTED);
}
});
});