mirror of
https://github.com/MBombeck/HealthLog.git
synced 2026-11-10 21:32:18 +00:00
Settings now reads as nine groups: Account, Tracking, Arrangement, Notifications, Health profile, AI, Connections, Data and System. On a phone one button names the current section and opens the grouped list, replacing the strip of every section. What moved, every old address still working: - Sessions, trusted devices and security activity sit under Security; the privacy page anchors forward there. - Notifications holds every reminder kind: the Coach nudges and the checkup reminder switches joined, and a card names the reminders set where their subject lives (dose times, cycle reminders). - Arrangement holds order and view only. Mood tags, the labs biomarker catalog and on-device text recognition, and the injection sites live on module pages under /settings/modules/<id>, linked from each Modules row and from the module's own wrench. /settings/layout/mood redirects. - The timezone saves on change in the Display card, with a one-tap offer of the device zone. - Screening hints sit beside the life circumstances they come from. - System gains Preview features with the Topics switch. Duplicates: the Coach and MCP pages show their state and link to the one switch in Modules (turning MCP off keeps tokens), the Coach overlay shows the provider and model with a link to Settings > AI instead of a second set of selects, and the privacy page drops its copy of the export links. A guard walks every historical /settings address through the redirects and every forwarded anchor to a page that renders.
102 lines
3.6 KiB
TypeScript
102 lines
3.6 KiB
TypeScript
/**
|
|
* v1.25.11 (#148) — the "Layout" hub (slug stays `layout`).
|
|
*
|
|
* The hub is a navigable index: it lists each module as a clickable row
|
|
* (`<a href="/settings/layout/<id>">` with the localized title + description +
|
|
* a chevron) and links to the module's own subpage. NOTHING is stacked inline.
|
|
* These tests pin the hub contract — the rows link to the right subpages, the
|
|
* module gate hides a disabled module's row, the never-gated rows always show,
|
|
* and the titles resolve through i18n. The child section components are NOT
|
|
* rendered by the hub, so they need no mocking here (each has its own test and
|
|
* its own subpage).
|
|
*/
|
|
import { describe, expect, it, vi } from "vitest";
|
|
import { renderToStaticMarkup } from "react-dom/server";
|
|
|
|
// Mutable modules ref so each render can pick a different gate map.
|
|
const authRef: { modules?: Record<string, boolean> } = {};
|
|
vi.mock("@/hooks/use-auth", () => ({
|
|
useAuth: () => ({
|
|
user: { id: "u1", username: "t", role: "USER", modules: authRef.modules },
|
|
isAuthenticated: true,
|
|
isLoading: false,
|
|
refetch: vi.fn(),
|
|
}),
|
|
}));
|
|
|
|
// The hub renders post-mount fail-open gating via `useMounted()`. Under SSR
|
|
// (`renderToStaticMarkup`) `useMounted()` returns `false`, which would render
|
|
// EVERY row regardless of the gate map. Force it to the hydrated (`true`)
|
|
// snapshot so these tests exercise the real module filter.
|
|
vi.mock("@/hooks/use-mounted", () => ({
|
|
useMounted: () => true,
|
|
}));
|
|
|
|
import { I18nProvider } from "@/lib/i18n/context";
|
|
import { LayoutSection } from "../layout-section";
|
|
import { primeLocales } from "@/__tests__/helpers/prime-locales";
|
|
|
|
primeLocales("de");
|
|
|
|
function render(modules?: Record<string, boolean>, locale: "en" | "de" = "en") {
|
|
authRef.modules = modules;
|
|
return renderToStaticMarkup(
|
|
<I18nProvider initialLocale={locale}>
|
|
<LayoutSection />
|
|
</I18nProvider>,
|
|
);
|
|
}
|
|
|
|
describe("<LayoutSection> hub", () => {
|
|
it("lists every module as a clickable row linking to its subpage", () => {
|
|
const html = render(undefined);
|
|
for (const id of [
|
|
"dashboard",
|
|
"insights",
|
|
"medications",
|
|
"labs",
|
|
"illness",
|
|
"vorsorge",
|
|
]) {
|
|
expect(html).toContain(`href="/settings/layout/${id}"`);
|
|
}
|
|
});
|
|
|
|
it("hides a module's row when that module is disabled", () => {
|
|
const html = render({ medications: false, labs: false });
|
|
expect(html).not.toContain('href="/settings/layout/medications"');
|
|
expect(html).not.toContain('href="/settings/layout/labs"');
|
|
// Other modules unaffected; Vorsorge is never gated.
|
|
expect(html).toContain('href="/settings/layout/illness"');
|
|
expect(html).toContain('href="/settings/layout/vorsorge"');
|
|
});
|
|
|
|
it("always lists dashboard, insights, and vorsorge (no module gate)", () => {
|
|
const html = render({
|
|
medications: false,
|
|
mood: false,
|
|
labs: false,
|
|
illness: false,
|
|
});
|
|
expect(html).toContain('href="/settings/layout/dashboard"');
|
|
expect(html).toContain('href="/settings/layout/insights"');
|
|
expect(html).toContain('href="/settings/layout/vorsorge"');
|
|
// v1.44 — mood is tag management and left Arrangement for Modules.
|
|
expect(html).not.toContain('href="/settings/layout/mood"');
|
|
});
|
|
|
|
it("resolves its row titles via i18n (no raw keys leak)", () => {
|
|
const html = render();
|
|
expect(html).toContain("Medications");
|
|
expect(html).toContain("Labs");
|
|
expect(html).not.toContain("settings.sections.layout.");
|
|
});
|
|
|
|
it("resolves the titles in German too", () => {
|
|
const html = render(undefined, "de");
|
|
expect(html).toContain("Medikamente");
|
|
expect(html).toContain("Labor");
|
|
expect(html).not.toContain("settings.sections.layout.");
|
|
});
|
|
});
|