test: prime only the EN catalog in the unit setup

The setup file runs once per test file and imported all eight message
catalogs, several hundred kilobytes each, which made setup the largest
share of a component-suite run. It now primes EN, the fallback floor of
the t() chain. A test that renders another locale primes it at module
scope through primeLocales(), which loads the catalog via the production
loader in a file-level beforeAll.
This commit is contained in:
Marc-André Bombeck committed 2026-10-20 08:38:08 +02:00
1 parent 5cfd129b02
commit 9b93247ae0
91 files changed
+307 -21

No files matched your search

+25
View File
@@ -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)));
});
}
@@ -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(
@@ -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() }),
@@ -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() }),
@@ -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() }),
@@ -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 = {
@@ -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();
@@ -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() }),
@@ -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,
@@ -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(
@@ -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(
@@ -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",
@@ -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".
@@ -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 `<TrendCard>`.
@@ -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(
@@ -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", () => {
@@ -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.
@@ -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(
@@ -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,
@@ -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(
@@ -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.
@@ -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;
@@ -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;
@@ -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.
@@ -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({
@@ -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.
@@ -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
@@ -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({
@@ -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.
@@ -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(
@@ -339,6 +339,9 @@ describe("<CoachInput>", () => {
// 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", () => {
@@ -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
@@ -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.
@@ -760,6 +760,9 @@ describe("<MessageThread>", () => {
// 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", () => {
@@ -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(
@@ -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(
@@ -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(
@@ -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";
@@ -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 {
@@ -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;
@@ -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: ({
@@ -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<typeof LabConvertedNote>[0]["reading"],
@@ -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.",
@@ -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,
@@ -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",
@@ -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"],
@@ -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",
@@ -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(
@@ -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(
@@ -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
@@ -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
@@ -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.
@@ -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();
@@ -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
@@ -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();
@@ -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
@@ -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: () => ({}),
@@ -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.
@@ -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.
@@ -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();
@@ -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";
@@ -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", () => {
@@ -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: [
@@ -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") {
@@ -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") {
@@ -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<string, boolean>) {
return renderToStaticMarkup(
@@ -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(
@@ -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(
@@ -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
@@ -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(
@@ -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(
@@ -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(
@@ -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<string, boolean>, locale: "en" | "de" = "en") {
authRef.modules = modules;
@@ -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
@@ -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
@@ -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(
@@ -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: {
@@ -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[] },
@@ -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(
@@ -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",
@@ -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];
@@ -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(
@@ -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"
@@ -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.
@@ -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 — `<ConsistencyStrip>` replaces the v1.4.22 per-card
@@ -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";
@@ -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", () => ({
@@ -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
@@ -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
+3
View File
@@ -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.
+15 -21
View File
@@ -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.