Files
ApiCore/ApiCore-Frontend/src/app/providers.spec.tsx
T
Nick.RosenloecherandClaude Opus 5.5 cf53cf35ac feat(t5): Darstellung hell/dunkel/Sonne ohne Flackern (T5a A2)
Blockierendes Skript im <head> setzt Darstellung, Kontrast und
Bewegung vor dem ersten Zeichnen (gleiche Logik wie resolveAppearance,
für alle Eingaben getestet), theme-color je Satz, Wahl je Gerät in
localStorage mit Rückfall bei gesperrtem Speicher; Abgleich über Tabs,
Systemeinstellungen und Seitenwechsel an einer Stelle. Profil-Karte
„Darstellung“, Sonnenmodus im Benutzermenü; Dunkel bleibt bis A4 hinter
NEXT_PUBLIC_APPEARANCE_DARK. next-themes entfernt. ARCHITECTURE §9:
'unsafe-inline' der Frontend-CSP als getragenes Risiko mit Begründung.

Refs #26

Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
2026-10-16 16:35:28 +02:00

65 lines
2.4 KiB
TypeScript

import { useMutation } from "@tanstack/react-query";
import { act, render, screen, waitFor } from "@testing-library/react";
import userEvent from "@testing-library/user-event";
import { afterEach, describe, expect, it, vi } from "vitest";
import { readFileSync } from "node:fs";
import path from "node:path";
import { Providers } from "./providers";
vi.mock("@/lib/auth/auth-context", () => ({
AuthProvider: ({ children }: { children: React.ReactNode }) => <>{children}</>,
}));
function Probe({ mutationFn }: { mutationFn: () => Promise<string> }) {
const mutation = useMutation({ mutationFn });
return <button onClick={() => mutation.mutate()}>Speichern</button>;
}
describe("Providers", () => {
afterEach(() => {
vi.restoreAllMocks();
// TanStack's onlineManager tracks the browser events, so restore it too.
act(() => {
window.dispatchEvent(new Event("online"));
});
});
it("runs mutations while navigator.onLine is false", async () => {
vi.spyOn(window.navigator, "onLine", "get").mockReturnValue(false);
const mutationFn = vi.fn().mockResolvedValue("ok");
render(
<Providers>
<Probe mutationFn={mutationFn} />
</Providers>,
);
// onlineManager (listening once mounted) only learns about airplane mode through the window event.
act(() => {
window.dispatchEvent(new Event("offline"));
});
await userEvent.click(screen.getByRole("button", { name: "Speichern" }));
await waitFor(() => expect(mutationFn).toHaveBeenCalledTimes(1));
});
it("does not apply the motion level itself any more (the inline script does)", () => {
document.documentElement.removeAttribute("data-motion");
window.localStorage.setItem("apicore.motion", "off");
render(
<Providers>
<p>x</p>
</Providers>,
);
expect(document.documentElement).not.toHaveAttribute("data-motion");
window.localStorage.clear();
});
});
describe("theme handling", () => {
it("the toaster follows resolvedTheme and next-themes is not imported", () => {
const toaster = readFileSync(path.resolve(__dirname, "../components/ui/sonner.tsx"), "utf8");
expect(toaster).toContain("resolvedTheme");
expect(toaster).not.toContain("next-themes");
const pkg = JSON.parse(readFileSync(path.resolve(__dirname, "../../package.json"), "utf8"));
expect(pkg.dependencies["next-themes"]).toBeUndefined();
});
});