test: localStorage-backed state modules (+38)
Via subagent, no bugs: - state/utils/atomWithLocalStorage (9): get/set helpers + atom write-through. - state/scheduledMessages (6): Map<->Record round-trip, persistence, mount-gated hydration (atomWithStorage w/o getOnInit — modeled with a subscription). - state/spaceRooms (9): Set dedupe + no-write-when-unchanged + serialization. - state/navToActivePath (8): per-user Map<->Object serialization. - state/callPreferences (6): the privacy rule forcing video=false on load+persist. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,116 @@
|
||||
import { test } from 'node:test';
|
||||
import assert from 'node:assert/strict';
|
||||
import { createStore } from 'jotai';
|
||||
import type { CallPreferences } from './callPreferences';
|
||||
|
||||
// `makeCallPreferencesAtom(userId)` is a factory backed by `atomWithLocalStorage`.
|
||||
// localStorage is read when the returned atom is created; we install an
|
||||
// in-memory mock first so reads/writes resolve against it.
|
||||
const CALL_PREFERENCES = 'callPreferences';
|
||||
const storeKeyFor = (userId: string): string => `${CALL_PREFERENCES}${userId}`;
|
||||
|
||||
const installStorage = (): Map<string, string> => {
|
||||
const map = new Map<string, string>();
|
||||
(globalThis as { localStorage?: unknown }).localStorage = {
|
||||
getItem: (k: string) => (map.has(k) ? map.get(k)! : null),
|
||||
setItem: (k: string, v: string) => {
|
||||
map.set(k, v);
|
||||
},
|
||||
removeItem: (k: string) => {
|
||||
map.delete(k);
|
||||
},
|
||||
};
|
||||
return map;
|
||||
};
|
||||
|
||||
installStorage();
|
||||
// eslint-disable-next-line import/first
|
||||
import { makeCallPreferencesAtom } from './callPreferences';
|
||||
|
||||
const USER = '@user:server';
|
||||
|
||||
test('defaults to mic on, sound on, video off', () => {
|
||||
installStorage();
|
||||
const prefsAtom = makeCallPreferencesAtom(USER);
|
||||
const store = createStore();
|
||||
assert.deepEqual(store.get(prefsAtom), {
|
||||
microphone: true,
|
||||
video: false,
|
||||
sound: true,
|
||||
});
|
||||
});
|
||||
|
||||
test('forces video to false on LOAD even when stored as true', () => {
|
||||
const backing = installStorage();
|
||||
backing.set(storeKeyFor(USER), JSON.stringify({ microphone: false, video: true, sound: false }));
|
||||
|
||||
const prefsAtom = makeCallPreferencesAtom(USER);
|
||||
const store = createStore();
|
||||
const prefs = store.get(prefsAtom);
|
||||
|
||||
// video is overridden, but mic/sound are preserved from storage.
|
||||
assert.equal(prefs.video, false);
|
||||
assert.equal(prefs.microphone, false);
|
||||
assert.equal(prefs.sound, false);
|
||||
});
|
||||
|
||||
test('forces video to false on PERSIST even when set to true', () => {
|
||||
const backing = installStorage();
|
||||
const prefsAtom = makeCallPreferencesAtom(USER);
|
||||
const store = createStore();
|
||||
|
||||
store.set(prefsAtom, { microphone: true, video: true, sound: true });
|
||||
|
||||
const raw = backing.get(storeKeyFor(USER));
|
||||
assert.ok(raw);
|
||||
const persisted = JSON.parse(raw!) as CallPreferences;
|
||||
assert.equal(persisted.video, false);
|
||||
// mic/sound persisted as given.
|
||||
assert.equal(persisted.microphone, true);
|
||||
assert.equal(persisted.sound, true);
|
||||
});
|
||||
|
||||
test('the in-memory atom value also has video forced off after a write', () => {
|
||||
installStorage();
|
||||
const prefsAtom = makeCallPreferencesAtom(USER);
|
||||
const store = createStore();
|
||||
|
||||
// atomWithLocalStorage write-through stores newValue verbatim on the atom,
|
||||
// while only the persisted copy is sanitized. The next load re-sanitizes.
|
||||
store.set(prefsAtom, { microphone: true, video: true, sound: true });
|
||||
|
||||
// Re-create the atom to model a fresh load from the (sanitized) storage.
|
||||
const reloaded = makeCallPreferencesAtom(USER);
|
||||
assert.equal(store.get(reloaded).video, false);
|
||||
});
|
||||
|
||||
test('preserves mic/sound toggles across a persist + reload cycle', () => {
|
||||
installStorage();
|
||||
const prefsAtom = makeCallPreferencesAtom(USER);
|
||||
const store = createStore();
|
||||
|
||||
store.set(prefsAtom, { microphone: false, video: false, sound: false });
|
||||
|
||||
const reloaded = makeCallPreferencesAtom(USER);
|
||||
const prefs = store.get(reloaded);
|
||||
assert.equal(prefs.microphone, false);
|
||||
assert.equal(prefs.sound, false);
|
||||
assert.equal(prefs.video, false);
|
||||
});
|
||||
|
||||
test('preferences are scoped per userId', () => {
|
||||
const backing = installStorage();
|
||||
const userA = '@a:s';
|
||||
const userB = '@b:s';
|
||||
const atomA = makeCallPreferencesAtom(userA);
|
||||
const atomB = makeCallPreferencesAtom(userB);
|
||||
const store = createStore();
|
||||
|
||||
store.set(atomA, { microphone: false, video: false, sound: true });
|
||||
store.set(atomB, { microphone: true, video: false, sound: false });
|
||||
|
||||
assert.ok(backing.has(storeKeyFor(userA)));
|
||||
assert.ok(backing.has(storeKeyFor(userB)));
|
||||
assert.equal(store.get(atomA).microphone, false);
|
||||
assert.equal(store.get(atomB).microphone, true);
|
||||
});
|
||||
Reference in New Issue
Block a user