Files
HealthLog/src/components/settings/__tests__/notifications-section-module-gating.test.tsx
T
Marc-André Bombeck 71dbef1742 feat(settings): nine groups, module pages and one switch per state
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.
2026-10-21 19:43:02 +02:00

91 lines
3.4 KiB
TypeScript

/**
* Settings > Notifications is the one home for every reminder kind, each
* card gated on its module and failing open.
*
* v1.18.0 — mood reminder on `mood`, low-stock on `medications`.
* v1.44 — the Coach nudges came back (gated on the Coach surface), the
* checkup reminder switches joined (not a module, always shown), and a card
* names the reminders set where their subject lives (dose times on the
* medication, the cycle reminders in the per-event matrix).
*/
import { describe, expect, it, vi } from "vitest";
import { renderToStaticMarkup } from "react-dom/server";
vi.mock("next/navigation", () => ({
useRouter: () => ({ push: vi.fn(), replace: vi.fn() }),
usePathname: () => "/settings/notifications",
useSearchParams: () => new URLSearchParams(),
}));
vi.mock("@tanstack/react-query", () => ({
useQuery: () => ({ data: null, isLoading: false, refetch: vi.fn() }),
useQueryClient: () => ({ invalidateQueries: vi.fn() }),
useMutation: () => ({ mutate: vi.fn(), isPending: false }),
}));
// Mounted, so the Coach gate reads the module map like a hydrated page.
vi.mock("@/hooks/use-mounted", () => ({ useMounted: () => true }));
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(),
}),
}));
import { I18nProvider } from "@/lib/i18n/context";
import { NotificationsSection } from "../notifications-section";
function render(modules?: Record<string, boolean>) {
authRef.modules = modules;
return renderToStaticMarkup(
<I18nProvider initialLocale="en">
<NotificationsSection />
</I18nProvider>,
);
}
describe("<NotificationsSection> reminder kinds", () => {
it("shows every reminder card when no module map (fails open)", () => {
const html = render(undefined);
expect(html).toContain('id="mood-reminder"');
expect(html).toContain('id="low-stock"');
expect(html).toContain('id="coach-nudge"');
expect(html).toContain('id="vorsorge-reminders"');
expect(html).toContain('data-slot="notifications-elsewhere"');
expect(html).toContain('id="cycle-reminders"');
});
it("hides the mood card when the mood module is disabled", () => {
const html = render({ mood: false });
expect(html).not.toContain('id="mood-reminder"');
expect(html).toContain('id="low-stock"');
});
it("hides the low-stock card and the dose row when medications is disabled", () => {
const html = render({ medications: false });
expect(html).not.toContain('id="low-stock"');
expect(html).not.toContain('href="/medications"');
});
it("hides the Coach nudges when the Coach is off", () => {
expect(render({ coach: false })).not.toContain('id="coach-nudge"');
});
it("drops the elsewhere card when neither of its reminders applies", () => {
const html = render({ medications: false, cycle: false });
expect(html).not.toContain('data-slot="notifications-elsewhere"');
// The checkup reminders are not a module and stay.
expect(html).toContain('id="vorsorge-reminders"');
});
it("links to the per-event matrix and to the delivery channels", () => {
const html = render({});
expect(html).toContain('href="/notifications"');
expect(html).toContain('href="/settings/integrations#channels"');
});
});