@esmx/router 3.0.0-rc.17 → 3.0.0-rc.19
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +1 -1
- package/README.md +70 -0
- package/README.zh-CN.md +70 -0
- package/dist/error.d.ts +23 -0
- package/dist/error.mjs +61 -0
- package/dist/increment-id.d.ts +7 -0
- package/dist/increment-id.mjs +11 -0
- package/dist/index.d.ts +5 -3
- package/dist/index.mjs +14 -3
- package/dist/index.test.mjs +8 -0
- package/dist/location.d.ts +15 -0
- package/dist/location.mjs +53 -0
- package/dist/location.test.d.ts +8 -0
- package/dist/location.test.mjs +370 -0
- package/dist/matcher.d.ts +3 -0
- package/dist/matcher.mjs +44 -0
- package/dist/matcher.test.mjs +1492 -0
- package/dist/micro-app.d.ts +18 -0
- package/dist/micro-app.dom.test.d.ts +1 -0
- package/dist/micro-app.dom.test.mjs +532 -0
- package/dist/micro-app.mjs +80 -0
- package/dist/navigation.d.ts +43 -0
- package/dist/navigation.mjs +143 -0
- package/dist/navigation.test.d.ts +1 -0
- package/dist/navigation.test.mjs +681 -0
- package/dist/options.d.ts +4 -0
- package/dist/options.mjs +88 -0
- package/dist/route-task.d.ts +40 -0
- package/dist/route-task.mjs +75 -0
- package/dist/route-task.test.d.ts +1 -0
- package/dist/route-task.test.mjs +673 -0
- package/dist/route-transition.d.ts +53 -0
- package/dist/route-transition.mjs +307 -0
- package/dist/route-transition.test.d.ts +1 -0
- package/dist/route-transition.test.mjs +146 -0
- package/dist/route.d.ts +72 -0
- package/dist/route.mjs +194 -0
- package/dist/route.test.d.ts +1 -0
- package/dist/route.test.mjs +1664 -0
- package/dist/router-back.test.d.ts +1 -0
- package/dist/router-back.test.mjs +361 -0
- package/dist/router-forward.test.d.ts +1 -0
- package/dist/router-forward.test.mjs +376 -0
- package/dist/router-go.test.d.ts +1 -0
- package/dist/router-go.test.mjs +73 -0
- package/dist/router-guards-cleanup.test.d.ts +1 -0
- package/dist/router-guards-cleanup.test.mjs +437 -0
- package/dist/router-link.d.ts +10 -0
- package/dist/router-link.mjs +126 -0
- package/dist/router-push.test.d.ts +1 -0
- package/dist/router-push.test.mjs +115 -0
- package/dist/router-replace.test.d.ts +1 -0
- package/dist/router-replace.test.mjs +114 -0
- package/dist/router-resolve.test.d.ts +1 -0
- package/dist/router-resolve.test.mjs +393 -0
- package/dist/router-restart-app.dom.test.d.ts +1 -0
- package/dist/router-restart-app.dom.test.mjs +616 -0
- package/dist/router-window-navigation.test.d.ts +1 -0
- package/dist/router-window-navigation.test.mjs +359 -0
- package/dist/router.d.ts +109 -102
- package/dist/router.mjs +260 -361
- package/dist/types.d.ts +246 -0
- package/dist/types.mjs +18 -0
- package/dist/util.d.ts +26 -0
- package/dist/util.mjs +53 -0
- package/dist/util.test.d.ts +1 -0
- package/dist/util.test.mjs +1020 -0
- package/package.json +10 -13
- package/src/error.ts +84 -0
- package/src/increment-id.ts +12 -0
- package/src/index.test.ts +9 -0
- package/src/index.ts +54 -3
- package/src/location.test.ts +406 -0
- package/src/location.ts +96 -0
- package/src/matcher.test.ts +1685 -0
- package/src/matcher.ts +59 -0
- package/src/micro-app.dom.test.ts +708 -0
- package/src/micro-app.ts +101 -0
- package/src/navigation.test.ts +858 -0
- package/src/navigation.ts +195 -0
- package/src/options.ts +131 -0
- package/src/route-task.test.ts +901 -0
- package/src/route-task.ts +105 -0
- package/src/route-transition.test.ts +178 -0
- package/src/route-transition.ts +425 -0
- package/src/route.test.ts +2014 -0
- package/src/route.ts +308 -0
- package/src/router-back.test.ts +487 -0
- package/src/router-forward.test.ts +506 -0
- package/src/router-go.test.ts +91 -0
- package/src/router-guards-cleanup.test.ts +595 -0
- package/src/router-link.ts +235 -0
- package/src/router-push.test.ts +140 -0
- package/src/router-replace.test.ts +139 -0
- package/src/router-resolve.test.ts +475 -0
- package/src/router-restart-app.dom.test.ts +783 -0
- package/src/router-window-navigation.test.ts +457 -0
- package/src/router.ts +289 -470
- package/src/types.ts +341 -0
- package/src/util.test.ts +1262 -0
- package/src/util.ts +116 -0
- package/dist/history/abstract.d.ts +0 -29
- package/dist/history/abstract.mjs +0 -107
- package/dist/history/base.d.ts +0 -79
- package/dist/history/base.mjs +0 -275
- package/dist/history/html.d.ts +0 -22
- package/dist/history/html.mjs +0 -183
- package/dist/history/index.d.ts +0 -7
- package/dist/history/index.mjs +0 -16
- package/dist/matcher/create-matcher.d.ts +0 -5
- package/dist/matcher/create-matcher.mjs +0 -218
- package/dist/matcher/create-matcher.spec.mjs +0 -0
- package/dist/matcher/index.d.ts +0 -1
- package/dist/matcher/index.mjs +0 -1
- package/dist/task-pipe/index.d.ts +0 -1
- package/dist/task-pipe/index.mjs +0 -1
- package/dist/task-pipe/task.d.ts +0 -30
- package/dist/task-pipe/task.mjs +0 -66
- package/dist/utils/bom.d.ts +0 -5
- package/dist/utils/bom.mjs +0 -10
- package/dist/utils/encoding.d.ts +0 -48
- package/dist/utils/encoding.mjs +0 -44
- package/dist/utils/guards.d.ts +0 -9
- package/dist/utils/guards.mjs +0 -12
- package/dist/utils/index.d.ts +0 -7
- package/dist/utils/index.mjs +0 -27
- package/dist/utils/path.d.ts +0 -60
- package/dist/utils/path.mjs +0 -281
- package/dist/utils/path.spec.mjs +0 -27
- package/dist/utils/scroll.d.ts +0 -25
- package/dist/utils/scroll.mjs +0 -59
- package/dist/utils/utils.d.ts +0 -16
- package/dist/utils/utils.mjs +0 -11
- package/dist/utils/warn.d.ts +0 -2
- package/dist/utils/warn.mjs +0 -12
- package/src/history/abstract.ts +0 -149
- package/src/history/base.ts +0 -408
- package/src/history/html.ts +0 -228
- package/src/history/index.ts +0 -20
- package/src/matcher/create-matcher.spec.ts +0 -3
- package/src/matcher/create-matcher.ts +0 -293
- package/src/matcher/index.ts +0 -1
- package/src/task-pipe/index.ts +0 -1
- package/src/task-pipe/task.ts +0 -97
- package/src/utils/bom.ts +0 -14
- package/src/utils/encoding.ts +0 -153
- package/src/utils/guards.ts +0 -25
- package/src/utils/index.ts +0 -27
- package/src/utils/path.spec.ts +0 -32
- package/src/utils/path.ts +0 -417
- package/src/utils/scroll.ts +0 -120
- package/src/utils/utils.ts +0 -30
- package/src/utils/warn.ts +0 -13
- /package/dist/{matcher/create-matcher.spec.d.ts → index.test.d.ts} +0 -0
- /package/dist/{utils/path.spec.d.ts → matcher.test.d.ts} +0 -0
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
import { afterEach, beforeEach, describe, expect, test } from "vitest";
|
|
2
|
+
import { Router } from "./router.mjs";
|
|
3
|
+
import { RouteType, RouterMode } from "./types.mjs";
|
|
4
|
+
describe("Router Replace Tests", () => {
|
|
5
|
+
let router;
|
|
6
|
+
beforeEach(async () => {
|
|
7
|
+
router = new Router({
|
|
8
|
+
mode: RouterMode.memory,
|
|
9
|
+
base: new URL("http://localhost:3000/"),
|
|
10
|
+
routes: [
|
|
11
|
+
{ path: "/", component: () => "Home" },
|
|
12
|
+
{ path: "/user/:id", component: () => "User" },
|
|
13
|
+
{ path: "/about", component: () => "About" },
|
|
14
|
+
{
|
|
15
|
+
path: "/async",
|
|
16
|
+
asyncComponent: () => new Promise(
|
|
17
|
+
(resolve) => setTimeout(() => resolve("AsyncComponent"), 10)
|
|
18
|
+
)
|
|
19
|
+
}
|
|
20
|
+
]
|
|
21
|
+
});
|
|
22
|
+
await router.replace("/");
|
|
23
|
+
});
|
|
24
|
+
afterEach(() => {
|
|
25
|
+
router.destroy();
|
|
26
|
+
});
|
|
27
|
+
describe("Basic replace navigation", () => {
|
|
28
|
+
test("should successfully replace current route", async () => {
|
|
29
|
+
await router.push("/about");
|
|
30
|
+
const route = await router.replace("/user/123");
|
|
31
|
+
expect(route.path).toBe("/user/123");
|
|
32
|
+
expect(route.params.id).toBe("123");
|
|
33
|
+
expect(route.type).toBe(RouteType.replace);
|
|
34
|
+
expect(route.handle).not.toBe(null);
|
|
35
|
+
expect(router.route.path).toBe("/user/123");
|
|
36
|
+
});
|
|
37
|
+
test("should handle query parameters in replace", async () => {
|
|
38
|
+
const route = await router.replace(
|
|
39
|
+
"/user/123?tab=profile&active=true"
|
|
40
|
+
);
|
|
41
|
+
expect(route.params.id).toBe("123");
|
|
42
|
+
expect(route.query.tab).toBe("profile");
|
|
43
|
+
expect(route.query.active).toBe("true");
|
|
44
|
+
});
|
|
45
|
+
test("should handle async component loading", async () => {
|
|
46
|
+
const route = await router.replace("/async");
|
|
47
|
+
expect(route.path).toBe("/async");
|
|
48
|
+
expect(route.handle).not.toBe(null);
|
|
49
|
+
});
|
|
50
|
+
});
|
|
51
|
+
describe("Error handling", () => {
|
|
52
|
+
test("should throw error for async component loading failure", async () => {
|
|
53
|
+
const errorRouter = new Router({
|
|
54
|
+
mode: RouterMode.memory,
|
|
55
|
+
base: new URL("http://localhost:3000/"),
|
|
56
|
+
routes: [
|
|
57
|
+
{ path: "/", component: () => "Home" },
|
|
58
|
+
{
|
|
59
|
+
path: "/error",
|
|
60
|
+
asyncComponent: () => Promise.reject(new Error("Loading failed"))
|
|
61
|
+
}
|
|
62
|
+
]
|
|
63
|
+
});
|
|
64
|
+
await errorRouter.replace("/");
|
|
65
|
+
await expect(errorRouter.replace("/error")).rejects.toThrow();
|
|
66
|
+
errorRouter.destroy();
|
|
67
|
+
});
|
|
68
|
+
test("should throw error when guard prevents navigation", async () => {
|
|
69
|
+
const guardRouter = new Router({
|
|
70
|
+
mode: RouterMode.memory,
|
|
71
|
+
base: new URL("http://localhost:3000/"),
|
|
72
|
+
routes: [
|
|
73
|
+
{ path: "/", component: () => "Home" },
|
|
74
|
+
{
|
|
75
|
+
path: "/protected",
|
|
76
|
+
component: () => "Protected",
|
|
77
|
+
beforeEnter: () => false
|
|
78
|
+
}
|
|
79
|
+
]
|
|
80
|
+
});
|
|
81
|
+
await guardRouter.replace("/");
|
|
82
|
+
await expect(guardRouter.replace("/protected")).rejects.toThrow();
|
|
83
|
+
guardRouter.destroy();
|
|
84
|
+
});
|
|
85
|
+
});
|
|
86
|
+
describe("History management", () => {
|
|
87
|
+
test("should not create new history entry", async () => {
|
|
88
|
+
await router.push("/about");
|
|
89
|
+
await router.replace("/user/123");
|
|
90
|
+
const backRoute = await router.back();
|
|
91
|
+
expect(backRoute == null ? void 0 : backRoute.path).toBe("/");
|
|
92
|
+
});
|
|
93
|
+
test("should replace current entry in history", async () => {
|
|
94
|
+
await router.push("/about");
|
|
95
|
+
await router.push("/user/456");
|
|
96
|
+
await router.replace("/user/789");
|
|
97
|
+
const backRoute = await router.back();
|
|
98
|
+
expect(backRoute == null ? void 0 : backRoute.path).toBe("/about");
|
|
99
|
+
});
|
|
100
|
+
});
|
|
101
|
+
describe("Edge cases", () => {
|
|
102
|
+
test("should handle replace to current route", async () => {
|
|
103
|
+
await router.push("/about");
|
|
104
|
+
const route = await router.replace("/about");
|
|
105
|
+
expect(route.path).toBe("/about");
|
|
106
|
+
expect(route.handle).not.toBe(null);
|
|
107
|
+
});
|
|
108
|
+
test("should handle empty parameter", async () => {
|
|
109
|
+
const route = await router.replace("");
|
|
110
|
+
expect(route).toBeDefined();
|
|
111
|
+
expect(typeof route.path).toBe("string");
|
|
112
|
+
});
|
|
113
|
+
});
|
|
114
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,393 @@
|
|
|
1
|
+
import { afterEach, beforeEach, describe, expect, test } from "vitest";
|
|
2
|
+
import { Router } from "./router.mjs";
|
|
3
|
+
import { RouterMode } from "./types.mjs";
|
|
4
|
+
const createTestRouter = () => {
|
|
5
|
+
return new Router({
|
|
6
|
+
mode: RouterMode.memory,
|
|
7
|
+
base: new URL("http://localhost:3000/"),
|
|
8
|
+
routes: createTestRoutes()
|
|
9
|
+
});
|
|
10
|
+
};
|
|
11
|
+
const createTestRoutes = () => {
|
|
12
|
+
return [
|
|
13
|
+
{
|
|
14
|
+
path: "/",
|
|
15
|
+
component: () => "Home",
|
|
16
|
+
meta: { title: "Home Page" }
|
|
17
|
+
},
|
|
18
|
+
{
|
|
19
|
+
path: "/about",
|
|
20
|
+
component: () => "About",
|
|
21
|
+
meta: { title: "About Page", requiresAuth: false }
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
path: "/user/:id",
|
|
25
|
+
component: () => "User",
|
|
26
|
+
meta: { title: "User Profile", requiresAuth: true },
|
|
27
|
+
children: [
|
|
28
|
+
{
|
|
29
|
+
path: "/profile",
|
|
30
|
+
component: () => "UserProfile",
|
|
31
|
+
meta: { section: "profile" }
|
|
32
|
+
},
|
|
33
|
+
{
|
|
34
|
+
path: "/settings",
|
|
35
|
+
component: () => "UserSettings",
|
|
36
|
+
meta: { section: "settings" }
|
|
37
|
+
}
|
|
38
|
+
]
|
|
39
|
+
},
|
|
40
|
+
{
|
|
41
|
+
path: "/admin",
|
|
42
|
+
component: () => "Admin",
|
|
43
|
+
meta: { requiresAuth: true, role: "admin" },
|
|
44
|
+
children: [
|
|
45
|
+
{
|
|
46
|
+
path: "/users",
|
|
47
|
+
component: () => "AdminUsers",
|
|
48
|
+
meta: { section: "users" }
|
|
49
|
+
},
|
|
50
|
+
{
|
|
51
|
+
path: "/settings",
|
|
52
|
+
component: () => "AdminSettings",
|
|
53
|
+
meta: { section: "settings" }
|
|
54
|
+
}
|
|
55
|
+
]
|
|
56
|
+
},
|
|
57
|
+
{
|
|
58
|
+
path: "/products/:category/:id",
|
|
59
|
+
component: () => "Product",
|
|
60
|
+
meta: { title: "Product Detail" }
|
|
61
|
+
}
|
|
62
|
+
];
|
|
63
|
+
};
|
|
64
|
+
const createTestCases = () => {
|
|
65
|
+
return {
|
|
66
|
+
validPaths: [
|
|
67
|
+
{ path: "/", shouldMatch: true },
|
|
68
|
+
{ path: "/about", shouldMatch: true },
|
|
69
|
+
{ path: "/user/123", shouldMatch: true },
|
|
70
|
+
{ path: "/admin/users", shouldMatch: true }
|
|
71
|
+
],
|
|
72
|
+
invalidPaths: [
|
|
73
|
+
{ path: "/invalid", shouldMatch: false },
|
|
74
|
+
{ path: "/user", shouldMatch: false }
|
|
75
|
+
// Missing required parameter
|
|
76
|
+
]
|
|
77
|
+
};
|
|
78
|
+
};
|
|
79
|
+
describe("Router.resolve method tests", () => {
|
|
80
|
+
let router;
|
|
81
|
+
beforeEach(async () => {
|
|
82
|
+
router = createTestRouter();
|
|
83
|
+
await router.replace("/");
|
|
84
|
+
});
|
|
85
|
+
afterEach(() => {
|
|
86
|
+
router.destroy();
|
|
87
|
+
});
|
|
88
|
+
describe("Core resolution functionality", () => {
|
|
89
|
+
test("should return complete Route object with all required properties", () => {
|
|
90
|
+
const route = router.resolve("/about");
|
|
91
|
+
expect(route).toBeInstanceOf(Object);
|
|
92
|
+
expect(route.type).toBe("push");
|
|
93
|
+
expect(route.path).toBe("/about");
|
|
94
|
+
expect(route.fullPath).toBe("/about");
|
|
95
|
+
expect(route.url).toBeInstanceOf(URL);
|
|
96
|
+
expect(route.params).toBeInstanceOf(Object);
|
|
97
|
+
expect(route.query).toBeInstanceOf(Object);
|
|
98
|
+
expect(route.meta).toBeInstanceOf(Object);
|
|
99
|
+
expect(route.matched).toBeInstanceOf(Array);
|
|
100
|
+
});
|
|
101
|
+
test("should not trigger actual navigation", () => {
|
|
102
|
+
const originalPath = router.route.path;
|
|
103
|
+
router.resolve("/about");
|
|
104
|
+
router.resolve("/user/123");
|
|
105
|
+
router.resolve("/admin/users");
|
|
106
|
+
expect(router.route.path).toBe(originalPath);
|
|
107
|
+
});
|
|
108
|
+
test("should correctly resolve string path", () => {
|
|
109
|
+
const route = router.resolve("/user/123");
|
|
110
|
+
expect(route.path).toBe("/user/123");
|
|
111
|
+
expect(route.params.id).toBe("123");
|
|
112
|
+
expect(route.matched.length).toBeGreaterThan(0);
|
|
113
|
+
expect(route.config).not.toBeNull();
|
|
114
|
+
});
|
|
115
|
+
test("should correctly resolve object configuration", () => {
|
|
116
|
+
const route = router.resolve({
|
|
117
|
+
path: "/user/456",
|
|
118
|
+
query: { tab: "profile", active: "true" },
|
|
119
|
+
hash: "#section1"
|
|
120
|
+
});
|
|
121
|
+
expect(route.path).toBe("/user/456");
|
|
122
|
+
expect(route.params.id).toBe("456");
|
|
123
|
+
expect(route.query.tab).toBe("profile");
|
|
124
|
+
expect(route.query.active).toBe("true");
|
|
125
|
+
expect(route.url.hash).toBe("#section1");
|
|
126
|
+
expect(route.fullPath).toBe(
|
|
127
|
+
"/user/456?tab=profile&active=true#section1"
|
|
128
|
+
);
|
|
129
|
+
});
|
|
130
|
+
});
|
|
131
|
+
describe("Path resolution and parameter extraction", () => {
|
|
132
|
+
test("should correctly resolve single path parameter", () => {
|
|
133
|
+
const route = router.resolve("/user/123");
|
|
134
|
+
expect(route.params.id).toBe("123");
|
|
135
|
+
expect(route.path).toBe("/user/123");
|
|
136
|
+
expect(route.matched.length).toBe(1);
|
|
137
|
+
});
|
|
138
|
+
test("should correctly resolve multiple path parameters", () => {
|
|
139
|
+
const route = router.resolve(
|
|
140
|
+
"/products/electronics/laptop-123"
|
|
141
|
+
);
|
|
142
|
+
expect(route.params.category).toBe("electronics");
|
|
143
|
+
expect(route.params.id).toBe("laptop-123");
|
|
144
|
+
expect(route.path).toBe("/products/electronics/laptop-123");
|
|
145
|
+
});
|
|
146
|
+
test("should correctly resolve query parameters", () => {
|
|
147
|
+
const route = router.resolve(
|
|
148
|
+
"/about?lang=en&theme=dark&debug=true"
|
|
149
|
+
);
|
|
150
|
+
expect(route.query.lang).toBe("en");
|
|
151
|
+
expect(route.query.theme).toBe("dark");
|
|
152
|
+
expect(route.query.debug).toBe("true");
|
|
153
|
+
expect(route.queryArray.lang).toEqual(["en"]);
|
|
154
|
+
expect(route.queryArray.theme).toEqual(["dark"]);
|
|
155
|
+
});
|
|
156
|
+
test("should correctly handle duplicate query parameters", () => {
|
|
157
|
+
const route = router.resolve(
|
|
158
|
+
"/about?tags=vue&tags=router&tags=test"
|
|
159
|
+
);
|
|
160
|
+
expect(route.query.tags).toBe("vue");
|
|
161
|
+
expect(route.queryArray.tags).toEqual(["vue", "router", "test"]);
|
|
162
|
+
});
|
|
163
|
+
test("should correctly resolve hash fragment", () => {
|
|
164
|
+
const route = router.resolve("/about#introduction");
|
|
165
|
+
expect(route.url.hash).toBe("#introduction");
|
|
166
|
+
expect(route.fullPath).toBe("/about#introduction");
|
|
167
|
+
});
|
|
168
|
+
test("should correctly handle complex URL combination", () => {
|
|
169
|
+
const route = router.resolve(
|
|
170
|
+
"/user/123?tab=profile&edit=true#personal-info"
|
|
171
|
+
);
|
|
172
|
+
expect(route.params.id).toBe("123");
|
|
173
|
+
expect(route.query.tab).toBe("profile");
|
|
174
|
+
expect(route.query.edit).toBe("true");
|
|
175
|
+
expect(route.url.hash).toBe("#personal-info");
|
|
176
|
+
expect(route.fullPath).toBe(
|
|
177
|
+
"/user/123?tab=profile&edit=true#personal-info"
|
|
178
|
+
);
|
|
179
|
+
});
|
|
180
|
+
});
|
|
181
|
+
describe("Nested route resolution", () => {
|
|
182
|
+
test("should correctly resolve nested routes", () => {
|
|
183
|
+
var _a, _b;
|
|
184
|
+
const route = router.resolve("/user/123/profile");
|
|
185
|
+
expect(route.params.id).toBe("123");
|
|
186
|
+
expect(route.path).toBe("/user/123/profile");
|
|
187
|
+
expect(route.matched.length).toBe(2);
|
|
188
|
+
expect((_b = (_a = route.config) == null ? void 0 : _a.meta) == null ? void 0 : _b.section).toBe("profile");
|
|
189
|
+
});
|
|
190
|
+
test("should correctly resolve deeply nested routes", () => {
|
|
191
|
+
var _a, _b;
|
|
192
|
+
const route = router.resolve("/admin/users");
|
|
193
|
+
expect(route.path).toBe("/admin/users");
|
|
194
|
+
expect(route.matched.length).toBe(2);
|
|
195
|
+
expect((_b = (_a = route.config) == null ? void 0 : _a.meta) == null ? void 0 : _b.section).toBe("users");
|
|
196
|
+
});
|
|
197
|
+
test("should return last matched route configuration", () => {
|
|
198
|
+
var _a, _b;
|
|
199
|
+
const route = router.resolve("/user/123/settings");
|
|
200
|
+
expect((_b = (_a = route.config) == null ? void 0 : _a.meta) == null ? void 0 : _b.section).toBe("settings");
|
|
201
|
+
expect(route.meta.section).toBe("settings");
|
|
202
|
+
expect(route.meta.title).toBeUndefined();
|
|
203
|
+
});
|
|
204
|
+
});
|
|
205
|
+
describe("Meta information handling", () => {
|
|
206
|
+
test("should correctly return route meta information", () => {
|
|
207
|
+
const route = router.resolve("/about");
|
|
208
|
+
expect(route.meta.title).toBe("About Page");
|
|
209
|
+
expect(route.meta.requiresAuth).toBe(false);
|
|
210
|
+
});
|
|
211
|
+
test("should return last matched route meta in nested routes", () => {
|
|
212
|
+
const route = router.resolve("/user/123/profile");
|
|
213
|
+
expect(route.meta.section).toBe("profile");
|
|
214
|
+
expect(route.meta.title).toBeUndefined();
|
|
215
|
+
expect(route.meta.requiresAuth).toBeUndefined();
|
|
216
|
+
});
|
|
217
|
+
test("should return empty object when no meta information exists", () => {
|
|
218
|
+
const testRouter = new Router({
|
|
219
|
+
mode: RouterMode.memory,
|
|
220
|
+
base: new URL("http://localhost:3000/"),
|
|
221
|
+
routes: [
|
|
222
|
+
{
|
|
223
|
+
path: "/no-meta",
|
|
224
|
+
component: () => "NoMeta"
|
|
225
|
+
}
|
|
226
|
+
]
|
|
227
|
+
});
|
|
228
|
+
const route = testRouter.resolve("/no-meta");
|
|
229
|
+
expect(route.meta).toEqual({});
|
|
230
|
+
testRouter.destroy();
|
|
231
|
+
});
|
|
232
|
+
});
|
|
233
|
+
describe("Error handling and edge cases", () => {
|
|
234
|
+
test("should correctly handle non-existent routes", () => {
|
|
235
|
+
const route = router.resolve("/non-existent");
|
|
236
|
+
expect(route.matched).toEqual([]);
|
|
237
|
+
expect(route.config).toBeNull();
|
|
238
|
+
expect(route.meta).toEqual({});
|
|
239
|
+
expect(route.params).toEqual({});
|
|
240
|
+
expect(route.path).toBe("/non-existent");
|
|
241
|
+
});
|
|
242
|
+
test("should correctly handle root path", () => {
|
|
243
|
+
const route = router.resolve("/");
|
|
244
|
+
expect(route.path).toBe("/");
|
|
245
|
+
expect(route.matched.length).toBe(1);
|
|
246
|
+
expect(route.meta.title).toBe("Home Page");
|
|
247
|
+
});
|
|
248
|
+
test("should correctly handle empty string path", () => {
|
|
249
|
+
const route = router.resolve("");
|
|
250
|
+
expect(route.path).toBe("/");
|
|
251
|
+
expect(route.matched.length).toBe(1);
|
|
252
|
+
});
|
|
253
|
+
test("should correctly handle relative path", () => {
|
|
254
|
+
const route = router.resolve("about");
|
|
255
|
+
expect(route.path).toBe("/about");
|
|
256
|
+
expect(route.matched.length).toBe(1);
|
|
257
|
+
});
|
|
258
|
+
test("should correctly handle paths with special characters", () => {
|
|
259
|
+
const route = router.resolve("/user/test%20user");
|
|
260
|
+
expect(route.params.id).toBe("test%20user");
|
|
261
|
+
expect(route.path).toBe("/user/test%20user");
|
|
262
|
+
});
|
|
263
|
+
test("should correctly handle URL encoded parameters", () => {
|
|
264
|
+
const route = router.resolve("/user/john%40example.com");
|
|
265
|
+
expect(route.params.id).toBe("john%40example.com");
|
|
266
|
+
expect(route.path).toBe("/user/john%40example.com");
|
|
267
|
+
});
|
|
268
|
+
});
|
|
269
|
+
describe("Object parameter resolution", () => {
|
|
270
|
+
test("should correctly handle object with params", () => {
|
|
271
|
+
const route = router.resolve({
|
|
272
|
+
path: "/user/789"
|
|
273
|
+
});
|
|
274
|
+
expect(route.params.id).toBe("789");
|
|
275
|
+
expect(route.path).toBe("/user/789");
|
|
276
|
+
});
|
|
277
|
+
test("should correctly handle object with query", () => {
|
|
278
|
+
const route = router.resolve({
|
|
279
|
+
path: "/about",
|
|
280
|
+
query: { lang: "zh", version: "2.0" }
|
|
281
|
+
});
|
|
282
|
+
expect(route.query.lang).toBe("zh");
|
|
283
|
+
expect(route.query.version).toBe("2.0");
|
|
284
|
+
expect(route.fullPath).toBe("/about?lang=zh&version=2.0");
|
|
285
|
+
});
|
|
286
|
+
test("should correctly handle object with hash", () => {
|
|
287
|
+
const route = router.resolve({
|
|
288
|
+
path: "/about",
|
|
289
|
+
hash: "#features"
|
|
290
|
+
});
|
|
291
|
+
expect(route.url.hash).toBe("#features");
|
|
292
|
+
expect(route.fullPath).toBe("/about#features");
|
|
293
|
+
});
|
|
294
|
+
test("should correctly handle object with state", () => {
|
|
295
|
+
const customState = {
|
|
296
|
+
from: "navigation",
|
|
297
|
+
timestamp: 1234567890
|
|
298
|
+
};
|
|
299
|
+
const route = router.resolve({
|
|
300
|
+
path: "/about",
|
|
301
|
+
state: customState
|
|
302
|
+
});
|
|
303
|
+
expect(route.state).toEqual(customState);
|
|
304
|
+
});
|
|
305
|
+
test("should correctly handle keepScrollPosition option", () => {
|
|
306
|
+
const route = router.resolve({
|
|
307
|
+
path: "/about",
|
|
308
|
+
keepScrollPosition: true
|
|
309
|
+
});
|
|
310
|
+
expect(route.keepScrollPosition).toBe(true);
|
|
311
|
+
});
|
|
312
|
+
});
|
|
313
|
+
describe("URL handling", () => {
|
|
314
|
+
test("should correctly handle complete URL", () => {
|
|
315
|
+
const route = router.resolve("http://localhost:3000/about");
|
|
316
|
+
expect(route.path).toBe("/about");
|
|
317
|
+
expect(route.url.href).toBe("http://localhost:3000/about");
|
|
318
|
+
});
|
|
319
|
+
test("should correctly handle different domain URL", () => {
|
|
320
|
+
const route = router.resolve("https://example.com/external");
|
|
321
|
+
expect(route.matched).toEqual([]);
|
|
322
|
+
expect(route.config).toBeNull();
|
|
323
|
+
});
|
|
324
|
+
test("should correctly handle URL with different port", () => {
|
|
325
|
+
const route = router.resolve("http://localhost:8080/about");
|
|
326
|
+
expect(route.matched).toEqual([]);
|
|
327
|
+
expect(route.config).toBeNull();
|
|
328
|
+
});
|
|
329
|
+
});
|
|
330
|
+
describe("Type and status validation", () => {
|
|
331
|
+
test("resolved route should have correct type", () => {
|
|
332
|
+
const route = router.resolve("/about");
|
|
333
|
+
expect(route.type).toBe("push");
|
|
334
|
+
expect(route.isPush).toBe(true);
|
|
335
|
+
});
|
|
336
|
+
test("resolved route should have correct handle state", () => {
|
|
337
|
+
const route = router.resolve("/about");
|
|
338
|
+
expect(route.handle).toBe(null);
|
|
339
|
+
});
|
|
340
|
+
test("resolved route should have correct URL object properties", () => {
|
|
341
|
+
const route = router.resolve("/about?lang=en#intro");
|
|
342
|
+
expect(route.url).toBeInstanceOf(URL);
|
|
343
|
+
expect(route.url.pathname).toBe("/about");
|
|
344
|
+
expect(route.url.search).toBe("?lang=en");
|
|
345
|
+
expect(route.url.hash).toBe("#intro");
|
|
346
|
+
});
|
|
347
|
+
test("resolved route should have frozen matched array", () => {
|
|
348
|
+
const route = router.resolve("/about");
|
|
349
|
+
expect(Object.isFrozen(route.matched)).toBe(true);
|
|
350
|
+
});
|
|
351
|
+
});
|
|
352
|
+
describe("Practical use case scenarios", () => {
|
|
353
|
+
test("should support generating link URL without triggering navigation", () => {
|
|
354
|
+
const route = router.resolve("/user/123?tab=profile");
|
|
355
|
+
const linkUrl = route.url.href;
|
|
356
|
+
expect(linkUrl).toBe("http://localhost:3000/user/123?tab=profile");
|
|
357
|
+
expect(router.route.path).toBe("/");
|
|
358
|
+
});
|
|
359
|
+
test("should support pre-checking route matching status", () => {
|
|
360
|
+
const validRoute = router.resolve("/about");
|
|
361
|
+
const invalidRoute = router.resolve("/non-existent");
|
|
362
|
+
expect(validRoute.matched.length).toBeGreaterThan(0);
|
|
363
|
+
expect(invalidRoute.matched.length).toBe(0);
|
|
364
|
+
});
|
|
365
|
+
test("should support extracting route parameters and meta information", () => {
|
|
366
|
+
var _a;
|
|
367
|
+
const route = router.resolve("/user/123/profile");
|
|
368
|
+
expect(route.params.id).toBe("123");
|
|
369
|
+
expect(route.meta.section).toBe("profile");
|
|
370
|
+
expect((_a = route.config) == null ? void 0 : _a.path).toBe("/profile");
|
|
371
|
+
});
|
|
372
|
+
test("should support testing route configuration validity with valid paths", () => {
|
|
373
|
+
const testCases = createTestCases().validPaths;
|
|
374
|
+
testCases.forEach(({ path, shouldMatch }) => {
|
|
375
|
+
const route = router.resolve(path);
|
|
376
|
+
if (shouldMatch) {
|
|
377
|
+
expect(route.matched.length).toBeGreaterThan(0);
|
|
378
|
+
expect(route.config).not.toBeNull();
|
|
379
|
+
}
|
|
380
|
+
});
|
|
381
|
+
});
|
|
382
|
+
test("should support testing route configuration validity with invalid paths", () => {
|
|
383
|
+
const testCases = createTestCases().invalidPaths;
|
|
384
|
+
testCases.forEach(({ path, shouldMatch }) => {
|
|
385
|
+
const route = router.resolve(path);
|
|
386
|
+
if (!shouldMatch) {
|
|
387
|
+
expect(route.matched.length).toBe(0);
|
|
388
|
+
expect(route.config).toBeNull();
|
|
389
|
+
}
|
|
390
|
+
});
|
|
391
|
+
});
|
|
392
|
+
});
|
|
393
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|