103 lines
3.8 KiB
TypeScript
103 lines
3.8 KiB
TypeScript
import { describe, expect, it } from "vitest";
|
|
import { t } from "@/lib/i18n";
|
|
import sidebarCatalog from "@/src/locales/en/sidebar.json";
|
|
|
|
/**
|
|
* Byte-for-byte pin on the settings nav's English copy.
|
|
*
|
|
* Externalizing a string can silently REWRITE it: point a label at an existing
|
|
* key whose wording has drifted and lint, `i18n:check`, the ratchet and the unit
|
|
* suite all still pass — the string is externalized, the key exists, the
|
|
* catalogs are in sync. Nothing downstream compares the rendered sentence to
|
|
* what users saw before.
|
|
*
|
|
* Nav labels are the sharpest case, because the same word usually exists twice:
|
|
* once here, and once as the route's own page title in
|
|
* `components/settings/settings-layout-client.tsx`. These assertions are the
|
|
* diff, kept.
|
|
*/
|
|
const NAV_LABELS: Array<[key: string, english: string]> = [
|
|
["settings:preferences", "Preferences"],
|
|
["settings:workspacesAndAccess", "Workspaces & Access"],
|
|
["settings:accessControl", "Access Control"],
|
|
["settings:terminalAndEditors", "Terminal & Editors"],
|
|
["settings:taskBehavior", "Task Behavior"],
|
|
["system:navDataStorage", "Data & Logs"],
|
|
["system:storageTitle", "Storage"],
|
|
["common:prompts", "Prompts"],
|
|
["settings:utilityAgents", "Utility Agents"],
|
|
["settings:globalSecrets", "Global Secrets"],
|
|
["common:externalMcp", "External MCP"],
|
|
["common:plugins", "Plugins"],
|
|
["common:workspaces", "Workspaces"],
|
|
["sidebar:repositories", "Repositories"],
|
|
["workflows:workflows", "Workflows"],
|
|
["common:integrations", "Integrations"],
|
|
["common:automations", "Automations"],
|
|
["common:executors", "Executors"],
|
|
["common:agents", "Agents"],
|
|
["common:settings", "Settings"],
|
|
// No `sidebar:account`: the restructure folded those rows into the
|
|
// "Access Control" section, pinned above as `settings:accessControl`. The key
|
|
// and its label are gone, so pinning them here pinned nothing.
|
|
["sidebar:profileAndPassword", "Profile & Password"],
|
|
["sidebar:apiTokens", "API Tokens"],
|
|
];
|
|
|
|
/**
|
|
* Labels that also exist as a route page title, keyed by
|
|
* `SEGMENT_LABEL_KEYS` in components/settings/settings-breadcrumb-labels.ts. The
|
|
* nav and the title are the same words for the same destination and the user
|
|
* sees both at once (nav on the left, title above), so they must resolve
|
|
* through ONE key. Two keys with identical English today is a translator's
|
|
* licence to make them disagree tomorrow.
|
|
*
|
|
* `sidebar:utilityAgents` was exactly that mistake and
|
|
* were removed in favour of the existing `settings:` keys.
|
|
*/
|
|
const OWNED_BY_ANOTHER_NAMESPACE = [
|
|
"Prompts",
|
|
"Utility Agents",
|
|
"Global Secrets",
|
|
"External MCP",
|
|
"Plugins",
|
|
"Executors",
|
|
"Integrations",
|
|
"Automations",
|
|
"Workspaces",
|
|
"Agents",
|
|
"Settings",
|
|
"Workflows",
|
|
];
|
|
|
|
describe("settings nav copy", () => {
|
|
it.each(NAV_LABELS)("renders %s as its pinned English", (key, english) => {
|
|
expect(t(key)).toBe(english);
|
|
});
|
|
|
|
it("keeps every nav label non-empty and free of a leaking key", () => {
|
|
for (const [key] of NAV_LABELS) {
|
|
const value = t(key);
|
|
expect(value).not.toBe("");
|
|
// A missing key echoes the key itself, which reads as copy in the UI.
|
|
expect(value).not.toBe(key);
|
|
expect(value).not.toContain(":");
|
|
}
|
|
});
|
|
});
|
|
|
|
describe("settings nav vs route title", () => {
|
|
it("does not re-declare a label another namespace already owns", () => {
|
|
const duplicates = Object.entries(sidebarCatalog)
|
|
.filter(([, value]) => OWNED_BY_ANOTHER_NAMESPACE.includes(value))
|
|
.map(([key, value]) => `sidebar:${key} = ${JSON.stringify(value)}`);
|
|
|
|
expect(
|
|
duplicates,
|
|
"These labels already have a key in common:/settings:/workflows: and are " +
|
|
"rendered as the route's page title from that key. Reuse it instead of " +
|
|
"adding a sidebar: twin, or the nav and the title can drift apart.",
|
|
).toEqual([]);
|
|
});
|
|
});
|