diff --git a/src/__tests__/helpers/prime-locales.ts b/src/__tests__/helpers/prime-locales.ts new file mode 100644 index 000000000..669d9196f --- /dev/null +++ b/src/__tests__/helpers/prime-locales.ts @@ -0,0 +1,25 @@ +/** + * Prime client-side message catalogs beyond EN for one test file. + * + * `vitest.setup.ts` seeds only the EN catalog, because every catalog is + * several hundred kilobytes and the setup file runs once per test file. A + * test that renders `I18nProvider` in another locale — usually through the + * synchronous `renderToStaticMarkup`, which cannot wait for the dynamic + * import a locale switch would trigger — calls this at module scope: + * + * primeLocales("de"); + * primeLocales(...locales); // every locale the app ships + * + * It registers a file-level `beforeAll` that loads each catalog through the + * production loader, so the cache holds exactly what a locale switch in the + * app would put there. + */ +import { beforeAll } from "vitest"; +import type { Locale } from "@/lib/i18n/config"; +import { loadMessages } from "@/lib/i18n/load-locale"; + +export function primeLocales(...wanted: readonly Locale[]): void { + beforeAll(async () => { + await Promise.all(wanted.map((locale) => loadMessages(locale))); + }); +} diff --git a/src/app/achievements/__tests__/page.test.tsx b/src/app/achievements/__tests__/page.test.tsx index 5f11c7a5b..8cdec5cf2 100644 --- a/src/app/achievements/__tests__/page.test.tsx +++ b/src/app/achievements/__tests__/page.test.tsx @@ -166,6 +166,9 @@ vi.mock("@/lib/queries/use-achievements-query", () => ({ })); import AchievementsPage, { groupByCategory } from "../page"; +import { primeLocales } from "@/__tests__/helpers/prime-locales"; + +primeLocales("de"); function render(locale: "en" | "de" = "en") { return renderToStaticMarkup( diff --git a/src/components/admin/__tests__/backup-restore-status.test.tsx b/src/components/admin/__tests__/backup-restore-status.test.tsx index 94b604c10..d29ae2964 100644 --- a/src/components/admin/__tests__/backup-restore-status.test.tsx +++ b/src/components/admin/__tests__/backup-restore-status.test.tsx @@ -27,6 +27,9 @@ import { visibleRestoreJobs, } from "@/components/admin/backup-restore-status"; import type { BackupRestoreJobView } from "@/lib/jobs/backup-restore"; +import { primeLocales } from "@/__tests__/helpers/prime-locales"; + +primeLocales("de"); vi.mock("next/navigation", () => ({ useRouter: () => ({ push: vi.fn(), replace: vi.fn() }), diff --git a/src/components/admin/__tests__/backups-restore-refusal-notice.test.tsx b/src/components/admin/__tests__/backups-restore-refusal-notice.test.tsx index 0553d6859..f85d99c02 100644 --- a/src/components/admin/__tests__/backups-restore-refusal-notice.test.tsx +++ b/src/components/admin/__tests__/backups-restore-refusal-notice.test.tsx @@ -31,6 +31,9 @@ import { missingSectionsOf, } from "@/components/admin/backups-section"; import type { MissingBackupSection } from "@/lib/export/restore-skips"; +import { primeLocales } from "@/__tests__/helpers/prime-locales"; + +primeLocales("de"); vi.mock("next/navigation", () => ({ useRouter: () => ({ push: vi.fn(), replace: vi.fn() }), diff --git a/src/components/admin/__tests__/backups-restore-skip-report.test.tsx b/src/components/admin/__tests__/backups-restore-skip-report.test.tsx index 5a23df5af..e01f9353c 100644 --- a/src/components/admin/__tests__/backups-restore-skip-report.test.tsx +++ b/src/components/admin/__tests__/backups-restore-skip-report.test.tsx @@ -24,6 +24,9 @@ import { renderToStaticMarkup } from "react-dom/server"; import { I18nProvider } from "@/lib/i18n/context"; import { RestoreSkipReport } from "@/components/admin/backups-section"; import type { RestoreSkipSummary } from "@/lib/export/restore-skips"; +import { primeLocales } from "@/__tests__/helpers/prime-locales"; + +primeLocales("de"); vi.mock("next/navigation", () => ({ useRouter: () => ({ push: vi.fn(), replace: vi.fn() }), diff --git a/src/components/admin/__tests__/backups-section-empty.test.tsx b/src/components/admin/__tests__/backups-section-empty.test.tsx index 85e81600d..14211f4bc 100644 --- a/src/components/admin/__tests__/backups-section-empty.test.tsx +++ b/src/components/admin/__tests__/backups-section-empty.test.tsx @@ -83,6 +83,9 @@ vi.mock("@/hooks/use-auth", () => ({ import { I18nProvider } from "@/lib/i18n/context"; import { BackupsSection } from "../backups-section"; +import { primeLocales } from "@/__tests__/helpers/prime-locales"; + +primeLocales("de"); function render(locale: "en" | "de" = "en") { mocks.queryData = { diff --git a/src/components/admin/__tests__/invite-tokens-section.test.tsx b/src/components/admin/__tests__/invite-tokens-section.test.tsx index 3b76578ed..2f1b59f4e 100644 --- a/src/components/admin/__tests__/invite-tokens-section.test.tsx +++ b/src/components/admin/__tests__/invite-tokens-section.test.tsx @@ -21,6 +21,9 @@ import { clampMaxUses, } from "@/components/admin/invite-tokens-section"; import { queryKeys } from "@/lib/query-keys"; +import { primeLocales } from "@/__tests__/helpers/prime-locales"; + +primeLocales("de"); const FUTURE = new Date(Date.now() + 7 * 86_400_000).toISOString(); const PAST = new Date(Date.now() - 86_400_000).toISOString(); diff --git a/src/components/admin/__tests__/key-backup-card.test.tsx b/src/components/admin/__tests__/key-backup-card.test.tsx index fc5163141..0ddc67a44 100644 --- a/src/components/admin/__tests__/key-backup-card.test.tsx +++ b/src/components/admin/__tests__/key-backup-card.test.tsx @@ -19,6 +19,9 @@ import { queryKeys } from "@/lib/query-keys"; import { KeyBackupCard } from "@/components/admin/key-backup-card"; import { KeyBackupBanner } from "@/components/dashboard/key-backup-banner"; import type { KeyBackupStatus } from "@/components/admin/use-key-backup-status"; +import { primeLocales } from "@/__tests__/helpers/prime-locales"; + +primeLocales("de"); vi.mock("next/navigation", () => ({ useRouter: () => ({ push: vi.fn(), replace: vi.fn() }), diff --git a/src/components/admin/__tests__/pulse-day-mean-card.test.tsx b/src/components/admin/__tests__/pulse-day-mean-card.test.tsx index 317901ea6..d63ba45ac 100644 --- a/src/components/admin/__tests__/pulse-day-mean-card.test.tsx +++ b/src/components/admin/__tests__/pulse-day-mean-card.test.tsx @@ -16,6 +16,9 @@ import { PulseDayMeanCard } from "@/components/admin/pulse-day-mean-card"; import { I18nProvider } from "@/lib/i18n/context"; import { queryKeys } from "@/lib/query-keys"; import type { PulseDayMeanStatus } from "@/lib/validations/pulse-day-mean"; +import { primeLocales } from "@/__tests__/helpers/prime-locales"; + +primeLocales("de"); const RESULT = { accounts: 2, diff --git a/src/components/auth/__tests__/insecure-transport-banner.test.tsx b/src/components/auth/__tests__/insecure-transport-banner.test.tsx index 4446df712..61cea7fea 100644 --- a/src/components/auth/__tests__/insecure-transport-banner.test.tsx +++ b/src/components/auth/__tests__/insecure-transport-banner.test.tsx @@ -9,6 +9,9 @@ import { renderToStaticMarkup } from "react-dom/server"; import { I18nProvider } from "@/lib/i18n/context"; import { InsecureTransportBanner } from "@/components/auth/insecure-transport-banner"; import type { TransportVerdict } from "@/lib/auth/client-transport"; +import { primeLocales } from "@/__tests__/helpers/prime-locales"; + +primeLocales("de"); function render(verdict: TransportVerdict, locale: "en" | "de" = "en") { return renderToStaticMarkup( diff --git a/src/components/charts/__tests__/chart-data-table.test.tsx b/src/components/charts/__tests__/chart-data-table.test.tsx index 507010898..8eac4fd3c 100644 --- a/src/components/charts/__tests__/chart-data-table.test.tsx +++ b/src/components/charts/__tests__/chart-data-table.test.tsx @@ -7,6 +7,9 @@ import { type ChartDataTableProps, type ChartTablePoint, } from "../chart-data-table"; +import { primeLocales } from "@/__tests__/helpers/prime-locales"; + +primeLocales("de"); function render(node: React.ReactNode) { return renderToStaticMarkup( diff --git a/src/components/charts/__tests__/chart-overlay-controls.test.tsx b/src/components/charts/__tests__/chart-overlay-controls.test.tsx index 000c158f5..9fa0f9d9c 100644 --- a/src/components/charts/__tests__/chart-overlay-controls.test.tsx +++ b/src/components/charts/__tests__/chart-overlay-controls.test.tsx @@ -2,6 +2,9 @@ import { describe, it, expect, vi } from "vitest"; import { renderToStaticMarkup } from "react-dom/server"; import { I18nProvider } from "@/lib/i18n/context"; import type { AccountAccess } from "@/lib/sharing/account-access-view"; +import { primeLocales } from "@/__tests__/helpers/prime-locales"; + +primeLocales("de"); const OWNER = { accountId: "acct-owner", diff --git a/src/components/charts/__tests__/trend-card-7d-trend.test.tsx b/src/components/charts/__tests__/trend-card-7d-trend.test.tsx index 2622c9a9c..133332514 100644 --- a/src/components/charts/__tests__/trend-card-7d-trend.test.tsx +++ b/src/components/charts/__tests__/trend-card-7d-trend.test.tsx @@ -4,6 +4,9 @@ import { Activity, Smile } from "lucide-react"; import { TrendCard } from "../trend-card"; import { I18nProvider } from "@/lib/i18n/context"; +import { primeLocales } from "@/__tests__/helpers/prime-locales"; + +primeLocales("de"); /** * v1.4.15 Fix 4 — "7-Tage-Schnitt" → "7-Tage-Trend". diff --git a/src/components/charts/__tests__/trend-card-stale-days.test.tsx b/src/components/charts/__tests__/trend-card-stale-days.test.tsx index 72fde7852..2f068072b 100644 --- a/src/components/charts/__tests__/trend-card-stale-days.test.tsx +++ b/src/components/charts/__tests__/trend-card-stale-days.test.tsx @@ -4,6 +4,9 @@ import { Activity } from "lucide-react"; import { TrendCard } from "../trend-card"; import { I18nProvider } from "@/lib/i18n/context"; +import { primeLocales } from "@/__tests__/helpers/prime-locales"; + +primeLocales("de"); /** * v1.4.34 IW-B — pin the bucket-aware stale-hint copy on ``. diff --git a/src/components/charts/__tests__/trend-hint.test.tsx b/src/components/charts/__tests__/trend-hint.test.tsx index 9d677dedf..724037533 100644 --- a/src/components/charts/__tests__/trend-hint.test.tsx +++ b/src/components/charts/__tests__/trend-hint.test.tsx @@ -3,6 +3,9 @@ import { renderToStaticMarkup } from "react-dom/server"; import { TrendHint } from "../trend-hint"; import { I18nProvider } from "@/lib/i18n/context"; +import { primeLocales } from "@/__tests__/helpers/prime-locales"; + +primeLocales("de"); function render(node: React.ReactNode, locale: "en" | "de" = "en") { return renderToStaticMarkup( diff --git a/src/components/cycle/__tests__/custom-symptom-icon-picker.test.tsx b/src/components/cycle/__tests__/custom-symptom-icon-picker.test.tsx index e828dc7cb..7fc103522 100644 --- a/src/components/cycle/__tests__/custom-symptom-icon-picker.test.tsx +++ b/src/components/cycle/__tests__/custom-symptom-icon-picker.test.tsx @@ -35,6 +35,9 @@ vi.mock("../use-cycle", async (importOriginal) => ({ import { CUSTOM_SYMPTOM_ICON_ALLOWLIST } from "@/lib/cycle/custom-symptoms-shared"; import { I18nProvider } from "@/lib/i18n/context"; import { AddSymptomChip } from "../log-day-sheet"; +import { primeLocales } from "@/__tests__/helpers/prime-locales"; + +primeLocales("de"); describe("cycle custom-symptom icon picker", () => { it("has 44 px targets on a phone and translated icon names", () => { diff --git a/src/components/daily/__tests__/today-hero.test.tsx b/src/components/daily/__tests__/today-hero.test.tsx index 7e58ad751..f3cecf42d 100644 --- a/src/components/daily/__tests__/today-hero.test.tsx +++ b/src/components/daily/__tests__/today-hero.test.tsx @@ -9,6 +9,9 @@ import type { PriorityItem } from "@/lib/daily/priority-item"; import type { TodayFact } from "@/lib/daily/today-overview"; import { SCORE_VERSION } from "@/lib/analytics/score/types"; import { DIGEST_AI_AVAILABLE } from "@/__tests__/helpers/ai-capability-fixtures"; +import { primeLocales } from "@/__tests__/helpers/prime-locales"; + +primeLocales("de"); // The hero now wires the coach check-in card's keep / let-go taps through // `useCoachCheckinAction`, so it needs a QueryClient in the tree. diff --git a/src/components/dashboard/__tests__/dashboard-header.test.tsx b/src/components/dashboard/__tests__/dashboard-header.test.tsx index f5f765e3e..51d4e6297 100644 --- a/src/components/dashboard/__tests__/dashboard-header.test.tsx +++ b/src/components/dashboard/__tests__/dashboard-header.test.tsx @@ -48,6 +48,9 @@ import { CAPTURE_KIND_ORDER, visibleCaptureKinds, } from "@/components/layout/capture-picker"; +import { primeLocales } from "@/__tests__/helpers/prime-locales"; + +primeLocales("de"); function renderSSR(node: React.ReactElement, locale: "en" | "de" = "de") { return renderToStaticMarkup( diff --git a/src/components/dashboard/__tests__/medication-intake-quick-add.test.tsx b/src/components/dashboard/__tests__/medication-intake-quick-add.test.tsx index 9b0ca7e92..351fa9641 100644 --- a/src/components/dashboard/__tests__/medication-intake-quick-add.test.tsx +++ b/src/components/dashboard/__tests__/medication-intake-quick-add.test.tsx @@ -61,6 +61,9 @@ import { type MedicationOption, } from "../medication-intake-quick-add"; import { pickDefaultMedicationId } from "@/lib/medications/default-medication"; +import { primeLocales } from "@/__tests__/helpers/prime-locales"; + +primeLocales("de"); function makeMed( id: string, diff --git a/src/components/insights/__tests__/correlation-card.test.tsx b/src/components/insights/__tests__/correlation-card.test.tsx index c20823f30..31010bf90 100644 --- a/src/components/insights/__tests__/correlation-card.test.tsx +++ b/src/components/insights/__tests__/correlation-card.test.tsx @@ -23,6 +23,9 @@ vi.mock("next/dynamic", () => ({ })); import { CorrelationCard } from "../correlation-card"; +import { primeLocales } from "@/__tests__/helpers/prime-locales"; + +primeLocales("de"); function render(node: React.ReactNode, locale: "en" | "de" = "en") { return renderToStaticMarkup( diff --git a/src/components/insights/__tests__/daily-briefing.test.tsx b/src/components/insights/__tests__/daily-briefing.test.tsx index 80e3039fc..91e00e2a2 100644 --- a/src/components/insights/__tests__/daily-briefing.test.tsx +++ b/src/components/insights/__tests__/daily-briefing.test.tsx @@ -3,6 +3,9 @@ import { renderToStaticMarkup } from "react-dom/server"; import { I18nProvider } from "@/lib/i18n/context"; import { DailyBriefing } from "../daily-briefing"; import type { DailyBriefing as DailyBriefingPayload } from "@/lib/ai/schema"; +import { primeLocales } from "@/__tests__/helpers/prime-locales"; + +primeLocales("de"); // The updated-at footer reads the profile timezone via `useAuth`; stub it so the // SSR render does not reach for a QueryClient the static-markup test omits. diff --git a/src/components/insights/__tests__/health-score-card.test.tsx b/src/components/insights/__tests__/health-score-card.test.tsx index 7fc8504d2..f709f6f53 100644 --- a/src/components/insights/__tests__/health-score-card.test.tsx +++ b/src/components/insights/__tests__/health-score-card.test.tsx @@ -19,6 +19,9 @@ import { pillarObservedText, type Translate, } from "../health-score-pillar-detail"; +import { primeLocales } from "@/__tests__/helpers/prime-locales"; + +primeLocales("de"); let mockAuthUser: { unitPreference: string; diff --git a/src/components/insights/__tests__/health-score-pillar-detail-bp-basis.test.tsx b/src/components/insights/__tests__/health-score-pillar-detail-bp-basis.test.tsx index 6c952ca25..13ce5c2ff 100644 --- a/src/components/insights/__tests__/health-score-pillar-detail-bp-basis.test.tsx +++ b/src/components/insights/__tests__/health-score-pillar-detail-bp-basis.test.tsx @@ -23,6 +23,9 @@ import { pillarDetailLines, type Translate, } from "../health-score-pillar-detail"; +import { primeLocales } from "@/__tests__/helpers/prime-locales"; + +primeLocales("de"); function translator(locale: Locale = "en"): Translate { let captured: Translate | null = null; diff --git a/src/components/insights/__tests__/hero-strip.test.tsx b/src/components/insights/__tests__/hero-strip.test.tsx index f45599a3d..41898aca4 100644 --- a/src/components/insights/__tests__/hero-strip.test.tsx +++ b/src/components/insights/__tests__/hero-strip.test.tsx @@ -9,6 +9,9 @@ import type { ScorePillarResult, } from "@/lib/analytics/score/types"; import { SCORE_VERSION } from "@/lib/analytics/score/types"; +import { primeLocales } from "@/__tests__/helpers/prime-locales"; + +primeLocales("de"); // The "generated" freshness line reads the profile timezone via `useAuth`; stub // it so the SSR render does not reach for a QueryClient the test omits. diff --git a/src/components/insights/__tests__/integer-metric-precision-guard.test.tsx b/src/components/insights/__tests__/integer-metric-precision-guard.test.tsx index 357ad8ff8..1e56b9872 100644 --- a/src/components/insights/__tests__/integer-metric-precision-guard.test.tsx +++ b/src/components/insights/__tests__/integer-metric-precision-guard.test.tsx @@ -73,6 +73,9 @@ vi.mock("@tanstack/react-query", async (importOriginal) => { import { CoachReadStrip } from "../derived/coach-read-strip"; import InsightsStepsPage from "@/app/insights/steps/page"; +import { primeLocales } from "@/__tests__/helpers/prime-locales"; + +primeLocales("de"); function render(node: React.ReactNode, locale: "en" | "de" = "de") { const queryClient = new QueryClient({ diff --git a/src/components/insights/__tests__/sleep-stage-stacked-bar.test.tsx b/src/components/insights/__tests__/sleep-stage-stacked-bar.test.tsx index 6ea7b026f..15be7c3aa 100644 --- a/src/components/insights/__tests__/sleep-stage-stacked-bar.test.tsx +++ b/src/components/insights/__tests__/sleep-stage-stacked-bar.test.tsx @@ -12,6 +12,9 @@ import { type SleepStageBreakdown, type SleepStageNight, } from "../sleep-stage-stacked-bar"; +import { primeLocales } from "@/__tests__/helpers/prime-locales"; + +primeLocales("de"); /** * v1.4.25 W4c / W3f — sleep-stage stacked-bar chart unit tests. diff --git a/src/components/insights/__tests__/trend-annotation.test.tsx b/src/components/insights/__tests__/trend-annotation.test.tsx index 649c447d1..affaba2e9 100644 --- a/src/components/insights/__tests__/trend-annotation.test.tsx +++ b/src/components/insights/__tests__/trend-annotation.test.tsx @@ -2,6 +2,9 @@ import { describe, it, expect } from "vitest"; import { renderToStaticMarkup } from "react-dom/server"; import { I18nProvider } from "@/lib/i18n/context"; import { TrendAnnotation } from "../trend-annotation"; +import { primeLocales } from "@/__tests__/helpers/prime-locales"; + +primeLocales("de"); /** * v1.4.20 phase B3 — single-sentence trend annotation under each diff --git a/src/components/insights/__tests__/trend-descriptor-caption.test.tsx b/src/components/insights/__tests__/trend-descriptor-caption.test.tsx index 9b0bdbd09..851236296 100644 --- a/src/components/insights/__tests__/trend-descriptor-caption.test.tsx +++ b/src/components/insights/__tests__/trend-descriptor-caption.test.tsx @@ -23,6 +23,9 @@ vi.mock("@/hooks/use-auth", () => ({ })); import { TrendDescriptorCaption } from "../trend-annotation"; +import { primeLocales } from "@/__tests__/helpers/prime-locales"; + +primeLocales("de"); function seededClient(seed: (client: QueryClient) => void): QueryClient { const client = new QueryClient({ diff --git a/src/components/insights/coach-panel/__tests__/coach-drawer-mobile-trays.test.tsx b/src/components/insights/coach-panel/__tests__/coach-drawer-mobile-trays.test.tsx index da352e3ac..cc264f6ac 100644 --- a/src/components/insights/coach-panel/__tests__/coach-drawer-mobile-trays.test.tsx +++ b/src/components/insights/coach-panel/__tests__/coach-drawer-mobile-trays.test.tsx @@ -3,6 +3,9 @@ import { renderToStaticMarkup } from "react-dom/server"; import { I18nProvider } from "@/lib/i18n/context"; import { CoachDrawerBody } from "../coach-drawer-body"; +import { primeLocales } from "@/__tests__/helpers/prime-locales"; + +primeLocales("de"); /** * v1.4.20 phase B4 — Coach drawer rail-tray strip. diff --git a/src/components/insights/coach-panel/__tests__/coach-hero.test.tsx b/src/components/insights/coach-panel/__tests__/coach-hero.test.tsx index c99ade4de..c6fde0eb0 100644 --- a/src/components/insights/coach-panel/__tests__/coach-hero.test.tsx +++ b/src/components/insights/coach-panel/__tests__/coach-hero.test.tsx @@ -4,6 +4,9 @@ import { renderToStaticMarkup } from "react-dom/server"; import { I18nProvider } from "@/lib/i18n/context"; import { CoachHero } from "../coach-hero"; import { splitProseSegments } from "../streamed-prose"; +import { primeLocales } from "@/__tests__/helpers/prime-locales"; + +primeLocales("de"); function render(node: React.ReactNode, locale: "en" | "de" = "en") { return renderToStaticMarkup( diff --git a/src/components/insights/coach-panel/__tests__/coach-input.test.tsx b/src/components/insights/coach-panel/__tests__/coach-input.test.tsx index d6f200bf4..0c514fbda 100644 --- a/src/components/insights/coach-panel/__tests__/coach-input.test.tsx +++ b/src/components/insights/coach-panel/__tests__/coach-input.test.tsx @@ -339,6 +339,9 @@ describe("", () => { // textarea's auto-grow effect. Pin its math so the textarea never // collapses below a single line or grows past the 6-line ceiling. import { computeAutoGrowHeight } from "../coach-input"; +import { primeLocales } from "@/__tests__/helpers/prime-locales"; + +primeLocales("de"); describe("computeAutoGrowHeight", () => { it("clamps to the single-line minimum when scrollHeight is empty", () => { diff --git a/src/components/insights/coach-panel/__tests__/guided-dialog.test.tsx b/src/components/insights/coach-panel/__tests__/guided-dialog.test.tsx index e79a5c3c1..dc8ce9610 100644 --- a/src/components/insights/coach-panel/__tests__/guided-dialog.test.tsx +++ b/src/components/insights/coach-panel/__tests__/guided-dialog.test.tsx @@ -8,6 +8,9 @@ import { GuidedSummaryBubble, } from "../guided-dialog-bubbles"; import { GuidedQuestionsCard } from "../guided-questions-card"; +import { primeLocales } from "@/__tests__/helpers/prime-locales"; + +primeLocales("de"); /** * v1.16.5 — markup-level coverage for the guided clarifying-questions diff --git a/src/components/insights/coach-panel/__tests__/history-rail.test.tsx b/src/components/insights/coach-panel/__tests__/history-rail.test.tsx index 121e67ccd..91c272f36 100644 --- a/src/components/insights/coach-panel/__tests__/history-rail.test.tsx +++ b/src/components/insights/coach-panel/__tests__/history-rail.test.tsx @@ -6,6 +6,9 @@ import { I18nProvider } from "@/lib/i18n/context"; import { queryKeys } from "@/lib/query-keys"; import { HistoryRail } from "../history-rail"; import type { CoachConversationsPage } from "@/lib/ai/coach/types"; +import { primeLocales } from "@/__tests__/helpers/prime-locales"; + +primeLocales("de"); /** * v1.4.20 phase B2b — history-rail render snapshot. diff --git a/src/components/insights/coach-panel/__tests__/message-thread.test.tsx b/src/components/insights/coach-panel/__tests__/message-thread.test.tsx index 3ddcfb18c..f03b233ad 100644 --- a/src/components/insights/coach-panel/__tests__/message-thread.test.tsx +++ b/src/components/insights/coach-panel/__tests__/message-thread.test.tsx @@ -760,6 +760,9 @@ describe("", () => { // changes can't silently demote the daily-limit / rate-limit copy // back to the generic provider-unavailable fallback. import { errorCodeToI18nKey } from "../message-thread"; +import { primeLocales } from "@/__tests__/helpers/prime-locales"; + +primeLocales("de"); describe("errorCodeToI18nKey", () => { it("maps daily-budget exceedance to the dedicated key", () => { diff --git a/src/components/insights/coach-panel/__tests__/method-line.test.tsx b/src/components/insights/coach-panel/__tests__/method-line.test.tsx index 4bad227d3..1b5885887 100644 --- a/src/components/insights/coach-panel/__tests__/method-line.test.tsx +++ b/src/components/insights/coach-panel/__tests__/method-line.test.tsx @@ -3,6 +3,9 @@ import { renderToStaticMarkup } from "react-dom/server"; import { I18nProvider } from "@/lib/i18n/context"; import { CoachMethodLine } from "../method-line"; +import { primeLocales } from "@/__tests__/helpers/prime-locales"; + +primeLocales("de"); function render(node: React.ReactNode, locale: "en" | "de" = "en") { return renderToStaticMarkup( diff --git a/src/components/insights/coach-panel/__tests__/mobile-rail-tray.test.tsx b/src/components/insights/coach-panel/__tests__/mobile-rail-tray.test.tsx index 3c1d17d45..cda0b2e62 100644 --- a/src/components/insights/coach-panel/__tests__/mobile-rail-tray.test.tsx +++ b/src/components/insights/coach-panel/__tests__/mobile-rail-tray.test.tsx @@ -39,6 +39,9 @@ vi.mock("@/components/ui/sheet", () => ({ })); import { MobileRailTray } from "../mobile-rail-tray"; +import { primeLocales } from "@/__tests__/helpers/prime-locales"; + +primeLocales("de"); function render(node: React.ReactNode, locale: "en" | "de" = "en") { return renderToStaticMarkup( diff --git a/src/components/insights/coach-panel/__tests__/turn-activity.test.tsx b/src/components/insights/coach-panel/__tests__/turn-activity.test.tsx index 6787b7ae7..2c8257529 100644 --- a/src/components/insights/coach-panel/__tests__/turn-activity.test.tsx +++ b/src/components/insights/coach-panel/__tests__/turn-activity.test.tsx @@ -24,6 +24,9 @@ import { stepIcon, stepLabel, } from "../turn-activity"; +import { primeLocales } from "@/__tests__/helpers/prime-locales"; + +primeLocales("de", "pl"); function render(node: React.ReactNode, locale: Locale = "en") { return renderToStaticMarkup( diff --git a/src/components/insights/derived/__tests__/coach-read-strip-same-hours.test.tsx b/src/components/insights/derived/__tests__/coach-read-strip-same-hours.test.tsx index 2999b1d8d..1667411bd 100644 --- a/src/components/insights/derived/__tests__/coach-read-strip-same-hours.test.tsx +++ b/src/components/insights/derived/__tests__/coach-read-strip-same-hours.test.tsx @@ -23,6 +23,9 @@ vi.mock("@/hooks/use-auth", () => ({ vi.mock("@/hooks/use-mounted", () => ({ useMounted: () => true })); import { CoachReadStrip } from "../coach-read-strip"; +import { primeLocales } from "@/__tests__/helpers/prime-locales"; + +primeLocales("de"); const METRIC = "BLOOD_GLUCOSE"; diff --git a/src/components/insights/glucose/__tests__/glucose-clinical-panel.test.tsx b/src/components/insights/glucose/__tests__/glucose-clinical-panel.test.tsx index bc39ce83f..5367c0650 100644 --- a/src/components/insights/glucose/__tests__/glucose-clinical-panel.test.tsx +++ b/src/components/insights/glucose/__tests__/glucose-clinical-panel.test.tsx @@ -6,6 +6,9 @@ import { computeGlucoseClinicalMetrics } from "@/lib/analytics/glucose-metrics"; import type { DataSummary } from "@/lib/analytics/trends"; import { GlucoseTirBar } from "../glucose-tir-bar"; import { GlucoseAdvancedDisclosure } from "../glucose-advanced-disclosure"; +import { primeLocales } from "@/__tests__/helpers/prime-locales"; + +primeLocales("de"); /** Minimal valid `DataSummary` — the panel + disclosure only read `count`. */ function summary(count: number): DataSummary { diff --git a/src/components/insights/nutrients/__tests__/micronutrients-upper-limit.test.tsx b/src/components/insights/nutrients/__tests__/micronutrients-upper-limit.test.tsx index 53e25e3cb..ebde65007 100644 --- a/src/components/insights/nutrients/__tests__/micronutrients-upper-limit.test.tsx +++ b/src/components/insights/nutrients/__tests__/micronutrients-upper-limit.test.tsx @@ -22,6 +22,9 @@ vi.mock("@/hooks/use-auth", () => ({ })); import { MicronutrientsCard } from "../micronutrients-card"; +import { primeLocales } from "@/__tests__/helpers/prime-locales"; + +primeLocales("de"); function render(rows: NutrientOverviewRow[], locale: "en" | "de" = "en") { state.rows = rows; diff --git a/src/components/labs/__tests__/biomarker-aliases.test.tsx b/src/components/labs/__tests__/biomarker-aliases.test.tsx index faf6b5608..9c94bb33e 100644 --- a/src/components/labs/__tests__/biomarker-aliases.test.tsx +++ b/src/components/labs/__tests__/biomarker-aliases.test.tsx @@ -11,6 +11,9 @@ vi.mock("@/hooks/use-record-capabilities", () => ({ import { BiomarkerAliases } from "../biomarker-aliases"; import { BiomarkerMergeSheet } from "../biomarker-merge-sheet"; import type { BiomarkerDto } from "../types"; +import { primeLocales } from "@/__tests__/helpers/prime-locales"; + +primeLocales("de"); vi.mock("@/components/ui/responsive-sheet", () => ({ ResponsiveSheet: ({ diff --git a/src/components/labs/__tests__/lab-converted-note.test.tsx b/src/components/labs/__tests__/lab-converted-note.test.tsx index af70a67d7..5ead9e5b0 100644 --- a/src/components/labs/__tests__/lab-converted-note.test.tsx +++ b/src/components/labs/__tests__/lab-converted-note.test.tsx @@ -4,6 +4,9 @@ import { describe, expect, it } from "vitest"; import { I18nProvider } from "@/lib/i18n/context"; import { LabConvertedNote } from "../lab-converted-note"; +import { primeLocales } from "@/__tests__/helpers/prime-locales"; + +primeLocales("de"); function render( reading: Parameters[0]["reading"], diff --git a/src/components/labs/__tests__/ocr-marker-match.test.tsx b/src/components/labs/__tests__/ocr-marker-match.test.tsx index 6e89bba6b..846a364c9 100644 --- a/src/components/labs/__tests__/ocr-marker-match.test.tsx +++ b/src/components/labs/__tests__/ocr-marker-match.test.tsx @@ -11,6 +11,9 @@ import { } from "../ocr-review-types"; import { toCommitRow } from "../ocr-review-validation"; import { OcrRowEditor } from "../ocr-row-editor"; +import { primeLocales } from "@/__tests__/helpers/prime-locales"; + +primeLocales("de"); const dto: OcrExtractedRowDto = { analyte: "Bilirubin ges.", diff --git a/src/components/labs/__tests__/ocr-review-dialog.test.tsx b/src/components/labs/__tests__/ocr-review-dialog.test.tsx index 7383165f8..62d1ece19 100644 --- a/src/components/labs/__tests__/ocr-review-dialog.test.tsx +++ b/src/components/labs/__tests__/ocr-review-dialog.test.tsx @@ -15,6 +15,9 @@ import { OcrReviewDialog, unitSkippedAnalytes, } from "../ocr-review-dialog"; +import { primeLocales } from "@/__tests__/helpers/prime-locales"; + +primeLocales("de"); const hookState = vi.hoisted(() => ({ extractPending: false, diff --git a/src/components/labs/__tests__/ocr-row-editor.test.tsx b/src/components/labs/__tests__/ocr-row-editor.test.tsx index 971170ffb..4c9544922 100644 --- a/src/components/labs/__tests__/ocr-row-editor.test.tsx +++ b/src/components/labs/__tests__/ocr-row-editor.test.tsx @@ -5,6 +5,9 @@ import { I18nProvider } from "@/lib/i18n/context"; import type { OcrReviewRow } from "../ocr-review-types"; import { OcrRowEditor } from "../ocr-row-editor"; +import { primeLocales } from "@/__tests__/helpers/prime-locales"; + +primeLocales("de"); const baseRow: OcrReviewRow = { key: "LDL-0", diff --git a/src/components/layout/__tests__/keyboard-shortcuts-dialog.test.tsx b/src/components/layout/__tests__/keyboard-shortcuts-dialog.test.tsx index ecd79740a..ee621a503 100644 --- a/src/components/layout/__tests__/keyboard-shortcuts-dialog.test.tsx +++ b/src/components/layout/__tests__/keyboard-shortcuts-dialog.test.tsx @@ -29,6 +29,9 @@ vi.mock("@/components/ui/responsive-sheet", () => ({ })); import { KeyboardShortcutsDialog } from "../keyboard-shortcuts-dialog"; +import { primeLocales } from "@/__tests__/helpers/prime-locales"; + +primeLocales("de", "es", "fr", "it", "pl", "ko", "nl"); const EVERYTHING = { navHrefs: ["/", "/medications", "/labs", "/timeline", "/insights", "/coach"], diff --git a/src/components/measurements/__tests__/duration-read-as.test.tsx b/src/components/measurements/__tests__/duration-read-as.test.tsx index eb7eb2554..76ee4f647 100644 --- a/src/components/measurements/__tests__/duration-read-as.test.tsx +++ b/src/components/measurements/__tests__/duration-read-as.test.tsx @@ -6,6 +6,9 @@ import { renderToStaticMarkup } from "react-dom/server"; import { I18nProvider } from "@/lib/i18n/context"; import { parseDurationEntry } from "@/lib/measurements/parse-duration"; import { DurationReadAs } from "../duration-read-as"; +import { primeLocales } from "@/__tests__/helpers/prime-locales"; + +primeLocales("de", "ko"); /** * The hint under a duration field says how the entry was read, so "7.5", diff --git a/src/components/measurements/__tests__/measurement-list-filter-select.test.tsx b/src/components/measurements/__tests__/measurement-list-filter-select.test.tsx index f7ec021bf..b3fefea4f 100644 --- a/src/components/measurements/__tests__/measurement-list-filter-select.test.tsx +++ b/src/components/measurements/__tests__/measurement-list-filter-select.test.tsx @@ -63,6 +63,9 @@ vi.mock("@/hooks/use-auth", () => ({ import { I18nProvider } from "@/lib/i18n/context"; import { MeasurementList } from "../measurement-list"; +import { primeLocales } from "@/__tests__/helpers/prime-locales"; + +primeLocales("de"); function render(locale: "en" | "de" = "en") { return renderToStaticMarkup( diff --git a/src/components/measurements/__tests__/measurement-list-step-grouping.test.tsx b/src/components/measurements/__tests__/measurement-list-step-grouping.test.tsx index 13ef32e92..5394b2261 100644 --- a/src/components/measurements/__tests__/measurement-list-step-grouping.test.tsx +++ b/src/components/measurements/__tests__/measurement-list-step-grouping.test.tsx @@ -64,6 +64,9 @@ vi.mock("@/hooks/use-auth", () => ({ import { I18nProvider } from "@/lib/i18n/context"; import { MeasurementList } from "../measurement-list"; +import { primeLocales } from "@/__tests__/helpers/prime-locales"; + +primeLocales("de"); function render(locale: "en" | "de" = "en") { return renderToStaticMarkup( diff --git a/src/components/medications/__tests__/add-inventory-dialog.test.tsx b/src/components/medications/__tests__/add-inventory-dialog.test.tsx index d6861fec3..886895218 100644 --- a/src/components/medications/__tests__/add-inventory-dialog.test.tsx +++ b/src/components/medications/__tests__/add-inventory-dialog.test.tsx @@ -16,6 +16,9 @@ import { resolve } from "node:path"; import { renderToStaticMarkup } from "react-dom/server"; import { I18nProvider } from "@/lib/i18n/context"; +import { primeLocales } from "@/__tests__/helpers/prime-locales"; + +primeLocales("de"); // The Radix Dialog / Select portal at runtime, so their bodies never // materialise in static markup. Collapse the primitives to plain diff --git a/src/components/medications/__tests__/injection-site-picker.test.tsx b/src/components/medications/__tests__/injection-site-picker.test.tsx index 5c3b971a7..dce77a99e 100644 --- a/src/components/medications/__tests__/injection-site-picker.test.tsx +++ b/src/components/medications/__tests__/injection-site-picker.test.tsx @@ -7,6 +7,9 @@ import { INJECTION_SITE_KEYS, type InjectionSiteKey, } from "@/lib/medications/injection-sites"; +import { primeLocales } from "@/__tests__/helpers/prime-locales"; + +primeLocales("de"); /** * v1.4.25 W4d-tests — RTL-style coverage for the body-map injection diff --git a/src/components/medications/__tests__/intake-history-list-v2.test.tsx b/src/components/medications/__tests__/intake-history-list-v2.test.tsx index f409b3586..3bf35055e 100644 --- a/src/components/medications/__tests__/intake-history-list-v2.test.tsx +++ b/src/components/medications/__tests__/intake-history-list-v2.test.tsx @@ -5,6 +5,9 @@ import { QueryClient, QueryClientProvider } from "@tanstack/react-query"; import { I18nProvider } from "@/lib/i18n/context"; import { IntakeHistoryListV2 } from "@/components/medications/intake-history-list-v2"; import { queryKeys } from "@/lib/query-keys"; +import { primeLocales } from "@/__tests__/helpers/prime-locales"; + +primeLocales("de"); /** * v1.4.36 W4a — IntakeHistoryListV2 SSR smoke tests. diff --git a/src/components/medications/__tests__/inventory-section-actions.test.tsx b/src/components/medications/__tests__/inventory-section-actions.test.tsx index f691f49c0..c51adb382 100644 --- a/src/components/medications/__tests__/inventory-section-actions.test.tsx +++ b/src/components/medications/__tests__/inventory-section-actions.test.tsx @@ -17,6 +17,9 @@ import { describe, expect, it, vi } from "vitest"; import { renderToStaticMarkup } from "react-dom/server"; import { I18nProvider } from "@/lib/i18n/context"; +import { primeLocales } from "@/__tests__/helpers/prime-locales"; + +primeLocales("de"); const useQueryMock = vi.fn(); diff --git a/src/components/medications/__tests__/inventory-section-dates.test.tsx b/src/components/medications/__tests__/inventory-section-dates.test.tsx index adf6c3dd5..31e8b9b10 100644 --- a/src/components/medications/__tests__/inventory-section-dates.test.tsx +++ b/src/components/medications/__tests__/inventory-section-dates.test.tsx @@ -17,6 +17,9 @@ import { describe, expect, it, vi } from "vitest"; import { renderToStaticMarkup } from "react-dom/server"; import { I18nProvider } from "@/lib/i18n/context"; +import { primeLocales } from "@/__tests__/helpers/prime-locales"; + +primeLocales("de"); // Now-anchored future expiry (the fixed 2027-06-01 was a fuse: once the // calendar passes it, an "expiring later" fixture silently becomes an diff --git a/src/components/medications/__tests__/inventory-section-summary.test.tsx b/src/components/medications/__tests__/inventory-section-summary.test.tsx index 9c9fa416b..906e76665 100644 --- a/src/components/medications/__tests__/inventory-section-summary.test.tsx +++ b/src/components/medications/__tests__/inventory-section-summary.test.tsx @@ -20,6 +20,9 @@ import { renderToStaticMarkup } from "react-dom/server"; import { I18nProvider } from "@/lib/i18n/context"; import { summariseSupply } from "@/lib/medications/inventory/summary"; +import { primeLocales } from "@/__tests__/helpers/prime-locales"; + +primeLocales("de"); const useQueryMock = vi.fn(); diff --git a/src/components/medications/__tests__/medication-card-glp1.test.tsx b/src/components/medications/__tests__/medication-card-glp1.test.tsx index 2ee9567b7..9cddeffc1 100644 --- a/src/components/medications/__tests__/medication-card-glp1.test.tsx +++ b/src/components/medications/__tests__/medication-card-glp1.test.tsx @@ -8,6 +8,9 @@ import { type Glp1Medication, } from "@/components/medications/glp1-medication-card"; import { MedicationCard } from "@/components/medications/medication-card"; +import { primeLocales } from "@/__tests__/helpers/prime-locales"; + +primeLocales("de"); /** * v1.4.25 W4d-tests — RTL-style component coverage for the GLP-1 diff --git a/src/components/medications/__tests__/medication-order-editor.test.tsx b/src/components/medications/__tests__/medication-order-editor.test.tsx index 55e71c5c8..262d0f7fd 100644 --- a/src/components/medications/__tests__/medication-order-editor.test.tsx +++ b/src/components/medications/__tests__/medication-order-editor.test.tsx @@ -20,6 +20,9 @@ import { describe, expect, it, vi } from "vitest"; import { renderToStaticMarkup } from "react-dom/server"; import { I18nProvider } from "@/lib/i18n/context"; +import { primeLocales } from "@/__tests__/helpers/prime-locales"; + +primeLocales("de"); vi.mock("@tanstack/react-query", () => ({ useQueryClient: () => ({}), diff --git a/src/components/medications/__tests__/side-effects-section.test.tsx b/src/components/medications/__tests__/side-effects-section.test.tsx index 7737bd290..f5c43705e 100644 --- a/src/components/medications/__tests__/side-effects-section.test.tsx +++ b/src/components/medications/__tests__/side-effects-section.test.tsx @@ -40,6 +40,9 @@ vi.mock("@/components/ui/query-error-card", async () => { }); import { SideEffectsSection } from "@/components/medications/side-effects-section"; +import { primeLocales } from "@/__tests__/helpers/prime-locales"; + +primeLocales("de"); /** * v1.4.25 W19d — SideEffectsSection SSR smoke tests. diff --git a/src/components/medications/__tests__/take-all-due.test.tsx b/src/components/medications/__tests__/take-all-due.test.tsx index f6aa792e2..643ae8be7 100644 --- a/src/components/medications/__tests__/take-all-due.test.tsx +++ b/src/components/medications/__tests__/take-all-due.test.tsx @@ -14,6 +14,9 @@ import { type DueDerivationMedication, type DueMedication, } from "@/components/medications/take-all-due"; +import { primeLocales } from "@/__tests__/helpers/prime-locales"; + +primeLocales("de"); /** * v1.16.11 (#316) — "take all due at once" coverage. diff --git a/src/components/medications/__tests__/units-per-dose-field.test.tsx b/src/components/medications/__tests__/units-per-dose-field.test.tsx index 9a6bfc953..351ae6457 100644 --- a/src/components/medications/__tests__/units-per-dose-field.test.tsx +++ b/src/components/medications/__tests__/units-per-dose-field.test.tsx @@ -13,6 +13,9 @@ import { renderToStaticMarkup } from "react-dom/server"; import { I18nProvider } from "@/lib/i18n/context"; import { summariseSupply } from "@/lib/medications/inventory/summary"; import { UnitsPerDoseField } from "../units-per-dose-field"; +import { primeLocales } from "@/__tests__/helpers/prime-locales"; + +primeLocales("de"); const useQueryMock = vi.fn(); diff --git a/src/components/medications/scheduling/__tests__/schedule-history-timeline.test.tsx b/src/components/medications/scheduling/__tests__/schedule-history-timeline.test.tsx index f212012ae..5bce9f830 100644 --- a/src/components/medications/scheduling/__tests__/schedule-history-timeline.test.tsx +++ b/src/components/medications/scheduling/__tests__/schedule-history-timeline.test.tsx @@ -28,6 +28,9 @@ import { type ScheduleRevisionRow, } from "@/components/medications/scheduling/schedule-history-timeline"; import { queryKeys } from "@/lib/query-keys"; +import { primeLocales } from "@/__tests__/helpers/prime-locales"; + +primeLocales("de"); const MED_ID = "med-1"; diff --git a/src/components/mood/__tests__/mood-tag-icon-picker.test.tsx b/src/components/mood/__tests__/mood-tag-icon-picker.test.tsx index e16ce4265..982fcca10 100644 --- a/src/components/mood/__tests__/mood-tag-icon-picker.test.tsx +++ b/src/components/mood/__tests__/mood-tag-icon-picker.test.tsx @@ -15,6 +15,9 @@ import { } from "@/lib/mood/icon-catalog"; import { isMoodTagIconName } from "../mood-tag-icons"; import { MoodTagIconPicker, filterIconCatalog } from "../mood-tag-icon-picker"; +import { primeLocales } from "@/__tests__/helpers/prime-locales"; + +primeLocales("de", "es", "fr", "it", "pl"); describe("filterIconCatalog", () => { it("returns the full drawable catalog for an empty query", () => { diff --git a/src/components/mood/__tests__/mood-tag-picker-custom.test.tsx b/src/components/mood/__tests__/mood-tag-picker-custom.test.tsx index 835e99511..4d491b507 100644 --- a/src/components/mood/__tests__/mood-tag-picker-custom.test.tsx +++ b/src/components/mood/__tests__/mood-tag-picker-custom.test.tsx @@ -51,6 +51,9 @@ vi.mock("@/hooks/use-auth", () => ({ import { I18nProvider } from "@/lib/i18n/context"; import { MoodTagPicker } from "../mood-tag-picker"; +import { primeLocales } from "@/__tests__/helpers/prime-locales"; + +primeLocales("de"); const CATALOG = { categories: [ diff --git a/src/components/onboarding/__tests__/baseline-fields-rejection-slots.test.tsx b/src/components/onboarding/__tests__/baseline-fields-rejection-slots.test.tsx index 57aec24f9..43cbc628e 100644 --- a/src/components/onboarding/__tests__/baseline-fields-rejection-slots.test.tsx +++ b/src/components/onboarding/__tests__/baseline-fields-rejection-slots.test.tsx @@ -30,6 +30,9 @@ import { describeBaselineSaveOutcome, putBaselineProfile, } from "../baseline-form-utils"; +import { primeLocales } from "@/__tests__/helpers/prime-locales"; + +primeLocales("de"); /** A `t` backed by the shipped bundle — no stand-in strings. */ function translator(locale: "en" | "de") { diff --git a/src/components/onboarding/__tests__/baseline-form-rejected-fields.test.ts b/src/components/onboarding/__tests__/baseline-form-rejected-fields.test.ts index 8094ddbe0..aecf162e5 100644 --- a/src/components/onboarding/__tests__/baseline-form-rejected-fields.test.ts +++ b/src/components/onboarding/__tests__/baseline-form-rejected-fields.test.ts @@ -19,6 +19,9 @@ import { describeBaselineSaveOutcome, putBaselineProfile, } from "../baseline-form-utils"; +import { primeLocales } from "@/__tests__/helpers/prime-locales"; + +primeLocales("de"); /** A `t` backed by the shipped bundle — no stand-in strings. */ function translator(locale: "en" | "de") { diff --git a/src/components/onboarding/__tests__/tour.test.tsx b/src/components/onboarding/__tests__/tour.test.tsx index 9b7ed282b..6f5347f87 100644 --- a/src/components/onboarding/__tests__/tour.test.tsx +++ b/src/components/onboarding/__tests__/tour.test.tsx @@ -18,6 +18,9 @@ vi.mock("next/navigation", () => ({ import { I18nProvider } from "@/lib/i18n/context"; import { OnboardingTour } from "../tour"; +import { primeLocales } from "@/__tests__/helpers/prime-locales"; + +primeLocales("de"); function renderTour(modules?: Record) { return renderToStaticMarkup( diff --git a/src/components/settings/__tests__/advanced-account-delete.test.tsx b/src/components/settings/__tests__/advanced-account-delete.test.tsx index fa59afecd..6212992d9 100644 --- a/src/components/settings/__tests__/advanced-account-delete.test.tsx +++ b/src/components/settings/__tests__/advanced-account-delete.test.tsx @@ -30,6 +30,9 @@ vi.mock("@tanstack/react-query", () => ({ })); import { AdvancedSection } from "../advanced-section"; +import { primeLocales } from "@/__tests__/helpers/prime-locales"; + +primeLocales("de"); function render(locale: "en" | "de" = "en") { return renderToStaticMarkup( diff --git a/src/components/settings/__tests__/ai-section.test.tsx b/src/components/settings/__tests__/ai-section.test.tsx index c06f2e1ba..bf394bde3 100644 --- a/src/components/settings/__tests__/ai-section.test.tsx +++ b/src/components/settings/__tests__/ai-section.test.tsx @@ -126,6 +126,9 @@ vi.mock("@tanstack/react-query", () => ({ import { I18nProvider } from "@/lib/i18n/context"; import { AiSection } from "../ai-section"; +import { primeLocales } from "@/__tests__/helpers/prime-locales"; + +primeLocales("de"); function render(locale: "en" | "de" = "en") { return renderToStaticMarkup( diff --git a/src/components/settings/__tests__/coach-section.test.tsx b/src/components/settings/__tests__/coach-section.test.tsx index 157b489e1..e2fa7ad86 100644 --- a/src/components/settings/__tests__/coach-section.test.tsx +++ b/src/components/settings/__tests__/coach-section.test.tsx @@ -61,6 +61,9 @@ vi.mock("@/hooks/use-auth", () => ({ import { I18nProvider } from "@/lib/i18n/context"; import { SettingsSectionFrame } from "./section-frame-harness"; import { CoachSection } from "../coach-section"; +import { primeLocales } from "@/__tests__/helpers/prime-locales"; + +primeLocales("de"); function render(locale: "en" | "de" = "en") { // v1.18.6 (W9) — the visible heading lives in the shared frame the route diff --git a/src/components/settings/__tests__/dashboard-layout-section.test.tsx b/src/components/settings/__tests__/dashboard-layout-section.test.tsx index e7e48ff62..56693609b 100644 --- a/src/components/settings/__tests__/dashboard-layout-section.test.tsx +++ b/src/components/settings/__tests__/dashboard-layout-section.test.tsx @@ -85,6 +85,9 @@ import { reorderWidgets, setHeroItemKindEnabled, } from "../dashboard-layout-section"; +import { primeLocales } from "@/__tests__/helpers/prime-locales"; + +primeLocales("de"); function render(node: React.ReactElement, locale: "en" | "de" = "en") { return renderToStaticMarkup( diff --git a/src/components/settings/__tests__/export-section.test.tsx b/src/components/settings/__tests__/export-section.test.tsx index aaf2fb52b..623abf838 100644 --- a/src/components/settings/__tests__/export-section.test.tsx +++ b/src/components/settings/__tests__/export-section.test.tsx @@ -46,6 +46,9 @@ vi.mock("@/hooks/use-auth", () => ({ import { I18nProvider } from "@/lib/i18n/context"; import { SettingsSectionFrame } from "./section-frame-harness"; import { ExportSection } from "../export-section"; +import { primeLocales } from "@/__tests__/helpers/prime-locales"; + +primeLocales("de"); function render(node: React.ReactElement, locale: "en" | "de" = "en") { return renderToStaticMarkup( diff --git a/src/components/settings/__tests__/integration-status-pill.test.tsx b/src/components/settings/__tests__/integration-status-pill.test.tsx index e7d83467e..73444500f 100644 --- a/src/components/settings/__tests__/integration-status-pill.test.tsx +++ b/src/components/settings/__tests__/integration-status-pill.test.tsx @@ -21,6 +21,9 @@ import { renderToStaticMarkup } from "react-dom/server"; import { I18nProvider } from "@/lib/i18n/context"; import { IntegrationStatusPill } from "../integration-status-pill"; +import { primeLocales } from "@/__tests__/helpers/prime-locales"; + +primeLocales("de"); function render(node: React.ReactNode, locale: "en" | "de" = "en") { return renderToStaticMarkup( diff --git a/src/components/settings/__tests__/layout-section.test.tsx b/src/components/settings/__tests__/layout-section.test.tsx index 68264986a..5b4935d64 100644 --- a/src/components/settings/__tests__/layout-section.test.tsx +++ b/src/components/settings/__tests__/layout-section.test.tsx @@ -34,6 +34,9 @@ vi.mock("@/hooks/use-mounted", () => ({ import { I18nProvider } from "@/lib/i18n/context"; import { LayoutSection } from "../layout-section"; +import { primeLocales } from "@/__tests__/helpers/prime-locales"; + +primeLocales("de"); function render(modules?: Record, locale: "en" | "de" = "en") { authRef.modules = modules; diff --git a/src/components/settings/__tests__/medications-section.test.tsx b/src/components/settings/__tests__/medications-section.test.tsx index 8704f98e9..ba5cd17f4 100644 --- a/src/components/settings/__tests__/medications-section.test.tsx +++ b/src/components/settings/__tests__/medications-section.test.tsx @@ -67,6 +67,9 @@ vi.mock("@/hooks/use-auth", () => ({ import { I18nProvider } from "@/lib/i18n/context"; import { SettingsSectionFrame } from "./section-frame-harness"; import { MedicationsSection } from "../medications-section"; +import { primeLocales } from "@/__tests__/helpers/prime-locales"; + +primeLocales("de"); function render(locale: "en" | "de" = "en"): string { // v1.18.6 (W9) — the visible heading (with the historic id) comes from the diff --git a/src/components/settings/__tests__/mood-section.test.tsx b/src/components/settings/__tests__/mood-section.test.tsx index 0ded8ad9d..50daf200f 100644 --- a/src/components/settings/__tests__/mood-section.test.tsx +++ b/src/components/settings/__tests__/mood-section.test.tsx @@ -133,6 +133,9 @@ vi.mock("@/hooks/use-auth", () => ({ import { I18nProvider } from "@/lib/i18n/context"; import { SettingsSectionFrame } from "./section-frame-harness"; import { MoodSection } from "../mood-section"; +import { primeLocales } from "@/__tests__/helpers/prime-locales"; + +primeLocales("de"); function render(locale: "en" | "de" = "en"): string { // v1.18.6 (W9) — the visible heading (with the historic id) comes from the diff --git a/src/components/settings/__tests__/notification-status-card.test.tsx b/src/components/settings/__tests__/notification-status-card.test.tsx index a472b5593..c2e23bd43 100644 --- a/src/components/settings/__tests__/notification-status-card.test.tsx +++ b/src/components/settings/__tests__/notification-status-card.test.tsx @@ -45,6 +45,9 @@ vi.mock("@/hooks/use-mounted", () => ({ import { I18nProvider } from "@/lib/i18n/context"; import { NotificationStatusCard } from "../notification-status-card"; +import { primeLocales } from "@/__tests__/helpers/prime-locales"; + +primeLocales("de"); function render(node: React.ReactElement, locale: "en" | "de" = "en") { return renderToStaticMarkup( diff --git a/src/components/settings/__tests__/score-change-notice.test.tsx b/src/components/settings/__tests__/score-change-notice.test.tsx index 4c50ba780..a326ae2a4 100644 --- a/src/components/settings/__tests__/score-change-notice.test.tsx +++ b/src/components/settings/__tests__/score-change-notice.test.tsx @@ -12,6 +12,9 @@ import { renderToStaticMarkup } from "react-dom/server"; import { I18nProvider } from "@/lib/i18n/context"; import type { Locale } from "@/lib/i18n/config"; import { ScoreChangeNotice } from "../score-change-notice"; +import { primeLocales } from "@/__tests__/helpers/prime-locales"; + +primeLocales("de", "es", "fr", "it", "pl"); function render( props: { diff --git a/src/components/settings/__tests__/score-composition-notice.test.tsx b/src/components/settings/__tests__/score-composition-notice.test.tsx index 178e25a19..206fc612f 100644 --- a/src/components/settings/__tests__/score-composition-notice.test.tsx +++ b/src/components/settings/__tests__/score-composition-notice.test.tsx @@ -15,6 +15,9 @@ import { I18nProvider } from "@/lib/i18n/context"; import type { Locale } from "@/lib/i18n/config"; import type { ScorePillarId } from "@/lib/analytics/score/types"; import { ScoreCompositionNotice } from "../score-composition-notice"; +import { primeLocales } from "@/__tests__/helpers/prime-locales"; + +primeLocales("de", "ko"); function render( props: { left: ScorePillarId[]; joined: ScorePillarId[] }, diff --git a/src/components/settings/__tests__/score-pillar-row.test.tsx b/src/components/settings/__tests__/score-pillar-row.test.tsx index 1f73430cc..40e84c6da 100644 --- a/src/components/settings/__tests__/score-pillar-row.test.tsx +++ b/src/components/settings/__tests__/score-pillar-row.test.tsx @@ -12,6 +12,9 @@ import { I18nProvider } from "@/lib/i18n/context"; import type { Locale } from "@/lib/i18n/config"; import type { ScoreConfigRow } from "@/lib/score-config/rows"; import { ScorePillarRow } from "../score-pillar-row"; +import { primeLocales } from "@/__tests__/helpers/prime-locales"; + +primeLocales("de", "es", "fr", "it", "pl"); function render(row: ScoreConfigRow, locale: Locale = "en"): string { return renderToStaticMarkup( diff --git a/src/components/settings/__tests__/sections.test.tsx b/src/components/settings/__tests__/sections.test.tsx index 84ea150f2..ac9db4cbe 100644 --- a/src/components/settings/__tests__/sections.test.tsx +++ b/src/components/settings/__tests__/sections.test.tsx @@ -127,6 +127,9 @@ import { NotificationsSection } from "../notifications-section"; // `thresholds-section.tsx` so the component name + filename match the // slug like every other settings section. import { ThresholdsSection } from "../thresholds-section"; +import { primeLocales } from "@/__tests__/helpers/prime-locales"; + +primeLocales("de"); const CALLBACK_URLS = { withings: "https://app.example/api/withings/callback", diff --git a/src/components/settings/__tests__/settings-shell.test.tsx b/src/components/settings/__tests__/settings-shell.test.tsx index 82a9db23a..3d858b2b8 100644 --- a/src/components/settings/__tests__/settings-shell.test.tsx +++ b/src/components/settings/__tests__/settings-shell.test.tsx @@ -59,6 +59,9 @@ import { SettingsShell, isSettingsSectionSlug, } from "../settings-shell"; +import { primeLocales } from "@/__tests__/helpers/prime-locales"; + +primeLocales("de"); function renderShell(props: { active?: (typeof SETTINGS_SECTION_SLUGS)[number]; diff --git a/src/components/settings/__tests__/sharing-section.test.tsx b/src/components/settings/__tests__/sharing-section.test.tsx index 7cadc1354..b7ec19038 100644 --- a/src/components/settings/__tests__/sharing-section.test.tsx +++ b/src/components/settings/__tests__/sharing-section.test.tsx @@ -64,6 +64,9 @@ vi.mock("@tanstack/react-query", () => ({ import { I18nProvider } from "@/lib/i18n/context"; import { SharingSection } from "../sharing-section"; +import { primeLocales } from "@/__tests__/helpers/prime-locales"; + +primeLocales("de"); function render(locale: "en" | "de" = "en") { return renderToStaticMarkup( diff --git a/src/components/settings/__tests__/timezone-picker.test.tsx b/src/components/settings/__tests__/timezone-picker.test.tsx index 5a52ca40f..05e4642d5 100644 --- a/src/components/settings/__tests__/timezone-picker.test.tsx +++ b/src/components/settings/__tests__/timezone-picker.test.tsx @@ -3,6 +3,9 @@ import { renderToStaticMarkup } from "react-dom/server"; import { I18nProvider } from "@/lib/i18n/context"; import { TimezonePicker } from "../timezone-picker"; +import { primeLocales } from "@/__tests__/helpers/prime-locales"; + +primeLocales("de"); /** * v1.4.37 — the "Browser-Zeitzone übernehmen" / "Use browser timezone" diff --git a/src/components/settings/integrations/__tests__/integration-card.test.tsx b/src/components/settings/integrations/__tests__/integration-card.test.tsx index 9a1a09fdb..a630eaed4 100644 --- a/src/components/settings/integrations/__tests__/integration-card.test.tsx +++ b/src/components/settings/integrations/__tests__/integration-card.test.tsx @@ -39,6 +39,9 @@ import { } from "../oauth-integration-card"; import type { IntegrationStatusViewModel } from "../shared"; import { healthFixture } from "./health-fixture"; +import { primeLocales } from "@/__tests__/helpers/prime-locales"; + +primeLocales("de"); // "Last data: today" only holds when the fixture's hours-ago stays on the same // local day, so the clock is pinned to midday rather than the wall clock. diff --git a/src/components/targets/__tests__/consistency-strip.test.tsx b/src/components/targets/__tests__/consistency-strip.test.tsx index 18639cc3e..b37778ded 100644 --- a/src/components/targets/__tests__/consistency-strip.test.tsx +++ b/src/components/targets/__tests__/consistency-strip.test.tsx @@ -3,6 +3,9 @@ import { renderToStaticMarkup } from "react-dom/server"; import { I18nProvider } from "@/lib/i18n/context"; import { ConsistencyStrip } from "../consistency-strip"; +import { primeLocales } from "@/__tests__/helpers/prime-locales"; + +primeLocales("de"); /** * v1.4.25 W3e — `` replaces the v1.4.22 per-card diff --git a/src/components/timeline/__tests__/no-raw-code-guard.test.tsx b/src/components/timeline/__tests__/no-raw-code-guard.test.tsx index 2c5e7c4f3..0859365b1 100644 --- a/src/components/timeline/__tests__/no-raw-code-guard.test.tsx +++ b/src/components/timeline/__tests__/no-raw-code-guard.test.tsx @@ -44,6 +44,9 @@ import { TimelineChronicle } from "../timeline-chronicle"; import { layoutTimeline } from "../timeline-geometry"; import { useSeriesValueFormat } from "../use-series-value-format"; import { TODAY, item, wordsIn } from "./timeline-fixture"; +import { primeLocales } from "@/__tests__/helpers/prime-locales"; + +primeLocales("de"); type Locale = "de" | "en"; diff --git a/src/components/timeline/__tests__/range-fields.test.tsx b/src/components/timeline/__tests__/range-fields.test.tsx index 7c12cdbd7..82b4b6197 100644 --- a/src/components/timeline/__tests__/range-fields.test.tsx +++ b/src/components/timeline/__tests__/range-fields.test.tsx @@ -8,6 +8,9 @@ import { renderToStaticMarkup } from "react-dom/server"; import { I18nProvider } from "@/lib/i18n/context"; import type { DateFieldProps } from "@/components/ui/date-field"; +import { primeLocales } from "@/__tests__/helpers/prime-locales"; + +primeLocales("de"); const fields: DateFieldProps[] = []; vi.mock("@/components/ui/date-field", () => ({ diff --git a/src/components/timeline/__tests__/timeline-components.test.tsx b/src/components/timeline/__tests__/timeline-components.test.tsx index e2542bc63..ab0196b6f 100644 --- a/src/components/timeline/__tests__/timeline-components.test.tsx +++ b/src/components/timeline/__tests__/timeline-components.test.tsx @@ -30,6 +30,9 @@ import { TimelineChronicle } from "../timeline-chronicle"; import { layoutTimeline } from "../timeline-geometry"; import { useSeriesValueFormat } from "../use-series-value-format"; import { TODAY, fullTimeline, readiness, wordsIn } from "./timeline-fixture"; +import { primeLocales } from "@/__tests__/helpers/prime-locales"; + +primeLocales("de"); function render(node: React.ReactElement, locale: "de" | "en" = "de") { // A fresh client with no account answer: the unit preference resolves to diff --git a/src/components/ui/__tests__/filter-bar.test.tsx b/src/components/ui/__tests__/filter-bar.test.tsx index bde2d54db..8ab5fd593 100644 --- a/src/components/ui/__tests__/filter-bar.test.tsx +++ b/src/components/ui/__tests__/filter-bar.test.tsx @@ -5,6 +5,9 @@ import { renderToStaticMarkup } from "react-dom/server"; import { I18nProvider } from "@/lib/i18n/context"; import { FilterBar, FilterBarDateRange, FilterBarSelect } from "../filter-bar"; +import { primeLocales } from "@/__tests__/helpers/prime-locales"; + +primeLocales("de"); /** * v1.16.1 — unified filter rail. SSR-markup guards for the shared diff --git a/src/lib/i18n/__tests__/t-count.test.tsx b/src/lib/i18n/__tests__/t-count.test.tsx index cf8f5ca10..186a44e05 100644 --- a/src/lib/i18n/__tests__/t-count.test.tsx +++ b/src/lib/i18n/__tests__/t-count.test.tsx @@ -3,6 +3,9 @@ import { renderToStaticMarkup } from "react-dom/server"; import { I18nProvider, useTranslations } from "@/lib/i18n/context"; import type { Locale } from "@/lib/i18n/config"; +import { primeLocales } from "@/__tests__/helpers/prime-locales"; + +primeLocales("de", "pl"); /** * `tCount` is the plural-aware `t` the components call. diff --git a/vitest.setup.ts b/vitest.setup.ts index ee1bfca15..2b48ae93d 100644 --- a/vitest.setup.ts +++ b/vitest.setup.ts @@ -1,35 +1,29 @@ /** * Unit-test setup. * - * Seed the client-side locale cache with every message bundle. In - * production only EN ships statically; the active locale arrives as an - * RSC prop from the root layout and other locales load via dynamic - * import on switch (see `src/lib/i18n/load-locale.ts`). Tests mount + * Seed the client-side locale cache with the EN bundle. In production + * only the active locale reaches the client (boot script, then dynamic + * import on switch — see `src/lib/i18n/load-locale.ts`). Tests mount * `I18nProvider` standalone — often with `renderToStaticMarkup`, which - * is synchronous and cannot await a dynamic import — so the cache is - * primed up front and `t()` resolves every locale on the first render, - * exactly like the server-handoff path does in the app. + * is synchronous and cannot await a dynamic import — so EN, the fallback + * floor of the t() chain, is primed up front. + * + * Only EN. Every catalog is several hundred kilobytes and this file runs + * once per test file, so priming all of them made setup the largest share + * of a unit run. A test that renders another locale primes it itself: + * + * import deMessages from "../../../messages/de.json"; + * primeMessages("de", deMessages); + * + * Server-side translation (`getServerTranslator`, `shared-resolve`) holds + * every catalog on its own and needs no priming. */ import { afterEach } from "vitest"; import { settleBackgroundTasks } from "@/lib/logging/background-tasks"; import { primeMessages } from "@/lib/i18n/load-locale"; -import deMessages from "./messages/de.json"; import enMessages from "./messages/en.json"; -import esMessages from "./messages/es.json"; -import frMessages from "./messages/fr.json"; -import itMessages from "./messages/it.json"; -import plMessages from "./messages/pl.json"; -import koMessages from "./messages/ko.json"; -import nlMessages from "./messages/nl.json"; -primeMessages("de", deMessages); primeMessages("en", enMessages); -primeMessages("es", esMessages); -primeMessages("fr", frMessages); -primeMessages("it", itMessages); -primeMessages("pl", plMessages); -primeMessages("ko", koMessages); -primeMessages("nl", nlMessages); /** * Leave no background work in flight when a test ends.