import { afterEach, describe, expect, it, vi } from "vitest"; import { buttonKeys, defaultPreferences, keyMap, keyName, loadPreferences, rebind, savePreferences, } from "../src/ui/preferences"; function storage(initial: Record = {}) { const values = new Map(Object.entries(initial)); return { getItem: (key: string) => values.get(key) ?? null, setItem: (key: string, value: string) => void values.set(key, value), values, }; } describe("interface preferences", () => { afterEach(() => vi.unstubAllGlobals()); it("defaults to the original keys and standard text", () => { const preferences = defaultPreferences(); expect(preferences.textScale).toBe(1); const map = keyMap(preferences); expect(map.get("KeyX")).toBe("a"); expect(map.get("KeyZ")).toBe("b"); expect(map.get("Enter")).toBe("start"); expect(map.get("ShiftRight")).toBe("select"); expect(buttonKeys(preferences, "select")).toBe("Shift"); expect(buttonKeys(preferences, "up")).toBe("↑"); }); it("names keys readably", () => { expect(keyName("KeyQ")).toBe("Q"); expect(keyName("Digit4")).toBe("4"); expect(keyName("ArrowLeft")).toBe("←"); expect(keyName("Semicolon")).toBe("Semicolon"); }); it("moves a key to its new button and refuses reserved keys", () => { let preferences = rebind(defaultPreferences(), "a", "KeyZ"); expect(preferences.keys.a).toEqual(["KeyZ"]); expect(preferences.keys.b).toEqual([]); expect(buttonKeys(preferences, "b")).toBe("None"); preferences = rebind(preferences, "start", "Tab"); expect(preferences.keys.start).toEqual(["Enter"]); }); it("round-trips through storage and rejects invalid entries", () => { const store = storage(); vi.stubGlobal("localStorage", store); const saved = { ...rebind(defaultPreferences(), "start", "KeyQ"), textScale: 1.5 as const }; savePreferences(saved); expect(loadPreferences()).toEqual(saved); // A duplicated key or an unknown scale falls back to the defaults. store.values.set( "crystal-preferences-v1", JSON.stringify({ textScale: 3, keys: { ...saved.keys, b: ["KeyQ"] } }), ); expect(loadPreferences()).toEqual(defaultPreferences()); store.values.set("crystal-preferences-v1", "{not json"); expect(loadPreferences()).toEqual(defaultPreferences()); }); it("falls back to the defaults without storage", () => { vi.stubGlobal("localStorage", { getItem: () => { throw new Error("blocked"); }, setItem: () => { throw new Error("blocked"); }, }); expect(loadPreferences()).toEqual(defaultPreferences()); expect(() => savePreferences(defaultPreferences())).not.toThrow(); }); });