aboutsummaryrefslogtreecommitdiff
path: root/tests/storage.test.ts
blob: 434a67d979ffaa3339a132e14af2dec3a0c20c67 (plain) (blame)
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();
});