mirror of
https://github.com/MBombeck/HealthLog.git
synced 2026-11-09 13:01:00 +00:00
iOS-operator-blocking hotfix for REG-11 — Home dashboard tile renders neither chart nor latest value for BP / Puls / Körperfett when the most recent reading is older than 7 days. Root cause was in `/api/dashboard/summary` SQL gates; `latestIn7d` + `sparkBuckets` both required `measured_at >= sevenDaysAgo`, which returned empty for sparse accounts, leaving the iOS tile with `latestValue: null` + `sparkline: []` to render from. Detail-screen (`/api/measurements/series?kind=…`) escaped because of its broader window. Server-side changes: - `latestIn7d` → `latestEver` (drops 7-day filter; DISTINCT ON returns latest reading EVER per type, bounded at N_metrics rows). - `sparkBuckets` rewritten via ROW_NUMBER() OVER (PARTITION BY type ORDER BY bucket_start DESC) so the trailing N daily buckets per type render regardless of age, bounded at SPARK_DAYS × N_metrics. - BP + Pulse metrics.push gated on `latest || allTimeCount > 0` like BodyFat already is — accounts with zero readings ever no longer get empty placeholder tiles. - 4 new regression tests in summary/__tests__/route.test.ts cover sparse accounts, zero-data accounts, within-7d guards. Other develop work bundled in this release: - W4-QoL — copy nits. - W5-WORKOUTS — user source priority threaded through write-time picker + batch route. - W7-WITHINGS-RELIABILITY — response classifier + sync hardening. - W8-OPS — ops phase docs. - B11 — NEXT_PUBLIC_APP_VERSION build-arg routing so /api/version cannot drift from CI tag. - env-check workflow — manifest / example lockstep enforcement. Resolves PR #194 (squash-merge produced via local -X theirs strategy because GitHub's UI couldn't auto-resolve develop's post-v1.4.42 contiguous work against the v1.4.42 squash on main; resulting tree is byte-identical to develop's HEAD).
45 lines
1.3 KiB
TypeScript
45 lines
1.3 KiB
TypeScript
import { afterEach, describe, expect, it, vi } from "vitest";
|
|
|
|
import { scrollBehaviorForUser } from "@/lib/motion";
|
|
|
|
// The vitest config runs unit tests under Node (no jsdom). The helper
|
|
// only touches `window.matchMedia`, so we mount a minimal stub via
|
|
// `vi.stubGlobal` and tear it down after each case.
|
|
|
|
function mountWindow(matchesReducedMotion: boolean) {
|
|
vi.stubGlobal("window", {
|
|
matchMedia: (query: string) => ({
|
|
matches:
|
|
matchesReducedMotion && query === "(prefers-reduced-motion: reduce)",
|
|
media: query,
|
|
onchange: null,
|
|
addListener: () => {},
|
|
removeListener: () => {},
|
|
addEventListener: () => {},
|
|
removeEventListener: () => {},
|
|
dispatchEvent: () => false,
|
|
}),
|
|
});
|
|
}
|
|
|
|
describe("scrollBehaviorForUser", () => {
|
|
afterEach(() => {
|
|
vi.unstubAllGlobals();
|
|
});
|
|
|
|
it("returns `auto` when window is undefined (SSR)", () => {
|
|
vi.stubGlobal("window", undefined);
|
|
expect(scrollBehaviorForUser()).toBe("auto");
|
|
});
|
|
|
|
it("returns `auto` when prefers-reduced-motion matches", () => {
|
|
mountWindow(true);
|
|
expect(scrollBehaviorForUser()).toBe("auto");
|
|
});
|
|
|
|
it("returns `smooth` when prefers-reduced-motion does not match", () => {
|
|
mountWindow(false);
|
|
expect(scrollBehaviorForUser()).toBe("smooth");
|
|
});
|
|
});
|