urlState.test.js 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198
  1. /* @vitest-environment node */
  2. import { describe, it, expect } from "vitest";
  3. import {
  4. SEARCH_SCOPE,
  5. SEARCH_LIMITS,
  6. DEFAULT_SEARCH_LIMIT,
  7. parseBranchesCsv,
  8. serializeBranchesCsv,
  9. parseSearchUrlState,
  10. serializeSearchUrlState,
  11. } from "./urlState.js";
  12. describe("lib/frontend/search/urlState", () => {
  13. describe("parseBranchesCsv / serializeBranchesCsv", () => {
  14. it("parses CSV into a unique, trimmed, sorted list", () => {
  15. expect(parseBranchesCsv(" NL20, NL06 ,NL06,, ")).toEqual([
  16. "NL06",
  17. "NL20",
  18. ]);
  19. });
  20. it("normalizes to uppercase (fail-open) and sorts deterministically", () => {
  21. expect(parseBranchesCsv("nl20, NL06")).toEqual(["NL06", "NL20"]);
  22. });
  23. it("serializes branches into CSV (sorted + deduped)", () => {
  24. expect(serializeBranchesCsv(["NL20", " NL06 ", "NL20"])).toBe(
  25. "NL06,NL20"
  26. );
  27. });
  28. it("returns null when serializing empty branches", () => {
  29. expect(serializeBranchesCsv([])).toBe(null);
  30. expect(serializeBranchesCsv(null)).toBe(null);
  31. });
  32. });
  33. describe("parseSearchUrlState", () => {
  34. it("defaults to SINGLE with routeBranch when no params are present", () => {
  35. const sp = new URLSearchParams();
  36. const state = parseSearchUrlState(sp, { routeBranch: "NL01" });
  37. expect(state).toEqual({
  38. q: null,
  39. scope: SEARCH_SCOPE.SINGLE,
  40. branch: "NL01",
  41. branches: [],
  42. limit: DEFAULT_SEARCH_LIMIT,
  43. from: null,
  44. to: null,
  45. });
  46. });
  47. it("parses SINGLE with explicit branch param (legacy URL)", () => {
  48. const sp = new URLSearchParams({ q: " test ", branch: "nl02" });
  49. const state = parseSearchUrlState(sp, { routeBranch: "NL01" });
  50. expect(state.scope).toBe(SEARCH_SCOPE.SINGLE);
  51. expect(state.q).toBe("test");
  52. expect(state.branch).toBe("NL02");
  53. expect(state.branches).toEqual([]);
  54. expect(SEARCH_LIMITS.includes(state.limit)).toBe(true);
  55. });
  56. it("parses ALL when scope=all is set (highest precedence)", () => {
  57. const sp = new URLSearchParams({
  58. q: "x",
  59. scope: "all",
  60. branch: "NL01",
  61. branches: "NL06,NL20",
  62. limit: "200",
  63. });
  64. const state = parseSearchUrlState(sp, { routeBranch: "NL99" });
  65. expect(state).toEqual({
  66. q: "x",
  67. scope: SEARCH_SCOPE.ALL,
  68. branch: null,
  69. branches: [],
  70. limit: 200,
  71. from: null,
  72. to: null,
  73. });
  74. });
  75. it("parses MULTI when scope=multi is set", () => {
  76. const sp = new URLSearchParams({
  77. q: " reifen ",
  78. scope: "multi",
  79. branches: "NL06, NL20, NL06",
  80. limit: "50",
  81. });
  82. const state = parseSearchUrlState(sp, { routeBranch: "NL01" });
  83. expect(state.scope).toBe(SEARCH_SCOPE.MULTI);
  84. expect(state.q).toBe("reifen");
  85. expect(state.branch).toBe(null);
  86. expect(state.branches).toEqual(["NL06", "NL20"]);
  87. expect(state.limit).toBe(50);
  88. });
  89. it("parses MULTI when branches=... is present even without scope", () => {
  90. const sp = new URLSearchParams({
  91. q: "x",
  92. branches: "NL20,NL06",
  93. });
  94. const state = parseSearchUrlState(sp, { routeBranch: "NL01" });
  95. expect(state.scope).toBe(SEARCH_SCOPE.MULTI);
  96. expect(state.branches).toEqual(["NL06", "NL20"]);
  97. expect(state.limit).toBe(DEFAULT_SEARCH_LIMIT);
  98. });
  99. it("keeps from/to when provided", () => {
  100. const sp = new URLSearchParams({
  101. q: "x",
  102. branch: "NL01",
  103. from: "2025-12-01",
  104. to: "2025-12-31",
  105. limit: "200",
  106. });
  107. const state = parseSearchUrlState(sp, { routeBranch: "NL01" });
  108. expect(state.from).toBe("2025-12-01");
  109. expect(state.to).toBe("2025-12-31");
  110. expect(state.limit).toBe(200);
  111. });
  112. it("falls back to default limit for invalid values", () => {
  113. const sp = new URLSearchParams({ q: "x", branch: "NL01", limit: "999" });
  114. const state = parseSearchUrlState(sp, { routeBranch: "NL01" });
  115. expect(state.limit).toBe(DEFAULT_SEARCH_LIMIT);
  116. });
  117. });
  118. describe("serializeSearchUrlState", () => {
  119. it("serializes SINGLE as q only (path branch is SoT)", () => {
  120. const qs = serializeSearchUrlState({
  121. q: "bridgestone",
  122. scope: SEARCH_SCOPE.SINGLE,
  123. branch: "NL01",
  124. limit: DEFAULT_SEARCH_LIMIT,
  125. });
  126. expect(qs).toBe("q=bridgestone");
  127. });
  128. it("serializes MULTI as q + scope=multi + branches", () => {
  129. const qs = serializeSearchUrlState({
  130. q: "reifen",
  131. scope: SEARCH_SCOPE.MULTI,
  132. branches: ["NL20", "NL06"],
  133. limit: DEFAULT_SEARCH_LIMIT,
  134. });
  135. expect(qs).toBe("q=reifen&scope=multi&branches=NL06%2CNL20");
  136. });
  137. it("serializes ALL as q + scope=all", () => {
  138. const qs = serializeSearchUrlState({
  139. q: "x",
  140. scope: SEARCH_SCOPE.ALL,
  141. limit: DEFAULT_SEARCH_LIMIT,
  142. });
  143. expect(qs).toBe("q=x&scope=all");
  144. });
  145. it("includes limit when non-default", () => {
  146. const qs = serializeSearchUrlState({
  147. q: "x",
  148. scope: SEARCH_SCOPE.SINGLE,
  149. branch: "NL01",
  150. limit: 200,
  151. });
  152. expect(qs).toBe("q=x&limit=200");
  153. });
  154. it("includes from/to when present (future-proof for RHL-025)", () => {
  155. const qs = serializeSearchUrlState({
  156. q: "x",
  157. scope: SEARCH_SCOPE.SINGLE,
  158. branch: "NL01",
  159. limit: 200,
  160. from: "2025-12-01",
  161. to: "2025-12-31",
  162. });
  163. expect(qs).toBe("q=x&limit=200&from=2025-12-01&to=2025-12-31");
  164. });
  165. });
  166. });