alepha
Version:
Easy-to-use modern TypeScript framework for building many kind of applications.
177 lines (157 loc) • 5.47 kB
text/typescript
import { Alepha } from "alepha";
import { FileSystemProvider, MemoryFileSystemProvider } from "alepha/system";
import { beforeEach, describe, expect, it } from "vitest";
import { I18nCheckService } from "../services/I18nCheckService.ts";
describe("I18nCheckService", () => {
const ROOT = "/proj";
const createEnv = () => {
const alepha = Alepha.create().with({
provide: FileSystemProvider,
use: MemoryFileSystemProvider,
});
const service = alepha.inject(I18nCheckService);
const fs = alepha.inject(MemoryFileSystemProvider);
return { service, fs };
};
const dictionary = (keys: Record<string, string>) => {
const body = Object.entries(keys)
.map(([k, v]) => ` "${k}": "${v}",`)
.join("\n");
return `import { $dictionary } from "alepha/react/i18n";
export class I18n {
en = $dictionary({
lazy: async () => ({
default: {
${body}
},
}),
});
}
`;
};
let env: ReturnType<typeof createEnv>;
beforeEach(() => {
env = createEnv();
});
it("reports keys with no quoted reference as unused", async () => {
await env.fs.mkdir(`${ROOT}/src/web`, { recursive: true });
await env.fs.writeFile(
`${ROOT}/src/web/I18n.ts`,
dictionary({ "home.title": "Home", "home.unused": "Nothing" }),
);
await env.fs.writeFile(
`${ROOT}/src/web/Home.tsx`,
`export const Home = () => tr("home.title");`,
);
const result = await env.service.check({
root: ROOT,
scan: ["src"],
dynamicPrefixes: [],
exclude: [],
});
expect(result.totalKeys).toBe(2);
expect(result.unused).toEqual(["home.unused"]);
expect(result.dictionaryFiles).toHaveLength(1);
});
it("exempts keys matching a dynamic prefix", async () => {
await env.fs.mkdir(`${ROOT}/src`, { recursive: true });
await env.fs.writeFile(
`${ROOT}/src/I18n.ts`,
dictionary({
"archive.type.directory": "Folder",
"archive.type.folio": "Folio",
"home.title": "Home",
}),
);
await env.fs.writeFile(
`${ROOT}/src/App.tsx`,
"tr(`archive.type.${k}`); tr('home.title');",
);
const result = await env.service.check({
root: ROOT,
scan: ["src"],
dynamicPrefixes: ["archive.type."],
exclude: [],
});
expect(result.unused).toEqual([]);
expect(result.exemptKeys).toBe(2);
});
it("skips excluded paths and built-in excludes", async () => {
await env.fs.mkdir(`${ROOT}/src/__tests__`, { recursive: true });
await env.fs.writeFile(
`${ROOT}/src/I18n.ts`,
dictionary({ "home.title": "Home" }),
);
// Reference lives only in an excluded test file — should not count.
await env.fs.writeFile(
`${ROOT}/src/__tests__/Home.spec.ts`,
`tr("home.title");`,
);
const result = await env.service.check({
root: ROOT,
scan: ["src"],
dynamicPrefixes: [],
exclude: [],
});
expect(result.unused).toEqual(["home.title"]);
});
it("extracts keys from lazily-imported per-language files", async () => {
await env.fs.mkdir(`${ROOT}/src/web/i18n`, { recursive: true });
// Marker file declares `$dictionary` but the keys live in split,
// markerless per-language files referenced via `lazy: () => import(...)`.
// A sibling `$page` lazy import must NOT be mistaken for a key file.
await env.fs.writeFile(
`${ROOT}/src/web/Router.ts`,
`import { $dictionary } from "alepha/react/i18n";
export class Router {
fr = $dictionary({ lazy: () => import("./i18n/fr.ts"), lang: "fr" });
en = $dictionary({ lazy: () => import("./i18n/en.ts"), lang: "en" });
page = $page({ lazy: () => import("./Home.tsx") });
}
`,
);
await env.fs.writeFile(
`${ROOT}/src/web/i18n/fr.ts`,
`export default { "home.title": "Accueil", "home.unused": "Rien" };`,
);
await env.fs.writeFile(
`${ROOT}/src/web/i18n/en.ts`,
`export default { "home.title": "Home", "home.unused": "Nothing" };`,
);
// The only reference — `home.unused` is dead in both languages. The
// `"home.title": "…"` lines in the sibling language file must not count
// as references (key files are excluded from the usage corpus).
await env.fs.writeFile(
`${ROOT}/src/web/Home.tsx`,
`export const Home = () => tr("home.title");`,
);
const result = await env.service.check({
root: ROOT,
scan: ["src"],
dynamicPrefixes: [],
exclude: [],
});
expect(result.totalKeys).toBe(2);
expect(result.unused).toEqual(["home.unused"]);
// The resolved language file is treated as a dictionary; the sibling
// `$page` lazy import is not (it contributed no keys and stays in the
// usage corpus). `en.ts` is also a dictionary file but adds no *new*
// keys, so only the first contributor is listed.
expect(result.dictionaryFiles).toContain(`${ROOT}/src/web/i18n/fr.ts`);
expect(result.dictionaryFiles).not.toContain(`${ROOT}/src/web/Home.tsx`);
});
it("returns totalKeys=0 when no dictionary is found", async () => {
await env.fs.mkdir(`${ROOT}/src`, { recursive: true });
await env.fs.writeFile(
`${ROOT}/src/App.tsx`,
`export const App = () => "hi";`,
);
const result = await env.service.check({
root: ROOT,
scan: ["src"],
dynamicPrefixes: [],
exclude: [],
});
expect(result.totalKeys).toBe(0);
});
});