Skip to content

Commit 16e50e0

Browse files
authored
fix: resolve false positive "could not find match from" (#4610)
fixes #4284
1 parent b9f88c5 commit 16e50e0

File tree

3 files changed

+345
-7
lines changed

3 files changed

+345
-7
lines changed

packages/react-router/tests/link.test.tsx

Lines changed: 318 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1383,6 +1383,132 @@ describe('Link', () => {
13831383
expect(window.location.pathname).toBe('/posts/id1')
13841384
})
13851385

1386+
test('when navigating from /posts/$postId to "/"', async () => {
1387+
const rootRoute = createRootRoute({
1388+
component: () => {
1389+
return (
1390+
<>
1391+
<Link to="/" data-testid="home-link">
1392+
Home
1393+
</Link>
1394+
<Link to="/posts" data-testid="posts-link">
1395+
Posts
1396+
</Link>
1397+
<Outlet />
1398+
</>
1399+
)
1400+
},
1401+
})
1402+
1403+
const indexRoute = createRoute({
1404+
getParentRoute: () => rootRoute,
1405+
path: '/',
1406+
component: () => {
1407+
return (
1408+
<>
1409+
<h1 data-testid="home-heading">Index</h1>
1410+
</>
1411+
)
1412+
},
1413+
})
1414+
1415+
const PostsComponent = () => {
1416+
return (
1417+
<>
1418+
<h1>Posts</h1>
1419+
<Link
1420+
to="/posts/$postId"
1421+
params={{ postId: 'id1' }}
1422+
data-testid="post1-link"
1423+
>
1424+
To first post
1425+
</Link>
1426+
<Outlet />
1427+
</>
1428+
)
1429+
}
1430+
1431+
const postsRoute = createRoute({
1432+
getParentRoute: () => rootRoute,
1433+
path: 'posts',
1434+
component: PostsComponent,
1435+
})
1436+
1437+
const PostsIndexComponent = () => {
1438+
return (
1439+
<>
1440+
<h1 data-testid="posts-index-heading">Posts Index</h1>
1441+
<Outlet />
1442+
</>
1443+
)
1444+
}
1445+
1446+
const postsIndexRoute = createRoute({
1447+
getParentRoute: () => postsRoute,
1448+
path: '/',
1449+
component: PostsIndexComponent,
1450+
})
1451+
1452+
const PostComponent = () => {
1453+
const params = useParams({ strict: false })
1454+
return (
1455+
<>
1456+
<span data-testid="post-param">Params: {params.postId}</span>
1457+
</>
1458+
)
1459+
}
1460+
1461+
const postRoute = createRoute({
1462+
getParentRoute: () => postsRoute,
1463+
path: '$postId',
1464+
component: PostComponent,
1465+
})
1466+
1467+
const router = createRouter({
1468+
routeTree: rootRoute.addChildren([
1469+
indexRoute,
1470+
postsRoute.addChildren([postsIndexRoute, postRoute]),
1471+
]),
1472+
history,
1473+
})
1474+
1475+
render(<RouterProvider router={router} />)
1476+
1477+
const postsLink = await screen.findByTestId('posts-link')
1478+
1479+
expect(postsLink).toHaveAttribute('href', '/posts')
1480+
1481+
await act(() => fireEvent.click(postsLink))
1482+
1483+
const postsText = await screen.findByTestId('posts-index-heading')
1484+
expect(postsText).toBeInTheDocument()
1485+
1486+
const postLink = await screen.findByTestId('post1-link')
1487+
1488+
expect(postLink).toHaveAttribute('href', '/posts/id1')
1489+
1490+
await act(() => fireEvent.click(postLink))
1491+
1492+
const paramText = await screen.findByTestId('post-param')
1493+
expect(paramText).toBeInTheDocument()
1494+
1495+
expect(window.location.pathname).toBe('/posts/id1')
1496+
1497+
const homeLink = await screen.findByTestId('home-link')
1498+
1499+
const consoleWarnSpy = vi.spyOn(console, 'warn')
1500+
1501+
await act(() => fireEvent.click(homeLink))
1502+
1503+
expect(window.location.pathname).toBe('/')
1504+
const homeHeading = await screen.findByTestId('home-heading')
1505+
expect(homeHeading).toBeInTheDocument()
1506+
1507+
expect(consoleWarnSpy).not.toHaveBeenCalled()
1508+
1509+
consoleWarnSpy.mockRestore()
1510+
})
1511+
13861512
test('when navigating from /posts to ../posts/$postId', async () => {
13871513
const rootRoute = createRootRoute()
13881514
const indexRoute = createRoute({
@@ -1756,6 +1882,192 @@ describe('Link', () => {
17561882
expect(ErrorComponent).not.toHaveBeenCalled()
17571883
})
17581884

1885+
test('when navigating from /dashboard/posts/$postId to /dashboard/users', async () => {
1886+
const ErrorComponent = vi.fn(() => <div>Something went wrong!</div>)
1887+
1888+
const rootRoute = createRootRoute({
1889+
errorComponent: ErrorComponent,
1890+
})
1891+
1892+
const indexRoute = createRoute({
1893+
getParentRoute: () => rootRoute,
1894+
path: '/',
1895+
component: () => {
1896+
return (
1897+
<>
1898+
<h1>Index</h1>
1899+
<Link to="/dashboard" data-testid="dashboard-link">
1900+
dashboard
1901+
</Link>
1902+
</>
1903+
)
1904+
},
1905+
})
1906+
1907+
const dashboardRoute = createRoute({
1908+
getParentRoute: () => rootRoute,
1909+
path: 'dashboard',
1910+
component: () => {
1911+
return (
1912+
<>
1913+
<h1 data-testid="dashboard-heading">dashboard</h1>
1914+
<Link to="/dashboard/posts" data-testid="posts-link">
1915+
posts
1916+
</Link>
1917+
<Link to="/dashboard/users" data-testid="users-link">
1918+
users
1919+
</Link>
1920+
<Outlet />
1921+
</>
1922+
)
1923+
},
1924+
})
1925+
1926+
const PostsComponent = () => {
1927+
return (
1928+
<>
1929+
<h1 data-testid="posts-heading">Posts</h1>
1930+
<Link
1931+
to="/dashboard/posts/$postid"
1932+
data-testid="post1-link"
1933+
params={{ postid: 'id1' }}
1934+
>
1935+
Post1
1936+
</Link>
1937+
<Link
1938+
to="/dashboard/posts/$postid"
1939+
data-testid="post2-link"
1940+
params={{ postid: 'id2' }}
1941+
>
1942+
Post2
1943+
</Link>
1944+
<Outlet />
1945+
</>
1946+
)
1947+
}
1948+
1949+
const UsersComponent = () => {
1950+
return (
1951+
<>
1952+
<h1 data-testid="users-heading">Users</h1>
1953+
<Link
1954+
to="/dashboard/users/$userid"
1955+
data-testid="user1-link"
1956+
params={{ userid: 'id1' }}
1957+
>
1958+
User1
1959+
</Link>
1960+
<Link
1961+
to="/dashboard/users/$userid"
1962+
data-testid="user2-link"
1963+
params={{ userid: 'id2' }}
1964+
>
1965+
User2
1966+
</Link>
1967+
<Outlet />
1968+
</>
1969+
)
1970+
}
1971+
1972+
const postsRoute = createRoute({
1973+
getParentRoute: () => dashboardRoute,
1974+
path: 'posts',
1975+
component: PostsComponent,
1976+
})
1977+
1978+
const usersRoute = createRoute({
1979+
getParentRoute: () => dashboardRoute,
1980+
path: 'users',
1981+
component: UsersComponent,
1982+
})
1983+
1984+
const PostComponent = () => {
1985+
const params = useParams({ strict: false })
1986+
return (
1987+
<>
1988+
<span data-testid="post-component">Params: {params.postId}</span>
1989+
</>
1990+
)
1991+
}
1992+
1993+
const UserComponent = () => {
1994+
const params = useParams({ strict: false })
1995+
return (
1996+
<>
1997+
<span data-testid="user-component">Params: {params.userId}</span>
1998+
</>
1999+
)
2000+
}
2001+
const postRoute = createRoute({
2002+
getParentRoute: () => postsRoute,
2003+
path: '$postid',
2004+
component: PostComponent,
2005+
})
2006+
2007+
const userRoute = createRoute({
2008+
getParentRoute: () => usersRoute,
2009+
path: '$userid',
2010+
component: UserComponent,
2011+
})
2012+
2013+
const router = createRouter({
2014+
routeTree: rootRoute.addChildren([
2015+
indexRoute,
2016+
dashboardRoute.addChildren([
2017+
postsRoute.addChildren([postRoute]),
2018+
usersRoute.addChildren([userRoute]),
2019+
]),
2020+
]),
2021+
history,
2022+
})
2023+
2024+
render(<RouterProvider router={router} />)
2025+
2026+
const dashboardLink = await screen.findByTestId('dashboard-link')
2027+
2028+
await act(() => fireEvent.click(dashboardLink))
2029+
2030+
const dashboardHeading = await screen.findByTestId('dashboard-heading')
2031+
2032+
expect(dashboardHeading).toBeInTheDocument()
2033+
2034+
const postsLink = await screen.findByTestId('posts-link')
2035+
await act(() => fireEvent.click(postsLink))
2036+
2037+
const postsHeading = await screen.findByTestId('posts-heading')
2038+
2039+
expect(window.location.pathname).toEqual('/dashboard/posts')
2040+
expect(postsHeading).toBeInTheDocument()
2041+
2042+
const post1Link = await screen.findByTestId('post1-link')
2043+
await act(() => fireEvent.click(post1Link))
2044+
const post1Heading = await screen.findByTestId('post-component')
2045+
2046+
expect(window.location.pathname).toEqual('/dashboard/posts/id1')
2047+
expect(post1Heading).toBeInTheDocument()
2048+
2049+
const consoleWarnSpy = vi.spyOn(console, 'warn')
2050+
2051+
const usersLink = await screen.findByTestId('users-link')
2052+
await act(() => fireEvent.click(usersLink))
2053+
2054+
const usersHeading = await screen.findByTestId('users-heading')
2055+
2056+
expect(window.location.pathname).toEqual('/dashboard/users')
2057+
expect(usersHeading).toBeInTheDocument()
2058+
2059+
const user1Link = await screen.findByTestId('user1-link')
2060+
await act(() => fireEvent.click(user1Link))
2061+
const user1Heading = await screen.findByTestId('user-component')
2062+
2063+
expect(window.location.pathname).toEqual('/dashboard/users/id1')
2064+
expect(user1Heading).toBeInTheDocument()
2065+
2066+
expect(consoleWarnSpy).not.toHaveBeenCalled()
2067+
2068+
consoleWarnSpy.mockRestore()
2069+
})
2070+
17592071
test('when navigating from /posts/$postId to ./info and the current route is /posts/$postId/details', async () => {
17602072
const ErrorComponent = vi.fn(() => <div>Something went wrong!</div>)
17612073

@@ -2589,6 +2901,12 @@ describe('Link', () => {
25892901

25902902
await act(() => fireEvent.click(postsLink))
25912903

2904+
const invoicesLink = await screen.findByRole('link', {
2905+
name: 'To Invoices',
2906+
})
2907+
2908+
fireEvent.click(invoicesLink)
2909+
25922910
expect(consoleWarnSpy).toHaveBeenCalledWith(
25932911
'Could not find match for from: /invoices',
25942912
)

packages/router-core/src/RouterProvider.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,5 +42,6 @@ export type BuildLocationFn = <
4242
opts: ToOptions<TRouter, TFrom, TTo, TMaskFrom, TMaskTo> & {
4343
leaveParams?: boolean
4444
_includeValidateSearch?: boolean
45+
_isNavigate?: boolean
4546
},
4647
) => ParsedLocation

0 commit comments

Comments
 (0)