diff --git a/src/lib/Route/Route.svelte.test.ts b/src/lib/Route/Route.svelte.test.ts index f1fbc88..cd8ea49 100644 --- a/src/lib/Route/Route.svelte.test.ts +++ b/src/lib/Route/Route.svelte.test.ts @@ -458,8 +458,17 @@ function routeBindingTestsForUniverse(setup: ReturnType { + if (hash === false) return "http://example.com/user/123"; // Path routing + if (hash === true) return "http://example.com/#/user/123"; // Single hash routing + if (typeof hash === 'string') return `http://example.com/#${hash}=/user/123`; // Multi-hash routing + // Implicit routing + if (ru.defaultHash === false) return "http://example.com/user/123"; // Implicit path routing + if (ru.defaultHash === true) return "http://example.com/#/user/123"; // Implicit single hash routing + if (typeof ru.defaultHash === 'string') return `http://example.com/#${ru.defaultHash}=/user/123`; // Implicit multi-hash routing + return "http://example.com/user/123"; // Default to path routing + })(); + location.url.href = url; await vi.waitFor(() => {}); // Assert. @@ -494,8 +503,16 @@ function routeBindingTestsForUniverse(setup: ReturnType { + if (hash === false) return "http://example.com/about"; + if (hash === true) return "http://example.com/#/about"; + if (typeof hash === 'string') return `http://example.com/#${hash}=/about`; + if (ru.defaultHash === false) return "http://example.com/about"; + if (ru.defaultHash === true) return "http://example.com/#/about"; + if (typeof ru.defaultHash === 'string') return `http://example.com/#${ru.defaultHash}=/about`; + return "http://example.com/about"; + })(); + location.url.href = url; await vi.waitFor(() => {}); // Assert. @@ -524,8 +541,16 @@ function routeBindingTestsForUniverse(setup: ReturnType { + if (hash === false) return "http://example.com/other"; + if (hash === true) return "http://example.com/#/other"; + if (typeof hash === 'string') return `http://example.com/#${hash}=/other`; + if (ru.defaultHash === false) return "http://example.com/other"; + if (ru.defaultHash === true) return "http://example.com/#/other"; + if (typeof ru.defaultHash === 'string') return `http://example.com/#${ru.defaultHash}=/other`; + return "http://example.com/other"; + })(); + location.url.href = url; await vi.waitFor(() => {}); // Assert. @@ -552,8 +577,16 @@ function routeBindingTestsForUniverse(setup: ReturnType { + if (hash === false) return "http://example.com/user/123"; + if (hash === true) return "http://example.com/#/user/123"; + if (typeof hash === 'string') return `http://example.com/#${hash}=/user/123`; + if (ru.defaultHash === false) return "http://example.com/user/123"; + if (ru.defaultHash === true) return "http://example.com/#/user/123"; + if (typeof ru.defaultHash === 'string') return `http://example.com/#${ru.defaultHash}=/user/123`; + return "http://example.com/user/123"; + })(); + location.url.href = url; await vi.waitFor(() => {}); const firstParams = capturedParams; @@ -567,7 +600,16 @@ function routeBindingTestsForUniverse(setup: ReturnType { + if (hash === false) return "http://example.com/user/456"; + if (hash === true) return "http://example.com/#/user/456"; + if (typeof hash === 'string') return `http://example.com/#${hash}=/user/456`; + if (ru.defaultHash === false) return "http://example.com/user/456"; + if (ru.defaultHash === true) return "http://example.com/#/user/456"; + if (typeof ru.defaultHash === 'string') return `http://example.com/#${ru.defaultHash}=/user/456`; + return "http://example.com/user/456"; + })(); + location.url.href = url2; await vi.waitFor(() => {}); // Assert. @@ -595,8 +637,16 @@ function routeBindingTestsForUniverse(setup: ReturnType { + if (hash === false) return "http://example.com/user/123/post/456"; + if (hash === true) return "http://example.com/#/user/123/post/456"; + if (typeof hash === 'string') return `http://example.com/#${hash}=/user/123/post/456`; + if (ru.defaultHash === false) return "http://example.com/user/123/post/456"; + if (ru.defaultHash === true) return "http://example.com/#/user/123/post/456"; + if (typeof ru.defaultHash === 'string') return `http://example.com/#${ru.defaultHash}=/user/123/post/456`; + return "http://example.com/user/123/post/456"; + })(); + location.url.href = url; await vi.waitFor(() => {}); // Assert. @@ -631,8 +681,16 @@ function routeBindingTestsForUniverse(setup: ReturnType { + if (hash === false) return "http://example.com/files/documents/readme.txt"; + if (hash === true) return "http://example.com/#/files/documents/readme.txt"; + if (typeof hash === 'string') return `http://example.com/#${hash}=/files/documents/readme.txt`; + if (ru.defaultHash === false) return "http://example.com/files/documents/readme.txt"; + if (ru.defaultHash === true) return "http://example.com/#/files/documents/readme.txt"; + if (typeof ru.defaultHash === 'string') return `http://example.com/#${ru.defaultHash}=/files/documents/readme.txt`; + return "http://example.com/files/documents/readme.txt"; + })(); + location.url.href = url; await vi.waitFor(() => {}); // Assert. diff --git a/src/lib/kernel/LocationFull.test.ts b/src/lib/kernel/LocationFull.test.ts index bb563de..0b9c6f6 100644 --- a/src/lib/kernel/LocationFull.test.ts +++ b/src/lib/kernel/LocationFull.test.ts @@ -110,7 +110,7 @@ describe("LocationFull", () => { // Assert. expect(location.getState(ALL_HASHES.path)).toEqual(state.path); expect(location.getState(ALL_HASHES.single)).toEqual(state.hash.single); - expect(location.getState('p1')).toEqual(state.hash.p1); + expect(location.getState('tp')).toEqual(state.hash.tp); }); }); }); \ No newline at end of file diff --git a/src/lib/kernel/RouterEngine.svelte.test.ts b/src/lib/kernel/RouterEngine.svelte.test.ts index a7fc87a..f2bf8f3 100644 --- a/src/lib/kernel/RouterEngine.svelte.test.ts +++ b/src/lib/kernel/RouterEngine.svelte.test.ts @@ -3,7 +3,7 @@ import { routePatternsKey, RouterEngine } from "./RouterEngine.svelte.js"; import { init } from "../init.js"; import { registerRouter } from "./trace.svelte.js"; import { location } from "./Location.js"; -import type { State, RouteInfo, ExtendedRoutingOptions } from "../types.js"; +import type { State, RouteInfo, ExtendedRoutingOptions, PatternRouteInfo } from "../types.js"; import { setupBrowserMocks, addRoutes, ROUTING_UNIVERSES, ALL_HASHES } from "$test/test-utils.js"; import { resetRoutingOptions, setRoutingOptions } from "./options.js"; @@ -295,7 +295,13 @@ ROUTING_UNIVERSES.forEach(universe => { expectedState = state.hash[universe.hash]; } else { // For implicit modes (hash === undefined), the behavior depends on defaultHash - expectedState = universe.defaultHash === false ? state.path : state.hash.single; + if (universe.defaultHash === false) { + expectedState = state.path; + } else if (universe.defaultHash === true) { + expectedState = state.hash.single; + } else if (typeof universe.defaultHash === 'string') { + expectedState = state.hash[universe.defaultHash]; + } } expect(router.state).toBe(expectedState); }); diff --git a/src/lib/kernel/calculateHref.test.ts b/src/lib/kernel/calculateHref.test.ts index 20ec1e7..a29f05a 100644 --- a/src/lib/kernel/calculateHref.test.ts +++ b/src/lib/kernel/calculateHref.test.ts @@ -53,7 +53,7 @@ describe("calculateHref", () => { const basePath = "/base/path"; const baseHash = universe.hashMode === 'multi' - ? "#p1=path/one;p2=path/two" + ? `#${universe.hash || universe.defaultHash}=path/one;p2=path/two` : "#base/hash"; beforeEach(() => { @@ -69,6 +69,11 @@ describe("calculateHref", () => { if (universe.hash === ALL_HASHES.path) return '/sample/path'; if (universe.hash === ALL_HASHES.single) return '#/sample/path'; if (universe.hash === ALL_HASHES.implicit) { + // Handle implicit routing + if (universe.hashMode === 'multi' && typeof universe.defaultHash === 'string') { + // IMHR - implicit multi-hash routing + return `#${universe.defaultHash}=/sample/path;p2=path/two`; + } return universe.defaultHash === false ? '/sample/path' : '#/sample/path'; } // Multi-hash routing - preserves existing paths and adds/updates the specified hash @@ -87,6 +92,11 @@ describe("calculateHref", () => { if (universe.hash === ALL_HASHES.path) return `/sample/path${baseHash}`; if (universe.hash === ALL_HASHES.single) return '#/sample/path'; if (universe.hash === ALL_HASHES.implicit) { + // Handle implicit routing + if (universe.hashMode === 'multi' && typeof universe.defaultHash === 'string') { + // IMHR - implicit multi-hash routing + return `#${universe.defaultHash}=/sample/path;p2=path/two`; + } return universe.defaultHash === false ? `/sample/path${baseHash}` : '#/sample/path'; } // Multi-hash routing - preserveHash doesn't apply to hash routing @@ -121,6 +131,11 @@ describe("calculateHref", () => { if (universe.hash === ALL_HASHES.path) return newPath; if (universe.hash === ALL_HASHES.single) return `#${newPath}`; if (universe.hash === ALL_HASHES.implicit) { + // Handle implicit routing + if (universe.hashMode === 'multi' && typeof universe.defaultHash === 'string') { + // IMHR - implicit multi-hash routing + return `#${universe.defaultHash}=${newPath};p2=path/two`; + } return universe.defaultHash === false ? newPath : `#${newPath}`; } // Multi-hash routing @@ -165,8 +180,8 @@ describe("calculateHref", () => { test("Should preserve all existing paths when updating an existing path", () => { // Arrange const newPath = "/sample/path"; - const existingHashId = 'p1'; - const expected = baseHash.replace(/(p1=).+;/i, `$1${newPath};`); + const existingHashId = universe.hash || universe.defaultHash; // Use the universe's hash ID + const expected = baseHash.replace(new RegExp(`(${existingHashId}=)[^;]+`), `$1${newPath}`); // Act const href = calculateHref({ hash: existingHashId }, newPath); @@ -182,7 +197,13 @@ describe("calculateHref", () => { test("Should resolve implicit hash according to defaultHash", () => { // Arrange const newPath = "/sample/path"; - const expectedHref = universe.defaultHash === false ? newPath : `#${newPath}`; + const expectedHref = (() => { + if (universe.hashMode === 'multi' && typeof universe.defaultHash === 'string') { + // IMHR - implicit multi-hash routing + return `#${universe.defaultHash}=${newPath};p2=path/two`; + } + return universe.defaultHash === false ? newPath : `#${newPath}`; + })(); // Act const href = calculateHref({ hash: universe.hash }, newPath); diff --git a/src/lib/kernel/calculateState.test.ts b/src/lib/kernel/calculateState.test.ts index 0f37c4e..fd04f27 100644 --- a/src/lib/kernel/calculateState.test.ts +++ b/src/lib/kernel/calculateState.test.ts @@ -120,8 +120,8 @@ describe('calculateState', () => { const newState = calculateState(ALL_HASHES.path, { path: 'new' }); // Assert - All existing hash states should be preserved - if (universe.text === 'IMP') { - // IMP universe may not have existing state from setup + if (universe.text === 'IPR') { + // IPR universe may not have existing state from setup expect(newState).toEqual({ path: { path: 'new' }, hash: {} @@ -145,8 +145,8 @@ describe('calculateState', () => { const newState = calculateState(ALL_HASHES.single, { single: 'new' }); // Assert - Path and other hash states should be preserved - if (universe.text === 'IMP') { - // IMP universe may not have existing state from setup + if (universe.text === 'IPR') { + // IPR universe may not have existing state from setup expect(newState).toEqual({ path: undefined, hash: { single: { single: 'new' } } @@ -235,13 +235,19 @@ describe('calculateState', () => { // Act - use single-parameter overload for implicit mode const newState = calculateState(testState); - // Assert - calculateState preserves existing state + // Assert - calculateState preserves existing state and updates correct universe if (universe.defaultHash === false) { + // Implicit resolves to path routing expect(newState.path).toEqual(testState); expect(newState.hash).toBeDefined(); // Hash state preserved - } else { + } else if (universe.defaultHash === true) { + // Implicit resolves to single hash routing expect(newState.hash.single).toEqual(testState); expect(newState.path).toBeDefined(); // Path state preserved + } else if (typeof universe.defaultHash === 'string') { + // Implicit resolves to multi-hash routing with specific hash ID + expect(newState.hash[universe.defaultHash]).toEqual(testState); + expect(newState.path).toBeDefined(); // Path state preserved } }); }); diff --git a/src/lib/kernel/resolveHashValue.test.ts b/src/lib/kernel/resolveHashValue.test.ts index 3f170b0..b8fd2f2 100644 --- a/src/lib/kernel/resolveHashValue.test.ts +++ b/src/lib/kernel/resolveHashValue.test.ts @@ -21,7 +21,7 @@ describe("resolveHashValue", () => { expect(result).toBe(newDefaultHash); }); test.each([ - 'p1', + 'tp', false, true ])("Should return the provided hash %s value when defined.", (hash) => { diff --git a/src/testing/test-utils.ts b/src/testing/test-utils.ts index 0bed93f..33374ca 100644 --- a/src/testing/test-utils.ts +++ b/src/testing/test-utils.ts @@ -9,9 +9,18 @@ import { vi } from "vitest"; * Defines the necessary information to call the library's `init()` function for testing, plus additional metadata. */ export type RoutingUniverse = { - hash: Hash | undefined; - defaultHash: Hash; - hashMode: Exclude; + /** + * Sample hash value for testing the routing universe. + */ + hash?: Hash | undefined; + /** + * Default hash value that enables the routing universe implicitly. + */ + defaultHash?: Hash; + /** + * Necessary hash mode for this routing universe. + */ + hashMode?: RoutingOptions['hashMode']; /** * Short universe identifier. Used in test titles and descriptions. */ @@ -22,25 +31,46 @@ export type RoutingUniverse = { name: string; }; -/** - * Standard routing universe test configurations - */ export const ROUTING_UNIVERSES: RoutingUniverse[] = [ - { hash: undefined, defaultHash: false, hashMode: 'single', text: "IMP", name: "Implicit Path Routing" }, - { hash: undefined, defaultHash: true, hashMode: 'single', text: "IMH", name: "Implicit Hash Routing" }, - { hash: false, defaultHash: false, hashMode: 'single', text: "PR", name: "Path Routing" }, - { hash: true, defaultHash: false, hashMode: 'single', text: "HR", name: "Hash Routing" }, - { hash: 'p1', defaultHash: false, hashMode: 'multi', text: "MHR", name: "Multi Hash Routing" }, + { + text: "IPR", + name: "Implicit Path Routing", + defaultHash: false, + }, + { + text: 'PR', + name: 'Path Routing', + hash: false, + }, + { + text: "IHR", + name: "Implicit Hash Routing", + defaultHash: true, + }, + { + text: "HR", + name: "Hash Routing", + hash: true, + }, + { + text: "IMHR", + name: "Implicit Multi-Hash Routing", + defaultHash: 'tp', + hashMode: 'multi', + }, + { + text: "MHR", + name: "Multi-Hash Routing", + hash: 'tp', + hashMode: 'multi', + } ] as const; -/** - * All possible hash values for testing hash compatibility - */ export const ALL_HASHES = { - path: false, // Path routing - single: true, // Hash routing (single) - multi: 'p1', // Multi-hash routing - implicit: undefined // Implicit routing (depends on defaultHash) + path: false, + single: true, + multi: 'tp', + implicit: undefined, } as const; /**