Skip to content

Commit 6bdf3db

Browse files
useLazyLoadData test cases
1 parent 355e40e commit 6bdf3db

File tree

1 file changed

+205
-0
lines changed

1 file changed

+205
-0
lines changed
Lines changed: 205 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,205 @@
1+
import {renderHook} from '@testing-library/react';
2+
import {expect, describe} from '@jest/globals';
3+
4+
import {useLazyLoadData} from './useLazyLoadData';
5+
6+
const fetchData = jest.fn(async () => Promise.resolve('result'));
7+
const fetchDep = jest.fn(async () => Promise.resolve('dep'));
8+
const callback = jest.fn();
9+
10+
describe('useLazyLoadData', () => {
11+
it('should only invoke fetchData when returned function is invoked', async () => {
12+
const renderedHook = renderHook(() => useLazyLoadData(fetchData));
13+
const lazyFetchData = renderedHook.result.current;
14+
expect(fetchData).toHaveBeenCalledTimes(0);
15+
await lazyFetchData();
16+
expect(fetchData).toHaveBeenCalledTimes(1);
17+
});
18+
19+
it('should invoke fetchData once when passed no args', async () => {
20+
const renderedHook = renderHook(() => useLazyLoadData(fetchData));
21+
const lazyFetchData = renderedHook.result.current;
22+
const res1 = await lazyFetchData();
23+
const res2 = await lazyFetchData();
24+
25+
expect(res1).toBe('result');
26+
expect(res2).toBe('result');
27+
expect(fetchData).toHaveBeenCalledTimes(1);
28+
});
29+
30+
it('should distribute the same promise to immediate subsequent calls', async () => {
31+
const renderedHook = renderHook(() => useLazyLoadData(fetchData));
32+
const lazyFetchData = renderedHook.result.current;
33+
34+
const promise1 = lazyFetchData();
35+
const promise2 = lazyFetchData();
36+
37+
const res1 = await promise1;
38+
const res2 = await promise2;
39+
40+
expect(res1).toBe('result');
41+
expect(res2).toBe('result');
42+
43+
expect(fetchData).toHaveBeenCalledTimes(1);
44+
});
45+
46+
it('should return non-promise after having already resolved fetchData', async () => {
47+
const renderedHook = renderHook(() => useLazyLoadData(fetchData));
48+
const lazyFetchData = renderedHook.result.current;
49+
const promise1 = lazyFetchData();
50+
expect(promise1).toBeInstanceOf(Promise);
51+
await promise1;
52+
53+
const res2 = lazyFetchData();
54+
expect(res2).not.toBeInstanceOf(Promise);
55+
expect(res2).toBe('result');
56+
expect(fetchData).toHaveBeenCalledTimes(1);
57+
});
58+
59+
it('should re-invoke fetchData when cache is overridden', async () => {
60+
const renderedHook = renderHook(() => useLazyLoadData(fetchData));
61+
const lazyFetchData = renderedHook.result.current;
62+
const res1 = await lazyFetchData();
63+
const res2 = await lazyFetchData(true);
64+
65+
expect(res1).toBe('result');
66+
expect(res2).toBe('result');
67+
expect(fetchData).toHaveBeenCalledTimes(2);
68+
});
69+
70+
it('should not invoke fetchData when passed initialData', () => {
71+
const renderedHook = renderHook(() => useLazyLoadData(fetchData, [], 'cache'));
72+
const lazyFetchData = renderedHook.result.current;
73+
74+
const res1 = lazyFetchData();
75+
expect(res1).not.toBeInstanceOf(Promise);
76+
expect(res1).toBe('cache');
77+
expect(fetchData).not.toHaveBeenCalled();
78+
});
79+
80+
it('should pass result of dependency and args into fetchData', async () => {
81+
const renderedHook = renderHook(() => useLazyLoadData(fetchData, [fetchDep]));
82+
const lazyFetchData = renderedHook.result.current as any;
83+
84+
await lazyFetchData(false, 'arg');
85+
expect(fetchDep).toHaveBeenCalledTimes(1);
86+
expect(fetchData).toHaveBeenCalledTimes(1);
87+
expect(fetchData).toHaveBeenCalledWith(['dep'], 'arg');
88+
});
89+
90+
it('should re-invoke fetchData once for each time a different arg is passed', async () => {
91+
const renderedHook = renderHook(() => useLazyLoadData(fetchData, ((arg: any) => arg) as any));
92+
const lazyFetchData = renderedHook.result.current as any;
93+
const res1arg1 = await lazyFetchData(false, 'arg1');
94+
const res2arg1 = lazyFetchData(false, 'arg1');
95+
expect(res2arg1).not.toBeInstanceOf(Promise);
96+
expect(res1arg1).toBe('result');
97+
expect(fetchData).toHaveBeenCalledTimes(1);
98+
expect(fetchData).toHaveBeenCalledWith([], 'arg1');
99+
100+
const res1arg2 = await lazyFetchData(false, 'arg2');
101+
const res2arg2 = lazyFetchData(false, 'arg2');
102+
103+
expect(res2arg2).not.toBeInstanceOf(Promise);
104+
expect(res1arg2).toBe('result');
105+
expect(fetchData).toHaveBeenCalledTimes(2);
106+
expect(fetchData).toHaveBeenCalledWith([], 'arg2');
107+
108+
const res3arg1 = lazyFetchData(false, 'arg1');
109+
expect(res3arg1).not.toBeInstanceOf(Promise);
110+
expect(fetchData).toHaveBeenCalledTimes(2);
111+
});
112+
113+
it('should invoke callback with result data upon fetchData resolving', async () => {
114+
const renderedHook = renderHook(() => useLazyLoadData(fetchData, [], undefined, callback));
115+
const lazyFetchData = renderedHook.result.current as any;
116+
await lazyFetchData();
117+
118+
expect(callback).toHaveBeenCalledTimes(1);
119+
expect(callback).toHaveBeenCalledWith('result');
120+
});
121+
122+
it('should invoke callback with result data and provided args upon fetchData resolving', async () => {
123+
const renderedHook = renderHook(() => useLazyLoadData(fetchData, [], undefined, callback));
124+
const lazyFetchData = renderedHook.result.current as any;
125+
await lazyFetchData(false, 'arg1', 'arg2');
126+
127+
expect(callback).toHaveBeenCalledTimes(1);
128+
expect(callback).toHaveBeenCalledWith('result', 'arg1', 'arg2');
129+
});
130+
131+
it('should utilize provided cacheMap when applicable', async () => {
132+
const initialData = {
133+
arg1: 'res1',
134+
arg2: 'res2'
135+
};
136+
137+
const renderedHook = renderHook(() =>
138+
useLazyLoadData(fetchData, ((arg: any) => arg) as any, [], initialData, callback)
139+
);
140+
const lazyFetchData = renderedHook.result.current as any;
141+
const res1 = lazyFetchData(false, 'arg1');
142+
const res2 = lazyFetchData(false, 'arg2');
143+
expect(res1).not.toBeInstanceOf(Promise);
144+
expect(res1).toBe('res1');
145+
146+
expect(res2).not.toBeInstanceOf(Promise);
147+
expect(res2).toBe('res2');
148+
expect(fetchData).toHaveBeenCalledTimes(0);
149+
150+
const res3 = lazyFetchData(false, 'arg3');
151+
expect(res3).toBeInstanceOf(Promise);
152+
153+
await expect(res3).resolves.toBe('result');
154+
155+
const cachedRes3 = lazyFetchData(false, 'arg3');
156+
expect(cachedRes3).not.toBeInstanceOf(Promise);
157+
expect(cachedRes3).toBe('result');
158+
expect(fetchData).toHaveBeenCalledTimes(1);
159+
});
160+
161+
it('should not reuse error promises', async () => {
162+
const getFailThenSuccess = jest
163+
.fn()
164+
.mockImplementationOnce(async () => Promise.reject(Error()))
165+
.mockImplementationOnce(async () => Promise.resolve('result'));
166+
167+
const renderedHook = renderHook(() => useLazyLoadData(getFailThenSuccess));
168+
const lazyFetchData = renderedHook.result.current as any;
169+
170+
let result;
171+
172+
try {
173+
result = await lazyFetchData();
174+
} catch (error) {
175+
result = 'error';
176+
}
177+
178+
expect(result).toBe('error');
179+
expect(getFailThenSuccess).toHaveBeenCalledTimes(1);
180+
181+
result = await lazyFetchData();
182+
183+
expect(result).toBe('result');
184+
expect(getFailThenSuccess).toHaveBeenCalledTimes(2);
185+
});
186+
187+
it('should invoke callback any time returned data changes', async () => {
188+
const renderedHook = renderHook(() => useLazyLoadData(fetchData, [], 'data', callback));
189+
const lazyFetchData = renderedHook.result.current as any;
190+
await lazyFetchData();
191+
192+
expect(callback).toHaveBeenCalledTimes(1);
193+
expect(callback).toHaveBeenCalledWith('data');
194+
195+
lazyFetchData();
196+
expect(callback).toHaveBeenCalledTimes(1);
197+
198+
await lazyFetchData(true);
199+
expect(callback).toHaveBeenCalledTimes(2);
200+
expect(callback).toHaveBeenCalledWith('result');
201+
202+
lazyFetchData();
203+
expect(callback).toHaveBeenCalledTimes(2);
204+
});
205+
});

0 commit comments

Comments
 (0)