toast.test.js 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158
  1. /* @vitest-environment node */
  2. import { describe, it, expect, vi, beforeEach } from "vitest";
  3. vi.mock("sonner", () => {
  4. const api = {
  5. success: vi.fn(() => "toast-success-id"),
  6. error: vi.fn(() => "toast-error-id"),
  7. info: vi.fn(() => "toast-info-id"),
  8. warning: vi.fn(() => "toast-warning-id"),
  9. loading: vi.fn(() => "toast-loading-id"),
  10. dismiss: vi.fn(() => undefined),
  11. };
  12. return { toast: api };
  13. });
  14. import { toast } from "sonner";
  15. import { ApiClientError } from "@/lib/frontend/apiClient";
  16. import {
  17. notifySuccess,
  18. notifyError,
  19. notifyInfo,
  20. notifyWarning,
  21. notifyLoading,
  22. dismissToast,
  23. mapApiErrorToToast,
  24. notifyApiError,
  25. } from "./toast.js";
  26. describe("lib/frontend/ui/toast", () => {
  27. beforeEach(() => {
  28. vi.clearAllMocks();
  29. });
  30. it("notifySuccess calls toast.success with title + description", () => {
  31. const id = notifySuccess({
  32. title: "Gespeichert",
  33. description: "Änderungen wurden übernommen.",
  34. });
  35. expect(id).toBe("toast-success-id");
  36. expect(toast.success).toHaveBeenCalledWith("Gespeichert", {
  37. description: "Änderungen wurden übernommen.",
  38. });
  39. });
  40. it("notifyError calls toast.error", () => {
  41. const id = notifyError({ title: "Fehler", description: "Oh nein." });
  42. expect(id).toBe("toast-error-id");
  43. expect(toast.error).toHaveBeenCalledWith("Fehler", {
  44. description: "Oh nein.",
  45. });
  46. });
  47. it("notifyInfo / notifyWarning / notifyLoading forward correctly", () => {
  48. notifyInfo({ title: "Info", description: "Hinweis" });
  49. expect(toast.info).toHaveBeenCalledWith("Info", { description: "Hinweis" });
  50. notifyWarning({ title: "Warnung" });
  51. expect(toast.warning).toHaveBeenCalledWith("Warnung", {});
  52. notifyLoading({ title: "Lädt…" });
  53. expect(toast.loading).toHaveBeenCalledWith("Lädt…", {});
  54. });
  55. it("dismissToast forwards to toast.dismiss", () => {
  56. dismissToast("x");
  57. expect(toast.dismiss).toHaveBeenCalledWith("x");
  58. });
  59. it("mapApiErrorToToast maps network errors", () => {
  60. const err = new ApiClientError({
  61. status: 0,
  62. code: "CLIENT_NETWORK_ERROR",
  63. message: "Network error",
  64. });
  65. expect(mapApiErrorToToast(err)).toEqual({
  66. title: "Netzwerkfehler",
  67. description:
  68. "Bitte prüfen Sie Ihre Verbindung und versuchen Sie es erneut.",
  69. });
  70. });
  71. it("mapApiErrorToToast maps unauthenticated", () => {
  72. const err = new ApiClientError({
  73. status: 401,
  74. code: "AUTH_UNAUTHENTICATED",
  75. message: "Unauthorized",
  76. });
  77. expect(mapApiErrorToToast(err)).toEqual({
  78. title: "Sitzung abgelaufen",
  79. description: "Bitte melden Sie sich erneut an.",
  80. });
  81. });
  82. it("mapApiErrorToToast maps validation errors generically", () => {
  83. const err = new ApiClientError({
  84. status: 400,
  85. code: "VALIDATION_WEAK_PASSWORD",
  86. message: "Weak password",
  87. });
  88. expect(mapApiErrorToToast(err)).toEqual({
  89. title: "Ungültige Eingabe",
  90. description:
  91. "Bitte prüfen Sie Ihre Eingaben und versuchen Sie es erneut.",
  92. });
  93. });
  94. it("mapApiErrorToToast supports overrides by error code", () => {
  95. const err = new ApiClientError({
  96. status: 401,
  97. code: "AUTH_INVALID_CREDENTIALS",
  98. message: "Invalid credentials",
  99. });
  100. const mapped = mapApiErrorToToast(err, {
  101. overrides: {
  102. AUTH_INVALID_CREDENTIALS: {
  103. title: "Aktuelles Passwort ist falsch.",
  104. description: null,
  105. },
  106. },
  107. });
  108. expect(mapped).toEqual({
  109. title: "Aktuelles Passwort ist falsch.",
  110. description: null,
  111. });
  112. });
  113. it("notifyApiError uses toast.error with mapped copy", () => {
  114. const err = new ApiClientError({
  115. status: 0,
  116. code: "CLIENT_NETWORK_ERROR",
  117. message: "Network error",
  118. });
  119. const id = notifyApiError(err);
  120. expect(id).toBe("toast-error-id");
  121. expect(toast.error).toHaveBeenCalledWith("Netzwerkfehler", {
  122. description:
  123. "Bitte prüfen Sie Ihre Verbindung und versuchen Sie es erneut.",
  124. });
  125. });
  126. it("notifySuccess returns null and does not toast when title is missing", () => {
  127. const id = notifySuccess({ title: " " });
  128. expect(id).toBe(null);
  129. expect(toast.success).not.toHaveBeenCalled();
  130. });
  131. });