268 lines
12 KiB
JavaScript
268 lines
12 KiB
JavaScript
import { mkdir, mkdtemp, writeFile } from "node:fs/promises";
|
|
import { tmpdir } from "node:os";
|
|
import { join } from "node:path";
|
|
import { describe, expect, it } from "vitest";
|
|
|
|
import { getHistoricalBaselineDrift, historicalBaselineLimits, renderHardcodedStringReport, scanHardcodedStrings } from "./verify-hardcoded-strings.mjs";
|
|
|
|
async function createFixtureRoot(prefix = "i18nHardcodedStrings-hardcoded-strings-") {
|
|
return mkdtemp(join(tmpdir(), prefix));
|
|
}
|
|
|
|
describe("i18n Hardcoded Strings hardcoded-string drift guard", () => {
|
|
it("locks the historical baseline ratchet to the current repository counts", () => {
|
|
expect(historicalBaselineLimits).toEqual({
|
|
"brand-literal": 82,
|
|
"jsx-text": 21,
|
|
"user-facing-prop-literal": 12,
|
|
"user-facing-response-literal": 0,
|
|
});
|
|
});
|
|
|
|
it("ignores tests, generated assets, docs, dependencies, and TC artifacts", async () => {
|
|
const root = await createFixtureRoot();
|
|
await mkdir(join(root, "app", "routes"), { recursive: true });
|
|
await mkdir(join(root, "app", "generated"), { recursive: true });
|
|
await mkdir(join(root, "apps", "web", "build-portal"), { recursive: true });
|
|
await mkdir(join(root, "docs"), { recursive: true });
|
|
await mkdir(join(root, ".tc"), { recursive: true });
|
|
await mkdir(join(root, "node_modules", "ignored"), { recursive: true });
|
|
|
|
const ignored = "export function Page() { return <h1>Welcome aboard</h1>; }\nconst brand = 'HiveGrid';\nconst label = 'Visible Customer Label';\n";
|
|
await writeFile(join(root, "app", "routes", "sample.test.tsx"), ignored, "utf8");
|
|
await writeFile(join(root, "app", "generated", "ignored.tsx"), ignored, "utf8");
|
|
await writeFile(join(root, "apps", "web", "build-portal", "ignored.tsx"), ignored, "utf8");
|
|
await writeFile(join(root, "docs", "ignored.tsx"), ignored, "utf8");
|
|
await writeFile(join(root, ".tc", "ignored.tsx"), ignored, "utf8");
|
|
await writeFile(join(root, "node_modules", "ignored", "ignored.tsx"), ignored, "utf8");
|
|
|
|
const result = await scanHardcodedStrings(root);
|
|
const report = renderHardcodedStringReport(result);
|
|
|
|
expect(result.roots).toEqual(["apps"]);
|
|
expect(result.missingRoots).toEqual(["packages", "prisma", "server"]);
|
|
expect(result.scannedFiles).toBe(0);
|
|
expect(result.totalForbidden).toBe(0);
|
|
expect(report).toContain("skipped categories: generated/build/cache/docs/dependencies/TC/test-spec artifacts");
|
|
expect(report).toContain("total forbidden matches: 0");
|
|
});
|
|
|
|
it("reports unreviewed JSX, prop, and brand literals with deterministic file line samples", async () => {
|
|
const root = await createFixtureRoot();
|
|
await mkdir(join(root, "prisma", "seeders"), { recursive: true });
|
|
await writeFile(
|
|
join(root, "prisma", "seeders", "index.tsx"),
|
|
[
|
|
"const brand = 'HiveGrid';",
|
|
"export function Page() {",
|
|
' return <main aria-label="Customer Portal Heading"><h1>Welcome aboard</h1></main>;',
|
|
"}",
|
|
].join("\n"),
|
|
"utf8"
|
|
);
|
|
|
|
const result = await scanHardcodedStrings(root);
|
|
const report = renderHardcodedStringReport(result);
|
|
|
|
expect(result.totalForbidden).toBe(3);
|
|
expect(result.counts).toEqual({ "brand-literal": 1, "jsx-text": 1, "user-facing-prop-literal": 1, "user-facing-response-literal": 0 });
|
|
expect(result.findings).toEqual([
|
|
"prisma/seeders/index.tsx:1: [brand-literal] const brand = 'HiveGrid';",
|
|
'prisma/seeders/index.tsx:3: [jsx-text] return <main aria-label="Customer Portal Heading"><h1>Welcome aboard</h1></main>;',
|
|
'prisma/seeders/index.tsx:3: [user-facing-prop-literal] return <main aria-label="Customer Portal Heading"><h1>Welcome aboard</h1></main>;',
|
|
]);
|
|
expect(report).toContain("total forbidden matches: 3");
|
|
expect(report).toContain("forbidden samples:");
|
|
});
|
|
|
|
it("allows reviewed seed fixture and package brand/mail/i18n contract literals", async () => {
|
|
const root = await createFixtureRoot();
|
|
await mkdir(join(root, "packages", "brand", "src"), { recursive: true });
|
|
await mkdir(join(root, "packages", "mail", "src", "templates"), { recursive: true });
|
|
await mkdir(join(root, "packages", "i18n", "src"), { recursive: true });
|
|
await mkdir(join(root, "prisma", "seeds"), { recursive: true });
|
|
|
|
await writeFile(join(root, "packages", "brand", "src", "index.ts"), "export const defaultBrandTokens = { productName: 'HiveGrid' };\n", "utf8");
|
|
await writeFile(
|
|
join(root, "packages", "mail", "src", "templates", "transactional.ts"),
|
|
"export const subject = 'Welcome email subject'; export const name = 'HiveGrid mail template';\n",
|
|
"utf8"
|
|
);
|
|
await writeFile(join(root, "packages", "i18n", "src", "index.ts"), "export const defaultLocale = 'en'; export const title = 'Welcome aboard';\n", "utf8");
|
|
await writeFile(join(root, "prisma", "seeds", "fixture.ts"), "export const seedFixture = { label: 'Demo Workspace', brand: 'LegacySourceProduct' };\n", "utf8");
|
|
|
|
const result = await scanHardcodedStrings(root);
|
|
const report = renderHardcodedStringReport(result);
|
|
|
|
expect(result.totalForbidden).toBe(0);
|
|
expect(result.totalReviewed).toBeGreaterThan(0);
|
|
expect(report).toContain("total forbidden matches: 0");
|
|
expect(report).toContain("reviewed allowlist samples:");
|
|
});
|
|
|
|
it("allows worker build package-boundary literals without moving the historical baseline", async () => {
|
|
const root = await createFixtureRoot();
|
|
await mkdir(join(root, "apps", "worker", "scripts"), { recursive: true });
|
|
|
|
await writeFile(
|
|
join(root, "apps", "worker", "scripts", "build.mjs"),
|
|
[
|
|
'if (args.path === "@hivegrid/queue/runtime" || args.path.startsWith("@hivegrid/queue/runtime/")) return { external: true };',
|
|
'if (args.path.startsWith("@hivegrid/")) return undefined;',
|
|
].join("\n"),
|
|
"utf8"
|
|
);
|
|
|
|
const result = await scanHardcodedStrings(root);
|
|
|
|
expect(result.totalForbidden).toBe(0);
|
|
expect(result.reviewedCounts["brand-literal"]).toBe(3);
|
|
expect(result.historicalCounts["brand-literal"]).toBe(0);
|
|
});
|
|
|
|
it("allows technical package and protocol identifiers without moving the historical baseline", async () => {
|
|
const root = await createFixtureRoot();
|
|
await mkdir(join(root, "apps", "api", "src"), { recursive: true });
|
|
|
|
await writeFile(
|
|
join(root, "apps", "api", "src", "sample.ts"),
|
|
[
|
|
'import { getDbClient } from "@hivegrid/db/client.server";',
|
|
'export const tenantHeader = "x-hivegrid-tenant-id";',
|
|
'export const productIdempotencyKey = "hivegrid-product-cancel-deadbeef";',
|
|
].join("\n"),
|
|
"utf8"
|
|
);
|
|
|
|
const result = await scanHardcodedStrings(root);
|
|
|
|
expect(result.totalForbidden).toBe(0);
|
|
expect(result.reviewedCounts["brand-literal"]).toBe(3);
|
|
expect(result.historicalCounts["brand-literal"]).toBe(0);
|
|
});
|
|
|
|
it("allows the physical search index namespace without moving the historical baseline", async () => {
|
|
const root = await createFixtureRoot();
|
|
await mkdir(join(root, "packages", "search", "src"), { recursive: true });
|
|
|
|
await writeFile(
|
|
join(root, "packages", "search", "src", "index-name.ts"),
|
|
'export const SEARCH_INDEX_UID_PREFIX = "hivegrid-" as const;\n',
|
|
"utf8"
|
|
);
|
|
|
|
const result = await scanHardcodedStrings(root);
|
|
|
|
expect(result.totalForbidden).toBe(0);
|
|
expect(result.reviewedCounts["brand-literal"]).toBe(1);
|
|
expect(result.historicalCounts["brand-literal"]).toBe(0);
|
|
});
|
|
|
|
it("allows request-scope registry symbols and ignores generic function signatures", async () => {
|
|
const root = await createFixtureRoot();
|
|
await mkdir(join(root, "apps", "web", "app", "locale"), { recursive: true });
|
|
|
|
await writeFile(
|
|
join(root, "apps", "web", "app", "locale", "request-scope.ts"),
|
|
[
|
|
'export const storage = Symbol.for("hivegrid.i18n.request-storage");',
|
|
"export async function runWithRequestScope<T>(request: Request, callback: () => Promise<T>): Promise<T> {",
|
|
" return callback();",
|
|
"}",
|
|
].join("\n"),
|
|
"utf8"
|
|
);
|
|
|
|
const result = await scanHardcodedStrings(root);
|
|
|
|
expect(result.totalForbidden).toBe(0);
|
|
expect(result.reviewedCounts["brand-literal"]).toBe(1);
|
|
expect(result.historicalCounts["brand-literal"]).toBe(0);
|
|
expect(result.historicalCounts["jsx-text"]).toBe(0);
|
|
});
|
|
|
|
it("allows OpenAPI contract descriptions without moving the i18n historical baseline", async () => {
|
|
const root = await createFixtureRoot();
|
|
await mkdir(join(root, "apps", "api", "src"), { recursive: true });
|
|
|
|
await writeFile(
|
|
join(root, "apps", "api", "src", "openapi.ts"),
|
|
'export const schema = { title: "HiveGrid API", description: "Request correlation identifier echoed from the request." };\n',
|
|
"utf8"
|
|
);
|
|
|
|
const result = await scanHardcodedStrings(root);
|
|
|
|
expect(result.totalForbidden).toBe(0);
|
|
expect(result.reviewedCounts["brand-literal"]).toBe(1);
|
|
expect(result.reviewedCounts["user-facing-prop-literal"]).toBe(2);
|
|
expect(result.historicalCounts["brand-literal"]).toBe(0);
|
|
expect(result.historicalCounts["user-facing-prop-literal"]).toBe(0);
|
|
});
|
|
|
|
it("allows backend runtime strings without moving the UI historical baseline", async () => {
|
|
const root = await createFixtureRoot();
|
|
await mkdir(join(root, "apps", "api", "src"), { recursive: true });
|
|
|
|
await writeFile(join(root, "apps", "api", "src", "app.ts"), 'export const error = { message: "A required dependency is unavailable." };\n', "utf8");
|
|
|
|
const result = await scanHardcodedStrings(root);
|
|
|
|
expect(result.totalForbidden).toBe(0);
|
|
expect(result.reviewedCounts["user-facing-prop-literal"]).toBe(1);
|
|
expect(result.historicalCounts["user-facing-prop-literal"]).toBe(0);
|
|
});
|
|
|
|
it("classifies every static locale catalog string as reviewed translation copy", async () => {
|
|
const root = await createFixtureRoot();
|
|
await mkdir(join(root, "apps", "web", "public", "locales", "pl"), { recursive: true });
|
|
await writeFile(
|
|
join(root, "apps", "web", "public", "locales", "pl", "translations.json"),
|
|
'{"description":"HiveGrid jest dostępny","markup":"<p>Widoczny tekst produktu</p>"}\n',
|
|
"utf8"
|
|
);
|
|
|
|
const result = await scanHardcodedStrings(root);
|
|
|
|
expect(result.totalForbidden).toBe(0);
|
|
expect(result.reviewedCounts["brand-literal"]).toBe(1);
|
|
expect(result.reviewedCounts["jsx-text"]).toBe(1);
|
|
expect(result.totalHistorical).toBe(0);
|
|
});
|
|
|
|
it("reports historical baseline drift separately from reviewed allowlists", async () => {
|
|
const root = await createFixtureRoot();
|
|
await mkdir(join(root, "apps", "web", "app", "routes"), { recursive: true });
|
|
await writeFile(join(root, "apps", "web", "app", "routes", "sample.tsx"), "export function Page() { return <h1>Welcome aboard</h1>; }\n", "utf8");
|
|
|
|
const result = await scanHardcodedStrings(root);
|
|
const drift = getHistoricalBaselineDrift(result, { "brand-literal": 0, "jsx-text": 0, "user-facing-prop-literal": 0, "user-facing-response-literal": 0 });
|
|
|
|
expect(result.totalForbidden).toBe(0);
|
|
expect(result.historicalCounts["jsx-text"]).toBe(1);
|
|
expect(drift).toEqual([{ id: "jsx-text", count: 1, limit: 0, overage: 1 }]);
|
|
});
|
|
|
|
it("reports user-facing response payload and toast literals as ratcheted drift", async () => {
|
|
const root = await createFixtureRoot();
|
|
await mkdir(join(root, "apps", "web", "app", "routes"), { recursive: true });
|
|
await writeFile(
|
|
join(root, "apps", "web", "app", "routes", "sample.tsx"),
|
|
[
|
|
'export async function action() { return Response.json({ error: "Email body is required" }, { status: 400 }); }',
|
|
'export function Page() { toast.success("Copied to clipboard"); return null; }',
|
|
'export async function loader() { throw new Response("Not Found", { status: 404 }); }',
|
|
'export async function api() { return Response.json({ error: "method_not_allowed" }, { status: 405 }); }',
|
|
].join("\n"),
|
|
"utf8"
|
|
);
|
|
|
|
const result = await scanHardcodedStrings(root);
|
|
const drift = getHistoricalBaselineDrift(result, { "brand-literal": 0, "jsx-text": 0, "user-facing-prop-literal": 0, "user-facing-response-literal": 0 });
|
|
|
|
expect(result.totalForbidden).toBe(0);
|
|
expect(result.historicalCounts["user-facing-response-literal"]).toBe(3);
|
|
expect(drift).toEqual([{ id: "user-facing-response-literal", count: 3, limit: 0, overage: 3 }]);
|
|
});
|
|
});
|