import { expect, test } from "vitest" import { emptyRoute, isPartialUrl, isRoute, isRouteOptions, isRouteRecord, partialUrlToRouteRecord, partialUrlToUrl, Route, createRoute, urlToRouteRecord, } from "#Source/route/index.ts" test("isPartialUrl accepts string values and rejects non-strings", () => { expect(isPartialUrl("/users/42?tab=profile#section=info")).toBe(true) expect(isPartialUrl(42)).toBe(false) }) test("isRouteRecord accepts normalized route records and rejects incomplete objects", () => { expect(isRouteRecord(createRoute("/users/42?tab=profile#section=info").getRecord())).toBe(true) expect(isRouteRecord({ partialUrl: "/users/42" })).toBe(false) }) test("partialUrlToUrl resolves partial urls against the placeholder origin", () => { const result = partialUrlToUrl({ partialUrl: "profile?tab=security#panel=2", baseUrl: "/users/42/", }) expect(result.href).toBe("https://url.example.com/users/42/profile?tab=security#panel=2") }) test("urlToRouteRecord converts a URL into a normalized route record", () => { const record = urlToRouteRecord(new URL("https://example.com/users/42?tab=profile#section=info")) expect(record).toEqual({ partialUrl: "/users/42?tab=profile#section=info", pathname: "/users/42", search: "?tab=profile", hash: "#section=info", pathnameString: "/users/42", pathnameArray: ["", "users", "42"], query: "tab=profile", queryString: "tab=profile", queryObject: { tab: "profile" }, hashString: "#section=info", hashObject: { section: "info" }, }) }) test("partialUrlToRouteRecord normalizes pathname search and hash data", () => { expect(partialUrlToRouteRecord("users/42?tab=profile#section=info")).toEqual({ partialUrl: "/users/42?tab=profile#section=info", pathname: "/users/42", search: "?tab=profile", hash: "#section=info", pathnameString: "/users/42", pathnameArray: ["", "users", "42"], query: "tab=profile", queryString: "tab=profile", queryObject: { tab: "profile" }, hashString: "#section=info", hashObject: { section: "info" }, }) }) test("isRouteOptions accepts route option objects with a partialUrl", () => { expect(isRouteOptions({ partialUrl: "/users/42" })).toBe(true) expect(isRouteOptions({ pathname: "/users/42" })).toBe(false) }) test("Route.getPayload returns the currently attached payload", () => { const currentRoute = new Route<{ id: string }>({ partialUrl: "/users/42" }) expect(currentRoute.getPayload()).toBeUndefined() currentRoute.setPayload({ id: "42" }) expect(currentRoute.getPayload()).toEqual({ id: "42" }) }) test("Route.setPayload stores payload values and keeps chainability", () => { const currentRoute = new Route({ partialUrl: "/users/42" }) expect(currentRoute.setPayload(42)).toBe(currentRoute) expect(currentRoute.getPayload()).toBe(42) }) test("Route.getRecord returns a defensive clone of the internal record", () => { const currentRoute = createRoute("/users/42?tab=profile#section=info") const snapshot = currentRoute.getRecord() snapshot.queryObject["tab"] = "security" snapshot.pathnameArray.push("extra") expect(currentRoute.getRecord()).toEqual({ partialUrl: "/users/42?tab=profile#section=info", pathname: "/users/42", search: "?tab=profile", hash: "#section=info", pathnameString: "/users/42", pathnameArray: ["", "users", "42"], query: "tab=profile", queryString: "tab=profile", queryObject: { tab: "profile" }, hashString: "#section=info", hashObject: { section: "info" }, }) }) test("Route.resetSearch clears search and query fields while keeping pathname and hash", () => { const currentRoute = createRoute("/users/42?tab=profile#section=info") currentRoute.resetSearch() expect(currentRoute.getRecord()).toEqual({ partialUrl: "/users/42#section=info", pathname: "/users/42", search: "", hash: "#section=info", pathnameString: "/users/42", pathnameArray: ["", "users", "42"], query: "", queryString: "", queryObject: {}, hashString: "#section=info", hashObject: { section: "info" }, }) }) test("Route.resetQuery aliases resetSearch behavior", () => { const currentRoute = createRoute("/users/42?tab=profile#section=info") currentRoute.resetQuery() expect(currentRoute.getRecord().search).toBe("") expect(currentRoute.getRecord().queryObject).toEqual({}) expect(currentRoute.getRecord().partialUrl).toBe("/users/42#section=info") }) test("Route.resetHash clears hash fields while keeping pathname and search", () => { const currentRoute = createRoute("/users/42?tab=profile#section=info") currentRoute.resetHash() expect(currentRoute.getRecord()).toEqual({ partialUrl: "/users/42?tab=profile", pathname: "/users/42", search: "?tab=profile", hash: "", pathnameString: "/users/42", pathnameArray: ["", "users", "42"], query: "tab=profile", queryString: "tab=profile", queryObject: { tab: "profile" }, hashString: "", hashObject: {}, }) }) test("Route.url replaces the current state from a URL instance", () => { const currentRoute = createRoute("/users/42") currentRoute.url(new URL("https://example.com/projects/alpha?view=grid#panel=files")) expect(currentRoute.getRecord().partialUrl).toBe("/projects/alpha?view=grid#panel=files") }) test("Route.partialUrl replaces the current state from a partial url string", () => { const currentRoute = createRoute("/users/42") currentRoute.partialUrl("projects/alpha?view=grid#panel=files") expect(currentRoute.getRecord().partialUrl).toBe("/projects/alpha?view=grid#panel=files") }) test("Route.pathname normalizes the pathname and resets search and hash only when the path changes", () => { const currentRoute = createRoute("/users/42?tab=profile#section=info") currentRoute.pathname({ pathname: "/users/42" }) expect(currentRoute.getRecord().partialUrl).toBe("/users/42?tab=profile#section=info") currentRoute.pathname({ pathname: ["projects", "alpha", ""] }) expect(currentRoute.getRecord()).toEqual({ partialUrl: "/projects/alpha/", pathname: "/projects/alpha/", search: "", hash: "", pathnameString: "/projects/alpha/", pathnameArray: ["", "projects", "alpha", ""], query: "", queryString: "", queryObject: {}, hashString: "", hashObject: {}, }) }) test("Route.search rewrites search and derived query fields", () => { const currentRoute = createRoute("/users/42#section=info") currentRoute.search({ search: { tab: "profile", mode: "grid" } }) expect(currentRoute.getRecord()).toEqual({ partialUrl: "/users/42?tab=profile&mode=grid#section=info", pathname: "/users/42", search: "?tab=profile&mode=grid", hash: "#section=info", pathnameString: "/users/42", pathnameArray: ["", "users", "42"], query: "tab=profile&mode=grid", queryString: "tab=profile&mode=grid", queryObject: { tab: "profile", mode: "grid" }, hashString: "#section=info", hashObject: { section: "info" }, }) }) test("Route.upsertSearch merges new query values into the current search state", () => { const currentRoute = createRoute("/users/42?page=1&mode=list") currentRoute.upsertSearch({ search: "page=2&filter=active" }) expect(currentRoute.getRecord().search).toBe("?page=2&mode=list&filter=active") expect(currentRoute.getRecord().queryObject).toEqual({ page: "2", mode: "list", filter: "active", }) }) test("Route.hash rewrites hash data and resets when given an empty string", () => { const currentRoute = createRoute("/users/42?tab=profile#section=info") currentRoute.hash({ hash: { section: "permissions", panel: "advanced" } }) expect(currentRoute.getRecord().hash).toBe("#section=permissions&panel=advanced") expect(currentRoute.getRecord().hashObject).toEqual({ section: "permissions", panel: "advanced" }) currentRoute.hash({ hash: "" }) expect(currentRoute.getRecord().hash).toBe("") expect(currentRoute.getRecord().partialUrl).toBe("/users/42?tab=profile") }) test("Route.distinctByPartialUrl compares partial urls for both Route and RouteRecord inputs", () => { const left = createRoute("/users/42?tab=profile") const right = createRoute("/users/42?tab=security") expect(Route.distinctByPartialUrl(left, right)).toBe(true) expect(Route.distinctByPartialUrl(left.getRecord(), left)).toBe(false) }) test("Route.distinctByPathname compares normalized pathname strings", () => { const left = createRoute("/users/42?tab=profile") const right = createRoute("/users/99?tab=profile") expect(Route.distinctByPathname(left, right)).toBe(true) expect(Route.distinctByPathname(left, left.getRecord())).toBe(false) }) test("Route.distinctBySearch compares normalized search strings", () => { const left = createRoute("/users/42?tab=profile") const right = createRoute("/users/42?tab=security") expect(Route.distinctBySearch(left, right)).toBe(true) expect(Route.distinctBySearch(left, left.getRecord())).toBe(false) }) test("Route.distinctByHash compares normalized hash strings", () => { const left = createRoute("/users/42#section=profile") const right = createRoute("/users/42#section=security") expect(Route.distinctByHash(left, right)).toBe(true) expect(Route.distinctByHash(left, left.getRecord())).toBe(false) }) test("Route.isDistinctByPartialUrl compares the current route against another route", () => { const currentRoute = createRoute("/users/42?tab=profile") expect(currentRoute.isDistinctByPartialUrl(createRoute("/users/42?tab=security"))).toBe(true) expect(currentRoute.isDistinctByPartialUrl(currentRoute.getRecord())).toBe(false) }) test("Route.isDistinctByPathname compares pathname differences against another route", () => { const currentRoute = createRoute("/users/42?tab=profile") expect(currentRoute.isDistinctByPathname(createRoute("/users/99?tab=profile"))).toBe(true) expect(currentRoute.isDistinctByPathname(currentRoute.getRecord())).toBe(false) }) test("Route.isDistinctBySearch compares search differences against another route", () => { const currentRoute = createRoute("/users/42?tab=profile") expect(currentRoute.isDistinctBySearch(createRoute("/users/42?tab=security"))).toBe(true) expect(currentRoute.isDistinctBySearch(currentRoute.getRecord())).toBe(false) }) test("Route.isDistinctByHash compares hash differences against another route", () => { const currentRoute = createRoute("/users/42#section=profile") expect(currentRoute.isDistinctByHash(createRoute("/users/42#section=security"))).toBe(true) expect(currentRoute.isDistinctByHash(currentRoute.getRecord())).toBe(false) }) test("Route.isPathnameLooseIncludes checks whether requested pathname segments exist", () => { const currentRoute = createRoute("/workspace/projects/alpha") expect(currentRoute.isPathnameLooseIncludes(["workspace", "alpha"])).toBe(true) expect(currentRoute.isPathnameLooseIncludes(["workspace", "beta"])).toBe(false) }) test("Route.isPathnameStrictIncludes compares pathname fragments without delimiter collisions", () => { const currentRoute = createRoute("/a,b/c") expect(currentRoute.isPathnameStrictIncludes(["a,b", "c"])).toBe(true) expect(currentRoute.isPathnameStrictIncludes(["a", "b,c"])).toBe(false) }) test("Route.isPathnameLooseEqual compares normalized pathname equality without trailing slash sensitivity", () => { const currentRoute = createRoute("/workspace/projects/alpha/") expect(currentRoute.isPathnameLooseEqual("/workspace/projects/alpha")).toBe(true) expect(currentRoute.isPathnameLooseEqual("/workspace/projects/beta")).toBe(false) }) test("Route.isPathnameStrictEqual compares normalized pathname equality with delimiter sensitivity", () => { const currentRoute = createRoute("/a,b/c") expect(currentRoute.isPathnameStrictEqual("/a,b/c")).toBe(true) expect(currentRoute.isPathnameStrictEqual("/a/b,c")).toBe(false) }) test("Route.isSearchLooseIncludes checks whether requested query keys exist", () => { const currentRoute = createRoute("/users/42?tab=profile&mode=grid") expect(currentRoute.isSearchLooseIncludes(["tab"])).toBe(true) expect(currentRoute.isSearchLooseIncludes(["tab", "page"])).toBe(false) }) test("Route.isSearchStrictIncludes compares query key sets without concatenation collisions", () => { const currentRoute = createRoute("/demo?ab=1&c=2") expect(currentRoute.isSearchStrictIncludes(["ab", "c"])).toBe(true) expect(currentRoute.isSearchStrictIncludes(["a", "bc"])).toBe(false) }) test("Route.isSearchIncludes supports required and optional query keys", () => { const currentRoute = createRoute("/users/42?tab=profile&mode=grid") expect(currentRoute.isSearchIncludes({ tab: "required", mode: "optional" })).toBe(true) expect(currentRoute.isSearchIncludes({ tab: "required" })).toBe(false) }) test("Route.isSearchLooseEqual checks whether target query entries are a matching subset", () => { const currentRoute = createRoute("/users/42?tab=profile&mode=grid") expect(currentRoute.isSearchLooseEqual({ tab: "profile" })).toBe(true) expect(currentRoute.isSearchLooseEqual({ tab: "profile", page: "1" })).toBe(false) }) test("Route.isSearchStrictEqual checks whether query key sets and values are identical", () => { const currentRoute = createRoute("/users/42?tab=profile&mode=grid") expect(currentRoute.isSearchStrictEqual("?mode=grid&tab=profile")).toBe(true) expect(currentRoute.isSearchStrictEqual("?tab=profile")).toBe(false) }) test("Route.isHashLooseIncludes checks whether requested hash keys exist", () => { const currentRoute = createRoute("/users/42#section=profile&panel=overview") expect(currentRoute.isHashLooseIncludes(["section"])).toBe(true) expect(currentRoute.isHashLooseIncludes(["section", "mode"])).toBe(false) }) test("Route.isHashStrictIncludes compares hash key sets without concatenation collisions", () => { const currentRoute = createRoute("/demo#ab=1&c=2") expect(currentRoute.isHashStrictIncludes(["ab", "c"])).toBe(true) expect(currentRoute.isHashStrictIncludes(["a", "bc"])).toBe(false) }) test("Route.isHashIncludes supports required and optional hash keys", () => { const currentRoute = createRoute("/users/42#section=profile&panel=overview") expect(currentRoute.isHashIncludes({ section: "required", panel: "optional" })).toBe(true) expect(currentRoute.isHashIncludes({ section: "required" })).toBe(false) }) test("Route.isHashLooseEqual checks whether target hash entries are a matching subset", () => { const currentRoute = createRoute("/users/42#section=profile&panel=overview") expect(currentRoute.isHashLooseEqual({ section: "profile" })).toBe(true) expect(currentRoute.isHashLooseEqual({ section: "profile", tab: "files" })).toBe(false) }) test("Route.isHashStrictEqual checks whether hash key sets and values are identical", () => { const currentRoute = createRoute("/users/42#section=profile&panel=overview") expect(currentRoute.isHashStrictEqual("#panel=overview§ion=profile")).toBe(true) expect(currentRoute.isHashStrictEqual("#section=profile")).toBe(false) }) test("isRoute distinguishes Route instances from plain values", () => { expect(isRoute(createRoute("/users/42"))).toBe(true) expect(isRoute({ partialUrl: "/users/42" })).toBe(false) }) test("createRoute normalizes strings options records and existing route instances", () => { const fromString = createRoute("/users/42?tab=profile") const fromOptions = createRoute({ partialUrl: "/users/42?tab=profile" }) const fromRecord = createRoute(fromString.getRecord()) const reused = createRoute(fromString) expect(fromString.getRecord().partialUrl).toBe("/users/42?tab=profile") expect(fromOptions.getRecord().partialUrl).toBe("/users/42?tab=profile") expect(fromRecord.getRecord().partialUrl).toBe("/users/42?tab=profile") expect(reused).toBe(fromString) expect(() => createRoute(42 as never)).toThrow( '"target" is expected to be type of "String" or "Route" or "RouteRecord" or "RouteOptions".', ) }) test("emptyRoute creates a root route with empty search hash and payload", () => { const currentRoute = emptyRoute() expect(currentRoute.getPayload()).toBeUndefined() expect(currentRoute.getRecord()).toEqual({ partialUrl: "/", pathname: "/", search: "", hash: "", pathnameString: "/", pathnameArray: ["", ""], query: "", queryString: "", queryObject: {}, hashString: "", hashObject: {}, }) })