mirror of
https://github.com/MBombeck/HealthLog.git
synced 2026-11-14 06:40:50 +00:00
test(labs): cover the biomarker assessment, detail page, and marker delete/range
Add coverage for the new biomarker-assessment seam and the marker detail surface: - the generator serves today's cache, re-stamps unchanged readings without an LLM call, and regenerates only when the latest-reading fingerprint changes - the read-only endpoint returns the envelope, 422s a missing id, and honours the auth + insightStatus gates - the detail page paints the stat strip, the chart placeholder, and the assessment card - the biomarker resource route drops the marker with its readings in one userId-narrowed transaction and rejects an inverted target range Hold the input gate independent of the worker's forced run so an idle marker re-stamps cached text instead of regenerating, and list the new route on the non-Coach gate allowlist.
This commit is contained in:
1 parent
0790a96ca4
commit
f17131dbdf
6 files changed
+699
-2
No files matched your search
@@ -0,0 +1,142 @@
|
||||
import { describe, it, expect, vi } from "vitest";
|
||||
import { renderToStaticMarkup } from "react-dom/server";
|
||||
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
|
||||
|
||||
import { I18nProvider } from "@/lib/i18n/context";
|
||||
import { queryKeys } from "@/lib/query-keys";
|
||||
import type {
|
||||
BiomarkerDto,
|
||||
LabResultDto,
|
||||
LabResultListResponse,
|
||||
} from "../types";
|
||||
|
||||
/**
|
||||
* The per-biomarker detail page mirrors the metric sub-pages: a numbers-first
|
||||
* stat strip over the numeric readings, the (lazy) chart, the history, and the
|
||||
* AI assessment card. This test seeds the marker + reading queries and asserts
|
||||
* the strip, the chart placeholder, and the assessment card all paint.
|
||||
*/
|
||||
|
||||
vi.mock("@/lib/api/api-fetch", () => ({
|
||||
apiGet: () => new Promise(() => {}),
|
||||
apiDelete: vi.fn(),
|
||||
apiPut: vi.fn(),
|
||||
}));
|
||||
|
||||
vi.mock("next/navigation", () => ({
|
||||
useRouter: () => ({ push: vi.fn() }),
|
||||
}));
|
||||
|
||||
vi.mock("@/hooks/use-auth", () => ({
|
||||
useAuth: () => ({ isAuthenticated: true, user: { id: "u1" } }),
|
||||
}));
|
||||
|
||||
vi.mock("@/hooks/use-feature-flags", () => ({
|
||||
useFeatureFlags: () => ({ insightStatus: true }),
|
||||
}));
|
||||
|
||||
import { LabBiomarkerDetail } from "../lab-biomarker-detail";
|
||||
|
||||
const BIOMARKER_ID = "bm-1";
|
||||
|
||||
const MARKER: BiomarkerDto = {
|
||||
id: BIOMARKER_ID,
|
||||
name: "LDL Cholesterol",
|
||||
unit: "mg/dL",
|
||||
lowerBound: 0,
|
||||
upperBound: 100,
|
||||
panel: null,
|
||||
hasContext: false,
|
||||
context: null,
|
||||
hidden: false,
|
||||
createdAt: "2026-06-01T00:00:00.000Z",
|
||||
updatedAt: "2026-06-01T00:00:00.000Z",
|
||||
};
|
||||
|
||||
function reading(id: string, value: number, takenAt: string): LabResultDto {
|
||||
return {
|
||||
id,
|
||||
biomarkerId: BIOMARKER_ID,
|
||||
panel: null,
|
||||
analyte: "LDL Cholesterol",
|
||||
value,
|
||||
valueText: null,
|
||||
unit: "mg/dL",
|
||||
referenceLow: 0,
|
||||
referenceHigh: 100,
|
||||
takenAt,
|
||||
source: "MANUAL",
|
||||
hasNote: false,
|
||||
rangeStatus: "in-range",
|
||||
createdAt: takenAt,
|
||||
updatedAt: takenAt,
|
||||
};
|
||||
}
|
||||
|
||||
const LIST: LabResultListResponse = {
|
||||
results: [
|
||||
reading("r3", 95, "2026-06-20T08:00:00.000Z"),
|
||||
reading("r2", 88, "2026-05-20T08:00:00.000Z"),
|
||||
reading("r1", 102, "2026-04-20T08:00:00.000Z"),
|
||||
],
|
||||
meta: { total: 3, limit: 500, offset: 0 },
|
||||
};
|
||||
|
||||
function renderWithCache() {
|
||||
const queryClient = new QueryClient({
|
||||
defaultOptions: { queries: { retry: false } },
|
||||
});
|
||||
queryClient.setQueryData(queryKeys.biomarkerDetail(BIOMARKER_ID), MARKER);
|
||||
queryClient.setQueryData(
|
||||
queryKeys.labResultsList({
|
||||
biomarkerId: BIOMARKER_ID,
|
||||
analyte: undefined,
|
||||
panel: undefined,
|
||||
from: undefined,
|
||||
to: undefined,
|
||||
page: 0,
|
||||
sortDir: "desc",
|
||||
}),
|
||||
LIST,
|
||||
);
|
||||
queryClient.setQueryData(
|
||||
queryKeys.insightsBiomarkerAssessment(BIOMARKER_ID, "en"),
|
||||
{
|
||||
hasProvider: true,
|
||||
text: "Your LDL is steady inside the reference range.",
|
||||
cached: true,
|
||||
updatedAt: "2026-06-20T09:00:00.000Z",
|
||||
},
|
||||
);
|
||||
return renderToStaticMarkup(
|
||||
<QueryClientProvider client={queryClient}>
|
||||
<I18nProvider initialLocale="en">
|
||||
<LabBiomarkerDetail biomarkerId={BIOMARKER_ID} />
|
||||
</I18nProvider>
|
||||
</QueryClientProvider>,
|
||||
);
|
||||
}
|
||||
|
||||
describe("<LabBiomarkerDetail>", () => {
|
||||
const html = renderWithCache();
|
||||
|
||||
it("paints the marker heading", () => {
|
||||
expect(html).toContain("LDL Cholesterol");
|
||||
});
|
||||
|
||||
it("paints the numbers-first stat strip", () => {
|
||||
expect(html).toContain('data-slot="metric-stat-strip"');
|
||||
});
|
||||
|
||||
it("paints the chart placeholder (lazy chart shell)", () => {
|
||||
// The recharts chart is `next/dynamic({ ssr: false })`; the server render
|
||||
// paints the layout-stable skeleton shell in its place.
|
||||
expect(html).toContain('data-slot="skeleton"');
|
||||
});
|
||||
|
||||
it("paints the assessment card", () => {
|
||||
// useMounted() is false during SSR, so the card renders its loading
|
||||
// skeleton geometry — proof the assessment card mounted on the page.
|
||||
expect(html).toContain('data-testid="insight-status-card-loading"');
|
||||
});
|
||||
});
|
||||
Reference in new issue
Block a user