Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions e2e/react-start/server-functions/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,9 @@
"test:e2e": "rm -rf port*.txt; playwright test --project=chromium"
},
"dependencies": {
"@tanstack/react-query": "^5.66.0",
"@tanstack/react-router": "workspace:^",
"@tanstack/react-router-ssr-query": "workspace:^",
"@tanstack/react-router-devtools": "workspace:^",
"@tanstack/react-start": "workspace:^",
"js-cookie": "^3.0.5",
Expand Down
21 changes: 21 additions & 0 deletions e2e/react-start/server-functions/src/routeTree.gen.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import { Route as DeadCodePreserveRouteImport } from './routes/dead-code-preserv
import { Route as ConsistentRouteImport } from './routes/consistent'
import { Route as AbortSignalRouteImport } from './routes/abort-signal'
import { Route as IndexRouteImport } from './routes/index'
import { Route as PrimitivesIndexRouteImport } from './routes/primitives/index'
import { Route as MiddlewareIndexRouteImport } from './routes/middleware/index'
import { Route as FormdataRedirectIndexRouteImport } from './routes/formdata-redirect/index'
import { Route as FactoryIndexRouteImport } from './routes/factory/index'
Expand Down Expand Up @@ -97,6 +98,11 @@ const IndexRoute = IndexRouteImport.update({
path: '/',
getParentRoute: () => rootRouteImport,
} as any)
const PrimitivesIndexRoute = PrimitivesIndexRouteImport.update({
id: '/primitives/',
path: '/primitives/',
getParentRoute: () => rootRouteImport,
} as any)
const MiddlewareIndexRoute = MiddlewareIndexRouteImport.update({
id: '/middleware/',
path: '/middleware/',
Expand Down Expand Up @@ -168,6 +174,7 @@ export interface FileRoutesByFullPath {
'/factory': typeof FactoryIndexRoute
'/formdata-redirect': typeof FormdataRedirectIndexRoute
'/middleware': typeof MiddlewareIndexRoute
'/primitives': typeof PrimitivesIndexRoute
'/formdata-redirect/target/$name': typeof FormdataRedirectTargetNameRoute
}
export interface FileRoutesByTo {
Expand All @@ -192,6 +199,7 @@ export interface FileRoutesByTo {
'/factory': typeof FactoryIndexRoute
'/formdata-redirect': typeof FormdataRedirectIndexRoute
'/middleware': typeof MiddlewareIndexRoute
'/primitives': typeof PrimitivesIndexRoute
'/formdata-redirect/target/$name': typeof FormdataRedirectTargetNameRoute
}
export interface FileRoutesById {
Expand All @@ -217,6 +225,7 @@ export interface FileRoutesById {
'/factory/': typeof FactoryIndexRoute
'/formdata-redirect/': typeof FormdataRedirectIndexRoute
'/middleware/': typeof MiddlewareIndexRoute
'/primitives/': typeof PrimitivesIndexRoute
'/formdata-redirect/target/$name': typeof FormdataRedirectTargetNameRoute
}
export interface FileRouteTypes {
Expand All @@ -243,6 +252,7 @@ export interface FileRouteTypes {
| '/factory'
| '/formdata-redirect'
| '/middleware'
| '/primitives'
| '/formdata-redirect/target/$name'
fileRoutesByTo: FileRoutesByTo
to:
Expand All @@ -267,6 +277,7 @@ export interface FileRouteTypes {
| '/factory'
| '/formdata-redirect'
| '/middleware'
| '/primitives'
| '/formdata-redirect/target/$name'
id:
| '__root__'
Expand All @@ -291,6 +302,7 @@ export interface FileRouteTypes {
| '/factory/'
| '/formdata-redirect/'
| '/middleware/'
| '/primitives/'
| '/formdata-redirect/target/$name'
fileRoutesById: FileRoutesById
}
Expand All @@ -316,6 +328,7 @@ export interface RootRouteChildren {
FactoryIndexRoute: typeof FactoryIndexRoute
FormdataRedirectIndexRoute: typeof FormdataRedirectIndexRoute
MiddlewareIndexRoute: typeof MiddlewareIndexRoute
PrimitivesIndexRoute: typeof PrimitivesIndexRoute
FormdataRedirectTargetNameRoute: typeof FormdataRedirectTargetNameRoute
}

Expand Down Expand Up @@ -412,6 +425,13 @@ declare module '@tanstack/react-router' {
preLoaderRoute: typeof IndexRouteImport
parentRoute: typeof rootRouteImport
}
'/primitives/': {
id: '/primitives/'
path: '/primitives'
fullPath: '/primitives'
preLoaderRoute: typeof PrimitivesIndexRouteImport
parentRoute: typeof rootRouteImport
}
'/middleware/': {
id: '/middleware/'
path: '/middleware'
Expand Down Expand Up @@ -500,6 +520,7 @@ const rootRouteChildren: RootRouteChildren = {
FactoryIndexRoute: FactoryIndexRoute,
FormdataRedirectIndexRoute: FormdataRedirectIndexRoute,
MiddlewareIndexRoute: MiddlewareIndexRoute,
PrimitivesIndexRoute: PrimitivesIndexRoute,
FormdataRedirectTargetNameRoute: FormdataRedirectTargetNameRoute,
}
export const routeTree = rootRouteImport
Expand Down
4 changes: 4 additions & 0 deletions e2e/react-start/server-functions/src/router.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,11 @@ import { createRouter } from '@tanstack/react-router'
import { routeTree } from './routeTree.gen'
import { DefaultCatchBoundary } from './components/DefaultCatchBoundary'
import { NotFound } from './components/NotFound'
import { setupRouterSsrQueryIntegration } from '@tanstack/react-router-ssr-query'
import { QueryClient } from '@tanstack/react-query'

export function getRouter() {
const queryClient = new QueryClient()
const router = createRouter({
routeTree,
defaultPreload: 'intent',
Expand All @@ -16,6 +19,7 @@ export function getRouter() {
},
},
})
setupRouterSsrQueryIntegration({ router, queryClient })

return router
}
129 changes: 129 additions & 0 deletions e2e/react-start/server-functions/src/routes/primitives/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
import { useQuery } from '@tanstack/react-query'
import { createFileRoute } from '@tanstack/react-router'
import { createServerFn } from '@tanstack/react-start'
import { useCallback } from 'react'
import { z } from 'zod'
export const Route = createFileRoute('/primitives/')({
component: RouteComponent,
})

function stringify(data: any) {
return JSON.stringify(data === undefined ? '$undefined' : data)
}

const $stringPost = createServerFn({ method: 'POST' })
.inputValidator(z.string())
.handler((ctx) => ctx.data)

const $stringGet = createServerFn({ method: 'GET' })
.inputValidator(z.string())
.handler((ctx) => ctx.data)

const $undefinedPost = createServerFn({ method: 'POST' })
.inputValidator(z.undefined())
.handler((ctx) => ctx.data)

const $undefinedGet = createServerFn({ method: 'GET' })
.inputValidator(z.undefined())
.handler((ctx) => ctx.data)

const $nullPost = createServerFn({ method: 'POST' })
.inputValidator(z.null())
.handler((ctx) => ctx.data)

const $nullGet = createServerFn({ method: 'GET' })
.inputValidator(z.null())
.handler((ctx) => ctx.data)

interface PrimitiveComponentProps<T> {
serverFn: {
get: (opts: { data: T }) => Promise<T>
post: (opts: { data: T }) => Promise<T>
}
data: {
value: T
type: string
}
}

interface TestProps<T> extends PrimitiveComponentProps<T> {
method: 'get' | 'post'
}
function Test<T>(props: TestProps<T>) {
const queryFn = useCallback(async () => {
const result = await props.serverFn[props.method]({
data: props.data.value,
})
if (result === undefined) {
return '$undefined'
}
return result
}, [props])
const query = useQuery({ queryKey: [props.data.type, props.method], queryFn })
const testId = `${props.method}-${props.data.type}`
return (
<div>
<h3>serverFn method={props.method}</h3>
<h4> expected </h4>
<div data-testid={`expected-${testId}`}>
{stringify(props.data.value)}
</div>
<h4> result</h4>
{query.isSuccess ? (
<div data-testid={`result-${testId}`}>{stringify(query.data)}</div>
) : null}
</div>
)
}
function PrimitiveComponent<T>(props: PrimitiveComponentProps<T>) {
return (
<div>
<h2>data type: {props.data.type}</h2>
<Test {...props} method="post" />
<br />
<Test {...props} method="get" />
<br />
<br />
</div>
)
}

function makeTestCase<T>(props: PrimitiveComponentProps<T>) {
return props
}
const testCases = [
makeTestCase({
data: {
value: null,
type: 'null',
},
serverFn: {
get: $nullGet,
post: $nullPost,
},
}),
makeTestCase({
data: {
value: undefined,
type: 'undefined',
},
serverFn: {
get: $undefinedGet,
post: $undefinedPost,
},
}),
makeTestCase({
data: {
value: 'foo-bar',
type: 'string',
},
serverFn: {
get: $stringGet,
post: $stringPost,
},
}),
] as Array<PrimitiveComponentProps<any>>

function RouteComponent() {
return testCases.map((t) => <PrimitiveComponent {...t} key={t.data.type} />)
}
23 changes: 23 additions & 0 deletions e2e/react-start/server-functions/tests/server-functions.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -443,3 +443,26 @@ test('factory', async ({ page }) => {
)
}
})

test('primitives', async ({ page }) => {
await page.goto('/primitives')

const testCases = await page
.locator('[data-testid^="expected-"]')
.elementHandles()
for (const testCase of testCases) {
const testId = await testCase.getAttribute('data-testid')

if (!testId) {
throw new Error('testcase is missing data-testid')
}

const suffix = testId.replace('expected-', '')

const expected =
(await page.getByTestId(`expected-${suffix}`).textContent()) || ''
expect(expected).not.toBe('')

await expect(page.getByTestId(`result-${suffix}`)).toContainText(expected)
}
})
Loading
Loading