Skip to content

Commit c539363

Browse files
committed
Add unit tests.
1 parent 997a3f2 commit c539363

File tree

1 file changed

+368
-0
lines changed

1 file changed

+368
-0
lines changed

__tests__/index.test.js

Lines changed: 368 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,368 @@
1+
/* index.test.js - tests for React store context hooks functionality.
2+
* Copyright (c) 2019 - 2021 Richard Huang <rickypc@users.noreply.github.com>
3+
*
4+
* This Source Code Form is subject to the terms of the Mozilla Public
5+
* License, v. 2.0. If a copy of the MPL was not distributed with this
6+
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
7+
8+
import { act, render } from '@testing-library/react';
9+
import React, { useMemo } from 'react';
10+
import {
11+
isEmpty,
12+
useStore,
13+
useStores,
14+
withStore,
15+
} from '../src/index.js';
16+
17+
describe('React store context hooks module test', () => {
18+
describe('isEmpty', () => {
19+
it.each`
20+
value
21+
${undefined}
22+
${null}
23+
${false}
24+
${0}
25+
${''}
26+
${[]}
27+
${{}}
28+
`('should return truthy for $value', ({ value }) => {
29+
const actual = isEmpty(value);
30+
31+
expect(actual).toBeTruthy();
32+
});
33+
34+
it.each`
35+
value
36+
${true}
37+
${1}
38+
${-1}
39+
${'string'}
40+
${[1, 2, 3]}
41+
${{ 1: 2 }}
42+
${{ k: 'v' }}
43+
`('should return falsy for $value', ({ value }) => {
44+
const actual = isEmpty(value);
45+
46+
expect(actual).toBeFalsy();
47+
});
48+
});
49+
50+
describe('useStore', () => {
51+
it.each`
52+
value
53+
${undefined}
54+
${null}
55+
${false}
56+
${0}
57+
${''}
58+
${[]}
59+
${{}}
60+
${true}
61+
${1}
62+
${-1}
63+
${'string'}
64+
${[1, 2, 3]}
65+
${{ 1: 2 }}
66+
${{ k: 'v' }}
67+
`('should return expected value for $value', ({ value }) => {
68+
const isUndefined = value === undefined;
69+
const response = { rendered: 0 };
70+
const App = withStore(() => {
71+
response.rendered += 1;
72+
[response.get, response.set, response.del] = useStore('key');
73+
return null;
74+
});
75+
render(<App />);
76+
77+
// Initial value check.
78+
expect(response.get).toBeUndefined();
79+
expect(response.rendered).toBe(1);
80+
81+
// Post-write value check.
82+
act(() => response.set(value));
83+
expect(response.get).toBe(value);
84+
expect(response.rendered).toBe(isUndefined ? 1 : 2);
85+
86+
// Ignore same value write check.
87+
act(() => response.set(value));
88+
expect(response.get).toBe(value);
89+
expect(response.rendered).toBe(isUndefined ? 1 : 2);
90+
91+
// Post-delete value check.
92+
act(() => response.del());
93+
expect(response.get).toBeUndefined();
94+
expect(response.rendered).toBe(isUndefined ? 1 : 3);
95+
96+
// Ignore non-existence delete check.
97+
act(() => response.del());
98+
expect(response.get).toBeUndefined();
99+
expect(response.rendered).toBe(isUndefined ? 1 : 3);
100+
});
101+
102+
it.each`
103+
value
104+
${undefined}
105+
${null}
106+
${false}
107+
${0}
108+
${''}
109+
${[]}
110+
${{}}
111+
${true}
112+
${1}
113+
${-1}
114+
${'string'}
115+
${[1, 2, 3]}
116+
${{ 1: 2 }}
117+
${{ k: 'v' }}
118+
`('should return default value for $value', ({ value }) => {
119+
const response = { rendered: 0 };
120+
const App = withStore(() => {
121+
response.rendered += 1;
122+
[response.get, response.set, response.del] = useStore('key', value);
123+
return null;
124+
});
125+
render(<App />);
126+
127+
// Initial value check.
128+
expect(response.get).toBe(value);
129+
expect(response.rendered).toBe(1);
130+
131+
// Post-delete value check.
132+
act(() => response.del());
133+
expect(response.get).toBe(value);
134+
expect(response.rendered).toBe(1);
135+
136+
// Ignore non-existence delete check.
137+
act(() => response.del());
138+
expect(response.get).toBe(value);
139+
expect(response.rendered).toBe(1);
140+
});
141+
142+
it.each`
143+
value
144+
${undefined}
145+
${null}
146+
${false}
147+
${0}
148+
${''}
149+
${[]}
150+
${{}}
151+
${true}
152+
${1}
153+
${-1}
154+
${'string'}
155+
${[1, 2, 3]}
156+
${{ 1: 2 }}
157+
${{ k: 'v' }}
158+
`('should return expected value with memoized callback for $value', ({ value }) => {
159+
const isUndefined = value === undefined;
160+
const response = { rendered: 0 };
161+
const App = withStore(() => {
162+
response.rendered += 1;
163+
[response.get, response.set, response.del] = useStore('key');
164+
response.set = useMemo(() => response.set, []);
165+
return null;
166+
});
167+
render(<App />);
168+
169+
// Initial value check.
170+
expect(response.get).toBeUndefined();
171+
expect(response.rendered).toBe(1);
172+
173+
// Post-write value check.
174+
act(() => response.set(value));
175+
expect(response.get).toBe(value);
176+
expect(response.rendered).toBe(isUndefined ? 1 : 2);
177+
178+
// Ignore same value write check.
179+
act(() => response.set(value));
180+
expect(response.get).toBe(value);
181+
expect(response.rendered).toBe(isUndefined ? 1 : 2);
182+
183+
// Post-delete value check.
184+
act(() => response.del());
185+
expect(response.get).toBeUndefined();
186+
expect(response.rendered).toBe(isUndefined ? 1 : 3);
187+
188+
// Ignore non-existence delete check.
189+
act(() => response.del());
190+
expect(response.get).toBeUndefined();
191+
expect(response.rendered).toBe(isUndefined ? 1 : 3);
192+
});
193+
});
194+
195+
describe('useStores', () => {
196+
it.each`
197+
value
198+
${{ key: undefined }}
199+
${{ key: null }}
200+
${{ key: false }}
201+
${{ key: 0 }}
202+
${{ key: '' }}
203+
${{ key: [] }}
204+
${{ key: { } }}
205+
${{ key: true }}
206+
${{ key: 1 }}
207+
${{ key: -1 }}
208+
${{ key: 'string' }}
209+
${{ key: [1, 2, 3] }}
210+
${{ key: { 1: 2 } }}
211+
${{ key: { k: 'v' } }}
212+
`('should return expected value for $value', ({ value }) => {
213+
const isUndefined = value.key === undefined;
214+
const response = { rendered: 0 };
215+
const App = withStore(() => {
216+
response.rendered += 1;
217+
[response.get] = useStore('key');
218+
({ setStores: response.sets } = useStores());
219+
return null;
220+
});
221+
render(<App />);
222+
223+
// Initial value check.
224+
expect(response.get).toBeUndefined();
225+
expect(response.rendered).toBe(1);
226+
227+
// Post-write value check.
228+
act(() => response.sets(value));
229+
expect(response.get).toBe(value.key);
230+
expect(response.rendered).toBe(isUndefined ? 1 : 2);
231+
232+
// Ignore same value write check.
233+
act(() => response.sets(value));
234+
expect(response.get).toBe(value.key);
235+
expect(response.rendered).toBe(isUndefined ? 1 : 2);
236+
});
237+
});
238+
239+
describe('withStore', () => {
240+
it('should be able to share data between components', () => {
241+
const response = { renderedA: 0, renderedB: 0 };
242+
const App = withStore(() => (
243+
<>
244+
<CompA />
245+
<CompB />
246+
</>
247+
));
248+
const CompA = () => {
249+
response.renderedA += 1;
250+
[response.getA, response.setA, response.delA] = useStore('key');
251+
return null;
252+
};
253+
const CompB = () => {
254+
response.renderedB += 1;
255+
[response.getB, response.setB, response.delB] = useStore('key', 'default');
256+
return null;
257+
};
258+
259+
render(<App />);
260+
261+
// Initial value check.
262+
expect(response.getA).toBeUndefined();
263+
expect(response.getB).toBe('default');
264+
expect(response.renderedA).toBe(1);
265+
expect(response.renderedB).toBe(1);
266+
267+
// Post-write value check.
268+
let value = 'non-default-A';
269+
act(() => response.setA(value));
270+
expect(response.getA).toBe(value);
271+
expect(response.getB).toBe(value);
272+
expect(response.renderedA).toBe(2);
273+
expect(response.renderedB).toBe(2);
274+
275+
value = 'non-default-B';
276+
act(() => response.setB(value));
277+
expect(response.getA).toBe(value);
278+
expect(response.getB).toBe(value);
279+
expect(response.renderedA).toBe(3);
280+
expect(response.renderedB).toBe(3);
281+
282+
// Post-delete value check.
283+
act(() => response.delA());
284+
expect(response.getA).toBeUndefined();
285+
expect(response.getB).toBe('default');
286+
expect(response.renderedA).toBe(4);
287+
expect(response.renderedB).toBe(4);
288+
289+
// Prevent short-circuit.
290+
value = 'non-default-B';
291+
act(() => response.setB(value));
292+
expect(response.getA).toBe(value);
293+
expect(response.getB).toBe(value);
294+
expect(response.renderedA).toBe(5);
295+
expect(response.renderedB).toBe(5);
296+
297+
act(() => response.delB());
298+
expect(response.getA).toBeUndefined();
299+
expect(response.getB).toBe('default');
300+
expect(response.renderedA).toBe(6);
301+
expect(response.renderedB).toBe(6);
302+
});
303+
304+
it('should NOT be able to share data between different context', () => {
305+
const response = { renderedA: 0, renderedB: 0 };
306+
const App = () => (
307+
<>
308+
<CompA />
309+
<CompB />
310+
</>
311+
);
312+
const CompA = withStore(() => {
313+
response.renderedA += 1;
314+
[response.getA, response.setA, response.delA] = useStore('key');
315+
return null;
316+
});
317+
const CompB = withStore(() => {
318+
response.renderedB += 1;
319+
[response.getB, response.setB, response.delB] = useStore('key', 'default');
320+
return null;
321+
});
322+
323+
render(<App />);
324+
325+
// Initial value check.
326+
expect(response.getA).toBeUndefined();
327+
expect(response.getB).toBe('default');
328+
expect(response.renderedA).toBe(1);
329+
expect(response.renderedB).toBe(1);
330+
331+
// Post-write value check.
332+
let value = 'non-default-A';
333+
act(() => response.setA(value));
334+
expect(response.getA).toBe(value);
335+
expect(response.getB).toBe('default');
336+
expect(response.renderedA).toBe(2);
337+
expect(response.renderedB).toBe(1);
338+
339+
value = 'non-default-B';
340+
act(() => response.setB(value));
341+
expect(response.getA).toBe('non-default-A');
342+
expect(response.getB).toBe(value);
343+
expect(response.renderedA).toBe(2);
344+
expect(response.renderedB).toBe(2);
345+
346+
// Post-delete value check.
347+
act(() => response.delA());
348+
expect(response.getA).toBeUndefined();
349+
expect(response.getB).toBe(value);
350+
expect(response.renderedA).toBe(3);
351+
expect(response.renderedB).toBe(2);
352+
353+
// Prevent short-circuit.
354+
value = 'non-default-A';
355+
act(() => response.setA(value));
356+
expect(response.getA).toBe(value);
357+
expect(response.getB).toBe('non-default-B');
358+
expect(response.renderedA).toBe(4);
359+
expect(response.renderedB).toBe(2);
360+
361+
act(() => response.delB());
362+
expect(response.getA).toBe(value);
363+
expect(response.getB).toBe('default');
364+
expect(response.renderedA).toBe(4);
365+
expect(response.renderedB).toBe(3);
366+
});
367+
});
368+
});

0 commit comments

Comments
 (0)