117 lines
3.9 KiB
TypeScript
117 lines
3.9 KiB
TypeScript
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);
|
||
|
|
});
|