/* @vitest-environment node */ import { describe, it, expect, vi, beforeEach } from "vitest"; vi.mock("sonner", () => { const api = { success: vi.fn(() => "toast-success-id"), error: vi.fn(() => "toast-error-id"), info: vi.fn(() => "toast-info-id"), warning: vi.fn(() => "toast-warning-id"), loading: vi.fn(() => "toast-loading-id"), dismiss: vi.fn(() => undefined), }; return { toast: api }; }); import { toast } from "sonner"; import { ApiClientError } from "@/lib/frontend/apiClient"; import { notifySuccess, notifyError, notifyInfo, notifyWarning, notifyLoading, dismissToast, mapApiErrorToToast, notifyApiError, } from "./toast.js"; describe("lib/frontend/ui/toast", () => { beforeEach(() => { vi.clearAllMocks(); }); it("notifySuccess calls toast.success with title + description", () => { const id = notifySuccess({ title: "Gespeichert", description: "Änderungen wurden übernommen.", }); expect(id).toBe("toast-success-id"); expect(toast.success).toHaveBeenCalledWith("Gespeichert", { description: "Änderungen wurden übernommen.", }); }); it("notifyError calls toast.error", () => { const id = notifyError({ title: "Fehler", description: "Oh nein." }); expect(id).toBe("toast-error-id"); expect(toast.error).toHaveBeenCalledWith("Fehler", { description: "Oh nein.", }); }); it("notifyInfo / notifyWarning / notifyLoading forward correctly", () => { notifyInfo({ title: "Info", description: "Hinweis" }); expect(toast.info).toHaveBeenCalledWith("Info", { description: "Hinweis" }); notifyWarning({ title: "Warnung" }); expect(toast.warning).toHaveBeenCalledWith("Warnung", {}); notifyLoading({ title: "Lädt…" }); expect(toast.loading).toHaveBeenCalledWith("Lädt…", {}); }); it("dismissToast forwards to toast.dismiss", () => { dismissToast("x"); expect(toast.dismiss).toHaveBeenCalledWith("x"); }); it("mapApiErrorToToast maps network errors", () => { const err = new ApiClientError({ status: 0, code: "CLIENT_NETWORK_ERROR", message: "Network error", }); expect(mapApiErrorToToast(err)).toEqual({ title: "Netzwerkfehler", description: "Bitte prüfen Sie Ihre Verbindung und versuchen Sie es erneut.", }); }); it("mapApiErrorToToast maps unauthenticated", () => { const err = new ApiClientError({ status: 401, code: "AUTH_UNAUTHENTICATED", message: "Unauthorized", }); expect(mapApiErrorToToast(err)).toEqual({ title: "Sitzung abgelaufen", description: "Bitte melden Sie sich erneut an.", }); }); it("mapApiErrorToToast maps validation errors generically", () => { const err = new ApiClientError({ status: 400, code: "VALIDATION_WEAK_PASSWORD", message: "Weak password", }); expect(mapApiErrorToToast(err)).toEqual({ title: "Ungültige Eingabe", description: "Bitte prüfen Sie Ihre Eingaben und versuchen Sie es erneut.", }); }); it("mapApiErrorToToast supports overrides by error code", () => { const err = new ApiClientError({ status: 401, code: "AUTH_INVALID_CREDENTIALS", message: "Invalid credentials", }); const mapped = mapApiErrorToToast(err, { overrides: { AUTH_INVALID_CREDENTIALS: { title: "Aktuelles Passwort ist falsch.", description: null, }, }, }); expect(mapped).toEqual({ title: "Aktuelles Passwort ist falsch.", description: null, }); }); it("notifyApiError uses toast.error with mapped copy", () => { const err = new ApiClientError({ status: 0, code: "CLIENT_NETWORK_ERROR", message: "Network error", }); const id = notifyApiError(err); expect(id).toBe("toast-error-id"); expect(toast.error).toHaveBeenCalledWith("Netzwerkfehler", { description: "Bitte prüfen Sie Ihre Verbindung und versuchen Sie es erneut.", }); }); it("notifySuccess returns null and does not toast when title is missing", () => { const id = notifySuccess({ title: " " }); expect(id).toBe(null); expect(toast.success).not.toHaveBeenCalled(); }); });