diff --git a/e2e/solid-start/custom-basepath/src/routeTree.gen.ts b/e2e/solid-start/custom-basepath/src/routeTree.gen.ts index 6064fd7604f..1a94469caaf 100644 --- a/e2e/solid-start/custom-basepath/src/routeTree.gen.ts +++ b/e2e/solid-start/custom-basepath/src/routeTree.gen.ts @@ -11,11 +11,14 @@ import { Route as rootRouteImport } from './routes/__root' import { Route as UsersRouteImport } from './routes/users' import { Route as PostsRouteImport } from './routes/posts' +import { Route as LogoutRouteImport } from './routes/logout' import { Route as DeferredRouteImport } from './routes/deferred' import { Route as IndexRouteImport } from './routes/index' import { Route as UsersIndexRouteImport } from './routes/users.index' +import { Route as RedirectIndexRouteImport } from './routes/redirect.index' import { Route as PostsIndexRouteImport } from './routes/posts.index' import { Route as UsersUserIdRouteImport } from './routes/users.$userId' +import { Route as RedirectThrowItRouteImport } from './routes/redirect.throw-it' import { Route as PostsPostIdRouteImport } from './routes/posts.$postId' import { Route as ApiUsersRouteImport } from './routes/api/users' import { Route as PostsPostIdDeepRouteImport } from './routes/posts_.$postId.deep' @@ -31,6 +34,11 @@ const PostsRoute = PostsRouteImport.update({ path: '/posts', getParentRoute: () => rootRouteImport, } as any) +const LogoutRoute = LogoutRouteImport.update({ + id: '/logout', + path: '/logout', + getParentRoute: () => rootRouteImport, +} as any) const DeferredRoute = DeferredRouteImport.update({ id: '/deferred', path: '/deferred', @@ -46,6 +54,11 @@ const UsersIndexRoute = UsersIndexRouteImport.update({ path: '/', getParentRoute: () => UsersRoute, } as any) +const RedirectIndexRoute = RedirectIndexRouteImport.update({ + id: '/redirect/', + path: '/redirect/', + getParentRoute: () => rootRouteImport, +} as any) const PostsIndexRoute = PostsIndexRouteImport.update({ id: '/', path: '/', @@ -56,6 +69,11 @@ const UsersUserIdRoute = UsersUserIdRouteImport.update({ path: '/$userId', getParentRoute: () => UsersRoute, } as any) +const RedirectThrowItRoute = RedirectThrowItRouteImport.update({ + id: '/redirect/throw-it', + path: '/redirect/throw-it', + getParentRoute: () => rootRouteImport, +} as any) const PostsPostIdRoute = PostsPostIdRouteImport.update({ id: '/$postId', path: '/$postId', @@ -80,12 +98,15 @@ const ApiUsersUserIdRoute = ApiUsersUserIdRouteImport.update({ export interface FileRoutesByFullPath { '/': typeof IndexRoute '/deferred': typeof DeferredRoute + '/logout': typeof LogoutRoute '/posts': typeof PostsRouteWithChildren '/users': typeof UsersRouteWithChildren '/api/users': typeof ApiUsersRouteWithChildren '/posts/$postId': typeof PostsPostIdRoute + '/redirect/throw-it': typeof RedirectThrowItRoute '/users/$userId': typeof UsersUserIdRoute '/posts/': typeof PostsIndexRoute + '/redirect': typeof RedirectIndexRoute '/users/': typeof UsersIndexRoute '/api/users/$userId': typeof ApiUsersUserIdRoute '/posts/$postId/deep': typeof PostsPostIdDeepRoute @@ -93,10 +114,13 @@ export interface FileRoutesByFullPath { export interface FileRoutesByTo { '/': typeof IndexRoute '/deferred': typeof DeferredRoute + '/logout': typeof LogoutRoute '/api/users': typeof ApiUsersRouteWithChildren '/posts/$postId': typeof PostsPostIdRoute + '/redirect/throw-it': typeof RedirectThrowItRoute '/users/$userId': typeof UsersUserIdRoute '/posts': typeof PostsIndexRoute + '/redirect': typeof RedirectIndexRoute '/users': typeof UsersIndexRoute '/api/users/$userId': typeof ApiUsersUserIdRoute '/posts/$postId/deep': typeof PostsPostIdDeepRoute @@ -105,12 +129,15 @@ export interface FileRoutesById { __root__: typeof rootRouteImport '/': typeof IndexRoute '/deferred': typeof DeferredRoute + '/logout': typeof LogoutRoute '/posts': typeof PostsRouteWithChildren '/users': typeof UsersRouteWithChildren '/api/users': typeof ApiUsersRouteWithChildren '/posts/$postId': typeof PostsPostIdRoute + '/redirect/throw-it': typeof RedirectThrowItRoute '/users/$userId': typeof UsersUserIdRoute '/posts/': typeof PostsIndexRoute + '/redirect/': typeof RedirectIndexRoute '/users/': typeof UsersIndexRoute '/api/users/$userId': typeof ApiUsersUserIdRoute '/posts_/$postId/deep': typeof PostsPostIdDeepRoute @@ -120,12 +147,15 @@ export interface FileRouteTypes { fullPaths: | '/' | '/deferred' + | '/logout' | '/posts' | '/users' | '/api/users' | '/posts/$postId' + | '/redirect/throw-it' | '/users/$userId' | '/posts/' + | '/redirect' | '/users/' | '/api/users/$userId' | '/posts/$postId/deep' @@ -133,10 +163,13 @@ export interface FileRouteTypes { to: | '/' | '/deferred' + | '/logout' | '/api/users' | '/posts/$postId' + | '/redirect/throw-it' | '/users/$userId' | '/posts' + | '/redirect' | '/users' | '/api/users/$userId' | '/posts/$postId/deep' @@ -144,12 +177,15 @@ export interface FileRouteTypes { | '__root__' | '/' | '/deferred' + | '/logout' | '/posts' | '/users' | '/api/users' | '/posts/$postId' + | '/redirect/throw-it' | '/users/$userId' | '/posts/' + | '/redirect/' | '/users/' | '/api/users/$userId' | '/posts_/$postId/deep' @@ -158,9 +194,12 @@ export interface FileRouteTypes { export interface RootRouteChildren { IndexRoute: typeof IndexRoute DeferredRoute: typeof DeferredRoute + LogoutRoute: typeof LogoutRoute PostsRoute: typeof PostsRouteWithChildren UsersRoute: typeof UsersRouteWithChildren ApiUsersRoute: typeof ApiUsersRouteWithChildren + RedirectThrowItRoute: typeof RedirectThrowItRoute + RedirectIndexRoute: typeof RedirectIndexRoute PostsPostIdDeepRoute: typeof PostsPostIdDeepRoute } @@ -180,6 +219,13 @@ declare module '@tanstack/solid-router' { preLoaderRoute: typeof PostsRouteImport parentRoute: typeof rootRouteImport } + '/logout': { + id: '/logout' + path: '/logout' + fullPath: '/logout' + preLoaderRoute: typeof LogoutRouteImport + parentRoute: typeof rootRouteImport + } '/deferred': { id: '/deferred' path: '/deferred' @@ -201,6 +247,13 @@ declare module '@tanstack/solid-router' { preLoaderRoute: typeof UsersIndexRouteImport parentRoute: typeof UsersRoute } + '/redirect/': { + id: '/redirect/' + path: '/redirect' + fullPath: '/redirect' + preLoaderRoute: typeof RedirectIndexRouteImport + parentRoute: typeof rootRouteImport + } '/posts/': { id: '/posts/' path: '/' @@ -215,6 +268,13 @@ declare module '@tanstack/solid-router' { preLoaderRoute: typeof UsersUserIdRouteImport parentRoute: typeof UsersRoute } + '/redirect/throw-it': { + id: '/redirect/throw-it' + path: '/redirect/throw-it' + fullPath: '/redirect/throw-it' + preLoaderRoute: typeof RedirectThrowItRouteImport + parentRoute: typeof rootRouteImport + } '/posts/$postId': { id: '/posts/$postId' path: '/$postId' @@ -285,9 +345,12 @@ const ApiUsersRouteWithChildren = ApiUsersRoute._addFileChildren( const rootRouteChildren: RootRouteChildren = { IndexRoute: IndexRoute, DeferredRoute: DeferredRoute, + LogoutRoute: LogoutRoute, PostsRoute: PostsRouteWithChildren, UsersRoute: UsersRouteWithChildren, ApiUsersRoute: ApiUsersRouteWithChildren, + RedirectThrowItRoute: RedirectThrowItRoute, + RedirectIndexRoute: RedirectIndexRoute, PostsPostIdDeepRoute: PostsPostIdDeepRoute, } export const routeTree = rootRouteImport diff --git a/e2e/solid-start/custom-basepath/src/routes/logout.tsx b/e2e/solid-start/custom-basepath/src/routes/logout.tsx new file mode 100644 index 00000000000..7e13279beed --- /dev/null +++ b/e2e/solid-start/custom-basepath/src/routes/logout.tsx @@ -0,0 +1,32 @@ +import { createFileRoute, redirect } from '@tanstack/solid-router' +import { createServerFn } from '@tanstack/solid-start' + +const logoutFn = createServerFn({ + method: 'POST', +}).handler(async () => { + // do logout stuff here + throw redirect({ + to: '/', + }) +}) + +export const Route = createFileRoute('/logout')({ + component: Home, +}) + +function Home() { + return ( +
+

Logout Page

+

+ This form tests that server function URLs correctly include the app's + basepath. The form action should be '/custom/basepath/_serverFn/...' not + just '/_serverFn/...' +

+
+ + +
+
+ ) +} diff --git a/e2e/solid-start/custom-basepath/src/routes/posts.$postId.tsx b/e2e/solid-start/custom-basepath/src/routes/posts.$postId.tsx index c6b2fcf5f90..0a1daf6e13d 100644 --- a/e2e/solid-start/custom-basepath/src/routes/posts.$postId.tsx +++ b/e2e/solid-start/custom-basepath/src/routes/posts.$postId.tsx @@ -1,5 +1,4 @@ -import { ErrorComponent, Link, createFileRoute } from '@tanstack/solid-router' -import type { ErrorComponentProps } from '@tanstack/solid-router' +import { Link, createFileRoute } from '@tanstack/solid-router' import { fetchPost } from '~/utils/posts' import { NotFound } from '~/components/NotFound' @@ -18,7 +17,7 @@ function PostComponent() { const post = Route.useLoaderData() return ( -
+

{post().title}

{post().body}
+ +
Throw It
+ +
+ ) +} diff --git a/e2e/solid-start/custom-basepath/src/routes/redirect.throw-it.tsx b/e2e/solid-start/custom-basepath/src/routes/redirect.throw-it.tsx new file mode 100644 index 00000000000..d1164f1c588 --- /dev/null +++ b/e2e/solid-start/custom-basepath/src/routes/redirect.throw-it.tsx @@ -0,0 +1,10 @@ +import { createFileRoute, redirect } from '@tanstack/solid-router' + +export const Route = createFileRoute('/redirect/throw-it')({ + beforeLoad: () => { + throw redirect({ + to: '/posts/$postId', + params: { postId: '1' }, + }) + }, +}) diff --git a/e2e/solid-start/custom-basepath/tests/navigation.spec.ts b/e2e/solid-start/custom-basepath/tests/navigation.spec.ts index 2a6b1f36d28..c96cc384c88 100644 --- a/e2e/solid-start/custom-basepath/tests/navigation.spec.ts +++ b/e2e/solid-start/custom-basepath/tests/navigation.spec.ts @@ -32,3 +32,39 @@ test('Should change title on client side navigation', async ({ page }) => { await expect(page).toHaveTitle('Posts page') }) + +test('Server function URLs correctly include app basepath', async ({ + page, +}) => { + await page.goto('/logout') + + const form = page.locator('form') + const actionUrl = await form.getAttribute('action') + + expect(actionUrl).toMatch(/^\/custom\/basepath\/_serverFn\//) +}) + +test('client-side redirect', async ({ page, baseURL }) => { + await page.goto('/redirect') + await page.getByTestId('link-to-throw-it').click() + await page.waitForLoadState('networkidle') + + expect(await page.getByTestId('post-view').isVisible()).toBe(true) + expect(page.url()).toBe(`${baseURL}/posts/1`) +}) + +test('server-side redirect', async ({ page, baseURL }) => { + await page.goto('/redirect/throw-it') + await page.waitForLoadState('networkidle') + + expect(await page.getByTestId('post-view').isVisible()).toBe(true) + expect(page.url()).toBe(`${baseURL}/posts/1`) + + // do not follow redirects since we want to test the Location header + await page.request + .get('/redirect/throw-it', { maxRedirects: 0 }) + .then((res) => { + const headers = new Headers(res.headers()) + expect(headers.get('location')).toBe('/custom/basepath/posts/1') + }) +})