/** * Persisted search result group order. * * TRACES: UR-050 | DR-066 | UT-* */ import { describe, it, expect, beforeEach, beforeAll, afterAll, vi } from "vitest"; import { get } from "svelte/store"; import { DEFAULT_GROUP_ORDER } from "$lib/utils/searchScope"; const STORAGE_KEY = "jellytau-search-group-order"; // This jsdom setup doesn't expose localStorage, so stand in a minimal // implementation — the store only uses getItem/setItem. const store = new Map(); const localStorage = { getItem: (key: string) => store.get(key) ?? null, setItem: (key: string, value: string) => void store.set(key, value), removeItem: (key: string) => void store.delete(key), clear: () => store.clear(), }; beforeAll(() => { vi.stubGlobal("localStorage", localStorage); }); afterAll(() => { vi.unstubAllGlobals(); }); describe("searchGroupOrder", () => { beforeEach(() => { vi.resetModules(); localStorage.clear(); }); it("starts at the shipped default with nothing stored", async () => { const { searchGroupOrder } = await import("./searchGroupOrder"); expect(get(searchGroupOrder)).toEqual([...DEFAULT_GROUP_ORDER]); }); it("loads a stored order", async () => { localStorage.setItem( STORAGE_KEY, JSON.stringify(["episodes", "shows", "movies", "songs", "albums", "artists", "people"]) ); const { searchGroupOrder } = await import("./searchGroupOrder"); expect(get(searchGroupOrder)).toEqual([ "episodes", "shows", "movies", "songs", "albums", "artists", "people", ]); }); it("migrates a stored `tvShows` from before the group split", async () => { // Upgrading must keep the user's placement of TV, not append the two new // groups at the bottom. localStorage.setItem(STORAGE_KEY, JSON.stringify(["tvShows", "movies"])); const { searchGroupOrder } = await import("./searchGroupOrder"); expect(get(searchGroupOrder)).toEqual([ "shows", "episodes", "movies", "songs", "albums", "artists", "people", ]); }); it("appends groups a partial stored order does not mention", async () => { localStorage.setItem(STORAGE_KEY, JSON.stringify(["movies"])); const { searchGroupOrder } = await import("./searchGroupOrder"); expect(get(searchGroupOrder)).toEqual([ "movies", "shows", "episodes", "songs", "albums", "artists", "people", ]); }); it("falls back to the default on corrupt stored JSON", async () => { localStorage.setItem(STORAGE_KEY, "{not json"); const { searchGroupOrder } = await import("./searchGroupOrder"); expect(get(searchGroupOrder)).toEqual([...DEFAULT_GROUP_ORDER]); }); it("persists a move so the order survives a restart", async () => { const { searchGroupOrder } = await import("./searchGroupOrder"); // Default is shows, episodes, movies, songs, … — move movies up one. searchGroupOrder.move("movies", -1); const expected = [ "shows", "movies", "episodes", "songs", "albums", "artists", "people", ]; expect(get(searchGroupOrder)).toEqual(expected); expect(JSON.parse(localStorage.getItem(STORAGE_KEY)!)).toEqual(expected); // Simulate a fresh app start reading the same storage. vi.resetModules(); const reloaded = await import("./searchGroupOrder"); expect(get(reloaded.searchGroupOrder)).toEqual(expected); }); it("persists a drag reorder", async () => { const { searchGroupOrder } = await import("./searchGroupOrder"); // Drag "albums" (index 4) to the front. searchGroupOrder.reorder(4, 0); expect(get(searchGroupOrder)).toEqual([ "albums", "shows", "episodes", "movies", "songs", "artists", "people", ]); }); it("resets to the shipped default", async () => { const { searchGroupOrder } = await import("./searchGroupOrder"); searchGroupOrder.move("movies", -1); searchGroupOrder.reset(); expect(get(searchGroupOrder)).toEqual([...DEFAULT_GROUP_ORDER]); }); it("normalizes an explicitly set order", async () => { const { searchGroupOrder } = await import("./searchGroupOrder"); searchGroupOrder.set(["movies", "podcasts"] as never); expect(get(searchGroupOrder)).toEqual([ "movies", "shows", "episodes", "songs", "albums", "artists", "people", ]); }); });