diff --git a/frontend/src/test/useCatalogParams.test.tsx b/frontend/src/test/useCatalogParams.test.tsx
new file mode 100644
index 0000000..b4d63f6
--- /dev/null
+++ b/frontend/src/test/useCatalogParams.test.tsx
@@ -0,0 +1,116 @@
+import { it, expect } from "vitest";
+import { render, screen } from "@testing-library/react";
+import userEvent from "@testing-library/user-event";
+import { MemoryRouter, useLocation, useNavigate } from "react-router";
+
+import { useCatalogParams } from "@/hooks/useCatalogParams";
+import type { PartsOptions } from "@/api/parts";
+
+function TestPage({ defaultCategory }: { defaultCategory?: "CPU" }) {
+ const { search, setSearch, filtOptions, setFiltOptions } =
+ useCatalogParams(defaultCategory);
+ const location = useLocation();
+ const navigate = useNavigate();
+
+ return (
+ <>
+
+
+
+
+
+
+ >
+ );
+}
+
+function renderAt(url: string, defaultCategory?: "CPU") {
+ render(
+
+
+ ,
+ );
+}
+
+const state = () => JSON.parse(screen.getByTestId("state").textContent!);
+const url = () => screen.getByTestId("url").textContent;
+
+it("restores search, category and sorting from the URL", () => {
+ renderAt("/?search=ryzen&category=GPU&sortBy=PRICE&sortDirection=DESC");
+
+ expect(state()).toEqual({
+ search: "ryzen",
+ category: "GPU",
+ sortBy: "PRICE",
+ sortDirection: "DESC",
+ });
+});
+
+it("ignores invalid values in the URL", () => {
+ renderAt("/?category=Toaster&sortBy=COLOR&sortDirection=UP");
+
+ expect(state()).toEqual({ search: "" });
+});
+
+it("writes search and filters to the URL", async () => {
+ const user = userEvent.setup();
+ renderAt("/");
+
+ await user.click(screen.getByText("search"));
+ await user.click(screen.getByText("filter"));
+
+ const params = new URLSearchParams(url()!);
+ expect(params.get("search")).toBe("RTX 4070");
+ expect(params.get("category")).toBe("GPU");
+ expect(params.get("sortBy")).toBe("PRICE");
+ expect(params.get("sortDirection")).toBe("DESC");
+});
+
+it("clears the filters from the URL on reset but keeps the search", async () => {
+ const user = userEvent.setup();
+ renderAt("/?search=ryzen&category=GPU&sortBy=PRICE&sortDirection=DESC");
+
+ await user.click(screen.getByText("reset"));
+
+ expect(url()).toBe("?search=ryzen");
+ expect(state()).toEqual({ search: "ryzen" });
+});
+
+it("uses the default category and lets the URL override it", async () => {
+ const user = userEvent.setup();
+ renderAt("/", "CPU");
+ expect(state().category).toBe("CPU");
+
+ await user.click(screen.getByText("filter"));
+ expect(state().category).toBe("GPU");
+
+ await user.click(screen.getByText("reset"));
+ expect(state().category).toBe("CPU");
+ expect(url()).toBe("");
+});
+
+it("restores the previous filters on browser back", async () => {
+ const user = userEvent.setup();
+ renderAt("/");
+
+ await user.click(screen.getByText("filter"));
+ expect(state().category).toBe("GPU");
+
+ await user.click(screen.getByText("back"));
+ expect(state().category).toBeUndefined();
+});