import { renderHook } from '@testing-library/react-hooks'; import { useFetch } from '../hooks/useFetch'; const fakeData = { data: 'fake data' }; const url = 'https://fakeapi.com/data'; global.fetch = jest.fn(() => Promise.resolve({ json: () => Promise.resolve(fakeData), headers: new Headers(), ok: true, redirected: false, status: 200, statusText: 'OK', type: 'default', url: '', clone: jest.fn(), body: null, bodyUsed: false, arrayBuffer: jest.fn(), blob: jest.fn(), formData: jest.fn(), text: jest.fn(), }), ); beforeEach(() => { (global.fetch as jest.Mock).mockClear(); }); it('should return data from cache when url exists in cache', async () => { const { result, waitForNextUpdate } = renderHook(() => useFetch(url)); expect(result.current.data).toBeNull(); expect(result.current.isLoading).toBeTruthy(); await waitForNextUpdate(); expect(result.current.data).toEqual(fakeData); expect(result.current.isLoading).toBeFalsy(); const { result: result2, waitForNextUpdate: waitForNextUpdate2 } = renderHook(() => useFetch(url), ); await waitForNextUpdate2(); expect(result2.current.data).toEqual(fakeData); expect(result2.current.isLoading).toBeFalsy(); }); it('should call fetch when url does not exist in cache', async () => { (global.fetch as jest.Mock).mockImplementationOnce(() => Promise.resolve({ json: () => Promise.resolve(fakeData), }), ); const { result, waitForNextUpdate } = renderHook(() => useFetch(url)); expect(result.current.data).toBeNull(); expect(result.current.isLoading).toBeTruthy(); await waitForNextUpdate(); expect(result.current.data).toEqual(fakeData); expect(result.current.isLoading).toBeFalsy(); }); it('should set isLoading to true when loading', () => { const { result } = renderHook(() => useFetch(url)); expect(result.current.isLoading).toBeTruthy(); }); it('should set error when fetch request fails', async () => { (global.fetch as jest.Mock).mockImplementationOnce(() => Promise.reject(new Error('Fake error'))); const { result, waitForNextUpdate } = renderHook(() => useFetch(url)); expect(result.current.isLoading).toBeTruthy(); await waitForNextUpdate(); // expect(result.current.error?.message).toEqual('Fake error'); console.log(result.current.error); expect(result.current.isLoading).toBeFalsy(); }); it('should not update state when component is unmounted (request is cancelled)', async () => { (global.fetch as jest.Mock).mockImplementationOnce(() => Promise.resolve({ json: () => Promise.resolve(fakeData), }), ); const { result, waitForNextUpdate, unmount } = renderHook(() => useFetch(url)); expect(result.current.isLoading).toBeTruthy(); unmount(); try { await waitForNextUpdate(); } catch (error) { // We expect this to throw an error because the component was unmounted before the state could update } expect(result.current.isLoading).toBeTruthy(); // This should still be true because the state should not have updated });