Files
suaveplan/packages/ui/react/react-ui/scripts/check-no-hardcoded-strings.test.ts
2026-05-27 23:47:53 +01:00

152 lines
4.6 KiB
TypeScript

import { mkdtemp, writeFile } from "node:fs/promises";
import { tmpdir } from "node:os";
import { join } from "node:path";
import { describe, expect, it } from "@suaveplan/testing/runner";
import { checkRoots } from "../../../../../scripts/lint/check-no-hardcoded-strings.js";
async function makeTempFile(name: string, body: string): Promise<string> {
const dir = await mkdtemp(join(tmpdir(), "no-hardcoded-strings-"));
const file = join(dir, name);
await writeFile(file, body, "utf8");
return dir;
}
describe("check-no-hardcoded-strings", () => {
it(
"passes a t-wrapped JSX text node",
{ timeout: 10000 },
async () => {
const root = await makeTempFile(
"ok.tsx",
`import { useTranslation } from '@suaveplan/react-i18n';
export function X() {
const { t } = useTranslation();
return <button type="button">{t('react-ui.themeToggle.label')}</button>;
}
`
);
const diagnostics = await checkRoots([{ root }]);
expect(diagnostics).toEqual([]);
}
);
it(
"fails a bare JSX text node",
{ timeout: 10000 },
async () => {
const root = await makeTempFile(
"bad.tsx",
`export function X() {
return <button type="button">Close</button>;
}
`
);
const diagnostics = await checkRoots([{ root }]);
expect(diagnostics.length).toBeGreaterThanOrEqual(1);
expect(diagnostics[0]?.rule).toBe("jsx-text");
expect(diagnostics[0]?.snippet).toBe("Close");
}
);
it(
"fails a string literal aria-label",
{ timeout: 10000 },
async () => {
const root = await makeTempFile(
"aria.tsx",
`export function X() {
return <button type="button" aria-label="Close" />;
}
`
);
const diagnostics = await checkRoots([{ root }]);
expect(diagnostics.length).toBeGreaterThanOrEqual(1);
expect(diagnostics[0]?.rule).toBe("jsx-attr-aria-label");
}
);
it(
"fails an object-literal label string",
{ timeout: 10000 },
async () => {
const root = await makeTempFile(
"obj.ts",
`export const fields = { email: { label: 'Email' } };
`
);
const diagnostics = await checkRoots([{ root }]);
expect(diagnostics.length).toBeGreaterThanOrEqual(1);
expect(diagnostics[0]?.rule).toBe("object-label");
}
);
it(
"fails a JSX expression string-literal child",
{ timeout: 10000 },
async () => {
const root = await makeTempFile(
"literal.tsx",
`export function X() {
return <span>{'hardcoded'}</span>;
}
`
);
const diagnostics = await checkRoots([{ root }]);
expect(diagnostics.length).toBeGreaterThanOrEqual(1);
}
);
it(
"fails a string-literal placeholder attribute",
{ timeout: 10000 },
async () => {
const root = await makeTempFile(
"placeholder.tsx",
`export function X() {
return <input type="text" placeholder="Email address" />;
}
`
);
const diagnostics = await checkRoots([{ root }]);
expect(diagnostics.length).toBeGreaterThanOrEqual(1);
expect(diagnostics[0]?.rule).toBe("jsx-attr-placeholder");
}
);
it(
"skips test/spec/stories files via the skip suffix list",
{ timeout: 10000 },
async () => {
const root = await makeTempFile(
"thing.test.tsx",
`export function X() {
return <button type="button">hardcoded but exempt</button>;
}
`
);
const diagnostics = await checkRoots([{ root }]);
expect(diagnostics).toEqual([]);
}
);
it(
"skips spike directories",
{ timeout: 10000 },
async () => {
const dir = await mkdtemp(join(tmpdir(), "spike-skip-"));
const spikeDir = join(dir, "spike");
const fs = await import("node:fs/promises");
await fs.mkdir(spikeDir, { recursive: true });
await fs.writeFile(
join(spikeDir, "x.tsx"),
`export function X() {
return <button type="button">spike literal</button>;
}
`
);
const diagnostics = await checkRoots([{ root: dir }]);
expect(diagnostics).toEqual([]);
}
);
});