const React = require("react"); const { renderHook, act } = require("@testing-library/react"); const { usePerformanceOptimization, useVirtualScrolling, useLazyLoading, useDebouncedSearch, } = require("../../../src/tui/hooks/usePerformanceOptimization.js"); // Mock timers for testing jest.useFakeTimers(); describe("usePerformanceOptimization", () => { afterEach(() => { jest.clearAllTimers(); }); it("should provide performance optimization functions", () => { const { result } = renderHook(() => usePerformanceOptimization("test-component") ); expect(result.current).toHaveProperty("createDebouncedFunction"); expect(result.current).toHaveProperty("createThrottledFunction"); expect(result.current).toHaveProperty("createMemoizedFunction"); expect(result.current).toHaveProperty("createVirtualScrolling"); expect(result.current).toHaveProperty("createLazyLoading"); expect(result.current).toHaveProperty("registerEventListener"); expect(result.current).toHaveProperty("optimizeRender"); expect(result.current).toHaveProperty("createBatchedUpdate"); expect(result.current).toHaveProperty("forceCleanup"); }); it("should create debounced functions", () => { const { result } = renderHook(() => usePerformanceOptimization("test-component") ); let callCount = 0; const testFunction = () => { callCount++; }; act(() => { const debouncedFn = result.current.createDebouncedFunction( testFunction, 100 ); debouncedFn(); debouncedFn(); debouncedFn(); }); expect(callCount).toBe(0); act(() => { jest.advanceTimersByTime(150); }); expect(callCount).toBe(1); }); }); describe("useVirtualScrolling", () => { it("should provide virtual scrolling data", () => { const items = Array.from({ length: 100 }, (_, i) => ({ id: i, name: `Item ${i}`, })); const options = { itemHeight: 30, containerHeight: 300 }; const { result } = renderHook(() => useVirtualScrolling(items, options)); expect(result.current).toHaveProperty("visibleItems"); expect(result.current).toHaveProperty("totalHeight"); expect(result.current).toHaveProperty("startIndex"); expect(result.current).toHaveProperty("endIndex"); expect(result.current).toHaveProperty("handleScroll"); expect(result.current.totalHeight).toBe(3000); // 100 * 30 }); it("should handle scroll updates", () => { const items = Array.from({ length: 100 }, (_, i) => ({ id: i, name: `Item ${i}`, })); const options = { itemHeight: 30, containerHeight: 300 }; const { result } = renderHook(() => useVirtualScrolling(items, options)); act(() => { result.current.handleScroll(150); }); expect(result.current.scrollTop).toBe(150); expect(result.current.startIndex).toBe(5); // 150 / 30 }); }); describe("useLazyLoading", () => { it("should load data lazily", async () => { const mockLoadFunction = jest.fn().mockResolvedValue({ items: [ { id: 1, name: "Item 1" }, { id: 2, name: "Item 2" }, ], hasMore: true, }); const { result, waitForNextUpdate } = renderHook(() => useLazyLoading(mockLoadFunction, { pageSize: 2 }) ); // Initial state expect(result.current.loading).toBe(true); expect(result.current.items).toEqual([]); // Wait for initial load await waitForNextUpdate(); expect(result.current.loading).toBe(false); expect(result.current.items).toHaveLength(2); expect(result.current.hasMore).toBe(true); expect(mockLoadFunction).toHaveBeenCalledWith({ page: 0, pageSize: 2, offset: 0, }); }); it("should load more data", async () => { const mockLoadFunction = jest .fn() .mockResolvedValueOnce({ items: [{ id: 1, name: "Item 1" }], hasMore: true, }) .mockResolvedValueOnce({ items: [{ id: 2, name: "Item 2" }], hasMore: false, }); const { result, waitForNextUpdate } = renderHook(() => useLazyLoading(mockLoadFunction, { pageSize: 1, enablePreloading: false }) ); // Wait for initial load await waitForNextUpdate(); expect(result.current.items).toHaveLength(1); // Load more act(() => { result.current.loadMore(); }); await waitForNextUpdate(); expect(result.current.items).toHaveLength(2); expect(result.current.hasMore).toBe(false); }); it("should handle reload", async () => { const mockLoadFunction = jest.fn().mockResolvedValue({ items: [{ id: 1, name: "Item 1" }], hasMore: false, }); const { result, waitForNextUpdate } = renderHook(() => useLazyLoading(mockLoadFunction) ); // Wait for initial load await waitForNextUpdate(); expect(mockLoadFunction).toHaveBeenCalledTimes(1); // Reload act(() => { result.current.reload(); }); await waitForNextUpdate(); expect(mockLoadFunction).toHaveBeenCalledTimes(2); }); }); describe("useDebouncedSearch", () => { it("should debounce search queries", async () => { const mockSearchFunction = jest .fn() .mockResolvedValue([{ id: 1, name: "Test Result" }]); const { result, waitForNextUpdate } = renderHook(() => useDebouncedSearch(mockSearchFunction, 100) ); // Update query multiple times rapidly act(() => { result.current.updateQuery("t"); result.current.updateQuery("te"); result.current.updateQuery("tes"); result.current.updateQuery("test"); }); expect(result.current.query).toBe("test"); expect(result.current.loading).toBe(false); // Should not be loading yet due to debounce // Advance timers to trigger debounced search act(() => { jest.advanceTimersByTime(150); }); await waitForNextUpdate(); expect(mockSearchFunction).toHaveBeenCalledTimes(1); expect(mockSearchFunction).toHaveBeenCalledWith("test"); expect(result.current.results).toHaveLength(1); }); it("should clear search", () => { const mockSearchFunction = jest.fn().mockResolvedValue([]); const { result } = renderHook(() => useDebouncedSearch(mockSearchFunction)); act(() => { result.current.updateQuery("test"); }); expect(result.current.query).toBe("test"); act(() => { result.current.clearSearch(); }); expect(result.current.query).toBe(""); expect(result.current.results).toEqual([]); }); it("should handle empty queries", async () => { const mockSearchFunction = jest.fn().mockResolvedValue([]); const { result } = renderHook(() => useLazyLoading(() => Promise.resolve({ items: [{ id: 1 }], hasMore: false }) ) ); const { result: searchResult } = renderHook(() => useDebouncedSearch(mockSearchFunction) ); act(() => { searchResult.current.updateQuery(" "); // Whitespace only }); act(() => { jest.advanceTimersByTime(150); }); expect(mockSearchFunction).not.toHaveBeenCalled(); expect(searchResult.current.results).toEqual([]); }); });