1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
|
/*** IMPORT ------------------------------------------- ***/
import { beforeAll, expect, test } from "vitest";
/*** UTILITY ------------------------------------------ ***/
import {
createLocalStorage,
createMemoryStorage
} from "../source/library/state/storage.ts";
/*** HELPERS ------------------------------------------ ***/
function installLocalStorage(): void {
if (typeof globalThis.localStorage !== "undefined")
return;
const store = new Map<string, string>();
const shim: Storage = {
clear(): void {
store.clear();
},
getItem(key: string): string | null {
return store.has(key) ? store.get(key) ?? null : null;
},
key(index: number): string | null {
return Array.from(store.keys())[index] ?? null;
},
get length(): number {
return store.size;
},
removeItem(key: string): void {
store.delete(key);
},
setItem(key: string, value: string): void {
store.set(key, String(value));
}
};
Object.defineProperty(globalThis, "localStorage", {
configurable: true,
value: shim,
writable: true
});
}
beforeAll(() => {
installLocalStorage();
});
/*** TESTS -------------------------------------------- ***/
test("memory storage round-trips objects", () => {
const storage = createMemoryStorage();
storage.set("k", { hello: "world" });
expect(storage.get<{ hello: string }>("k")).toEqual({ hello: "world" });
});
test("memory storage returns null for missing keys", () => {
const storage = createMemoryStorage();
expect(storage.get("missing")).toEqual(null);
});
test("memory storage remove clears a key", () => {
const storage = createMemoryStorage();
storage.set("k", 42);
storage.remove("k");
expect(storage.get("k")).toEqual(null);
});
test("memory storage instances are isolated", () => {
const a = createMemoryStorage();
const b = createMemoryStorage();
a.set("shared", 1);
expect(b.get("shared")).toEqual(null);
});
test("local storage namespaces keys", () => {
globalThis.localStorage.clear();
const alpha = createLocalStorage("alpha");
const beta = createLocalStorage("beta");
alpha.set("shared", { tag: "a" });
beta.set("shared", { tag: "b" });
expect(alpha.get<{ tag: string }>("shared")).toEqual({ tag: "a" });
expect(beta.get<{ tag: string }>("shared")).toEqual({ tag: "b" });
expect(globalThis.localStorage.getItem("alpha:shared")).toEqual(JSON.stringify({ tag: "a" }));
expect(globalThis.localStorage.getItem("beta:shared")).toEqual(JSON.stringify({ tag: "b" }));
globalThis.localStorage.clear();
});
test("local storage remove respects the namespace", () => {
globalThis.localStorage.clear();
const alpha = createLocalStorage("alpha");
const beta = createLocalStorage("beta");
alpha.set("k", 1);
beta.set("k", 2);
alpha.remove("k");
expect(alpha.get("k")).toEqual(null);
expect(beta.get<number>("k")).toEqual(2);
globalThis.localStorage.clear();
});
test("local storage returns null on malformed JSON", () => {
globalThis.localStorage.clear();
globalThis.localStorage.setItem("alpha:bad", "not-json");
const alpha = createLocalStorage("alpha");
expect(alpha.get("bad")).toEqual(null);
globalThis.localStorage.clear();
});
|