diff --git a/src/__tests__/combinePaths.test.ts b/src/__tests__/combinePaths.test.ts index 67c102c..7c24bf2 100644 --- a/src/__tests__/combinePaths.test.ts +++ b/src/__tests__/combinePaths.test.ts @@ -1,13 +1,21 @@ import { combinePaths } from "../combinePaths"; -it("merges two simple paths correctly", () => { - expect(combinePaths("subpath", "/basepath/").pathname).toEqual( - "/basepath/subpath" - ); -}); +const testPaths: [paths: [base: string, sub: string], expected: string][] = [ + // adds leading and trailing slash to basepath + [["basepath", "subpath"], "/basepath/subpath"], + + // drops leading slash on subpath + [["/basepath/", "/subpath"], "/basepath/subpath"], + + // preserves trailing slash on subpath + [["/basepath/", "subpath/"], "/basepath/subpath/"], +]; -it("drops the basepath if subpath has a leading slash", () => { - expect(combinePaths("/subpath", "/basepath/").pathname).toEqual("/subpath"); +it("matches testPaths results", () => { + testPaths.forEach((testConfig) => { + const [paths, expected] = testConfig; + expect(combinePaths(paths[1], paths[0]).pathname).toEqual(expected); + }); }); it("preserves search params from both subpath and basepath", () => { @@ -15,3 +23,9 @@ it("preserves search params from both subpath and basepath", () => { combinePaths("subpath?subParam=1", "/basepath/?baseParam=2").search ).toEqual("?subParam=1&baseParam=2"); }); + +it("preserves the hash from only the subpath", () => { + expect( + combinePaths("subpath?subParam=1#testHash", "/basepath#ignoredHash").hash + ).toEqual("#testHash"); +}); diff --git a/src/__tests__/defineRoute.test.ts b/src/__tests__/defineRoute.test.ts new file mode 100644 index 0000000..73001e9 --- /dev/null +++ b/src/__tests__/defineRoute.test.ts @@ -0,0 +1,24 @@ +import { defineRoute } from "../defineRoute"; + +const testParams = { + paramA: "1234", + paramB: "4567", +}; + +const testConfig = (params: typeof testParams) => + `/base/sub/${params.paramA}/${params.paramB}?query=8910#hash`; + +it("templates route property correctly", () => { + // Query and hash should be dropped, and paramProxy should properly template the params + expect(defineRoute(testConfig).route).toEqual("/base/sub/:paramA/:paramB"); +}); + +it("extends a base route properly", () => { + const route = defineRoute(testConfig).extend( + (params: { paramC: string }) => `/extended/${params.paramC}` + ); + + expect(route.link({ ...testParams, paramC: "1121" })).toEqual( + `/base/sub/${testParams.paramA}/${testParams.paramB}/extended/1121?query=8910` + ); +}); diff --git a/src/__tests__/paramProxy.test.ts b/src/__tests__/paramProxy.test.ts deleted file mode 100644 index 5cb291e..0000000 --- a/src/__tests__/paramProxy.test.ts +++ /dev/null @@ -1,5 +0,0 @@ -import { paramProxy } from "../paramProxy"; - -it("reflects requested fields", () => { - expect((paramProxy as any).random).toEqual(":random"); -}); diff --git a/src/combinePaths.ts b/src/combinePaths.ts index c2f84d0..c306c28 100644 --- a/src/combinePaths.ts +++ b/src/combinePaths.ts @@ -1,7 +1,12 @@ +import { addTrailingSlash, removeLeadingSlash } from "./pathUtils"; + export const combinePaths = (path: string, basePath: string): URL => { const baseUrl = new URL(basePath, window.location.origin); - const url = new URL(path, baseUrl); + baseUrl.pathname = addTrailingSlash(baseUrl.pathname); + + const url = new URL(removeLeadingSlash(path), baseUrl); + baseUrl.searchParams.forEach((value, key) => { url.searchParams.set(key, value); }); diff --git a/src/pathUtils.ts b/src/pathUtils.ts new file mode 100644 index 0000000..d66d0c8 --- /dev/null +++ b/src/pathUtils.ts @@ -0,0 +1,5 @@ +export const addTrailingSlash = (s: string): string => + s.charAt(s.length - 1) === "/" ? s : s + "/"; + +export const removeLeadingSlash = (s: string): string => + s.charAt(0) === "/" ? s.slice(1) : s;