import { renderHook, act, cleanup } from '@testing-library/react-native'; import { useLoadingState, useSimpleLoading, useMultipleLoadingStates, } from '@/hooks/useLoadingState'; // Cleanup after each test to prevent unmounted renderer issues afterEach(() => { cleanup(); }); describe('useLoadingState', () => { it('initializes with default state', () => { const { result } = renderHook(() => useLoadingState()); expect(result.current.data).toBeNull(); expect(result.current.isLoading).toBe(false); expect(result.current.error).toBeNull(); expect(result.current.isRefreshing).toBe(false); }); it('initializes with provided initial data', () => { const { result } = renderHook(() => useLoadingState({ initialData: { id: 1, name: 'Test' } }) ); expect(result.current.data).toEqual({ id: 1, name: 'Test' }); }); it('setData updates data and clears error', () => { const { result } = renderHook(() => useLoadingState()); act(() => { result.current.setError('Previous error'); }); act(() => { result.current.setData('new data'); }); expect(result.current.data).toBe('new data'); expect(result.current.error).toBeNull(); }); it('setLoading updates loading state', () => { const { result } = renderHook(() => useLoadingState()); act(() => { result.current.setLoading(true); }); expect(result.current.isLoading).toBe(true); act(() => { result.current.setLoading(false); }); expect(result.current.isLoading).toBe(false); }); it('setError updates error and clears loading states', () => { const { result } = renderHook(() => useLoadingState()); act(() => { result.current.setLoading(true); }); act(() => { result.current.setError('Something went wrong'); }); expect(result.current.error).toBe('Something went wrong'); expect(result.current.isLoading).toBe(false); expect(result.current.isRefreshing).toBe(false); }); it('setRefreshing updates refreshing state', () => { const { result } = renderHook(() => useLoadingState()); act(() => { result.current.setRefreshing(true); }); expect(result.current.isRefreshing).toBe(true); }); it('reset restores initial state', () => { const { result } = renderHook(() => useLoadingState({ initialData: 'initial' }) ); act(() => { result.current.setData('modified'); result.current.setLoading(true); result.current.setError('error'); }); act(() => { result.current.reset(); }); expect(result.current.data).toBe('initial'); expect(result.current.isLoading).toBe(false); expect(result.current.error).toBeNull(); expect(result.current.isRefreshing).toBe(false); }); it('execute handles successful async function', async () => { const { result } = renderHook(() => useLoadingState()); await act(async () => { await result.current.execute(async () => { return 'success'; }); }); expect(result.current.data).toBe('success'); expect(result.current.isLoading).toBe(false); expect(result.current.error).toBeNull(); }); it('execute handles async function error', async () => { const { result } = renderHook(() => useLoadingState()); await act(async () => { await result.current.execute(async () => { throw new Error('Network error'); }); }); expect(result.current.error).toBe('Network error'); expect(result.current.isLoading).toBe(false); }); it('execute with refresh option returns data', async () => { const { result } = renderHook(() => useLoadingState()); await act(async () => { await result.current.execute( async () => { return 'refreshed data'; }, { refresh: true } ); }); expect(result.current.data).toBe('refreshed data'); expect(result.current.isRefreshing).toBe(false); }); it('execute uses transform function', async () => { const { result } = renderHook(() => useLoadingState()); await act(async () => { await result.current.execute( async () => { return { value: 'raw' }; }, { transform: (res) => res.value.toUpperCase() } ); }); expect(result.current.data).toBe('RAW'); }); }); describe('useSimpleLoading', () => { it('initializes with default state', () => { const { result } = renderHook(() => useSimpleLoading()); expect(result.current.isLoading).toBe(false); }); it('initializes with provided initial state', () => { const { result } = renderHook(() => useSimpleLoading(true)); expect(result.current.isLoading).toBe(true); }); it('startLoading sets isLoading to true', () => { const { result } = renderHook(() => useSimpleLoading()); act(() => { result.current.startLoading(); }); expect(result.current.isLoading).toBe(true); }); it('stopLoading sets isLoading to false', () => { const { result } = renderHook(() => useSimpleLoading(true)); act(() => { result.current.stopLoading(); }); expect(result.current.isLoading).toBe(false); }); it('toggleLoading toggles isLoading', () => { const { result } = renderHook(() => useSimpleLoading(false)); act(() => { result.current.toggleLoading(); }); expect(result.current.isLoading).toBe(true); act(() => { result.current.toggleLoading(); }); expect(result.current.isLoading).toBe(false); }); it('withLoading wraps async function', async () => { const { result } = renderHook(() => useSimpleLoading()); await act(async () => { const returnValue = await result.current.withLoading(async () => { return 'done'; }); expect(returnValue).toBe('done'); }); expect(result.current.isLoading).toBe(false); }); }); describe('useMultipleLoadingStates', () => { it('initializes with empty loading states', () => { const { result } = renderHook(() => useMultipleLoadingStates()); expect(result.current.loadingStates).toEqual({}); expect(result.current.anyLoading).toBe(false); }); it('setLoading updates individual loading state', () => { const { result } = renderHook(() => useMultipleLoadingStates()); act(() => { result.current.setLoading('item-1', true); }); expect(result.current.isLoading('item-1')).toBe(true); expect(result.current.isLoading('item-2')).toBe(false); expect(result.current.anyLoading).toBe(true); }); it('tracks multiple loading states', () => { const { result } = renderHook(() => useMultipleLoadingStates()); act(() => { result.current.setLoading('item-1', true); result.current.setLoading('item-2', true); }); expect(result.current.isLoading('item-1')).toBe(true); expect(result.current.isLoading('item-2')).toBe(true); expect(result.current.anyLoading).toBe(true); act(() => { result.current.setLoading('item-1', false); }); expect(result.current.isLoading('item-1')).toBe(false); expect(result.current.isLoading('item-2')).toBe(true); expect(result.current.anyLoading).toBe(true); }); it('clearAll clears all loading states', () => { const { result } = renderHook(() => useMultipleLoadingStates()); act(() => { result.current.setLoading('item-1', true); result.current.setLoading('item-2', true); }); act(() => { result.current.clearAll(); }); expect(result.current.loadingStates).toEqual({}); expect(result.current.anyLoading).toBe(false); }); });