src/test/i18nMock.ts holds the one fake t of the suite: markedT renders
t(key) or t(key[a=1,b=2]), and i18nMock({ language?, fixture? }) builds
the react-i18next module for vi.mock. The local echo of
useLicenseMachines.test.ts is replaced by markedT.
Refs #389
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
58 lines
2.5 KiB
TypeScript
58 lines
2.5 KiB
TypeScript
// i18nMock — the one fake `t` of the test suite (#389).
|
|
//
|
|
// A fake `t` that returns the key as is hides a forgotten call to `t`: a
|
|
// component that displays `license.removeKey.confirmValid` instead of the
|
|
// translated text passes every test (#387 review, S1). This fake MARKS what it
|
|
// translates, `t(key)` or `t(key[a=1,b=2])`, so an assertion written in that
|
|
// form fails on a string that skipped `t`. A substring assertion on a bare key
|
|
// still reads the key inside the mark.
|
|
//
|
|
// USE — one pattern, at the head of the test module, before the imports of
|
|
// the code under test (the factory of `vi.mock` is hoisted, hence the dynamic
|
|
// import inside it):
|
|
//
|
|
// vi.mock("react-i18next", async () => (await import("../test/i18nMock")).i18nMock());
|
|
//
|
|
// Options of the factory:
|
|
// - `language`: the `i18n.language` the component reads, "fr" by default. A
|
|
// function is read at each access, for a test that changes the language
|
|
// between two renders: `i18nMock({ language: () => language })` with a
|
|
// `let language` of the test module.
|
|
// - `fixture`: values `t` returns for some keys, as i18next returns what the
|
|
// locale declares under them (an object with `returnObjects`, for instance).
|
|
// Every other key goes through `markedT`.
|
|
//
|
|
// A test that calls a function taking `t` as a parameter passes `markedT`
|
|
// itself. `src/__integration__/i18n-mock-guard.test.ts` refuses any other
|
|
// fake `t` in a test file.
|
|
|
|
/** The marked translation: `t(key)`, or `t(key[a=1,b=2])` with options. */
|
|
export function markedT(key: string, options?: Record<string, unknown>): string {
|
|
const params = options
|
|
? `[${Object.entries(options)
|
|
.map(([name, value]) => `${name}=${String(value)}`)
|
|
.join(",")}]`
|
|
: "";
|
|
return `t(${key}${params})`;
|
|
}
|
|
|
|
export interface I18nMockOptions {
|
|
/** `i18n.language`, "fr" by default; a function is read at each access. */
|
|
language?: string | (() => string);
|
|
/** Values returned as is for these keys, instead of the mark. */
|
|
fixture?: Record<string, unknown>;
|
|
}
|
|
|
|
/** The module `react-i18next` for `vi.mock`: `useTranslation` with the marked `t`. */
|
|
export function i18nMock({ language = "fr", fixture }: I18nMockOptions = {}) {
|
|
const t = (key: string, options?: Record<string, unknown>): unknown =>
|
|
fixture && Object.prototype.hasOwnProperty.call(fixture, key)
|
|
? fixture[key]
|
|
: markedT(key, options);
|
|
const i18n = {
|
|
get language(): string {
|
|
return typeof language === "function" ? language() : language;
|
|
},
|
|
};
|
|
return { useTranslation: () => ({ t, i18n }) };
|
|
}
|