test(01-10): add red dashboard and score presentation contracts

- preserve useful Today narrative without duplicate score prose
- remove stored-document regeneration from the detail surface
- retain score identity while rejecting redundant method copy
This commit is contained in:
Marc-André Bombeck committed 2026-07-29 14:12:58 +02:00
1 parent 44b5d8da17
commit 8e944dbcd3
3 files changed
+104 -12

No files matched your search

@@ -76,6 +76,13 @@ function digest(over: Partial<DailyDigest> = {}): DailyDigest {
};
}
function visibleText(html: string): string {
return html
.replace(/<[^>]+>/g, " ")
.replace(/\s+/g, " ")
.trim();
}
describe("<TodayHero>", () => {
it("renders the score, the lead read, and the worth-a-look rail", () => {
const html = render(<TodayHero digest={digest()} />);
@@ -150,6 +157,45 @@ describe("<TodayHero>", () => {
expect(html).not.toContain('data-slot="today-hero-briefing-link"');
});
it("keeps the useful loaded narrative without repeating the ring's numeric score", () => {
const html = render(
<TodayHero
digest={digest({
briefingLead:
"Your health score is 82. Your week is trending steady.",
line: "Your health score is 82. Your week is trending steady.",
})}
/>,
);
const text = visibleText(html);
expect(text).toContain("Your week is trending steady.");
expect(text.match(/\b82\b/g)).toHaveLength(1);
expect(html).toContain('data-slot="today-hero-score"');
expect(html).toContain('data-slot="today-hero-lead"');
});
it("does not invent a score narrative while the digest is provisional", () => {
const html = render(
<TodayHero
digest={digest({
phase: "provisional",
sleepPending: true,
score: null,
briefingLead: null,
reactionLine: null,
line: "Your health score today is 82.",
worthALook: [doseItem],
})}
/>,
);
expect(html).toContain('data-slot="today-hero-sleep-pending"');
expect(html).toContain('data-provisional="true"');
expect(html).not.toContain("Your health score today is 82.");
expect(html).not.toContain('data-slot="today-hero-lead"');
});
it("degrades to nothing on a genuinely empty account", () => {
const html = render(
<TodayHero
@@ -55,10 +55,13 @@ describe("DocumentSummaryBlock — a stored summary", () => {
expect(html).toContain("A discharge letter from a city hospital.");
expect(html).toContain("14 Feb 2026");
expect(html).toContain('data-slot="document-detail-summary"');
// A stored result stays visible and can be explicitly regenerated.
// Opening a stored result is read-only: the detail surface no longer
// exposes the redundant regenerate action.
expect(html).not.toContain("being prepared");
expect(html).toContain('data-slot="document-detail-summary-regenerate"');
expect(html).toContain("Generate again");
expect(html).not.toContain(
'data-slot="document-detail-summary-regenerate"',
);
expect(html).not.toContain("Generate again");
const header = html.match(
/<div[^>]*data-slot="document-detail-summary-header"[^>]*>/,
);
@@ -67,18 +70,18 @@ describe("DocumentSummaryBlock — a stored summary", () => {
expect(html).not.toContain("document-detail-summary-generate");
});
it("disables regeneration while a replacement is being generated", () => {
it("keeps stored prose visible without surfacing replacement state", () => {
const html = base({
summary: "Stored prose.",
summaryState: "READY",
isGenerating: true,
});
const action = html.match(
/<button[^>]*data-slot="document-detail-summary-regenerate"[^>]*>/,
expect(html).toContain("Stored prose.");
expect(html).not.toContain(
'data-slot="document-detail-summary-regenerate"',
);
expect(action?.[0]).toMatch(/\sdisabled(=""|\s|>)/);
expect(html).toContain("Generating");
expect(html).not.toContain("Generating");
});
it("shows a stored summary even if the state disagrees", () => {
@@ -126,6 +126,38 @@ function report(overrides: Partial<HealthScoreReport> = {}): HealthScoreReport {
};
}
function scoredReport(
overrides: Partial<HealthScoreReport> = {},
): HealthScoreReport {
return report({
composite: {
status: "ok",
value: {
score: 86,
band: "green",
bandSetter: null,
composition: ["BLOOD_PRESSURE", "ACTIVITY", "SLEEP"],
noiseFloor: 3,
scoreVersion: 2,
},
coverage: {
requiredInputs: 3,
presentInputs: 3,
historyDays: 28,
missing: [],
},
confidence: { score: 100, band: "high" },
provenance: {
...provenance,
inputs: ["BLOOD_PRESSURE", "ACTIVITY", "SLEEP"],
},
},
delta: 2,
deltaReason: null,
...overrides,
});
}
beforeEach(() => {
mockAuthUser = { unitPreference: "metric", glucoseUnit: "mg/dL" };
});
@@ -253,17 +285,28 @@ describe("<HealthScoreCard>", () => {
expect(html).not.toContain("70 to 99 mg/dL");
});
it("renders the one-time method note and every score identity field", () => {
it("shows one headline score without algorithm, method, or composition copy", () => {
const html = render(
report({
scoredReport({
algorithmNotice: {
itemKey: "health_score_algorithm:2",
dismissed: false,
},
}),
);
expect(html).toContain('data-slot="health-score-algorithm-notice"');
expect(html).toContain("cardiometabolic reference ranges");
expect(html.match(/>86</g)).toHaveLength(1);
expect(html).not.toContain('data-slot="health-score-algorithm-notice"');
expect(html).not.toContain("cardiometabolic reference ranges");
expect(html).not.toContain("Equal-weight average of eligible pillars");
expect(html).not.toContain("The lowest pillar sets the overall band");
expect(html).not.toContain("Included:");
// Presentation cleanup must not sever score identity or source detail.
expect(html).toContain("Method version 2");
expect(html).toContain('data-slot="health-score-pillars"');
expect(html).toContain("ESH 2023");
expect(html).toContain('data-slot="health-score-delta"');
});
it("localises structured activity labels, references, and reasons", () => {