/* @vitest-environment node */ import { describe, it, expect } from "vitest"; import { SEARCH_SCOPE, parseBranchesCsv, serializeBranchesCsv, parseSearchUrlState, serializeSearchUrlState, } from "./urlState.js"; describe("lib/frontend/search/urlState", () => { describe("parseBranchesCsv / serializeBranchesCsv", () => { it("parses CSV into a unique, trimmed list", () => { expect(parseBranchesCsv(" NL06, NL20 ,NL06,, ")).toEqual([ "NL06", "NL20", ]); }); it("serializes branches into CSV with stable order and dedupe", () => { expect(serializeBranchesCsv(["NL20", " NL06 ", "NL20"])).toBe( "NL20,NL06" ); }); it("returns null when serializing empty branches", () => { expect(serializeBranchesCsv([])).toBe(null); expect(serializeBranchesCsv(null)).toBe(null); }); }); describe("parseSearchUrlState", () => { it("defaults to SINGLE with routeBranch when no params are present", () => { const sp = new URLSearchParams(); const state = parseSearchUrlState(sp, { routeBranch: "NL01" }); expect(state).toEqual({ q: null, scope: SEARCH_SCOPE.SINGLE, branch: "NL01", branches: [], from: null, to: null, }); }); it("parses SINGLE with explicit branch param", () => { const sp = new URLSearchParams({ q: " test ", branch: "NL02" }); const state = parseSearchUrlState(sp, { routeBranch: "NL01" }); expect(state.scope).toBe(SEARCH_SCOPE.SINGLE); expect(state.q).toBe("test"); expect(state.branch).toBe("NL02"); expect(state.branches).toEqual([]); }); it("parses ALL when scope=all is set (highest precedence)", () => { const sp = new URLSearchParams({ q: "x", scope: "all", branch: "NL01", branches: "NL06,NL20", }); const state = parseSearchUrlState(sp, { routeBranch: "NL99" }); expect(state).toEqual({ q: "x", scope: SEARCH_SCOPE.ALL, branch: null, branches: [], from: null, to: null, }); }); it("parses MULTI when scope=multi is set", () => { const sp = new URLSearchParams({ q: " reifen ", scope: "multi", branches: "NL06, NL20, NL06", }); const state = parseSearchUrlState(sp, { routeBranch: "NL01" }); expect(state.scope).toBe(SEARCH_SCOPE.MULTI); expect(state.q).toBe("reifen"); expect(state.branch).toBe(null); expect(state.branches).toEqual(["NL06", "NL20"]); }); it("parses MULTI when branches=... is present even without scope", () => { const sp = new URLSearchParams({ q: "x", branches: "NL06,NL20", }); const state = parseSearchUrlState(sp, { routeBranch: "NL01" }); expect(state.scope).toBe(SEARCH_SCOPE.MULTI); expect(state.branches).toEqual(["NL06", "NL20"]); }); it("keeps from/to when provided", () => { const sp = new URLSearchParams({ q: "x", branch: "NL01", from: "2025-12-01", to: "2025-12-31", }); const state = parseSearchUrlState(sp, { routeBranch: "NL01" }); expect(state.from).toBe("2025-12-01"); expect(state.to).toBe("2025-12-31"); }); }); describe("serializeSearchUrlState", () => { it("serializes SINGLE as q + branch (no scope param)", () => { const qs = serializeSearchUrlState({ q: "bridgestone", scope: SEARCH_SCOPE.SINGLE, branch: "NL01", }); expect(qs).toBe("q=bridgestone&branch=NL01"); }); it("serializes MULTI as q + scope=multi + branches", () => { const qs = serializeSearchUrlState({ q: "reifen", scope: SEARCH_SCOPE.MULTI, branches: ["NL06", "NL20"], }); expect(qs).toBe("q=reifen&scope=multi&branches=NL06%2CNL20"); }); it("serializes ALL as q + scope=all", () => { const qs = serializeSearchUrlState({ q: "x", scope: SEARCH_SCOPE.ALL, }); expect(qs).toBe("q=x&scope=all"); }); it("includes from/to when present (future-proof for RHL-025)", () => { const qs = serializeSearchUrlState({ q: "x", scope: SEARCH_SCOPE.SINGLE, branch: "NL01", from: "2025-12-01", to: "2025-12-31", }); expect(qs).toBe("q=x&branch=NL01&from=2025-12-01&to=2025-12-31"); }); }); });