test: mock the unit-display hook in the crosstab, vitals, and MCP suites

The crosstab, vitals, and cycle-summary renders are static server renders with
no QueryClient; the new preferred-unit hook reads through the auth query, so
mock it to a real metric-preference implementation (identity values, canonical
symbols) that matches the render those suites assert. Register the new unit
resolver on the MCP write-tools module mock so the preview path resolves.
This commit is contained in:
Marc-André Bombeck committed 2026-07-25 00:34:51 +02:00
1 parent ddc598afcb
commit 6cf56ce456
4 files changed
+81 -1

No files matched your search

@@ -1,6 +1,29 @@
import { describe, it, expect, vi, beforeEach } from "vitest";
import { renderToStaticMarkup } from "react-dom/server";
// The embedded crosstab reads the unit preference through `useUnitDisplay`,
// which hangs off `useAuth` (a react-query hook). These are static SSR renders
// with no QueryClient, so mock the hook to a real metric-preference impl.
vi.mock("@/hooks/use-unit-display", async () => {
const dt = await import("@/lib/measurements/display-transform");
const forType = (t: string) => dt.getDisplayTransform(t, "metric");
return {
useUnitDisplay: () => ({
preference: "metric" as const,
transformFor: forType,
toDisplay: (t: string, v: number) =>
dt.applyDisplayTransform(v, forType(t)),
fromDisplay: (t: string, v: number) =>
dt.invertDisplayTransform(v, forType(t)),
toDisplayDelta: (t: string, v: number) =>
dt.applyDisplayTransformDelta(v, forType(t)),
unitFor: (t: string) => forType(t).displayUnit,
decimalsFor: (t: string) => forType(t).decimals,
isTransformed: (t: string) => dt.hasDisplayTransform(t),
}),
};
});
import { I18nProvider } from "@/lib/i18n/context";
import type { CalendarResponse } from "../types";
import type { CycleInsightsResponse } from "../use-cycle";
@@ -1,6 +1,31 @@
import { describe, it, expect } from "vitest";
import { describe, it, expect, vi } from "vitest";
import { renderToStaticMarkup } from "react-dom/server";
// The crosstab reads the unit preference through `useUnitDisplay`, which hangs
// off `useAuth` (a react-query hook). These are static SSR renders with no
// QueryClient, so mock the hook to a real metric-preference implementation —
// metric is the identity on values and yields the canonical display symbols,
// so the assertions below (kg, °C, unchanged numbers) hold.
vi.mock("@/hooks/use-unit-display", async () => {
const dt = await import("@/lib/measurements/display-transform");
const forType = (t: string) => dt.getDisplayTransform(t, "metric");
return {
useUnitDisplay: () => ({
preference: "metric" as const,
transformFor: forType,
toDisplay: (t: string, v: number) =>
dt.applyDisplayTransform(v, forType(t)),
fromDisplay: (t: string, v: number) =>
dt.invertDisplayTransform(v, forType(t)),
toDisplayDelta: (t: string, v: number) =>
dt.applyDisplayTransformDelta(v, forType(t)),
unitFor: (t: string) => forType(t).displayUnit,
decimalsFor: (t: string) => forType(t).decimals,
isTransformed: (t: string) => dt.hasDisplayTransform(t),
}),
};
});
import {
CyclePhaseCrosstab,
CyclePhaseHeadline,
@@ -1,6 +1,31 @@
import { describe, it, expect, vi, beforeEach } from "vitest";
import { renderToStaticMarkup } from "react-dom/server";
// The vitals tiles read the unit preference through `useUnitDisplay`, which
// hangs off `useAuth` (a react-query hook). These are static SSR renders with
// no QueryClient, so mock the hook to a real metric-preference implementation
// — metric is the identity on values and yields the canonical display symbols
// (e.g. °C), matching the pre-fix render the assertions expect.
vi.mock("@/hooks/use-unit-display", async () => {
const dt = await import("@/lib/measurements/display-transform");
const forType = (t: string) => dt.getDisplayTransform(t, "metric");
return {
useUnitDisplay: () => ({
preference: "metric" as const,
transformFor: forType,
toDisplay: (t: string, v: number) =>
dt.applyDisplayTransform(v, forType(t)),
fromDisplay: (t: string, v: number) =>
dt.invertDisplayTransform(v, forType(t)),
toDisplayDelta: (t: string, v: number) =>
dt.applyDisplayTransformDelta(v, forType(t)),
unitFor: (t: string) => forType(t).displayUnit,
decimalsFor: (t: string) => forType(t).decimals,
isTransformed: (t: string) => dt.hasDisplayTransform(t),
}),
};
});
import { I18nProvider } from "@/lib/i18n/context";
import type {
DerivedMetricResponse,
@@ -5,12 +5,15 @@ const logMcpMood = vi.fn();
const logMcpBloodPressure = vi.fn();
const checkMcpMeasurement = vi.fn();
const checkMcpBloodPressure = vi.fn();
const resolveMcpMeasurementUnit = vi.fn();
vi.mock("../writes", () => ({
logMcpMeasurement: (...a: unknown[]) => logMcpMeasurement(...a),
logMcpMood: (...a: unknown[]) => logMcpMood(...a),
logMcpBloodPressure: (...a: unknown[]) => logMcpBloodPressure(...a),
checkMcpMeasurement: (...a: unknown[]) => checkMcpMeasurement(...a),
checkMcpBloodPressure: (...a: unknown[]) => checkMcpBloodPressure(...a),
resolveMcpMeasurementUnit: (...a: unknown[]) =>
resolveMcpMeasurementUnit(...a),
}));
const checkMcpWriteRateLimit = vi.fn();
@@ -49,6 +52,10 @@ beforeEach(() => {
// would-be record. Individual tests override to assert wouldFail.
checkMcpMeasurement.mockReturnValue({ ok: true });
checkMcpBloodPressure.mockReturnValue({ ok: true });
// Default: the unit hint resolves to canonical, echoing the value through.
resolveMcpMeasurementUnit.mockImplementation(
(_type: string, value: number) => ({ ok: true, value, unit: "kg" }),
);
});
describe("write-tool surface", () => {