1// @vitest-environment jsdom
2import * as Schema from "effect/Schema";
3import { act, createElement, useEffect } from "react";
4import { createRoot, type Root } from "react-dom/client";
5import { afterEach, beforeEach, describe, expect, it, vi } from "vite-plus/test";
6
7import { useLocalStorage } from "./useLocalStorage";
8
9const KEY = "corrupt-key";
10let root: Root;
11let current: ReturnType<typeof useLocalStorage<number, number>>;
12
13function Probe() {
14 const result = useLocalStorage(KEY, 0, Schema.Number);
15 useEffect(() => {
16 current = result;
17 });
18 return null;
19}
20
21beforeEach(async () => {
22 vi.stubGlobal("IS_REACT_ACT_ENVIRONMENT", true);
23 vi.spyOn(console, "error").mockImplementation(() => {});
24 // Valid JSON that no longer matches the schema, as after a schema change.
25 localStorage.setItem(KEY, JSON.stringify("not a number"));
26 root = createRoot(document.createElement("div"));
27 await act(() => root.render(createElement(Probe)));
28});
29
30afterEach(async () => {
31 await act(() => root.unmount());
32 localStorage.clear();
33 vi.restoreAllMocks();
34 vi.unstubAllGlobals();
35});
36
37describe("useLocalStorage with a stored value that fails decode", () => {
38 it("replaces it with a plain write", async () => {
39 expect(current[0]).toBe(0);
40
41 await act(() => current[1](42));
42
43 expect(localStorage.getItem(KEY)).toBe("42");
44 expect(current[0]).toBe(42);
45 });
46
47 it("runs an updater from the initial value", async () => {
48 await act(() => current[1]((value) => value + 1));
49
50 expect(localStorage.getItem(KEY)).toBe("1");
51 expect(current[0]).toBe(1);
52 });
53});