Add toast notification system with sonner library
- Create Toaster component wrapping sonner at bottom-right position - Add showToast utility with success/error/info methods - Error toasts persist until dismissed, others auto-dismiss after 5s - Migrate error handling to toasts across all pages: - Dashboard (override toggle errors) - Settings (save/load success/error) - Garmin settings (connection success/error) - Calendar (load errors) - Period History (load/delete errors) - Add dark mode support for toast styling - Add Toaster provider to root layout - 27 new tests (23 toaster component + 4 integration) - Total: 977 unit tests passing P5.2 COMPLETE - All P0-P5 items now complete. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -3,6 +3,16 @@
|
||||
import { fireEvent, render, screen, waitFor } from "@testing-library/react";
|
||||
import { beforeEach, describe, expect, it, vi } from "vitest";
|
||||
|
||||
// Mock showToast utility with vi.hoisted to avoid hoisting issues
|
||||
const mockShowToast = vi.hoisted(() => ({
|
||||
success: vi.fn(),
|
||||
error: vi.fn(),
|
||||
info: vi.fn(),
|
||||
}));
|
||||
vi.mock("@/components/ui/toaster", () => ({
|
||||
showToast: mockShowToast,
|
||||
}));
|
||||
|
||||
// Mock fetch globally
|
||||
const mockFetch = vi.fn();
|
||||
global.fetch = mockFetch;
|
||||
@@ -55,6 +65,9 @@ const mockUserResponse = {
|
||||
describe("Dashboard", () => {
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks();
|
||||
mockShowToast.success.mockClear();
|
||||
mockShowToast.error.mockClear();
|
||||
mockShowToast.info.mockClear();
|
||||
});
|
||||
|
||||
describe("rendering", () => {
|
||||
@@ -496,6 +509,42 @@ describe("Dashboard", () => {
|
||||
expect(screen.getByText(/flare mode active/i)).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
it("shows error toast when toggle fails", async () => {
|
||||
mockFetch
|
||||
.mockResolvedValueOnce({
|
||||
ok: true,
|
||||
json: () => Promise.resolve(mockTodayResponse),
|
||||
})
|
||||
.mockResolvedValueOnce({
|
||||
ok: true,
|
||||
json: () => Promise.resolve(mockUserResponse),
|
||||
});
|
||||
|
||||
render(<Dashboard />);
|
||||
|
||||
await waitFor(() => {
|
||||
expect(screen.getByText("Flare Mode")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
// Clear mock and set up for failed toggle
|
||||
mockFetch.mockClear();
|
||||
mockFetch.mockResolvedValueOnce({
|
||||
ok: false,
|
||||
json: () => Promise.resolve({ error: "Failed to update override" }),
|
||||
});
|
||||
|
||||
const flareCheckbox = screen.getByRole("checkbox", {
|
||||
name: /flare mode/i,
|
||||
});
|
||||
fireEvent.click(flareCheckbox);
|
||||
|
||||
await waitFor(() => {
|
||||
expect(mockShowToast.error).toHaveBeenCalledWith(
|
||||
"Failed to update override",
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("error handling", () => {
|
||||
|
||||
Reference in New Issue
Block a user