-
-
Notifications
You must be signed in to change notification settings - Fork 10.2k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
This prevents `*` from matching without a preceding `/` in the URL pathname. So e.g. `/users/*` matches `/users/mj` but not `/userstypo`. This also fixes `to="."` in splat routes so they point to the route path *including* the portion of the URL that was matched by the `*`, which also makes `*` more consistent with `:param` since it's treated just the same as any other param. There is however a subtle breaking change if you are using the low-level `match` API, e.g. the `match` object you get back from `matchRoutes()` or `useMatch()`. `match.pathname` in a splat route now includes the full URL pathname that was matched instead of only the portion before the `*`. There is a new variable, `match.pathnameStart` that you can use if you needed this for doing your own route matching. Fixes #7972
- Loading branch information
Showing
8 changed files
with
454 additions
and
238 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,82 @@ | ||
import * as React from "react"; | ||
import { create as createTestRenderer } from "react-test-renderer"; | ||
import { | ||
MemoryRouter as Router, | ||
Routes, | ||
Route, | ||
Outlet | ||
} from "react-router-dom"; | ||
|
||
describe("greedy matching", () => { | ||
let routes = ( | ||
<Routes> | ||
<Route path="/" element={<p>Root</p>} /> | ||
<Route | ||
path="home" | ||
element={ | ||
<div> | ||
Home Layout <Outlet /> | ||
</div> | ||
} | ||
> | ||
<Route index element={<p>Home</p>} /> | ||
<Route path="*" element={<p>Home Not Found</p>} /> | ||
</Route> | ||
<Route path="*" element={<p>Not Found</p>} /> | ||
</Routes> | ||
); | ||
|
||
it("matches the root route", () => { | ||
let renderer = createTestRenderer( | ||
<Router initialEntries={["/"]} children={routes} /> | ||
); | ||
|
||
expect(renderer.toJSON()).toMatchInlineSnapshot(` | ||
<p> | ||
Root | ||
</p> | ||
`); | ||
}); | ||
|
||
it("matches the index route", () => { | ||
let renderer = createTestRenderer( | ||
<Router initialEntries={["/home"]} children={routes} /> | ||
); | ||
|
||
expect(renderer.toJSON()).toMatchInlineSnapshot(` | ||
<div> | ||
Home Layout | ||
<p> | ||
Home | ||
</p> | ||
</div> | ||
`); | ||
}); | ||
|
||
it('matches the nested "not found" route', () => { | ||
let renderer = createTestRenderer( | ||
<Router initialEntries={["/home/typo"]} children={routes} /> | ||
); | ||
|
||
expect(renderer.toJSON()).toMatchInlineSnapshot(` | ||
<div> | ||
Home Layout | ||
<p> | ||
Home Not Found | ||
</p> | ||
</div> | ||
`); | ||
}); | ||
|
||
it('matches the "not found" route', () => { | ||
let renderer = createTestRenderer( | ||
<Router initialEntries={["/hometypo"]} children={routes} /> | ||
); | ||
|
||
expect(renderer.toJSON()).toMatchInlineSnapshot(` | ||
<p> | ||
Not Found | ||
</p> | ||
`); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,132 @@ | ||
import { matchPath } from "react-router"; | ||
|
||
describe("matchPath", () => { | ||
it("matches the root / URL", () => { | ||
let match = matchPath("/", "/")!; | ||
expect(match).not.toBeNull(); | ||
expect(match).toMatchObject({ | ||
pathname: "/", | ||
pathnameStart: "/" | ||
}); | ||
}); | ||
|
||
it("matches a pathname", () => { | ||
let match = matchPath("users", "/users")!; | ||
expect(match).not.toBeNull(); | ||
expect(match).toMatchObject({ | ||
pathname: "/users", | ||
pathnameStart: "/users" | ||
}); | ||
}); | ||
|
||
it("matches a pathname with multiple segments", () => { | ||
let match = matchPath("users/mj", "/users/mj")!; | ||
expect(match).not.toBeNull(); | ||
expect(match).toMatchObject({ | ||
pathname: "/users/mj", | ||
pathnameStart: "/users/mj" | ||
}); | ||
}); | ||
|
||
it("matches a pathname with a trailing slash", () => { | ||
let match = matchPath("/users", "/users/")!; | ||
expect(match).not.toBeNull(); | ||
expect(match).toMatchObject({ | ||
pathname: "/users/", | ||
pathnameStart: "/users/" | ||
}); | ||
}); | ||
|
||
it("matches a pathname with multiple segments and a trailing slash", () => { | ||
let match = matchPath("/users/mj", "/users/mj/")!; | ||
expect(match).not.toBeNull(); | ||
expect(match).toMatchObject({ | ||
pathname: "/users/mj/", | ||
pathnameStart: "/users/mj/" | ||
}); | ||
}); | ||
|
||
describe("with a / pattern and { end: false }", () => { | ||
it("matches a pathname", () => { | ||
let match = matchPath({ path: "/", end: false }, "/users")!; | ||
expect(match).not.toBeNull(); | ||
expect(match).toMatchObject({ | ||
pathname: "/", | ||
pathnameStart: "/" | ||
}); | ||
}); | ||
|
||
it("matches a pathname with multiple segments", () => { | ||
let match = matchPath({ path: "/", end: false }, "/users/mj")!; | ||
expect(match).not.toBeNull(); | ||
expect(match).toMatchObject({ | ||
pathname: "/", | ||
pathnameStart: "/" | ||
}); | ||
}); | ||
|
||
it("matches a pathname with a trailing slash", () => { | ||
let match = matchPath({ path: "/", end: false }, "/users/")!; | ||
expect(match).not.toBeNull(); | ||
expect(match).toMatchObject({ | ||
pathname: "/", | ||
pathnameStart: "/" | ||
}); | ||
}); | ||
|
||
it("matches a pathname with multiple segments and a trailing slash", () => { | ||
let match = matchPath({ path: "/", end: false }, "/users/mj/")!; | ||
expect(match).not.toBeNull(); | ||
expect(match).toMatchObject({ | ||
pathname: "/", | ||
pathnameStart: "/" | ||
}); | ||
}); | ||
}); | ||
|
||
it("is not case-sensitive by default", () => { | ||
let match = matchPath({ path: "/SystemDashboard" }, "/systemdashboard")!; | ||
expect(match).not.toBeNull(); | ||
expect(match).toMatchObject({ | ||
pathname: "/systemdashboard", | ||
pathnameStart: "/systemdashboard" | ||
}); | ||
}); | ||
|
||
it("matches a case-sensitive pathname", () => { | ||
let match = matchPath( | ||
{ path: "/SystemDashboard", caseSensitive: true }, | ||
"/SystemDashboard" | ||
)!; | ||
expect(match).not.toBeNull(); | ||
expect(match).toMatchObject({ | ||
pathname: "/SystemDashboard", | ||
pathnameStart: "/SystemDashboard" | ||
}); | ||
}); | ||
|
||
it("does not match a case-sensitive pathname with the wrong case", () => { | ||
let match = matchPath( | ||
{ path: "/SystemDashboard", caseSensitive: true }, | ||
"/systemDashboard" | ||
); | ||
expect(match).toBeNull(); | ||
}); | ||
|
||
describe("when the pattern has a trailing *", () => { | ||
it("matches the remaining portion of the pathname", () => { | ||
let match = matchPath("/files/*", "/files/mj.jpg")!; | ||
expect(match).not.toBeNull(); | ||
expect(match).toMatchObject({ | ||
params: { "*": "mj.jpg" }, | ||
pathname: "/files/mj.jpg", | ||
pathnameStart: "/files" | ||
}); | ||
}); | ||
|
||
it("matches only after a slash", () => { | ||
let match = matchPath("/files/*", "/filestypo"); | ||
expect(match).toBeNull(); | ||
}); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file was deleted.
Oops, something went wrong.
Oops, something went wrong.