@pyreon/router 0.7.11 → 0.7.13
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/package.json +4 -4
- package/src/tests/router.test.ts +105 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pyreon/router",
|
|
3
|
-
"version": "0.7.
|
|
3
|
+
"version": "0.7.13",
|
|
4
4
|
"description": "Official router for Pyreon",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -39,9 +39,9 @@
|
|
|
39
39
|
"prepublishOnly": "bun run build"
|
|
40
40
|
},
|
|
41
41
|
"dependencies": {
|
|
42
|
-
"@pyreon/core": "^0.7.
|
|
43
|
-
"@pyreon/reactivity": "^0.7.
|
|
44
|
-
"@pyreon/runtime-dom": "^0.7.
|
|
42
|
+
"@pyreon/core": "^0.7.13",
|
|
43
|
+
"@pyreon/reactivity": "^0.7.13",
|
|
44
|
+
"@pyreon/runtime-dom": "^0.7.13"
|
|
45
45
|
},
|
|
46
46
|
"devDependencies": {
|
|
47
47
|
"@happy-dom/global-registrator": "^20.8.3",
|
package/src/tests/router.test.ts
CHANGED
|
@@ -3919,3 +3919,108 @@ describe("onBeforeRouteUpdate", () => {
|
|
|
3919
3919
|
router.destroy()
|
|
3920
3920
|
})
|
|
3921
3921
|
})
|
|
3922
|
+
|
|
3923
|
+
// ─── beforeEach guard edge cases ──────────────────────────────────────────────
|
|
3924
|
+
|
|
3925
|
+
describe("beforeEach guard edge cases", () => {
|
|
3926
|
+
test("returning false prevents navigation and doesn't mutate state", async () => {
|
|
3927
|
+
const router = createRouter({ routes, url: "/" })
|
|
3928
|
+
const guardCalls: Array<{ to: string; from: string }> = []
|
|
3929
|
+
router.beforeEach((to, from) => {
|
|
3930
|
+
guardCalls.push({ to: to.path, from: from.path })
|
|
3931
|
+
return false
|
|
3932
|
+
})
|
|
3933
|
+
const initialRoute = router.currentRoute()
|
|
3934
|
+
await router.push("/about")
|
|
3935
|
+
// Route should not have changed
|
|
3936
|
+
expect(router.currentRoute().path).toBe("/")
|
|
3937
|
+
expect(router.currentRoute()).toBe(initialRoute)
|
|
3938
|
+
// Guard was called with correct args
|
|
3939
|
+
expect(guardCalls).toEqual([{ to: "/about", from: "/" }])
|
|
3940
|
+
})
|
|
3941
|
+
|
|
3942
|
+
test("throwing error doesn't break router state", async () => {
|
|
3943
|
+
const router = createRouter({ routes, url: "/" })
|
|
3944
|
+
router.beforeEach(() => {
|
|
3945
|
+
throw new Error("guard explosion")
|
|
3946
|
+
})
|
|
3947
|
+
await router.push("/about")
|
|
3948
|
+
// Navigation should be blocked but router still functional
|
|
3949
|
+
expect(router.currentRoute().path).toBe("/")
|
|
3950
|
+
// Subsequent navigation with a non-throwing guard should work
|
|
3951
|
+
// Remove the throwing guard by adding a new one that allows
|
|
3952
|
+
// (guards run in order; the throwing one still runs first, so replace via fresh router)
|
|
3953
|
+
})
|
|
3954
|
+
|
|
3955
|
+
test("throwing error doesn't prevent subsequent navigations", async () => {
|
|
3956
|
+
const router = createRouter({ routes, url: "/" })
|
|
3957
|
+
let shouldThrow = true
|
|
3958
|
+
router.beforeEach(() => {
|
|
3959
|
+
if (shouldThrow) throw new Error("guard error")
|
|
3960
|
+
return true
|
|
3961
|
+
})
|
|
3962
|
+
await router.push("/about")
|
|
3963
|
+
expect(router.currentRoute().path).toBe("/")
|
|
3964
|
+
|
|
3965
|
+
// Disable throwing and navigate again — router should still work
|
|
3966
|
+
shouldThrow = false
|
|
3967
|
+
await router.push("/about")
|
|
3968
|
+
expect(router.currentRoute().path).toBe("/about")
|
|
3969
|
+
})
|
|
3970
|
+
|
|
3971
|
+
test("navigation to same route keeps state stable", async () => {
|
|
3972
|
+
const router = createRouter({ routes, url: "/about" })
|
|
3973
|
+
const guardCalls: string[] = []
|
|
3974
|
+
router.beforeEach((to) => {
|
|
3975
|
+
guardCalls.push(to.path)
|
|
3976
|
+
return true
|
|
3977
|
+
})
|
|
3978
|
+
await router.push("/about")
|
|
3979
|
+
// State should remain at /about regardless of whether guards ran
|
|
3980
|
+
expect(router.currentRoute().path).toBe("/about")
|
|
3981
|
+
// Guard was invoked (router does not short-circuit same-route navigation)
|
|
3982
|
+
expect(guardCalls).toEqual(["/about"])
|
|
3983
|
+
})
|
|
3984
|
+
})
|
|
3985
|
+
|
|
3986
|
+
// ─── Optional params & trailing slash ─────────────────────────────────────────
|
|
3987
|
+
|
|
3988
|
+
describe("matchPath — optional params", () => {
|
|
3989
|
+
test("optional param matches path without the param", () => {
|
|
3990
|
+
const result = matchPath("/users/:id?", "/users")
|
|
3991
|
+
expect(result).not.toBeNull()
|
|
3992
|
+
expect(result).toEqual({})
|
|
3993
|
+
})
|
|
3994
|
+
|
|
3995
|
+
test("optional param matches path with the param", () => {
|
|
3996
|
+
const result = matchPath("/users/:id?", "/users/42")
|
|
3997
|
+
expect(result).not.toBeNull()
|
|
3998
|
+
expect(result).toEqual({ id: "42" })
|
|
3999
|
+
})
|
|
4000
|
+
|
|
4001
|
+
test("optional param at end doesn't match extra segments", () => {
|
|
4002
|
+
const result = matchPath("/users/:id?", "/users/42/extra")
|
|
4003
|
+
expect(result).toBeNull()
|
|
4004
|
+
})
|
|
4005
|
+
})
|
|
4006
|
+
|
|
4007
|
+
describe("resolveRoute — trailing slash normalization", () => {
|
|
4008
|
+
test("trailing slash on static path resolves the same route", () => {
|
|
4009
|
+
// resolveRoute uses split("/").filter(Boolean), so "/about/" and "/about" produce same segments
|
|
4010
|
+
const withSlash = resolveRoute("/about/", routes)
|
|
4011
|
+
const withoutSlash = resolveRoute("/about", routes)
|
|
4012
|
+
expect(withSlash.matched.length).toBe(withoutSlash.matched.length)
|
|
4013
|
+
if (withSlash.matched.length > 0 && withoutSlash.matched.length > 0) {
|
|
4014
|
+
expect(withSlash.matched[withSlash.matched.length - 1]?.component).toBe(
|
|
4015
|
+
withoutSlash.matched[withoutSlash.matched.length - 1]?.component,
|
|
4016
|
+
)
|
|
4017
|
+
}
|
|
4018
|
+
})
|
|
4019
|
+
|
|
4020
|
+
test("trailing slash on dynamic path resolves the same route", () => {
|
|
4021
|
+
const withSlash = resolveRoute("/user/42/", routes)
|
|
4022
|
+
const withoutSlash = resolveRoute("/user/42", routes)
|
|
4023
|
+
expect(withSlash.params).toEqual(withoutSlash.params)
|
|
4024
|
+
expect(withSlash.matched.length).toBe(withoutSlash.matched.length)
|
|
4025
|
+
})
|
|
4026
|
+
})
|