-
-
Notifications
You must be signed in to change notification settings - Fork 705
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: allow
useMatch
to not throw if match was not found
in this case, `matchRoute` will return undefined
- Loading branch information
1 parent
d6b8823
commit 7a16ada
Showing
4 changed files
with
201 additions
and
6 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
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 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,52 @@ | ||
import { describe, expectTypeOf, test } from 'vitest' | ||
import { createRootRoute, createRoute, createRouter, useMatch } from '../src' | ||
import type { AnyRouteMatch, MakeRouteMatch } from '../src/Matches' | ||
|
||
const rootRoute = createRootRoute() | ||
|
||
const indexRoute = createRoute({ | ||
getParentRoute: () => rootRoute, | ||
path: '/', | ||
}) | ||
|
||
const invoicesRoute = createRoute({ | ||
getParentRoute: () => rootRoute, | ||
path: 'invoices', | ||
}) | ||
|
||
const routeTree = rootRoute.addChildren([invoicesRoute, indexRoute]) | ||
|
||
const defaultRouter = createRouter({ routeTree }) | ||
|
||
type DefaultRouter = typeof defaultRouter | ||
|
||
describe('useMatch', () => { | ||
const from = '/invoices' | ||
test('return type is `RouteMatch` when shouldThrow = true', () => { | ||
const shouldThrow = true | ||
const match = useMatch< | ||
DefaultRouter['routeTree'], | ||
typeof from, | ||
true, | ||
MakeRouteMatch<DefaultRouter['routeTree']>, | ||
MakeRouteMatch<DefaultRouter['routeTree']>, | ||
typeof shouldThrow | ||
>({ from, shouldThrow }) | ||
|
||
expectTypeOf(match).toMatchTypeOf<AnyRouteMatch>() | ||
}) | ||
|
||
test('return type is `RouteMatch | undefined` when shouldThrow = false', () => { | ||
const shouldThrow = false | ||
const match = useMatch< | ||
DefaultRouter['routeTree'], | ||
typeof from, | ||
true, | ||
MakeRouteMatch<DefaultRouter['routeTree']>, | ||
MakeRouteMatch<DefaultRouter['routeTree']>, | ||
typeof shouldThrow | ||
>({ from, shouldThrow }) | ||
|
||
expectTypeOf(match).toMatchTypeOf<AnyRouteMatch | undefined>() | ||
}) | ||
}) |
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,113 @@ | ||
import { afterEach, describe, expect, it, test, vi } from 'vitest' | ||
import '@testing-library/jest-dom/vitest' | ||
import React from 'react' | ||
import { render, screen } from '@testing-library/react' | ||
import { | ||
Link, | ||
Outlet, | ||
RouterProvider, | ||
createMemoryHistory, | ||
createRootRoute, | ||
createRoute, | ||
createRouter, | ||
useMatch, | ||
} from '../src' | ||
import type { RouteComponent, RouterHistory } from '../src' | ||
|
||
describe('useMatch', () => { | ||
function setup({ | ||
RootComponent, | ||
history, | ||
}: { | ||
RootComponent: RouteComponent | ||
history?: RouterHistory | ||
}) { | ||
const rootRoute = createRootRoute({ | ||
component: RootComponent, | ||
}) | ||
const indexRoute = createRoute({ | ||
getParentRoute: () => rootRoute, | ||
path: '/', | ||
component: () => ( | ||
<React.Fragment> | ||
<h1>IndexTitle</h1> | ||
<Link to="/posts">Posts</Link> | ||
</React.Fragment> | ||
), | ||
}) | ||
|
||
const postsRoute = createRoute({ | ||
getParentRoute: () => rootRoute, | ||
path: '/posts', | ||
component: () => <h1>PostsTitle</h1>, | ||
}) | ||
|
||
const router = createRouter({ | ||
routeTree: rootRoute.addChildren([indexRoute, postsRoute]), | ||
history, | ||
}) | ||
|
||
render(<RouterProvider router={router} />) | ||
} | ||
|
||
describe('when match is found', () => { | ||
test.each([true, false, undefined])( | ||
'returns the match if shouldThrow = %s', | ||
async (shouldThrow) => { | ||
function RootComponent() { | ||
const match = useMatch({ from: '/posts', shouldThrow }) | ||
expect(match).toBeDefined() | ||
expect(match!.routeId).toBe('/posts') | ||
return <Outlet /> | ||
} | ||
|
||
setup({ | ||
RootComponent, | ||
history: createMemoryHistory({ initialEntries: ['/posts'] }), | ||
}) | ||
await screen.findByText('PostsTitle') | ||
}, | ||
) | ||
}) | ||
|
||
describe('when match is not found', () => { | ||
test.each([undefined, true])( | ||
'throws if shouldThrow = %s', | ||
async (shouldThrow) => { | ||
function RootComponent() { | ||
useMatch({ from: '/posts', shouldThrow }) | ||
return <Outlet /> | ||
} | ||
setup({ RootComponent }) | ||
expect( | ||
await screen.findByText( | ||
'Invariant failed: Could not find an active match from "/posts"', | ||
), | ||
).toBeInTheDocument() | ||
}, | ||
) | ||
|
||
describe('returns undefined if shouldThrow = false', () => { | ||
test('without select function', async () => { | ||
function RootComponent() { | ||
const match = useMatch({ from: 'posts', shouldThrow: false }) | ||
expect(match).toBeUndefined() | ||
return <Outlet /> | ||
} | ||
setup({ RootComponent }) | ||
expect(await screen.findByText('IndexTitle')).toBeInTheDocument() | ||
}) | ||
test('with select function', async () => { | ||
const select = vi.fn() | ||
function RootComponent() { | ||
const match = useMatch({ from: 'posts', shouldThrow: false, select }) | ||
expect(match).toBeUndefined() | ||
return <Outlet /> | ||
} | ||
setup({ RootComponent }) | ||
expect(await screen.findByText('IndexTitle')).toBeInTheDocument() | ||
expect(select).not.toHaveBeenCalled() | ||
}) | ||
}) | ||
}) | ||
}) |