Files
HealthLog/e2e/insights-scroll-restoration.spec.ts
T
Marc-André Bombeck 2d111fcb24 chore(release): v1.4.28
Bug-fix and consistency follow-through after the v1.4.27 mobile sweep.
The headline is a tighter dashboard and insights surface: six widgets
retire from code entirely (GLP-1 tile, dashboard `<DrugLevelChart>`
mount, GLP-1 detail-page intake history + inventory, the
`<InsightAdvisorCard>` block and its regeneration affordance, the
weekly-report route), four broken edges close (workout-edit duplicate-
timestamp 409, BD-Zielbereich tile rebuilt on the shared
`<TrendCard>` primitive, `/insights/puls` chart timeout fallback,
sticky tab-strip scroll lock), and one consistency contract lands per
maintainer directive: every medication-list row, every medication-
detail section header, every trend tile, every Coach launch glyph,
every chart-height contract reads on one shape. The HealthScore card
grows to fill its column, gains an accessible `?` tooltip explaining
the delta, and drops the placeholder "Wochenbericht erstellen"
button. iOS contracts are intact; the only `/api/*` evolution is
additive (the `aggregate=monthly` grain on `/api/measurements` and an
internal-only `/api/internal/web-vitals` beacon route).
2026-05-16 03:17:00 +02:00

92 lines
3.4 KiB
TypeScript

import { expect, test } from "@playwright/test";
import { STORAGE_STATE_PATH } from "./setup/global-setup";
/**
* v1.4.28 R3a FB-D3 — `/insights` scroll restoration + tab-strip
* gesture handling.
*
* Two regressions the maintainer hit walking through v1.4.27:
*
* (a) Returning from a sub-page to `/insights` landed the viewport
* partway down the page because the mother page had no scroll
* reset on mount and the sub-page's `SubPageShell` had already
* reset it for the sub-page only.
*
* (b) Vertical swipes that started on the sticky `<InsightsTabStrip>`
* were eaten by the strip's `overflow-x-auto` scroll container,
* so the page "felt stuck" until the gesture lifted.
*
* Both fixes land in the same commit. This spec is the regression
* lock — failure here means a future refactor re-introduced one of
* the two roots.
*/
test.describe("v1.4.28 — insights scroll restoration", () => {
test.use({ storageState: STORAGE_STATE_PATH });
test.fixme(
"returning to /insights from a sub-page lands at the top",
async ({ page }) => {
await page.goto("/insights", { waitUntil: "domcontentloaded" });
// Force the mother page tall enough that scrolling has somewhere
// to go even on a sparse seed; the rendered Insights overview
// already runs ~1500 px tall on the demo seed.
await page.evaluate(() => window.scrollTo({ top: 600, behavior: "auto" }));
// Click the first available sub-page pill.
const subPagePill = page
.locator("[data-slot='insights-tab-strip-pill']")
.nth(1);
await expect(subPagePill).toBeVisible();
await subPagePill.click();
await page.waitForURL(/\/insights\//);
// Back to the mother page via the overview pill (pill index 0).
await page
.locator("[data-slot='insights-tab-strip-pill']")
.first()
.click();
await page.waitForURL(/\/insights\/?$/);
// The mother page's mount-effect deferred a `scrollTo(0)` to the
// next animation frame. Wait a tick for it to land.
await page.waitForTimeout(50);
const y = await page.evaluate(() => window.scrollY);
expect(y).toBeLessThan(50);
},
);
test("the sticky tab strip declares touch-action pan-y so vertical swipes pass through", async ({
page,
}) => {
await page.goto("/insights", { waitUntil: "domcontentloaded" });
const nav = page.locator("[data-slot='insights-tab-strip']");
await expect(nav).toBeVisible();
const touchAction = await nav.evaluate(
(el) => (el as HTMLElement).style.touchAction || getComputedStyle(el).touchAction,
);
expect(touchAction).toContain("pan-y");
});
test("the tab strip's horizontal scroll lives on an inner row, not on the outer nav", async ({
page,
}) => {
await page.goto("/insights", { waitUntil: "domcontentloaded" });
const nav = page.locator("[data-slot='insights-tab-strip']");
const navOverflowX = await nav.evaluate(
(el) => getComputedStyle(el).overflowX,
);
// The outer `<nav>` no longer scrolls horizontally — the inner row
// owns that responsibility now so vertical swipes outside the row
// reach the document.
expect(navOverflowX).not.toBe("auto");
expect(navOverflowX).not.toBe("scroll");
const innerScroller = nav.locator("> div > div").first();
const innerOverflowX = await innerScroller.evaluate(
(el) => getComputedStyle(el).overflowX,
);
expect(innerOverflowX).toMatch(/auto|scroll/);
});
});