diff --git a/src/components/budget/BudgetTable.render.test.tsx b/src/components/budget/BudgetTable.render.test.tsx index f747d00..4650eee 100644 --- a/src/components/budget/BudgetTable.render.test.tsx +++ b/src/components/budget/BudgetTable.render.test.tsx @@ -10,9 +10,7 @@ import { renderToStaticMarkup } from "react-dom/server"; import type { BudgetYearRow } from "../../shared/types"; import type { CollapseAccessors } from "../../utils/collapsibleRows"; -vi.mock("react-i18next", () => ({ - useTranslation: () => ({ t: (k: string) => `t(${k})`, i18n: { language: "fr" } }), -})); +vi.mock("react-i18next", async () => (await import("../../test/i18nMock")).i18nMock()); const harness = vi.hoisted(() => ({ /** Keys of the open groups (`p:`): the grid opens collapsed. */ diff --git a/src/components/budget/BudgetTable.wiring.test.ts b/src/components/budget/BudgetTable.wiring.test.ts index b16be6b..b5a31c2 100644 --- a/src/components/budget/BudgetTable.wiring.test.ts +++ b/src/components/budget/BudgetTable.wiring.test.ts @@ -16,9 +16,7 @@ const ctx = vi.hoisted(() => ({ /** Keys of the open groups (`p:`): the grid opens collapsed. */ expanded: new Set(), })); -vi.mock("react-i18next", () => ({ - useTranslation: () => ({ t: (k: string) => `t(${k})`, i18n: { language: "fr" } }), -})); +vi.mock("react-i18next", async () => (await import("../../test/i18nMock")).i18nMock()); vi.mock("../../hooks/useCollapsibleGroups", async () => { const { collapsibleKeys, isCollapsedFor, visibleRows } = await import("../../utils/collapsibleRows"); return { diff --git a/src/components/dashboard/DashboardEmptyState.render.test.tsx b/src/components/dashboard/DashboardEmptyState.render.test.tsx index 30967e3..4c6adcc 100644 --- a/src/components/dashboard/DashboardEmptyState.render.test.tsx +++ b/src/components/dashboard/DashboardEmptyState.render.test.tsx @@ -7,13 +7,7 @@ import type { ReactElement, ReactNode } from "react"; import { renderToStaticMarkup } from "react-dom/server"; import { MemoryRouter } from "react-router-dom"; -vi.mock("react-i18next", () => ({ - useTranslation: () => ({ - t: (k: string, o?: Record) => - o ? `t(${k}[${Object.entries(o).map(([a, b]) => `${a}=${String(b)}`).join(",")}])` : `t(${k})`, - i18n: { language: "fr" }, - }), -})); +vi.mock("react-i18next", async () => (await import("../../test/i18nMock")).i18nMock()); import DashboardEmptyState from "./DashboardEmptyState"; import OnboardingChecklist from "./OnboardingChecklist"; diff --git a/src/components/layout/AppShell.render.test.tsx b/src/components/layout/AppShell.render.test.tsx index a3c830f..ddb3f4a 100644 --- a/src/components/layout/AppShell.render.test.tsx +++ b/src/components/layout/AppShell.render.test.tsx @@ -13,13 +13,7 @@ import { MemoryRouter, Route, Routes } from "react-router-dom"; // plugins) and useEntitlement (→ LicenseContext): stub both. vi.mock("../../services/db", () => ({ getDb: vi.fn(), withTransaction: vi.fn() })); vi.mock("../../contexts/LicenseContext", () => ({ useLicenseContext: vi.fn() })); -vi.mock("react-i18next", () => ({ - useTranslation: () => ({ - t: (k: string, o?: Record) => - o ? `t(${k}[${Object.entries(o).map(([a, b]) => `${a}=${String(b)}`).join(",")}])` : `t(${k})`, - i18n: { language: "fr" }, - }), -})); +vi.mock("react-i18next", async () => (await import("../../test/i18nMock")).i18nMock()); vi.mock("./Sidebar", () => ({ default: () => })); let onboarding: OnboardingValue; diff --git a/src/components/onboarding/OnboardingBanner.render.test.tsx b/src/components/onboarding/OnboardingBanner.render.test.tsx index fcdda0e..b9f0b98 100644 --- a/src/components/onboarding/OnboardingBanner.render.test.tsx +++ b/src/components/onboarding/OnboardingBanner.render.test.tsx @@ -7,15 +7,9 @@ import { isValidElement, type ReactElement, type ReactNode } from "react"; import { renderToStaticMarkup } from "react-dom/server"; import { Link, MemoryRouter } from "react-router-dom"; -// The fake `t` marks what it translates, `t(key[options])`: a string that +// The shared fake `t` (src/test/i18nMock.ts) marks what it translates, `t(key[options])`: a string that // skipped `t` would show unmarked. -vi.mock("react-i18next", () => ({ - useTranslation: () => ({ - t: (k: string, o?: Record) => - o ? `t(${k}[${Object.entries(o).map(([a, b]) => `${a}=${String(b)}`).join(",")}])` : `t(${k})`, - i18n: { language: "fr" }, - }), -})); +vi.mock("react-i18next", async () => (await import("../../test/i18nMock")).i18nMock()); import OnboardingBanner, { GETTING_STARTED_GUIDE, diff --git a/src/components/onboarding/StepCard.render.test.tsx b/src/components/onboarding/StepCard.render.test.tsx index 180ae88..d93d4ec 100644 --- a/src/components/onboarding/StepCard.render.test.tsx +++ b/src/components/onboarding/StepCard.render.test.tsx @@ -7,15 +7,9 @@ import { createRef, isValidElement, type ReactElement, type ReactNode } from "re import { renderToStaticMarkup } from "react-dom/server"; import { Link, MemoryRouter } from "react-router-dom"; -// The fake `t` marks what it translates, `t(key[options])`: a string that +// The shared fake `t` (src/test/i18nMock.ts) marks what it translates, `t(key[options])`: a string that // skipped `t` would show unmarked. -vi.mock("react-i18next", () => ({ - useTranslation: () => ({ - t: (k: string, o?: Record) => - o ? `t(${k}[${Object.entries(o).map(([a, b]) => `${a}=${String(b)}`).join(",")}])` : `t(${k})`, - i18n: { language: "fr" }, - }), -})); +vi.mock("react-i18next", async () => (await import("../../test/i18nMock")).i18nMock()); import StepCard, { type StepCardProps } from "./StepCard"; import { GETTING_STARTED_GUIDE } from "./OnboardingBanner"; diff --git a/src/components/settings/LicenseCard.render.test.tsx b/src/components/settings/LicenseCard.render.test.tsx index 3edc275..6b37a5c 100644 --- a/src/components/settings/LicenseCard.render.test.tsx +++ b/src/components/settings/LicenseCard.render.test.tsx @@ -10,16 +10,10 @@ vi.mock("@tauri-apps/api/core", () => ({ invoke: vi.fn() })); vi.mock("@tauri-apps/plugin-opener", () => ({ openUrl: vi.fn(() => Promise.resolve()) })); let language = "fr"; -// The fake `t` marks what it translates, `t(key[options])`: the assertions written in that form +// The shared fake `t` (src/test/i18nMock.ts) marks what it translates, `t(key[options])`: the assertions written in that form // fail on a string that skipped `t` (#387 review). Substring assertions on a bare key still read // the key inside the mark. -vi.mock("react-i18next", () => ({ - useTranslation: () => ({ - t: (k: string, o?: Record) => - o ? `t(${k}[${Object.entries(o).map(([a, b]) => `${a}=${String(b)}`).join(",")}])` : `t(${k})`, - i18n: { language }, - }), -})); +vi.mock("react-i18next", async () => (await import("../../test/i18nMock")).i18nMock({ language: () => language })); let context: Record; vi.mock("../../contexts/LicenseContext", async (importOriginal) => ({ diff --git a/src/pages/BudgetPage.render.test.tsx b/src/pages/BudgetPage.render.test.tsx index 0d6d7df..82b3af7 100644 --- a/src/pages/BudgetPage.render.test.tsx +++ b/src/pages/BudgetPage.render.test.tsx @@ -11,13 +11,7 @@ import { MemoryRouter } from "react-router-dom"; // The budget, collapse and preference services pull the database: stub the connection. vi.mock("../services/db", () => ({ getDb: vi.fn(), withTransaction: vi.fn() })); -vi.mock("react-i18next", () => ({ - useTranslation: () => ({ - t: (k: string, o?: Record) => - o ? `t(${k}[${Object.entries(o).map(([a, b]) => `${a}=${String(b)}`).join(",")}])` : `t(${k})`, - i18n: { language: "fr" }, - }), -})); +vi.mock("react-i18next", async () => (await import("../test/i18nMock")).i18nMock()); vi.mock("../components/shared/PageHelp", () => ({ PageHelp: () => null })); const harness = vi.hoisted(() => ({ budget: null as unknown, onboarding: null as unknown })); diff --git a/src/pages/BudgetPage.wiring.test.ts b/src/pages/BudgetPage.wiring.test.ts index 9791db7..c2d85c5 100644 --- a/src/pages/BudgetPage.wiring.test.ts +++ b/src/pages/BudgetPage.wiring.test.ts @@ -19,9 +19,7 @@ vi.mock("react-router-dom", async (importOriginal) => ({ ...(await importOriginal()), useLocation: () => ({ pathname: ctx.pathname }), })); -vi.mock("react-i18next", () => ({ - useTranslation: () => ({ t: (k: string) => `t(${k})`, i18n: { language: "fr" } }), -})); +vi.mock("react-i18next", async () => (await import("../test/i18nMock")).i18nMock()); // The page's modules pull the database: stub the connection. vi.mock("../services/db", () => ({ getDb: vi.fn(), withTransaction: vi.fn() })); vi.mock("../contexts/OnboardingContext", () => ({ useOnboarding: () => ctx.onboarding })); diff --git a/src/pages/ImportPage.render.test.tsx b/src/pages/ImportPage.render.test.tsx index 13ddbaa..a511b16 100644 --- a/src/pages/ImportPage.render.test.tsx +++ b/src/pages/ImportPage.render.test.tsx @@ -12,13 +12,7 @@ import { MemoryRouter } from "react-router-dom"; // Importing the real wizard module (for its initial state) pulls the import // services, then the database: stub the connection. vi.mock("../services/db", () => ({ getDb: vi.fn(), withTransaction: vi.fn() })); -vi.mock("react-i18next", () => ({ - useTranslation: () => ({ - t: (k: string, o?: Record) => - o ? `t(${k}[${Object.entries(o).map(([a, b]) => `${a}=${String(b)}`).join(",")}])` : `t(${k})`, - i18n: { language: "fr" }, - }), -})); +vi.mock("react-i18next", async () => (await import("../test/i18nMock")).i18nMock()); const harness = vi.hoisted(() => ({ wizard: null as unknown, onboarding: null as unknown })); diff --git a/src/pages/TransactionsPage.render.test.tsx b/src/pages/TransactionsPage.render.test.tsx index 04ba739..678e360 100644 --- a/src/pages/TransactionsPage.render.test.tsx +++ b/src/pages/TransactionsPage.render.test.tsx @@ -12,13 +12,7 @@ import type { TransactionFilters, TransactionRow } from "../shared/types"; // The page's services pull the database: stub the connection. vi.mock("../services/db", () => ({ getDb: vi.fn(), withTransaction: vi.fn() })); -vi.mock("react-i18next", () => ({ - useTranslation: () => ({ - t: (k: string, o?: Record) => - o ? `t(${k}[${Object.entries(o).map(([a, b]) => `${a}=${String(b)}`).join(",")}])` : `t(${k})`, - i18n: { language: "fr" }, - }), -})); +vi.mock("react-i18next", async () => (await import("../test/i18nMock")).i18nMock()); vi.mock("../components/shared/PageHelp", () => ({ PageHelp: () => null })); const harness = vi.hoisted(() => ({ diff --git a/src/pages/settings/UsersSettingsPage.test.tsx b/src/pages/settings/UsersSettingsPage.test.tsx index 953b3a0..ab4db00 100644 --- a/src/pages/settings/UsersSettingsPage.test.tsx +++ b/src/pages/settings/UsersSettingsPage.test.tsx @@ -11,9 +11,7 @@ import { isValidElement, type ReactElement, type ReactNode } from "react"; vi.mock("react", async (orig) => (await import("../../test/hookHarness")).fakeReact(await orig())); const ctx = vi.hoisted(() => ({ onboarding: null as unknown })); -vi.mock("react-i18next", () => ({ - useTranslation: () => ({ t: (k: string) => `t(${k})`, i18n: { language: "fr" } }), -})); +vi.mock("react-i18next", async () => (await import("../../test/i18nMock")).i18nMock()); vi.mock("../../contexts/OnboardingContext", () => ({ useOnboarding: () => ctx.onboarding, }));