Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
48 changes: 24 additions & 24 deletions src/lib/Redirector.svelte.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { afterAll, afterEach, beforeAll, describe, expect, vi, type MockInstance
import { testWithEffect as test } from "$test/testWithEffect.svelte.js";
import { ALL_HASHES, ROUTING_UNIVERSES } from "$test/test-utils.js";
import { init } from "$lib/init.js";
import type { Hash, PatternRouteInfo, RedirectedRouteInfo } from "$lib/types.js";
import type { Hash, RouteInfo, RedirectedRouteInfo } from "$lib/types.js";
import { resolveHashValue } from "./kernel/resolveHashValue.js";
import { Redirector } from "./Redirector.svelte.js";
import { location } from "./kernel/Location.js";
Expand Down Expand Up @@ -48,27 +48,27 @@ ROUTING_UNIVERSES.forEach((universe) => {
})[] = [
{
triggerUrl: '/old/path',
pattern: '/old/path',
path: '/old/path',
href: '/new/path',
expectedPath: '/new/path',
text: "Static pattern; static href"
},
{
pattern: '/old-path/:id',
path: '/old-path/:id',
triggerUrl: '/old-path/123',
expectedPath: '/new-path/123',
href: (rp) => `/new-path/${rp?.id}`,
text: "Parameterized pattern; dynamic href"
},
{
pattern: '/old-path/*',
path: '/old-path/*',
triggerUrl: '/old-path/any/number/of/segments',
expectedPath: '/new-path/any/number/of/segments',
href: (rp) => `/new-path${rp?.rest}`,
text: "Rest parameter; dynamic href"
},
{
pattern: '/conditional/:id',
path: '/conditional/:id',
triggerUrl: '/conditional/123',
expectedPath: '/allowed/123',
href: (rp) => `/allowed/${rp?.id}`,
Expand Down Expand Up @@ -104,7 +104,7 @@ ROUTING_UNIVERSES.forEach((universe) => {

// Act.
redirector.redirections.push({
pattern: '/old-path',
path: '/old-path',
href: '/new-path',
goTo: true,
});
Expand All @@ -123,7 +123,7 @@ ROUTING_UNIVERSES.forEach((universe) => {

// Act.
redirector.redirections.push({
pattern: '/conditional/:id',
path: '/conditional/:id',
href: '/not-allowed',
and: (rp) => (rp?.id as number) > 100,
});
Expand All @@ -143,11 +143,11 @@ ROUTING_UNIVERSES.forEach((universe) => {
// Act.
redirector.redirections.push(
{
pattern: '/multi/*',
path: '/multi/*',
href: '/first-match',
},
{
pattern: '/multi/test',
path: '/multi/test',
href: '/second-match',
}
);
Expand All @@ -166,7 +166,7 @@ ROUTING_UNIVERSES.forEach((universe) => {

// Act.
redirector.redirections.push({
pattern: '/test-replace',
path: '/test-replace',
href: '/replaced',
});
flushSync();
Expand All @@ -185,7 +185,7 @@ ROUTING_UNIVERSES.forEach((universe) => {

// Act.
redirector.redirections.push({
pattern: '/with-options',
path: '/with-options',
href: '/target',
options: { preserveQuery: true, state: { custom: 'data' } }
});
Expand All @@ -205,15 +205,15 @@ ROUTING_UNIVERSES.forEach((universe) => {

// Add initial redirection that won't match
redirector.redirections.push({
pattern: '/different-path',
path: '/different-path',
href: '/not-relevant'
});
flushSync();
navigateSpy.mockClear();

// Act.
redirector.redirections.push({
pattern: '/test-reactivity',
path: '/test-reactivity',
href: '/should-redirect'
});
flushSync();
Expand All @@ -227,14 +227,14 @@ ROUTING_UNIVERSES.forEach((universe) => {
location.navigate('/test-reactivity', { hash: universe.hash });
const redirector = new Redirector(universe.hash);
redirector.redirections.push({
pattern: '/different-path',
path: '/different-path',
href: '/punch-line'
});
flushSync();
navigateSpy.mockClear();

// Act.
(redirector.redirections[0] as PatternRouteInfo).pattern = '/test-reactivity';
redirector.redirections[0].path = '/test-reactivity';
flushSync();

// Assert.
Expand All @@ -251,7 +251,7 @@ ROUTING_UNIVERSES.forEach((universe) => {
// Act.
const redirector = new Redirector(universe.hash, { replace: false });
redirector.redirections.push({
pattern: '/explicit-hash',
path: '/explicit-hash',
href: '/redirected-explicit'
});
flushSync();
Expand All @@ -273,7 +273,7 @@ ROUTING_UNIVERSES.forEach((universe) => {
// Act.
const redirector = new Redirector(universe.hash, { replace: true });
redirector.redirections.push({
pattern: '/hash-resolution-test',
path: '/hash-resolution-test',
href: '/hash-resolved'
});
flushSync();
Expand All @@ -299,7 +299,7 @@ ROUTING_UNIVERSES.forEach((universe) => {
// Act.
const redirector = new Redirector({ replace: false });
redirector.redirections.push({
pattern: '/default-hash',
path: '/default-hash',
href: '/redirected-default'
});
flushSync();
Expand All @@ -319,7 +319,7 @@ ROUTING_UNIVERSES.forEach((universe) => {
// Act.
const redirector = new Redirector({}); // Empty options object
redirector.redirections.push({
pattern: '/minimal-options',
path: '/minimal-options',
href: '/redirected-minimal'
});
flushSync();
Expand Down Expand Up @@ -373,7 +373,7 @@ describe("Options-Only Constructor with Matching Library Defaults", () => {
// Act.
const redirector = new Redirector({ replace: false });
redirector.redirections.push({
pattern: '/hash-default-test',
path: '/hash-default-test',
href: '/hash-redirected'
});
flushSync();
Expand Down Expand Up @@ -414,7 +414,7 @@ describe("Options-Only Constructor with Matching Library Defaults", () => {
// Act.
const redirector = new Redirector({ replace: false });
redirector.redirections.push({
pattern: '/multi-hash-default-test',
path: '/multi-hash-default-test',
href: '/multi-hash-redirected'
});
flushSync();
Expand Down Expand Up @@ -461,7 +461,7 @@ describe("Cross-universe Redirection", () => {

// Act.
redirector.redirections.push({
pattern: '/old-path-route',
path: '/old-path-route',
href: '/new-hash-route',
options: { hash: true }
});
Expand All @@ -482,7 +482,7 @@ describe("Cross-universe Redirection", () => {

// Act.
redirector.redirections.push({
pattern: '/old-hash-route',
path: '/old-hash-route',
href: '/new-path-route',
options: { hash: false } // Target path universe
});
Expand Down Expand Up @@ -555,7 +555,7 @@ describe("Cross-universe Redirection", () => {

// Act.
redirector.redirections.push({
pattern: '/old-path-route',
path: '/old-path-route',
href: '/new-hash-route',
options: { hash: tc.destinationHash }
});
Expand Down
15 changes: 6 additions & 9 deletions src/lib/Route/Route.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -128,15 +128,12 @@
return;
}
// svelte-ignore ownership_invalid_mutation
untrack(() => router.routes)[key] =
path instanceof RegExp
? { regex: path, and: and as AndUntyped, ignoreForFallback }
: {
pattern: path,
and: and as AndUntyped,
ignoreForFallback,
caseSensitive
};
untrack(() => router.routes)[key] = {
path,
and: and as AndUntyped,
ignoreForFallback,
caseSensitive
};
return () => {
// svelte-ignore ownership_invalid_mutation
delete untrack(() => router.routes)[key];
Expand Down
52 changes: 26 additions & 26 deletions src/lib/Route/Route.svelte.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@ import { init } from "$lib/init.js";
import { location } from "$lib/kernel/Location.js";
import TestRouteWithRouter from "$test/TestRouteWithRouter.svelte";
import { resetRoutingOptions, setRoutingOptions } from "$lib/kernel/options.js";
import type { ExtendedRoutingOptions, RouteChildrenContext } from "$lib/types.js";
import type { ExtendedRoutingOptions, RouteChildrenContext, RouteParamsRecord } from "$lib/types.js";
import type { RouterEngine } from "$lib/kernel/RouterEngine.svelte.js";

function basicRouteTests(setup: ReturnType<typeof createRouterTestSetup>) {
beforeEach(() => {
Expand Down Expand Up @@ -88,7 +89,7 @@ function routePropsTests(setup: ReturnType<typeof createRouterTestSetup>) {
test("Should register string pattern route.", async () => {
// Arrange.
const { hash, context } = setup;
let routerInstance: any;
let routerInstance: RouterEngine;

// Act.
render(TestRouteWithRouter, {
Expand All @@ -103,16 +104,16 @@ function routePropsTests(setup: ReturnType<typeof createRouterTestSetup>) {
});

// Assert.
const route = routerInstance?.routes["pattern-route"];
const route = routerInstance!?.routes["pattern-route"];
expect(route).toBeDefined();
expect(route.pattern).toBe("/user/:id");
expect(route.path).toBe("/user/:id");
});

test("Should register regex route.", async () => {
// Arrange.
const { hash, context } = setup;
const regex = /^\/user\/(?<id>\d+)$/;
let routerInstance: any;
let routerInstance: RouterEngine;

// Act.
render(TestRouteWithRouter, {
Expand All @@ -127,9 +128,9 @@ function routePropsTests(setup: ReturnType<typeof createRouterTestSetup>) {
});

// Assert.
const route = routerInstance?.routes["regex-route"];
const route = routerInstance!?.routes["regex-route"];
expect(route).toBeDefined();
expect(route.regex).toBe(regex);
expect(route.path).toBe(regex);
});

test("Should register route with and function.", async () => {
Expand Down Expand Up @@ -216,7 +217,7 @@ function routeParamsTests(setup: ReturnType<typeof createRouterTestSetup>) {
test("Should bind route parameters.", async () => {
// Arrange.
const { hash, context } = setup;
let routerInstance: any;
let routerInstance: RouterEngine;

// Act.
render(TestRouteWithRouter, {
Expand All @@ -231,15 +232,15 @@ function routeParamsTests(setup: ReturnType<typeof createRouterTestSetup>) {
});

// Assert - Route should be registered with parameter pattern
const route = routerInstance?.routes["param-route"];
const route = routerInstance!?.routes["param-route"];
expect(route).toBeDefined();
expect(route.pattern).toBe("/user/:id");
expect(route.path).toBe("/user/:id");
});

test("Should handle route with rest parameter.", async () => {
// Arrange.
const { hash, context } = setup;
let routerInstance: any;
let routerInstance: RouterEngine;

// Act.
render(TestRouteWithRouter, {
Expand All @@ -254,9 +255,9 @@ function routeParamsTests(setup: ReturnType<typeof createRouterTestSetup>) {
});

// Assert.
const route = routerInstance?.routes["rest-route"];
const route = routerInstance!?.routes["rest-route"];
expect(route).toBeDefined();
expect(route.pattern).toBe("/files/*");
expect(route.path).toBe("/files/*");
});
}

Expand All @@ -274,7 +275,7 @@ function routeReactivityTests(setup: ReturnType<typeof createRouterTestSetup>) {
const { hash, context } = setup;
const initialPath = "/initial";
const updatedPath = "/updated";
let routerInstance: any;
let routerInstance: RouterEngine;

const { rerender } = render(TestRouteWithRouter, {
props: {
Expand All @@ -287,8 +288,8 @@ function routeReactivityTests(setup: ReturnType<typeof createRouterTestSetup>) {
context
});

const initialRoute = routerInstance?.routes["reactive-route"];
expect(initialRoute?.pattern).toBe(initialPath);
const initialRoute = routerInstance!?.routes["reactive-route"];
expect(initialRoute?.path).toBe(initialPath);

// Act.
await rerender({
Expand All @@ -300,8 +301,8 @@ function routeReactivityTests(setup: ReturnType<typeof createRouterTestSetup>) {
});

// Assert.
const updatedRoute = routerInstance?.routes["reactive-route"];
expect(updatedRoute?.pattern).toBe(updatedPath);
const updatedRoute = routerInstance!?.routes["reactive-route"];
expect(updatedRoute?.path).toBe(updatedPath);
});

test("Should update ignoreForFallback when prop changes (rerender).", async () => {
Expand Down Expand Up @@ -665,18 +666,17 @@ function routeBindingTestsForUniverse(setup: ReturnType<typeof createRouterTestS
test("Should bind rest parameter correctly.", async () => {
// Arrange.
const { hash, context } = setup;
let capturedParams: any;
let capturedParams: RouteParamsRecord;
const paramsSetter = vi.fn((value) => { capturedParams = value; });

// Act.
render(TestRouteWithRouter, {
render(Route, {
props: {
hash,
routeKey: "test-route",
routePath: "/files/*",
key: "test-route",
path: "/files/*",
get params() { return capturedParams; },
set params(value) { paramsSetter(value); },
children: createTestSnippet('<div>File path: {params?.rest}</div>')
},
context
});
Expand All @@ -692,18 +692,18 @@ function routeBindingTestsForUniverse(setup: ReturnType<typeof createRouterTestS
return "http://example.com/files/documents/readme.txt";
})();
location.url.href = url;
await vi.waitFor(() => { });
flushSync();

// Assert.
expect(paramsSetter).toHaveBeenCalled();
expect(paramsSetter).toHaveBeenCalledTimes(2);

// Multi-hash routing (MHR) has different behavior and may not work with simple URLs in tests
if (ru.text === 'MHR') {
// Skip assertion for MHR as it requires more complex setup
return;
}

expect(capturedParams).toEqual({ rest: "/documents/readme.txt" });
expect(capturedParams!).toEqual({ rest: "/documents/readme.txt" });
});
}

Expand Down
Loading