mirror of
https://github.com/J3vb/OwnCord.git
synced 2026-09-03 03:50:00 +03:00
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.
168 lines
6.0 KiB
TypeScript
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);
|
|
});
|
|
});
|
|
});
|