Skip to content

Commit a89c392

Browse files
author
Paulo Miguel Ferreira Jorge
committed
feat(tests): Implement further tests
1 parent aad8816 commit a89c392

File tree

3 files changed

+200
-24
lines changed

3 files changed

+200
-24
lines changed

src/index.ts

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,23 @@
11
import { useState } from "react";
22

3-
const isBrowser = typeof window !== "undefined";
4-
const supported = isBrowser && window.localStorage;
3+
import { SUPPORTED } from "./utils";
54

65
const useLocalState = <T>(
76
key: string,
87
defaultValue: T
98
): [T, (newValue: T) => void] => {
109
const [value, setValue] = useState<T>(() => {
11-
if (!supported) return defaultValue;
10+
if (!SUPPORTED) return defaultValue;
1211
const item = window.localStorage.getItem(key);
1312
return item ? JSON.parse(item) : defaultValue;
1413
});
1514

16-
const setValueMain = (newValue: T) => {
17-
if (supported) window.localStorage.setItem(key, JSON.stringify(newValue));
15+
const setLocalStateValue = (newValue: T) => {
16+
if (SUPPORTED) window.localStorage.setItem(key, JSON.stringify(newValue));
1817
setValue(newValue);
1918
};
2019

21-
return [value, setValueMain];
20+
return [value, setLocalStateValue];
2221
};
2322

2423
export default useLocalState;

src/utils.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
const IS_BROWSER = typeof window !== "undefined";
2+
3+
export const SUPPORTED = IS_BROWSER && window.localStorage;

test/index.test.tsx

Lines changed: 192 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,58 +1,232 @@
11
import { renderHook, act } from "@testing-library/react-hooks";
22

33
import useLocalState from "../src";
4+
import { SUPPORTED } from "../src/utils";
45

5-
describe("performs requests", () => {
6+
beforeEach(() => {
7+
if (SUPPORTED) localStorage.clear();
8+
});
9+
10+
describe("useLocalState()", () => {
611
it("uses initial value as string", async () => {
7-
const key = "key 0";
8-
const value = "something 0";
12+
const key = "key";
13+
const value = "something";
914
const { result } = renderHook(() => useLocalState(key, value));
10-
expect(result.current[0]).toEqual(value);
15+
16+
const [values] = result.current;
17+
expect(values).toEqual(value);
18+
});
19+
20+
it(`initial value isn't written into localStorage`, async () => {
21+
if (!SUPPORTED) return;
22+
23+
const key = "key";
24+
const value = "something";
25+
renderHook(() => useLocalState(key, value));
26+
27+
expect(localStorage.getItem(key)).toEqual(null);
1128
});
1229

1330
it("uses initial value as boolean", async () => {
14-
const key = "key1";
31+
const key = "key";
1532
const value = false;
1633
const { result } = renderHook(() => useLocalState(key, value));
17-
expect(result.current[0]).toEqual(value);
34+
35+
const [values] = result.current;
36+
expect(values).toEqual(value);
1837
});
1938

2039
it("uses initial value as object", async () => {
21-
const key = "key2";
40+
const key = "key";
2241
const value = {
2342
something: "else",
2443
};
2544
const { result } = renderHook(() => useLocalState(key, value));
26-
expect(result.current[0]).toEqual(value);
45+
46+
const [values] = result.current;
47+
expect(values).toEqual(value);
48+
});
49+
50+
it("uses initial value as array", async () => {
51+
const key = "todos";
52+
const values = ["first", "second"];
53+
const { result } = renderHook(() => useLocalState(key, values));
54+
55+
const [todos] = result.current;
56+
expect(todos).toEqual(values);
57+
});
58+
59+
it("accepts callback as a value", async () => {
60+
const key = "todos";
61+
const values = ["first", "second"];
62+
const callback = () => values;
63+
const { result } = renderHook(() => useLocalState(key, callback));
64+
65+
const [todos] = result.current;
66+
expect(todos).toEqual(values);
2767
});
2868

2969
it("can update value as string", async () => {
30-
const key = "key3";
31-
const value = "something 3";
32-
const value2 = "something else 3";
70+
const key = "key";
71+
const value = "something";
3372

3473
const { result } = renderHook(() => useLocalState(key, value));
3574
expect(result.current[0]).toEqual(value);
3675

76+
const newValue = "something else";
3777
act(() => {
38-
result.current[1](value2);
78+
const setValue = result.current[1];
79+
80+
setValue(newValue);
3981
});
4082

41-
expect(result.current[0]).toEqual(value2);
83+
const [values] = result.current;
84+
expect(values).toEqual(newValue);
4285
});
4386

4487
it("can update value as object", async () => {
45-
const key = "key4";
46-
const value = { something: "something 4" };
47-
const value2 = { something: "else 4" };
88+
const key = "key";
89+
const value = { something: "something" };
4890

4991
const { result } = renderHook(() => useLocalState(key, value));
5092
expect(result.current[0]).toEqual(value);
5193

94+
const newValue = { something: "else" };
95+
act(() => {
96+
const setValue = result.current[1];
97+
98+
setValue(newValue);
99+
});
100+
101+
const [values] = result.current;
102+
expect(values).toEqual(newValue);
103+
});
104+
105+
it("can update value as list", async () => {
106+
const key = "todos";
107+
const values = ["first", "second"];
108+
const { result } = renderHook(() => useLocalState(key, values));
109+
110+
const newValues = ["third", "fourth"];
111+
act(() => {
112+
const setValues = result.current[1];
113+
114+
setValues(newValues);
115+
});
116+
117+
const [todos] = result.current;
118+
expect(todos).toEqual(newValues);
119+
});
120+
121+
// todo(): this logic could be super handy...
122+
// it("updates state with callback function", () => {
123+
// const key = "todos";
124+
// const values = ["first", "second"];
125+
// const { result } = renderHook(() => useLocalState(key, values));
126+
127+
// const newValues = ["first", "second"];
128+
// act(() => {
129+
// const setTodos = result.current[1];
130+
131+
// setTodos((current) => [...current, ...newValues]);
132+
// });
133+
134+
// const [todos] = result.current;
135+
// expect(todos).toEqual([...values, ...newValues]);
136+
// });
137+
138+
it("does not fail even if invalid data is stored into localStorage", async () => {
139+
if (!SUPPORTED) return;
140+
141+
const key = "todos";
142+
const value = "something";
143+
144+
localStorage.setItem(key, value);
145+
146+
const newValues = ["first", "second"];
147+
const { result } = renderHook(() => useLocalState(key, newValues));
148+
149+
const [todos] = result.current;
150+
expect(todos).toEqual(newValues);
151+
});
152+
153+
it("gets initial value from localStorage if there is a value", async () => {
154+
if (!SUPPORTED) return;
155+
156+
const key = "todos";
157+
const values = ["first", "second"];
158+
159+
localStorage.setItem(key, JSON.stringify(values));
160+
161+
const { result } = renderHook(() => useLocalState(key, values));
162+
163+
const [todos] = result.current;
164+
expect(todos).toEqual(values);
165+
});
166+
167+
it("throws an error on two states with the same key", async () => {
168+
const consoleError = console.error;
169+
console.error = () => null;
170+
171+
const key = "todos";
172+
const valuesA = ["first", "second"];
173+
const valuesB = ["third", "fourth"];
174+
175+
expect(() => {
176+
renderHook(() => {
177+
useLocalState(key, valuesA);
178+
useLocalState(key, valuesB);
179+
});
180+
}).toThrow();
181+
182+
console.error = consoleError;
183+
});
184+
185+
it("does not throw an error with two states with different keys", async () => {
186+
const keyA = "todos";
187+
const keyB = "todos";
188+
const valuesA = ["first", "second"];
189+
const valuesB = ["third", "fourth"];
190+
191+
expect(() => {
192+
renderHook(() => {
193+
useLocalState(keyA, valuesA);
194+
useLocalState(keyB, valuesB);
195+
});
196+
}).not.toThrow();
197+
});
198+
199+
it("can handle `undefined` values", async () => {
200+
const key = "todos";
201+
const values = ["first", "second"];
202+
203+
const { result } = renderHook(() =>
204+
useLocalState<string[] | undefined>(key, values)
205+
);
206+
207+
act(() => {
208+
const [, setValues] = result.current;
209+
setValues(undefined);
210+
});
211+
212+
const [value] = result.current;
213+
expect(value).toBe(undefined);
214+
});
215+
216+
it("can handle `null` values", async () => {
217+
const key = "todos";
218+
const values = ["first", "second"];
219+
220+
const { result } = renderHook(() =>
221+
useLocalState<string[] | null>(key, values)
222+
);
223+
52224
act(() => {
53-
result.current[1](value2);
225+
const [, setValues] = result.current;
226+
setValues(null);
54227
});
55228

56-
expect(result.current[0]).toEqual(value2);
229+
const [value] = result.current;
230+
expect(value).toBe(null);
57231
});
58232
});

0 commit comments

Comments
 (0)