Files
OwnCord/Client/tauri-client/tests/unit/preferences.test.ts
T
jevb 5c616d53fe test: fix 10 test quality bugs (BUG-058–067) and resolve 115 TS type errors
BUG-058: Unblock prod-build E2E — created tsconfig.build.json excluding
tests from the production build. Added typecheck/typecheck:build scripts.

BUG-059: Harden native E2E — CDP timeout 30→60s with exponential backoff,
config timeouts doubled (test 120s, action 30s, nav 45s, expect 15s).

BUG-060: Add 25 Rust unit tests across commands.rs, ws_proxy.rs,
livekit_proxy.rs, credentials.rs (was zero behavioral tests).

BUG-061/067: Add behavioral assertions to server coverage_boost_test.go —
GracefulStop verifies client count, channel_focus verifies no error sent.

BUG-062: Upgrade low-signal test assertions in livekit-session,
device-manager, channel-controller (no-op checks → state checks).

BUG-063: Consolidate native E2E skip gates into beforeEach blocks
(voice-controls 7→1 skip, channel-navigation 4→1 skip).

BUG-064: Add 9 integration tests for channel CRUD, member lifecycle,
DM open/close, and presence events.

BUG-065: Replace 3 fixed sleeps with condition-based waits in E2E specs.

BUG-066: Verified toast/audio tests already cleaned in prior session.

TypeScript: Fix 115 type errors across 21 test files — add non-null
assertions for strict indexing, fix mock typing (vi.fn<any>()), add
missing fields (color, version, deleted) to test fixtures.
2026-03-30 16:35:02 +02:00

168 lines
6.0 KiB
TypeScript

import { describe, it, expect, vi, beforeEach, afterEach } from "vitest";
import { loadPref, savePref, STORAGE_PREFIX } from "../../src/lib/preferences";
/**
* Tests for src/lib/preferences.ts — localStorage preference helpers.
* Covers loadPref (cache miss, hit, type mismatch, corrupted JSON, null value),
* savePref (write, custom event dispatch, quota exceeded), and the STORAGE_PREFIX constant.
*/
describe("preferences", () => {
beforeEach(() => {
localStorage.clear();
});
// ── STORAGE_PREFIX constant ──────────────────────────────
describe("STORAGE_PREFIX", () => {
it("equals 'owncord:settings:'", () => {
expect(STORAGE_PREFIX).toBe("owncord:settings:");
});
});
// ── loadPref ─────────────────────────────────────────────
describe("loadPref", () => {
it("returns the fallback when the key does not exist", () => {
expect(loadPref("nonexistent", 42)).toBe(42);
});
it("returns the fallback when the key does not exist (string)", () => {
expect(loadPref("missing", "default")).toBe("default");
});
it("returns the stored value when it exists and matches the fallback type", () => {
localStorage.setItem(STORAGE_PREFIX + "volume", JSON.stringify(75));
expect(loadPref("volume", 50)).toBe(75);
});
it("returns the stored boolean value", () => {
localStorage.setItem(STORAGE_PREFIX + "compact", JSON.stringify(true));
expect(loadPref("compact", false)).toBe(true);
});
it("returns the stored string value", () => {
localStorage.setItem(STORAGE_PREFIX + "theme", JSON.stringify("dark"));
expect(loadPref("theme", "light")).toBe("dark");
});
it("returns the fallback when the stored type differs from the fallback type", () => {
// Stored a string, but fallback is a number
localStorage.setItem(STORAGE_PREFIX + "volume", JSON.stringify("not-a-number"));
expect(loadPref("volume", 50)).toBe(50);
});
it("returns the fallback when stored value is null", () => {
localStorage.setItem(STORAGE_PREFIX + "setting", JSON.stringify(null));
expect(loadPref("setting", "default")).toBe("default");
});
it("returns the fallback when stored JSON is corrupted (invalid JSON)", () => {
localStorage.setItem(STORAGE_PREFIX + "broken", "{invalid json");
expect(loadPref("broken", 100)).toBe(100);
});
it("returns the fallback when stored value is a boolean but fallback is a number", () => {
localStorage.setItem(STORAGE_PREFIX + "mixup", JSON.stringify(true));
expect(loadPref("mixup", 0)).toBe(0);
});
it("returns the fallback when stored value is a number but fallback is a string", () => {
localStorage.setItem(STORAGE_PREFIX + "mixup2", JSON.stringify(42));
expect(loadPref("mixup2", "hello")).toBe("hello");
});
it("returns the fallback when localStorage.getItem throws", () => {
const spy = vi.spyOn(Storage.prototype, "getItem").mockImplementation(() => {
throw new Error("Storage disabled");
});
expect(loadPref("any", "safe")).toBe("safe");
spy.mockRestore();
});
});
// ── savePref ─────────────────────────────────────────────
describe("savePref", () => {
it("writes a value to localStorage with the correct prefix", () => {
savePref("volume", 80);
const raw = localStorage.getItem(STORAGE_PREFIX + "volume");
expect(raw).toBe("80");
});
it("writes a string value to localStorage", () => {
savePref("theme", "neon-glow");
const raw = localStorage.getItem(STORAGE_PREFIX + "theme");
expect(raw).toBe('"neon-glow"');
});
it("writes a boolean value to localStorage", () => {
savePref("compact", true);
const raw = localStorage.getItem(STORAGE_PREFIX + "compact");
expect(raw).toBe("true");
});
it("overwrites an existing value", () => {
savePref("volume", 50);
savePref("volume", 75);
const raw = localStorage.getItem(STORAGE_PREFIX + "volume");
expect(raw).toBe("75");
});
it("dispatches an 'owncord:pref-change' CustomEvent with the key in detail", () => {
const handler = vi.fn();
window.addEventListener("owncord:pref-change", handler);
savePref("fontSize", 16);
expect(handler).toHaveBeenCalledOnce();
const event = handler.mock.calls[0]![0] as CustomEvent;
expect(event.detail).toEqual({ key: "fontSize" });
window.removeEventListener("owncord:pref-change", handler);
});
it("does not throw when localStorage.setItem throws (quota exceeded)", () => {
const spy = vi.spyOn(Storage.prototype, "setItem").mockImplementation(() => {
throw new Error("QuotaExceededError");
});
// Should not throw — silently swallows the error
expect(() => savePref("big", "data")).not.toThrow();
spy.mockRestore();
});
it("stores null values as JSON", () => {
savePref("nullable", null);
const raw = localStorage.getItem(STORAGE_PREFIX + "nullable");
expect(raw).toBe("null");
});
it("stores object values as JSON", () => {
savePref("obj", { a: 1, b: "two" });
const raw = localStorage.getItem(STORAGE_PREFIX + "obj");
expect(raw).toBe('{"a":1,"b":"two"}');
});
});
// ── round-trip ───────────────────────────────────────────
describe("round-trip (savePref → loadPref)", () => {
it("round-trips a number", () => {
savePref("num", 42);
expect(loadPref("num", 0)).toBe(42);
});
it("round-trips a string", () => {
savePref("str", "hello");
expect(loadPref("str", "")).toBe("hello");
});
it("round-trips a boolean", () => {
savePref("bool", false);
expect(loadPref("bool", true)).toBe(false);
});
});
});