2026-01-07 09:35:04 +00:00
|
|
|
import { renderHook, act, waitFor } from '@testing-library/react';
|
|
|
|
|
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
|
2026-01-13 18:47:57 +00:00
|
|
|
import {
|
|
|
|
|
useRoutePreload,
|
|
|
|
|
usePerformanceOptimization,
|
|
|
|
|
useErrorHandler,
|
|
|
|
|
useLoadingState,
|
|
|
|
|
} from './useRoutePreload';
|
2026-01-07 09:35:04 +00:00
|
|
|
|
|
|
|
|
describe('useRoutePreload additional hooks', () => {
|
|
|
|
|
beforeEach(() => {
|
|
|
|
|
vi.clearAllMocks();
|
|
|
|
|
vi.useFakeTimers();
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
afterEach(() => {
|
|
|
|
|
vi.useRealTimers();
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
describe('useRoutePreload', () => {
|
|
|
|
|
it('should preload route with delay', async () => {
|
|
|
|
|
const { result } = renderHook(() => useRoutePreload('/test-route', 100));
|
2026-01-13 18:47:57 +00:00
|
|
|
|
2026-01-07 09:35:04 +00:00
|
|
|
expect(result.current.isPreloading).toBe(false);
|
|
|
|
|
expect(result.current.isPreloaded).toBe(false);
|
2026-01-13 18:47:57 +00:00
|
|
|
|
2026-01-07 09:35:04 +00:00
|
|
|
act(() => {
|
|
|
|
|
result.current.preloadRoute();
|
|
|
|
|
});
|
2026-01-13 18:47:57 +00:00
|
|
|
|
2026-01-07 09:35:04 +00:00
|
|
|
expect(result.current.isPreloading).toBe(true);
|
2026-01-13 18:47:57 +00:00
|
|
|
|
2026-01-07 09:35:04 +00:00
|
|
|
await act(async () => {
|
|
|
|
|
vi.advanceTimersByTime(100);
|
|
|
|
|
await waitFor(() => {
|
|
|
|
|
expect(result.current.isPreloaded).toBe(true);
|
|
|
|
|
});
|
|
|
|
|
});
|
2026-01-13 18:47:57 +00:00
|
|
|
|
2026-01-07 09:35:04 +00:00
|
|
|
expect(result.current.isPreloading).toBe(false);
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
describe('usePerformanceOptimization', () => {
|
|
|
|
|
it('should return performance optimization hook', () => {
|
|
|
|
|
const { result } = renderHook(() => usePerformanceOptimization());
|
2026-01-13 18:47:57 +00:00
|
|
|
|
2026-01-07 09:35:04 +00:00
|
|
|
expect(result.current).toBeDefined();
|
|
|
|
|
expect(result.current).toHaveProperty('isVisible');
|
|
|
|
|
expect(result.current).toHaveProperty('optimizeForVisibility');
|
|
|
|
|
expect(result.current).toHaveProperty('throttle');
|
|
|
|
|
expect(result.current).toHaveProperty('debounce');
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it('should detect visibility changes', () => {
|
|
|
|
|
const { result } = renderHook(() => usePerformanceOptimization());
|
2026-01-13 18:47:57 +00:00
|
|
|
|
2026-01-07 09:35:04 +00:00
|
|
|
expect(result.current.isVisible).toBe(true);
|
2026-01-13 18:47:57 +00:00
|
|
|
|
2026-01-07 09:35:04 +00:00
|
|
|
Object.defineProperty(document, 'hidden', {
|
|
|
|
|
writable: true,
|
|
|
|
|
value: true,
|
|
|
|
|
});
|
2026-01-13 18:47:57 +00:00
|
|
|
|
2026-01-07 09:35:04 +00:00
|
|
|
document.dispatchEvent(new Event('visibilitychange'));
|
2026-01-13 18:47:57 +00:00
|
|
|
|
2026-01-07 09:35:04 +00:00
|
|
|
expect(result.current.isVisible).toBe(false);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it('should throttle function calls', () => {
|
|
|
|
|
const { result } = renderHook(() => usePerformanceOptimization());
|
|
|
|
|
const mockFn = vi.fn();
|
|
|
|
|
const throttled = result.current.throttle(mockFn, 100);
|
2026-01-13 18:47:57 +00:00
|
|
|
|
2026-01-07 09:35:04 +00:00
|
|
|
throttled();
|
|
|
|
|
throttled();
|
|
|
|
|
throttled();
|
2026-01-13 18:47:57 +00:00
|
|
|
|
2026-01-07 09:35:04 +00:00
|
|
|
expect(mockFn).toHaveBeenCalledTimes(1);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it('should debounce function calls', async () => {
|
|
|
|
|
const { result } = renderHook(() => usePerformanceOptimization());
|
|
|
|
|
const mockFn = vi.fn();
|
|
|
|
|
const debounced = result.current.debounce(mockFn, 100);
|
2026-01-13 18:47:57 +00:00
|
|
|
|
2026-01-07 09:35:04 +00:00
|
|
|
debounced();
|
|
|
|
|
debounced();
|
|
|
|
|
debounced();
|
2026-01-13 18:47:57 +00:00
|
|
|
|
2026-01-07 09:35:04 +00:00
|
|
|
expect(mockFn).not.toHaveBeenCalled();
|
2026-01-13 18:47:57 +00:00
|
|
|
|
2026-01-07 09:35:04 +00:00
|
|
|
await act(async () => {
|
|
|
|
|
vi.advanceTimersByTime(100);
|
|
|
|
|
});
|
2026-01-13 18:47:57 +00:00
|
|
|
|
2026-01-07 09:35:04 +00:00
|
|
|
expect(mockFn).toHaveBeenCalledTimes(1);
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
describe('useErrorHandler', () => {
|
|
|
|
|
it('should return error handler hook', () => {
|
|
|
|
|
const { result } = renderHook(() => useErrorHandler());
|
2026-01-13 18:47:57 +00:00
|
|
|
|
2026-01-07 09:35:04 +00:00
|
|
|
expect(result.current).toBeDefined();
|
|
|
|
|
expect(result.current).toHaveProperty('error');
|
|
|
|
|
expect(result.current).toHaveProperty('handleError');
|
|
|
|
|
expect(result.current).toHaveProperty('clearError');
|
|
|
|
|
expect(result.current).toHaveProperty('withErrorHandling');
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it('should handle errors', () => {
|
|
|
|
|
const { result } = renderHook(() => useErrorHandler());
|
|
|
|
|
const testError = new Error('Test error');
|
2026-01-13 18:47:57 +00:00
|
|
|
|
2026-01-07 09:35:04 +00:00
|
|
|
act(() => {
|
|
|
|
|
result.current.handleError(testError);
|
|
|
|
|
});
|
2026-01-13 18:47:57 +00:00
|
|
|
|
2026-01-07 09:35:04 +00:00
|
|
|
expect(result.current.error).toEqual(testError);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it('should clear errors', () => {
|
|
|
|
|
const { result } = renderHook(() => useErrorHandler());
|
|
|
|
|
const testError = new Error('Test error');
|
2026-01-13 18:47:57 +00:00
|
|
|
|
2026-01-07 09:35:04 +00:00
|
|
|
act(() => {
|
|
|
|
|
result.current.handleError(testError);
|
|
|
|
|
});
|
2026-01-13 18:47:57 +00:00
|
|
|
|
2026-01-07 09:35:04 +00:00
|
|
|
expect(result.current.error).toEqual(testError);
|
2026-01-13 18:47:57 +00:00
|
|
|
|
2026-01-07 09:35:04 +00:00
|
|
|
act(() => {
|
|
|
|
|
result.current.clearError();
|
|
|
|
|
});
|
2026-01-13 18:47:57 +00:00
|
|
|
|
2026-01-07 09:35:04 +00:00
|
|
|
expect(result.current.error).toBeNull();
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it('should wrap function with error handling', () => {
|
|
|
|
|
const { result } = renderHook(() => useErrorHandler());
|
|
|
|
|
const testFn = vi.fn(() => {
|
|
|
|
|
throw new Error('Test error');
|
|
|
|
|
});
|
2026-01-13 18:47:57 +00:00
|
|
|
|
2026-01-07 09:35:04 +00:00
|
|
|
const wrappedFn = result.current.withErrorHandling(testFn);
|
2026-01-13 18:47:57 +00:00
|
|
|
|
2026-01-07 09:35:04 +00:00
|
|
|
expect(() => wrappedFn()).toThrow();
|
|
|
|
|
expect(result.current.error).toBeDefined();
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
describe('useLoadingState', () => {
|
|
|
|
|
it('should return loading state hook', () => {
|
|
|
|
|
const { result } = renderHook(() => useLoadingState());
|
2026-01-13 18:47:57 +00:00
|
|
|
|
2026-01-07 09:35:04 +00:00
|
|
|
expect(result.current).toBeDefined();
|
|
|
|
|
expect(result.current).toHaveProperty('isLoading');
|
|
|
|
|
expect(result.current).toHaveProperty('setLoading');
|
|
|
|
|
expect(result.current).toHaveProperty('setLoadingFor');
|
|
|
|
|
expect(result.current).toHaveProperty('isLoadingFor');
|
|
|
|
|
expect(result.current).toHaveProperty('withLoading');
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it('should set loading state', () => {
|
|
|
|
|
const { result } = renderHook(() => useLoadingState());
|
2026-01-13 18:47:57 +00:00
|
|
|
|
2026-01-07 09:35:04 +00:00
|
|
|
act(() => {
|
|
|
|
|
result.current.setLoading(true);
|
|
|
|
|
});
|
2026-01-13 18:47:57 +00:00
|
|
|
|
2026-01-07 09:35:04 +00:00
|
|
|
expect(result.current.isLoading).toBe(true);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it('should set loading state for specific key', () => {
|
|
|
|
|
const { result } = renderHook(() => useLoadingState());
|
2026-01-13 18:47:57 +00:00
|
|
|
|
2026-01-07 09:35:04 +00:00
|
|
|
act(() => {
|
|
|
|
|
result.current.setLoadingFor('test-key', true);
|
|
|
|
|
});
|
2026-01-13 18:47:57 +00:00
|
|
|
|
2026-01-07 09:35:04 +00:00
|
|
|
expect(result.current.isLoadingFor('test-key')).toBe(true);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it('should handle async operations with loading', async () => {
|
|
|
|
|
const { result } = renderHook(() => useLoadingState());
|
|
|
|
|
const asyncFn = vi.fn(async () => {
|
2026-01-13 18:47:57 +00:00
|
|
|
await new Promise((resolve) => setTimeout(resolve, 100));
|
2026-01-07 09:35:04 +00:00
|
|
|
return 'result';
|
|
|
|
|
});
|
2026-01-13 18:47:57 +00:00
|
|
|
|
2026-01-07 09:35:04 +00:00
|
|
|
const promise = act(async () => {
|
|
|
|
|
return await result.current.withLoading(asyncFn);
|
|
|
|
|
});
|
2026-01-13 18:47:57 +00:00
|
|
|
|
2026-01-07 09:35:04 +00:00
|
|
|
expect(result.current.isLoading).toBe(true);
|
2026-01-13 18:47:57 +00:00
|
|
|
|
2026-01-07 09:35:04 +00:00
|
|
|
await act(async () => {
|
|
|
|
|
vi.advanceTimersByTime(100);
|
|
|
|
|
await promise;
|
|
|
|
|
});
|
2026-01-13 18:47:57 +00:00
|
|
|
|
2026-01-07 09:35:04 +00:00
|
|
|
expect(result.current.isLoading).toBe(false);
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
});
|