diff --git a/e2e/react-router/basic-file-based/src/routeTree.gen.ts b/e2e/react-router/basic-file-based/src/routeTree.gen.ts index 590f7df911..912fb05874 100644 --- a/e2e/react-router/basic-file-based/src/routeTree.gen.ts +++ b/e2e/react-router/basic-file-based/src/routeTree.gen.ts @@ -29,10 +29,13 @@ import { Route as groupLazyinsideImport } from './routes/(group)/lazyinside' import { Route as groupInsideImport } from './routes/(group)/inside' import { Route as groupLayoutImport } from './routes/(group)/_layout' import { Route as anotherGroupOnlyrouteinsideImport } from './routes/(another-group)/onlyrouteinside' +import { Route as RedirectComponentIndexImport } from './routes/redirect/component/index' import { Route as RedirectTargetIndexImport } from './routes/redirect/$target/index' import { Route as RedirectPreloadThirdImport } from './routes/redirect/preload/third' import { Route as RedirectPreloadSecondImport } from './routes/redirect/preload/second' import { Route as RedirectPreloadFirstImport } from './routes/redirect/preload/first' +import { Route as RedirectComponentSecondImport } from './routes/redirect/component/second' +import { Route as RedirectComponentFirstImport } from './routes/redirect/component/first' import { Route as RedirectTargetViaLoaderImport } from './routes/redirect/$target/via-loader' import { Route as RedirectTargetViaBeforeLoadImport } from './routes/redirect/$target/via-beforeLoad' import { Route as PostsPostIdEditImport } from './routes/posts_.$postId.edit' @@ -148,6 +151,12 @@ const anotherGroupOnlyrouteinsideRoute = getParentRoute: () => rootRoute, } as any) +const RedirectComponentIndexRoute = RedirectComponentIndexImport.update({ + id: '/redirect/component/', + path: '/redirect/component/', + getParentRoute: () => rootRoute, +} as any) + const RedirectTargetIndexRoute = RedirectTargetIndexImport.update({ id: '/', path: '/', @@ -172,6 +181,18 @@ const RedirectPreloadFirstRoute = RedirectPreloadFirstImport.update({ getParentRoute: () => rootRoute, } as any) +const RedirectComponentSecondRoute = RedirectComponentSecondImport.update({ + id: '/redirect/component/second', + path: '/redirect/component/second', + getParentRoute: () => rootRoute, +} as any) + +const RedirectComponentFirstRoute = RedirectComponentFirstImport.update({ + id: '/redirect/component/first', + path: '/redirect/component/first', + getParentRoute: () => rootRoute, +} as any) + const RedirectTargetViaLoaderRoute = RedirectTargetViaLoaderImport.update({ id: '/via-loader', path: '/via-loader', @@ -387,6 +408,20 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof RedirectTargetViaLoaderImport parentRoute: typeof RedirectTargetImport } + '/redirect/component/first': { + id: '/redirect/component/first' + path: '/redirect/component/first' + fullPath: '/redirect/component/first' + preLoaderRoute: typeof RedirectComponentFirstImport + parentRoute: typeof rootRoute + } + '/redirect/component/second': { + id: '/redirect/component/second' + path: '/redirect/component/second' + fullPath: '/redirect/component/second' + preLoaderRoute: typeof RedirectComponentSecondImport + parentRoute: typeof rootRoute + } '/redirect/preload/first': { id: '/redirect/preload/first' path: '/redirect/preload/first' @@ -415,6 +450,13 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof RedirectTargetIndexImport parentRoute: typeof RedirectTargetImport } + '/redirect/component/': { + id: '/redirect/component/' + path: '/redirect/component' + fullPath: '/redirect/component' + preLoaderRoute: typeof RedirectComponentIndexImport + parentRoute: typeof rootRoute + } } } @@ -523,10 +565,13 @@ export interface FileRoutesByFullPath { '/posts/$postId/edit': typeof PostsPostIdEditRoute '/redirect/$target/via-beforeLoad': typeof RedirectTargetViaBeforeLoadRoute '/redirect/$target/via-loader': typeof RedirectTargetViaLoaderRoute + '/redirect/component/first': typeof RedirectComponentFirstRoute + '/redirect/component/second': typeof RedirectComponentSecondRoute '/redirect/preload/first': typeof RedirectPreloadFirstRoute '/redirect/preload/second': typeof RedirectPreloadSecondRoute '/redirect/preload/third': typeof RedirectPreloadThirdRoute '/redirect/$target/': typeof RedirectTargetIndexRoute + '/redirect/component': typeof RedirectComponentIndexRoute } export interface FileRoutesByTo { @@ -549,10 +594,13 @@ export interface FileRoutesByTo { '/posts/$postId/edit': typeof PostsPostIdEditRoute '/redirect/$target/via-beforeLoad': typeof RedirectTargetViaBeforeLoadRoute '/redirect/$target/via-loader': typeof RedirectTargetViaLoaderRoute + '/redirect/component/first': typeof RedirectComponentFirstRoute + '/redirect/component/second': typeof RedirectComponentSecondRoute '/redirect/preload/first': typeof RedirectPreloadFirstRoute '/redirect/preload/second': typeof RedirectPreloadSecondRoute '/redirect/preload/third': typeof RedirectPreloadThirdRoute '/redirect/$target': typeof RedirectTargetIndexRoute + '/redirect/component': typeof RedirectComponentIndexRoute } export interface FileRoutesById { @@ -581,10 +629,13 @@ export interface FileRoutesById { '/posts_/$postId/edit': typeof PostsPostIdEditRoute '/redirect/$target/via-beforeLoad': typeof RedirectTargetViaBeforeLoadRoute '/redirect/$target/via-loader': typeof RedirectTargetViaLoaderRoute + '/redirect/component/first': typeof RedirectComponentFirstRoute + '/redirect/component/second': typeof RedirectComponentSecondRoute '/redirect/preload/first': typeof RedirectPreloadFirstRoute '/redirect/preload/second': typeof RedirectPreloadSecondRoute '/redirect/preload/third': typeof RedirectPreloadThirdRoute '/redirect/$target/': typeof RedirectTargetIndexRoute + '/redirect/component/': typeof RedirectComponentIndexRoute } export interface FileRouteTypes { @@ -611,10 +662,13 @@ export interface FileRouteTypes { | '/posts/$postId/edit' | '/redirect/$target/via-beforeLoad' | '/redirect/$target/via-loader' + | '/redirect/component/first' + | '/redirect/component/second' | '/redirect/preload/first' | '/redirect/preload/second' | '/redirect/preload/third' | '/redirect/$target/' + | '/redirect/component' fileRoutesByTo: FileRoutesByTo to: | '/' @@ -636,10 +690,13 @@ export interface FileRouteTypes { | '/posts/$postId/edit' | '/redirect/$target/via-beforeLoad' | '/redirect/$target/via-loader' + | '/redirect/component/first' + | '/redirect/component/second' | '/redirect/preload/first' | '/redirect/preload/second' | '/redirect/preload/third' | '/redirect/$target' + | '/redirect/component' id: | '__root__' | '/' @@ -666,10 +723,13 @@ export interface FileRouteTypes { | '/posts_/$postId/edit' | '/redirect/$target/via-beforeLoad' | '/redirect/$target/via-loader' + | '/redirect/component/first' + | '/redirect/component/second' | '/redirect/preload/first' | '/redirect/preload/second' | '/redirect/preload/third' | '/redirect/$target/' + | '/redirect/component/' fileRoutesById: FileRoutesById } @@ -686,9 +746,12 @@ export interface RootRouteChildren { StructuralSharingEnabledRoute: typeof StructuralSharingEnabledRoute RedirectIndexRoute: typeof RedirectIndexRoute PostsPostIdEditRoute: typeof PostsPostIdEditRoute + RedirectComponentFirstRoute: typeof RedirectComponentFirstRoute + RedirectComponentSecondRoute: typeof RedirectComponentSecondRoute RedirectPreloadFirstRoute: typeof RedirectPreloadFirstRoute RedirectPreloadSecondRoute: typeof RedirectPreloadSecondRoute RedirectPreloadThirdRoute: typeof RedirectPreloadThirdRoute + RedirectComponentIndexRoute: typeof RedirectComponentIndexRoute } const rootRouteChildren: RootRouteChildren = { @@ -704,9 +767,12 @@ const rootRouteChildren: RootRouteChildren = { StructuralSharingEnabledRoute: StructuralSharingEnabledRoute, RedirectIndexRoute: RedirectIndexRoute, PostsPostIdEditRoute: PostsPostIdEditRoute, + RedirectComponentFirstRoute: RedirectComponentFirstRoute, + RedirectComponentSecondRoute: RedirectComponentSecondRoute, RedirectPreloadFirstRoute: RedirectPreloadFirstRoute, RedirectPreloadSecondRoute: RedirectPreloadSecondRoute, RedirectPreloadThirdRoute: RedirectPreloadThirdRoute, + RedirectComponentIndexRoute: RedirectComponentIndexRoute, } export const routeTree = rootRoute @@ -731,9 +797,12 @@ export const routeTree = rootRoute "/structural-sharing/$enabled", "/redirect/", "/posts_/$postId/edit", + "/redirect/component/first", + "/redirect/component/second", "/redirect/preload/first", "/redirect/preload/second", - "/redirect/preload/third" + "/redirect/preload/third", + "/redirect/component/" ] }, "/": { @@ -845,6 +914,12 @@ export const routeTree = rootRoute "filePath": "redirect/$target/via-loader.tsx", "parent": "/redirect/$target" }, + "/redirect/component/first": { + "filePath": "redirect/component/first.tsx" + }, + "/redirect/component/second": { + "filePath": "redirect/component/second.tsx" + }, "/redirect/preload/first": { "filePath": "redirect/preload/first.tsx" }, @@ -857,6 +932,9 @@ export const routeTree = rootRoute "/redirect/$target/": { "filePath": "redirect/$target/index.tsx", "parent": "/redirect/$target" + }, + "/redirect/component/": { + "filePath": "redirect/component/index.tsx" } } } diff --git a/e2e/react-router/basic-file-based/src/routes/redirect/component/first.tsx b/e2e/react-router/basic-file-based/src/routes/redirect/component/first.tsx new file mode 100644 index 0000000000..c84304d5d1 --- /dev/null +++ b/e2e/react-router/basic-file-based/src/routes/redirect/component/first.tsx @@ -0,0 +1,19 @@ +import { createFileRoute, redirect } from '@tanstack/react-router' +import React from 'react' + +export const Route = createFileRoute('/redirect/component/first')({ + component: RouteComponent, +}) + +function useThrowRedirect() { + throw redirect({ to: '/redirect/component/second' }) +} + +function RouteComponent() { + useThrowRedirect() + return ( +
+

Redirecting...

+
+ ) +} diff --git a/e2e/react-router/basic-file-based/src/routes/redirect/component/index.tsx b/e2e/react-router/basic-file-based/src/routes/redirect/component/index.tsx new file mode 100644 index 0000000000..0f94685e63 --- /dev/null +++ b/e2e/react-router/basic-file-based/src/routes/redirect/component/index.tsx @@ -0,0 +1,23 @@ +import { Link, createFileRoute } from '@tanstack/react-router' + +export const Route = createFileRoute('/redirect/component/')({ + component: RouteComponent, +}) + +function RouteComponent() { + return ( +
+ + go to second + +
+ ) +} diff --git a/e2e/react-router/basic-file-based/src/routes/redirect/component/second.tsx b/e2e/react-router/basic-file-based/src/routes/redirect/component/second.tsx new file mode 100644 index 0000000000..f66dd3f33f --- /dev/null +++ b/e2e/react-router/basic-file-based/src/routes/redirect/component/second.tsx @@ -0,0 +1,9 @@ +import { createFileRoute } from '@tanstack/react-router' + +export const Route = createFileRoute('/redirect/component/second')({ + component: RouteComponent, +}) + +function RouteComponent() { + return
Hello "/redirect/component/second"!
+} diff --git a/e2e/react-router/basic-file-based/tests/redirect.spec.ts b/e2e/react-router/basic-file-based/tests/redirect.spec.ts index 6166d1cf50..637519160e 100644 --- a/e2e/react-router/basic-file-based/tests/redirect.spec.ts +++ b/e2e/react-router/basic-file-based/tests/redirect.spec.ts @@ -123,4 +123,20 @@ test.describe('redirects', () => { await page.waitForURL('/redirect/preload/third') await expect(page.getByTestId(`third`)).toBeInViewport() }) + + test.describe('throw redirect in route component', () => { + test('initial load', async ({ page }) => { + await page.goto(`/redirect/component/first`) + await page.waitForURL('/redirect/component/second') + await expect(page.getByTestId('second')).toBeInViewport() + }) + test('when navigating', async ({ page }) => { + await page.goto(`/redirect/component/`) + const link = page.getByTestId(`link`) + await link.focus() + await link.click() + await page.waitForURL('/redirect/component/second') + await expect(page.getByTestId('second')).toBeInViewport() + }) + }) }) diff --git a/e2e/start/basic/app/routeTree.gen.ts b/e2e/start/basic/app/routeTree.gen.ts index 3dc72a54ff..f8ef1287e3 100644 --- a/e2e/start/basic/app/routeTree.gen.ts +++ b/e2e/start/basic/app/routeTree.gen.ts @@ -31,6 +31,8 @@ import { Route as RedirectTargetImport } from './routes/redirect/$target' import { Route as PostsPostIdImport } from './routes/posts.$postId' import { Route as LayoutLayout2Import } from './routes/_layout/_layout-2' import { Route as RedirectTargetIndexImport } from './routes/redirect/$target/index' +import { Route as RedirectComponentSecondImport } from './routes/redirect/component/second' +import { Route as RedirectComponentFirstImport } from './routes/redirect/component/first' import { Route as RedirectTargetViaLoaderImport } from './routes/redirect/$target/via-loader' import { Route as RedirectTargetViaBeforeLoadImport } from './routes/redirect/$target/via-beforeLoad' import { Route as PostsPostIdDeepImport } from './routes/posts_.$postId.deep' @@ -161,6 +163,18 @@ const RedirectTargetIndexRoute = RedirectTargetIndexImport.update({ getParentRoute: () => RedirectTargetRoute, } as any) +const RedirectComponentSecondRoute = RedirectComponentSecondImport.update({ + id: '/redirect/component/second', + path: '/redirect/component/second', + getParentRoute: () => rootRoute, +} as any) + +const RedirectComponentFirstRoute = RedirectComponentFirstImport.update({ + id: '/redirect/component/first', + path: '/redirect/component/first', + getParentRoute: () => rootRoute, +} as any) + const RedirectTargetViaLoaderRoute = RedirectTargetViaLoaderImport.update({ id: '/via-loader', path: '/via-loader', @@ -392,6 +406,20 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof RedirectTargetViaLoaderImport parentRoute: typeof RedirectTargetImport } + '/redirect/component/first': { + id: '/redirect/component/first' + path: '/redirect/component/first' + fullPath: '/redirect/component/first' + preLoaderRoute: typeof RedirectComponentFirstImport + parentRoute: typeof rootRoute + } + '/redirect/component/second': { + id: '/redirect/component/second' + path: '/redirect/component/second' + fullPath: '/redirect/component/second' + preLoaderRoute: typeof RedirectComponentSecondImport + parentRoute: typeof rootRoute + } '/redirect/$target/': { id: '/redirect/$target/' path: '/' @@ -531,6 +559,8 @@ export interface FileRoutesByFullPath { '/posts/$postId/deep': typeof PostsPostIdDeepRoute '/redirect/$target/via-beforeLoad': typeof RedirectTargetViaBeforeLoadRoute '/redirect/$target/via-loader': typeof RedirectTargetViaLoaderRoute + '/redirect/component/first': typeof RedirectComponentFirstRoute + '/redirect/component/second': typeof RedirectComponentSecondRoute '/redirect/$target/': typeof RedirectTargetIndexRoute '/redirect/$target/serverFn/via-beforeLoad': typeof RedirectTargetServerFnViaBeforeLoadRoute '/redirect/$target/serverFn/via-loader': typeof RedirectTargetServerFnViaLoaderRoute @@ -559,6 +589,8 @@ export interface FileRoutesByTo { '/posts/$postId/deep': typeof PostsPostIdDeepRoute '/redirect/$target/via-beforeLoad': typeof RedirectTargetViaBeforeLoadRoute '/redirect/$target/via-loader': typeof RedirectTargetViaLoaderRoute + '/redirect/component/first': typeof RedirectComponentFirstRoute + '/redirect/component/second': typeof RedirectComponentSecondRoute '/redirect/$target': typeof RedirectTargetIndexRoute '/redirect/$target/serverFn/via-beforeLoad': typeof RedirectTargetServerFnViaBeforeLoadRoute '/redirect/$target/serverFn/via-loader': typeof RedirectTargetServerFnViaLoaderRoute @@ -592,6 +624,8 @@ export interface FileRoutesById { '/posts_/$postId/deep': typeof PostsPostIdDeepRoute '/redirect/$target/via-beforeLoad': typeof RedirectTargetViaBeforeLoadRoute '/redirect/$target/via-loader': typeof RedirectTargetViaLoaderRoute + '/redirect/component/first': typeof RedirectComponentFirstRoute + '/redirect/component/second': typeof RedirectComponentSecondRoute '/redirect/$target/': typeof RedirectTargetIndexRoute '/redirect/$target/serverFn/via-beforeLoad': typeof RedirectTargetServerFnViaBeforeLoadRoute '/redirect/$target/serverFn/via-loader': typeof RedirectTargetServerFnViaLoaderRoute @@ -625,6 +659,8 @@ export interface FileRouteTypes { | '/posts/$postId/deep' | '/redirect/$target/via-beforeLoad' | '/redirect/$target/via-loader' + | '/redirect/component/first' + | '/redirect/component/second' | '/redirect/$target/' | '/redirect/$target/serverFn/via-beforeLoad' | '/redirect/$target/serverFn/via-loader' @@ -652,6 +688,8 @@ export interface FileRouteTypes { | '/posts/$postId/deep' | '/redirect/$target/via-beforeLoad' | '/redirect/$target/via-loader' + | '/redirect/component/first' + | '/redirect/component/second' | '/redirect/$target' | '/redirect/$target/serverFn/via-beforeLoad' | '/redirect/$target/serverFn/via-loader' @@ -683,6 +721,8 @@ export interface FileRouteTypes { | '/posts_/$postId/deep' | '/redirect/$target/via-beforeLoad' | '/redirect/$target/via-loader' + | '/redirect/component/first' + | '/redirect/component/second' | '/redirect/$target/' | '/redirect/$target/serverFn/via-beforeLoad' | '/redirect/$target/serverFn/via-loader' @@ -707,6 +747,8 @@ export interface RootRouteChildren { RedirectTargetRoute: typeof RedirectTargetRouteWithChildren RedirectIndexRoute: typeof RedirectIndexRoute PostsPostIdDeepRoute: typeof PostsPostIdDeepRoute + RedirectComponentFirstRoute: typeof RedirectComponentFirstRoute + RedirectComponentSecondRoute: typeof RedirectComponentSecondRoute } const rootRouteChildren: RootRouteChildren = { @@ -725,6 +767,8 @@ const rootRouteChildren: RootRouteChildren = { RedirectTargetRoute: RedirectTargetRouteWithChildren, RedirectIndexRoute: RedirectIndexRoute, PostsPostIdDeepRoute: PostsPostIdDeepRoute, + RedirectComponentFirstRoute: RedirectComponentFirstRoute, + RedirectComponentSecondRoute: RedirectComponentSecondRoute, } export const routeTree = rootRoute @@ -751,7 +795,9 @@ export const routeTree = rootRoute "/users", "/redirect/$target", "/redirect/", - "/posts_/$postId/deep" + "/posts_/$postId/deep", + "/redirect/component/first", + "/redirect/component/second" ] }, "/": { @@ -859,6 +905,12 @@ export const routeTree = rootRoute "filePath": "redirect/$target/via-loader.tsx", "parent": "/redirect/$target" }, + "/redirect/component/first": { + "filePath": "redirect/component/first.tsx" + }, + "/redirect/component/second": { + "filePath": "redirect/component/second.tsx" + }, "/redirect/$target/": { "filePath": "redirect/$target/index.tsx", "parent": "/redirect/$target" diff --git a/e2e/start/basic/app/routes/redirect/component/first.tsx b/e2e/start/basic/app/routes/redirect/component/first.tsx new file mode 100644 index 0000000000..c84304d5d1 --- /dev/null +++ b/e2e/start/basic/app/routes/redirect/component/first.tsx @@ -0,0 +1,19 @@ +import { createFileRoute, redirect } from '@tanstack/react-router' +import React from 'react' + +export const Route = createFileRoute('/redirect/component/first')({ + component: RouteComponent, +}) + +function useThrowRedirect() { + throw redirect({ to: '/redirect/component/second' }) +} + +function RouteComponent() { + useThrowRedirect() + return ( +
+

Redirecting...

+
+ ) +} diff --git a/e2e/start/basic/app/routes/redirect/component/second.tsx b/e2e/start/basic/app/routes/redirect/component/second.tsx new file mode 100644 index 0000000000..f66dd3f33f --- /dev/null +++ b/e2e/start/basic/app/routes/redirect/component/second.tsx @@ -0,0 +1,9 @@ +import { createFileRoute } from '@tanstack/react-router' + +export const Route = createFileRoute('/redirect/component/second')({ + component: RouteComponent, +}) + +function RouteComponent() { + return
Hello "/redirect/component/second"!
+} diff --git a/e2e/start/basic/tests/redirect.spec.ts b/e2e/start/basic/tests/redirect.spec.ts index caff77d6ab..fa272da55f 100644 --- a/e2e/start/basic/tests/redirect.spec.ts +++ b/e2e/start/basic/tests/redirect.spec.ts @@ -193,4 +193,10 @@ test.describe('redirects', () => { } }) }) + + test('throw redirect in route component', async ({ page }) => { + await page.goto(`/redirect/component/first`) + await page.waitForURL('/redirect/component/second') + await expect(page.getByTestId('second')).toBeInViewport() + }) }) diff --git a/packages/react-router/src/CatchBoundary.tsx b/packages/react-router/src/CatchBoundary.tsx index 68197b2eb6..c3ddc3e689 100644 --- a/packages/react-router/src/CatchBoundary.tsx +++ b/packages/react-router/src/CatchBoundary.tsx @@ -1,4 +1,7 @@ import * as React from 'react' +import { useRouter } from './useRouter' +import { isRedirect } from './redirects' +import { Navigate } from './useNavigate' import type { ErrorRouteComponent } from './route' import type { ErrorInfo } from 'react' @@ -9,6 +12,7 @@ export function CatchBoundary(props: { onCatch?: (error: Error, errorInfo: ErrorInfo) => void }) { const errorComponent = props.errorComponent ?? ErrorComponent + const router = useRouter() return ( { if (error) { + if (isRedirect(error)) { + const redirect = router.resolveRedirect({ + ...error, + _fromLocation: router.state.location, + }) + return ( + + ) + } return React.createElement(errorComponent, { error, reset, diff --git a/packages/react-router/src/router.ts b/packages/react-router/src/router.ts index 6873cf0d98..dd3bab94ce 100644 --- a/packages/react-router/src/router.ts +++ b/packages/react-router/src/router.ts @@ -2696,7 +2696,11 @@ export class Router< })) } catch (err) { if (isResolvedRedirect(err)) { - await this.navigate(err) + await this.navigate({ + ...err, + replace: true, + ignoreBlocker: true, + }) } } })() diff --git a/packages/react-router/tests/redirect.test.tsx b/packages/react-router/tests/redirect.test.tsx index 679158c2c0..91067f2358 100644 --- a/packages/react-router/tests/redirect.test.tsx +++ b/packages/react-router/tests/redirect.test.tsx @@ -252,6 +252,84 @@ describe('redirect', () => { expect(await screen.findByText('Final')).toBeInTheDocument() expect(window.location.pathname).toBe('/final') }) + + test('when `redirect` is thrown in route component', async () => { + const nestedLoaderMock = vi.fn() + const nestedFooLoaderMock = vi.fn() + + const rootRoute = createRootRoute({}) + const indexRoute = createRoute({ + getParentRoute: () => rootRoute, + path: '/', + component: () => { + return ( +
+

Index page

+ link to about +
+ ) + }, + }) + const aboutRoute = createRoute({ + getParentRoute: () => rootRoute, + path: '/about', + component: () => { + throw redirect({ + to: '/nested/foo', + hash: 'some-hash', + search: { someSearch: 'hello123' }, + }) + }, + }) + const nestedRoute = createRoute({ + getParentRoute: () => rootRoute, + path: '/nested', + loader: async () => { + await sleep(WAIT_TIME) + nestedLoaderMock('nested') + }, + }) + const fooRoute = createRoute({ + validateSearch: (search) => { + return { + someSearch: search.someSearch as string, + } + }, + getParentRoute: () => nestedRoute, + path: '/foo', + loader: async () => { + await sleep(WAIT_TIME) + nestedFooLoaderMock('foo') + }, + component: () =>
Nested Foo page
, + }) + const routeTree = rootRoute.addChildren([ + nestedRoute.addChildren([fooRoute]), + aboutRoute, + indexRoute, + ]) + const router = createRouter({ routeTree }) + + render() + + const linkToAbout = await screen.findByText('link to about') + + expect(linkToAbout).toBeInTheDocument() + + fireEvent.click(linkToAbout) + + const fooElement = await screen.findByText('Nested Foo page') + + expect(fooElement).toBeInTheDocument() + + expect(router.state.location.href).toBe( + '/nested/foo?someSearch=hello123#some-hash', + ) + expect(window.location.pathname).toBe('/nested/foo') + + expect(nestedLoaderMock).toHaveBeenCalled() + expect(nestedFooLoaderMock).toHaveBeenCalled() + }) }) describe('SSR', () => {