App versioning + the create page's missing top padding

Versioning: package.json carries the app version (0.1.2 here; one bump per
task with `npm run version:bump`, patch 0-9 rolling into the minor at 10),
baked into the build via lib/version.ts, reported by /api/healthz and shown
in Settings next to the running commit. AGENTS.md documents the rule; the
native suite checks both the API and the Settings row.

Fix: on desktop the app header is hidden, so every page owns its top padding —
/create was the only one without `lg:pt-10`, which slammed the Study deck /
Make my homework switch against the top of the window while /homework had a
comfortable gap. Both now sit at 40px; the wizard suite asserts the clearance
and that the two pages stay in step (90 checks).

SW -> freezmo-v50.
This commit is contained in:
Martin Dvořák 2026-10-12 20:04:26 +00:00
commit aac6befb0d
12 changed files with 124 additions and 10 deletions

View file

@ -30,6 +30,25 @@ const browser = await launchBrowser();
await context.close();
}
/* 1b. the app version is reported and shown in Settings --------------- */
{
const { context, page } = await makePage(browser);
await seed(page, { decks: [] });
await page.goto(BASE, { waitUntil: "networkidle" });
await page.waitForTimeout(600);
const health = await page.evaluate(async () => (await fetch("/api/healthz", { cache: "no-store" })).json());
check("healthz reports the app version", /^\d+\.\d+\.\d+$/.test(health.version ?? ""), String(health.version));
await page.locator("nav.tabbar").getByRole("button").click();
await page.getByRole("dialog").waitFor({ timeout: 6000 });
await page.waitForTimeout(700);
check(
"settings shows the app version",
(await page.getByText(new RegExp(`v${String(health.version).replace(/\./g, "\\.")}`)).count()) >= 1,
String(health.version),
);
await context.close();
}
/* 2. offline: banner + generation disabled --------------------------- */
{
const { context, page } = await makePage(browser);

View file

@ -115,6 +115,33 @@ for (const viewport of [{ width: 390, height: 844 }, { width: 360, height: 740 }
await context.close();
}
/* 2b. the wizard clears the top edge on desktop ----------------------- */
/* On desktop the app header is hidden (`lg:hidden`), so each page owns its
top padding. /create was missing it and the mode switch sat flush against
the top of the window while /homework had a comfortable gap. */
{
const { context, page } = await makePage(browser, { width: 1280, height: 900 });
await seed(page, { decks: [] });
const switcherTop = async () => {
await page.waitForTimeout(700);
return page.evaluate(() => {
const switcher = document.querySelector('[role="group"][aria-label="Study deck or homework"]');
return switcher ? Math.round(switcher.getBoundingClientRect().top) : null;
});
};
await page.goto(`${BASE}/create`, { waitUntil: "networkidle" });
const createTop = await switcherTop();
check("create: the mode switch clears the top edge on desktop", createTop !== null && createTop >= 24, `${createTop}px`);
await page.goto(`${BASE}/homework`, { waitUntil: "networkidle" });
const homeworkTop = await switcherTop();
check(
"create and homework keep the same clearance",
homeworkTop !== null && homeworkTop >= 24 && createTop !== null && Math.abs(homeworkTop - createTop) <= 4,
`${createTop}px vs ${homeworkTop}px`,
);
await context.close();
}
/* 3. reduced motion: bar still pinned, wizard usable ----------------- */
{
const { context, page } = await makePage(browser, { reducedMotion: "reduce" });