Files
PriceUpdaterAppv2/tests/tui/hooks/usePerformanceOptimization.test.js

266 lines
6.6 KiB
JavaScript

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([]);
});
});