Files
HealthLog/e2e/utils/mock-mood-insights.ts
T
Marc-André Bombeck 7be05608f8 test: date fixtures that feed a clock-relative window relative to now
A fixture dated near the day it was written passes until the date slides
out of a window the route counts back from the clock. Five did: a snooze
until a fixed date that becomes the past in January, a prognosis read
against the wall clock over days seeded at a fixed date, sleep rows and a
weight streak read through windows that end now, and a sync token that
would expire on a fixed date. The e2e mood heatmap mock and a timeline
URL are dated from today as well.

A guard scans the integration and e2e trees for near-now absolute dates
within reach of a window parameter, with an allowlist for files whose
windows are fixed on both ends.
2026-10-15 12:23:09 +02:00

131 lines
3.5 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import type { Page } from "@playwright/test";
/**
* Route mocks for the /insights/mood density guard.
*
* The correlation cards and the discovered-relations list only render with
* data, and the seeded e2e account has none — an unmocked run would pass the
* tile-escape guard vacuously against empty states. These payloads pin the
* measured worst case: five populated correlation cards whose header caption
* ("269 gepaarte Tage · r = 0.37") used to escape the card edge by 14–39 px
* at 390/360, plus mood-involving discovered pairs whose stat caption wrapped
* to a 4-line sliver beside the finding sentence.
*/
function correlation(r: number, strength: string, n: number) {
return {
result: { r, strength, n },
n,
points: Array.from({ length: 24 }, (_, i) => ({
x: 1 + (i % 5),
y: 40 + ((i * 7) % 55),
})),
};
}
/**
* A calendar day this many days back, so the heatmap cells stay inside its
* 90-day window whenever the suite runs.
*/
function dayKeyDaysAgo(days: number): string {
return new Date(Date.now() - days * 86_400_000).toISOString().slice(0, 10);
}
const MOOD_INSIGHTS_PAYLOAD = {
summary: { totalEntries: 269, inTargetPct: 62 },
heatmap: {
windowDays: 90,
cells: [
{ date: dayKeyDaysAgo(14), score: 4, samples: 2 },
{ date: dayKeyDaysAgo(13), score: 3, samples: 1 },
],
},
distribution: [],
weekday: [],
timeOfDay: { reliable: false },
stability: null,
tags: [],
structuredTags: [],
tagInfluence: { flat: [], structured: [] },
betterDays: [],
tagMetricCrosstab: [],
factorCrosstab: [],
narratives: [],
correlations: {
sleep: correlation(0.37, "schwach", 269),
steps: correlation(0.52, "moderat", 231),
pulse: correlation(-0.44, "moderat", 258),
weight: correlation(0.21, "schwach", 244),
bloodPressureSystolic: correlation(-0.73, "stark", 212),
},
};
const CORRELATION_DISCOVERY_PAYLOAD = {
discovered: [
{
behaviour: "SLEEP_DURATION",
outcome: "MOOD",
n: 269,
r: 0.37,
pValue: 0.001,
qValue: 0.012,
interpretation: "",
lagDays: 1,
},
{
behaviour: "MOOD",
outcome: "RESTING_HEART_RATE",
n: 231,
r: -0.41,
pValue: 0.002,
qValue: 0.031,
interpretation: "",
lagDays: 1,
},
{
behaviour: "TIME_IN_DAYLIGHT",
outcome: "MOOD",
n: 187,
r: 0.29,
pValue: 0.004,
qValue: 0.048,
interpretation: "",
lagDays: 0,
},
],
pairsTested: 42,
fdrQ: 0.1,
minPairs: 20,
};
/** Serve populated mood-insights + discovery payloads for the density guard. */
export async function mockMoodInsights(page: Page): Promise<void> {
await page.route(/\/api\/insights\/comprehensive(\?|$)/, async (route) => {
await route.fulfill({
status: 200,
contentType: "application/json",
body: JSON.stringify({
data: { moodSummary: { count: 269 } },
error: null,
}),
});
});
await page.route(/\/api\/mood\/insights(\?|$)/, async (route) => {
await route.fulfill({
status: 200,
contentType: "application/json",
body: JSON.stringify({ data: MOOD_INSIGHTS_PAYLOAD, error: null }),
});
});
await page.route(/\/api\/insights\/correlations(\?|$)/, async (route) => {
await route.fulfill({
status: 200,
contentType: "application/json",
body: JSON.stringify({
data: CORRELATION_DISCOVERY_PAYLOAD,
error: null,
}),
});
});
}