/** * useLayouts Hook Tests * Following TDD principles */ import { describe, it, expect, vi, beforeEach } from "vitest"; import { renderHook, waitFor } from "@testing-library/react"; import { QueryClient, QueryClientProvider } from "@tanstack/react-query"; import type { ReactNode } from "react"; // We'll implement this hook import { useLayouts, useCreateLayout, useUpdateLayout, useDeleteLayout } from "../useLayouts"; global.fetch = vi.fn(); const createWrapper = () => { const queryClient = new QueryClient({ defaultOptions: { queries: { retry: false }, mutations: { retry: false }, }, }); return ({ children }: { children: ReactNode }) => ( {children} ); }; describe("useLayouts", (): void => { beforeEach((): void => { vi.clearAllMocks(); }); it("should fetch layouts on mount", async (): Promise => { const mockLayouts = [ { id: "1", name: "Default", isDefault: true, layout: [] }, { id: "2", name: "Custom", isDefault: false, layout: [] }, ]; (global.fetch as any).mockResolvedValueOnce({ ok: true, json: () => mockLayouts, }); const { result } = renderHook(() => useLayouts(), { wrapper: createWrapper(), }); await waitFor(() => { expect(result.current.data).toEqual(mockLayouts); }); }); it("should handle fetch errors", async (): Promise => { (global.fetch as any).mockRejectedValueOnce(new Error("API Error")); const { result } = renderHook(() => useLayouts(), { wrapper: createWrapper(), }); await waitFor(() => { expect(result.current.isError).toBe(true); }); }); it("should show loading state", (): void => { (global.fetch as any).mockImplementation(() => new Promise(() => {})); const { result } = renderHook(() => useLayouts(), { wrapper: createWrapper(), }); expect(result.current.isLoading).toBe(true); }); }); describe("useCreateLayout", (): void => { beforeEach((): void => { vi.clearAllMocks(); }); it("should create a new layout", async (): Promise => { const mockLayout = { id: "3", name: "New Layout", isDefault: false, layout: [], }; (global.fetch as any).mockResolvedValueOnce({ ok: true, json: () => mockLayout, }); const { result } = renderHook(() => useCreateLayout(), { wrapper: createWrapper(), }); result.current.mutate({ name: "New Layout", layout: [], }); await waitFor(() => { expect(result.current.isSuccess).toBe(true); expect(result.current.data).toEqual(mockLayout); }); }); it("should handle creation errors", async (): Promise => { (global.fetch as any).mockRejectedValueOnce(new Error("API Error")); const { result } = renderHook(() => useCreateLayout(), { wrapper: createWrapper(), }); result.current.mutate({ name: "New Layout", layout: [], }); await waitFor(() => { expect(result.current.isError).toBe(true); }); }); }); describe("useUpdateLayout", (): void => { beforeEach((): void => { vi.clearAllMocks(); }); it("should update an existing layout", async (): Promise => { const mockLayout = { id: "1", name: "Updated Layout", isDefault: false, layout: [{ i: "widget-1", x: 0, y: 0, w: 2, h: 2 }], }; (global.fetch as any).mockResolvedValueOnce({ ok: true, json: () => mockLayout, }); const { result } = renderHook(() => useUpdateLayout(), { wrapper: createWrapper(), }); result.current.mutate({ id: "1", name: "Updated Layout", layout: [{ i: "widget-1", x: 0, y: 0, w: 2, h: 2 }], }); await waitFor(() => { expect(result.current.isSuccess).toBe(true); expect(result.current.data).toEqual(mockLayout); }); }); it("should handle update errors", async (): Promise => { (global.fetch as any).mockRejectedValueOnce(new Error("API Error")); const { result } = renderHook(() => useUpdateLayout(), { wrapper: createWrapper(), }); result.current.mutate({ id: "1", name: "Updated Layout", }); await waitFor(() => { expect(result.current.isError).toBe(true); }); }); }); describe("useDeleteLayout", (): void => { beforeEach((): void => { vi.clearAllMocks(); }); it("should delete a layout", async (): Promise => { (global.fetch as any).mockResolvedValueOnce({ ok: true, json: () => ({ success: true }), }); const { result } = renderHook(() => useDeleteLayout(), { wrapper: createWrapper(), }); result.current.mutate("1"); await waitFor(() => { expect(result.current.isSuccess).toBe(true); }); }); it("should handle deletion errors", async (): Promise => { (global.fetch as any).mockRejectedValueOnce(new Error("API Error")); const { result } = renderHook(() => useDeleteLayout(), { wrapper: createWrapper(), }); result.current.mutate("1"); await waitFor(() => { expect(result.current.isError).toBe(true); }); }); });