diff --git a/src/components/insights/__tests__/daily-briefing.test.tsx b/src/components/insights/__tests__/daily-briefing.test.tsx new file mode 100644 index 000000000..a87e577be --- /dev/null +++ b/src/components/insights/__tests__/daily-briefing.test.tsx @@ -0,0 +1,203 @@ +import { describe, it, expect } from "vitest"; +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"; + +/** + * v1.4.20 phase B1 — full-width Daily Briefing card. + * + * The card sits below the hero strip on `/insights` and renders the + * narrative paragraph + 0-5 key-finding rows produced by the AI + * pipeline. Tests cover: paragraph rendering, finding rows + tones + + * deltas, locale-aware title, empty state with CTA, loading skeleton, + * meta slot. + */ + +function render(node: React.ReactNode, locale: "en" | "de" = "en") { + return renderToStaticMarkup( + {node}, + ); +} + +const baseBriefing: DailyBriefingPayload = { + paragraph: + "You're trending well this week. Blood pressure is in target band on 9 of 10 readings, weight is down 2.5 kg over the last 30 days, and your medication compliance streak is now 21 days.", + keyFindings: [ + { + tone: "good", + headline: "Blood pressure entered target", + detail: "9 of last 10 readings under 130/85.", + delta: "↓ 4 mmHg", + sourceWindow: "30d", + sourceMetric: "bp", + }, + { + tone: "watch", + headline: "Monday-morning systolic spike", + detail: "+6 mmHg vs other weekdays.", + delta: "+6 mmHg", + sourceWindow: "30d", + sourceMetric: "bp", + }, + { + tone: "info", + headline: "Weight down 30 d", + detail: "Linear, sustainable rate. BMI now 26.0.", + delta: null, + sourceWindow: "30d", + sourceMetric: "weight", + }, + ], +}; + +describe("", () => { + it("renders the title in English", () => { + const html = render(); + expect(html).toMatch(/data-slot="daily-briefing"/); + expect(html).toContain("Daily Briefing"); + }); + + it("renders the title in German", () => { + const html = render(, "de"); + expect(html).toContain("Tagesbriefing"); + }); + + it("renders the narrative paragraph", () => { + const html = render(); + expect(html).toMatch(/data-slot="daily-briefing-paragraph"/); + expect(html).toContain( + "Blood pressure is in target band on 9 of 10 readings", + ); + }); + + it("renders one row per key finding", () => { + const html = render(); + const matches = html.match(/data-slot="daily-briefing-finding"/g) ?? []; + expect(matches.length).toBe(3); + expect(html).toContain("Blood pressure entered target"); + expect(html).toContain("Monday-morning systolic spike"); + expect(html).toContain("Weight down 30 d"); + }); + + it("renders the delta string when supplied", () => { + const html = render(); + expect(html).toMatch(/data-slot="daily-briefing-delta"/); + expect(html).toContain("↓ 4 mmHg"); + expect(html).toContain("+6 mmHg"); + }); + + it("does NOT render a delta badge when delta is null", () => { + const html = render( + , + ); + // Only one finding rendered, and it has delta=null. + const deltas = html.match(/data-slot="daily-briefing-delta"/g) ?? []; + expect(deltas.length).toBe(0); + }); + + it("uses tone-specific colour classes per finding", () => { + const html = render(); + expect(html).toContain("bg-dracula-green"); // tone=good bar + expect(html).toContain("bg-dracula-orange"); // tone=watch bar + expect(html).toContain("bg-dracula-cyan"); // tone=info bar + }); + + it("renders the 'Generated