mirror of
https://github.com/MBombeck/HealthLog.git
synced 2026-11-08 12:32:43 +00:00
Largest feature delta in the v1.4.x line. Insights expands into seven dedicated metric sub-pages; GLP-1 medication tracking lands end-to-end (drug-level chart with Research Mode acknowledgment, EMA titration ladder, cadence + compliance, side-effect taxonomy, pen/vial inventory); cross-source priority becomes a two-axis resolver; per-user timezone threads through ten analytics surfaces; Withings activity + sleep v2 sync; onboarding rebuilt as a nested-route wizard; Personal Records ship end-to-end; Health Score provenance accordion; Coach runs native first-party prompts across six locales with a 1800-assertion refusal-probe matrix; OpenAPI drift gate hard-fails; multi-arch GHCR image. Migrations 0043 through 0060 are additive and forward-only. PROMPT_VERSION 4.24.0 → 4.25.0 with GROUND RULE 9 (refuses dose recommendations) and GROUND RULE 15 (refuses drug-level estimates with MDR + MDCG 2021-24 cites). See CHANGELOG.md for the full per-section delta.
55 lines
2.0 KiB
TypeScript
55 lines
2.0 KiB
TypeScript
import { expect, test } from "@playwright/test";
|
|
|
|
/**
|
|
* Locale-key drift smoke check — switch the cookie between EN and DE
|
|
* and assert the login page (the only public surface that's translated)
|
|
* never shows raw i18n keys like `auth.welcomeBack`.
|
|
*
|
|
* Drift catches: a typo'd t("foo.bar") with no key in either locale
|
|
* file would make the raw key surface in the DOM. messages/de.json and
|
|
* messages/en.json key parity is enforced by unit tests; this is the
|
|
* runtime cousin.
|
|
*/
|
|
test.describe("locale switch", () => {
|
|
for (const locale of ["en", "de"] as const) {
|
|
test(`renders /auth/login in ${locale} without raw i18n keys`, async ({
|
|
page,
|
|
context,
|
|
}) => {
|
|
// Cookies must be addressable to a real origin — `page.url()` on
|
|
// a fresh context returns `about:blank`, which Playwright rejects.
|
|
// Anchor at the configured base URL explicitly.
|
|
const baseURL =
|
|
test.info().project.use.baseURL ?? "http://localhost:3000";
|
|
await context.addCookies([
|
|
{
|
|
name: "healthlog-locale",
|
|
value: locale,
|
|
url: baseURL,
|
|
},
|
|
]);
|
|
await page.goto("/auth/login");
|
|
|
|
const bodyText = await page.locator("body").innerText();
|
|
|
|
// i18n keys look like `section.subkey` — letters, numbers,
|
|
// underscores, dots — and are never wrapped in normal sentences,
|
|
// so a regex match anywhere in body text is a sign the lookup
|
|
// fell through.
|
|
const rawKeyPattern = /\b[a-z]+(?:[A-Z][a-z]+)?\.[a-z][A-Za-z0-9_.]+\b/;
|
|
const match = bodyText.match(rawKeyPattern);
|
|
|
|
// Whitelist: filenames or version strings can look like keys,
|
|
// skip those by requiring at least one camelCase segment.
|
|
if (match) {
|
|
const candidate = match[0];
|
|
const looksLikeKey = /\.[a-z][A-Z]/.test(candidate);
|
|
expect(
|
|
looksLikeKey,
|
|
`Possible raw i18n key in ${locale}: ${candidate}`,
|
|
).toBe(false);
|
|
}
|
|
});
|
|
}
|
|
});
|