Skip to content

Commit 559856b

Browse files
test(router): add view-transition e2e suites (#5869)
* test(router): add view-transition e2e suites * ci: apply automated fixes * setup suites --------- Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
1 parent 44069d1 commit 559856b

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

47 files changed

+1611
-0
lines changed
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
node_modules
2+
.DS_Store
3+
dist
4+
dist-ssr
5+
*.local
6+
7+
/test-results/
8+
/playwright-report/
9+
/blob-report/
10+
/playwright/.cache/
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
{
2+
"files.watcherExclude": {
3+
"**/routeTree.gen.ts": true
4+
},
5+
"search.exclude": {
6+
"**/routeTree.gen.ts": true
7+
},
8+
"files.readonlyInclude": {
9+
"**/routeTree.gen.ts": true
10+
}
11+
}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
# Example
2+
3+
To run this example:
4+
5+
- `npm install` or `yarn`
6+
- `npm start` or `yarn start`
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
<!doctype html>
2+
<html lang="en">
3+
<head>
4+
<meta charset="UTF-8" />
5+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
6+
<title>Vite App</title>
7+
</head>
8+
<body>
9+
<div id="app"></div>
10+
<script type="module" src="/src/main.tsx"></script>
11+
</body>
12+
</html>
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
{
2+
"name": "tanstack-e2e-router-react-example-view-transitions",
3+
"private": true,
4+
"type": "module",
5+
"scripts": {
6+
"dev": "vite --port 3000",
7+
"dev:e2e": "vite",
8+
"build": "vite build && tsc --noEmit",
9+
"serve": "vite preview",
10+
"start": "vite",
11+
"test:e2e": "rm -rf port*.txt; playwright test --project=chromium"
12+
},
13+
"dependencies": {
14+
"@tailwindcss/postcss": "^4.1.15",
15+
"@tanstack/react-router": "workspace:^",
16+
"@tanstack/react-router-devtools": "workspace:^",
17+
"@tanstack/router-plugin": "workspace:^",
18+
"postcss": "^8.5.1",
19+
"react": "^19.0.0",
20+
"react-dom": "^19.0.0",
21+
"redaxios": "^0.5.1",
22+
"tailwindcss": "^4.1.15",
23+
"zod": "^3.24.2"
24+
},
25+
"devDependencies": {
26+
"@playwright/test": "^1.50.1",
27+
"@tanstack/router-e2e-utils": "workspace:^",
28+
"@types/react": "^19.0.8",
29+
"@types/react-dom": "^19.0.3",
30+
"@vitejs/plugin-react": "^4.3.4",
31+
"typescript": "^5.7.2",
32+
"vite": "^7.1.7"
33+
}
34+
}
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
import { defineConfig, devices } from '@playwright/test'
2+
import {
3+
getDummyServerPort,
4+
getTestServerPort,
5+
} from '@tanstack/router-e2e-utils'
6+
import packageJson from './package.json' with { type: 'json' }
7+
8+
const PORT = await getTestServerPort(packageJson.name)
9+
const EXTERNAL_PORT = await getDummyServerPort(packageJson.name)
10+
const baseURL = `http://localhost:${PORT}`
11+
/**
12+
* See https://playwright.dev/docs/test-configuration.
13+
*/
14+
export default defineConfig({
15+
testDir: './tests',
16+
workers: 1,
17+
18+
reporter: [['line']],
19+
20+
use: {
21+
/* Base URL to use in actions like `await page.goto('/')`. */
22+
baseURL,
23+
},
24+
25+
globalSetup: './tests/setup/global.setup.ts',
26+
globalTeardown: './tests/setup/global.teardown.ts',
27+
28+
webServer: {
29+
command: `VITE_NODE_ENV="test" VITE_SERVER_PORT=${PORT} VITE_EXTERNAL_PORT=${EXTERNAL_PORT} pnpm build && pnpm serve --port ${PORT}`,
30+
url: baseURL,
31+
reuseExistingServer: !process.env.CI,
32+
stdout: 'pipe',
33+
},
34+
35+
projects: [
36+
{
37+
name: 'chromium',
38+
use: { ...devices['Desktop Chrome'] },
39+
},
40+
],
41+
})
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
export default {
2+
plugins: {
3+
'@tailwindcss/postcss': {},
4+
},
5+
}
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
import React from 'react'
2+
import ReactDOM from 'react-dom/client'
3+
import { RouterProvider, createRouter } from '@tanstack/react-router'
4+
import { routeTree } from './routeTree.gen'
5+
import './styles.css'
6+
7+
// Set up a Router instance
8+
const router = createRouter({
9+
routeTree,
10+
defaultPreload: 'intent',
11+
defaultStaleTime: 5000,
12+
scrollRestoration: true,
13+
/*
14+
Using defaultViewTransition would prevent the need to
15+
manually add `viewTransition: true` to every navigation.
16+
17+
If defaultViewTransition.types is a function, it will be called with the
18+
location change info and should return an array of view transition types.
19+
This is useful if you want to have different view transitions depending on
20+
the navigation's specifics.
21+
22+
An example use case is sliding in a direction based on the index of the
23+
previous and next routes when navigating via browser history back and forth.
24+
*/
25+
// defaultViewTransition: true
26+
// OR
27+
// defaultViewTransition: {
28+
// types: ({ fromLocation, toLocation }) => {
29+
// let direction = 'none'
30+
31+
// if (fromLocation) {
32+
// const fromIndex = fromLocation.state.__TSR_index
33+
// const toIndex = toLocation.state.__TSR_index
34+
35+
// direction = fromIndex > toIndex ? 'right' : 'left'
36+
// }
37+
38+
// return [`slide-${direction}`]
39+
// },
40+
// },
41+
})
42+
43+
// Register things for typesafety
44+
declare module '@tanstack/react-router' {
45+
interface Register {
46+
router: typeof router
47+
}
48+
}
49+
50+
const rootElement = document.getElementById('app')!
51+
52+
if (!rootElement.innerHTML) {
53+
const root = ReactDOM.createRoot(rootElement)
54+
root.render(<RouterProvider router={router} />)
55+
}
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
import { notFound } from '@tanstack/react-router'
2+
import axios from 'redaxios'
3+
4+
export type PostType = {
5+
id: string
6+
title: string
7+
body: string
8+
}
9+
10+
export const fetchPost = async (postId: string) => {
11+
console.info(`Fetching post with id ${postId}...`)
12+
await new Promise((r) => setTimeout(r, 0))
13+
const post = await axios
14+
.get<PostType>(`https://jsonplaceholder.typicode.com/posts/${postId}`)
15+
.then((r) => r.data)
16+
.catch((err) => {
17+
if (err.status === 404) {
18+
throw notFound()
19+
}
20+
throw err
21+
})
22+
23+
return post
24+
}
25+
26+
export const fetchPosts = async () => {
27+
console.info('Fetching posts...')
28+
await new Promise((r) => setTimeout(r, 0))
29+
return axios
30+
.get<Array<PostType>>('https://jsonplaceholder.typicode.com/posts')
31+
.then((r) => r.data.slice(0, 10))
32+
}
Lines changed: 171 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,171 @@
1+
/* eslint-disable */
2+
3+
// @ts-nocheck
4+
5+
// noinspection JSUnusedGlobalSymbols
6+
7+
// This file was automatically generated by TanStack Router.
8+
// You should NOT make any changes in this file as it will be overwritten.
9+
// Additionally, you should also exclude this file from your linter and/or formatter to prevent it from being checked or modified.
10+
11+
import { Route as rootRouteImport } from './routes/__root'
12+
import { Route as HowItWorksRouteImport } from './routes/how-it-works'
13+
import { Route as ExploreRouteImport } from './routes/explore'
14+
import { Route as PostsRouteRouteImport } from './routes/posts.route'
15+
import { Route as IndexRouteImport } from './routes/index'
16+
import { Route as PostsIndexRouteImport } from './routes/posts.index'
17+
import { Route as PostsPostIdRouteImport } from './routes/posts.$postId'
18+
19+
const HowItWorksRoute = HowItWorksRouteImport.update({
20+
id: '/how-it-works',
21+
path: '/how-it-works',
22+
getParentRoute: () => rootRouteImport,
23+
} as any)
24+
const ExploreRoute = ExploreRouteImport.update({
25+
id: '/explore',
26+
path: '/explore',
27+
getParentRoute: () => rootRouteImport,
28+
} as any)
29+
const PostsRouteRoute = PostsRouteRouteImport.update({
30+
id: '/posts',
31+
path: '/posts',
32+
getParentRoute: () => rootRouteImport,
33+
} as any)
34+
const IndexRoute = IndexRouteImport.update({
35+
id: '/',
36+
path: '/',
37+
getParentRoute: () => rootRouteImport,
38+
} as any)
39+
const PostsIndexRoute = PostsIndexRouteImport.update({
40+
id: '/',
41+
path: '/',
42+
getParentRoute: () => PostsRouteRoute,
43+
} as any)
44+
const PostsPostIdRoute = PostsPostIdRouteImport.update({
45+
id: '/$postId',
46+
path: '/$postId',
47+
getParentRoute: () => PostsRouteRoute,
48+
} as any)
49+
50+
export interface FileRoutesByFullPath {
51+
'/': typeof IndexRoute
52+
'/posts': typeof PostsRouteRouteWithChildren
53+
'/explore': typeof ExploreRoute
54+
'/how-it-works': typeof HowItWorksRoute
55+
'/posts/$postId': typeof PostsPostIdRoute
56+
'/posts/': typeof PostsIndexRoute
57+
}
58+
export interface FileRoutesByTo {
59+
'/': typeof IndexRoute
60+
'/explore': typeof ExploreRoute
61+
'/how-it-works': typeof HowItWorksRoute
62+
'/posts/$postId': typeof PostsPostIdRoute
63+
'/posts': typeof PostsIndexRoute
64+
}
65+
export interface FileRoutesById {
66+
__root__: typeof rootRouteImport
67+
'/': typeof IndexRoute
68+
'/posts': typeof PostsRouteRouteWithChildren
69+
'/explore': typeof ExploreRoute
70+
'/how-it-works': typeof HowItWorksRoute
71+
'/posts/$postId': typeof PostsPostIdRoute
72+
'/posts/': typeof PostsIndexRoute
73+
}
74+
export interface FileRouteTypes {
75+
fileRoutesByFullPath: FileRoutesByFullPath
76+
fullPaths:
77+
| '/'
78+
| '/posts'
79+
| '/explore'
80+
| '/how-it-works'
81+
| '/posts/$postId'
82+
| '/posts/'
83+
fileRoutesByTo: FileRoutesByTo
84+
to: '/' | '/explore' | '/how-it-works' | '/posts/$postId' | '/posts'
85+
id:
86+
| '__root__'
87+
| '/'
88+
| '/posts'
89+
| '/explore'
90+
| '/how-it-works'
91+
| '/posts/$postId'
92+
| '/posts/'
93+
fileRoutesById: FileRoutesById
94+
}
95+
export interface RootRouteChildren {
96+
IndexRoute: typeof IndexRoute
97+
PostsRouteRoute: typeof PostsRouteRouteWithChildren
98+
ExploreRoute: typeof ExploreRoute
99+
HowItWorksRoute: typeof HowItWorksRoute
100+
}
101+
102+
declare module '@tanstack/react-router' {
103+
interface FileRoutesByPath {
104+
'/how-it-works': {
105+
id: '/how-it-works'
106+
path: '/how-it-works'
107+
fullPath: '/how-it-works'
108+
preLoaderRoute: typeof HowItWorksRouteImport
109+
parentRoute: typeof rootRouteImport
110+
}
111+
'/explore': {
112+
id: '/explore'
113+
path: '/explore'
114+
fullPath: '/explore'
115+
preLoaderRoute: typeof ExploreRouteImport
116+
parentRoute: typeof rootRouteImport
117+
}
118+
'/posts': {
119+
id: '/posts'
120+
path: '/posts'
121+
fullPath: '/posts'
122+
preLoaderRoute: typeof PostsRouteRouteImport
123+
parentRoute: typeof rootRouteImport
124+
}
125+
'/': {
126+
id: '/'
127+
path: '/'
128+
fullPath: '/'
129+
preLoaderRoute: typeof IndexRouteImport
130+
parentRoute: typeof rootRouteImport
131+
}
132+
'/posts/': {
133+
id: '/posts/'
134+
path: '/'
135+
fullPath: '/posts/'
136+
preLoaderRoute: typeof PostsIndexRouteImport
137+
parentRoute: typeof PostsRouteRoute
138+
}
139+
'/posts/$postId': {
140+
id: '/posts/$postId'
141+
path: '/$postId'
142+
fullPath: '/posts/$postId'
143+
preLoaderRoute: typeof PostsPostIdRouteImport
144+
parentRoute: typeof PostsRouteRoute
145+
}
146+
}
147+
}
148+
149+
interface PostsRouteRouteChildren {
150+
PostsPostIdRoute: typeof PostsPostIdRoute
151+
PostsIndexRoute: typeof PostsIndexRoute
152+
}
153+
154+
const PostsRouteRouteChildren: PostsRouteRouteChildren = {
155+
PostsPostIdRoute: PostsPostIdRoute,
156+
PostsIndexRoute: PostsIndexRoute,
157+
}
158+
159+
const PostsRouteRouteWithChildren = PostsRouteRoute._addFileChildren(
160+
PostsRouteRouteChildren,
161+
)
162+
163+
const rootRouteChildren: RootRouteChildren = {
164+
IndexRoute: IndexRoute,
165+
PostsRouteRoute: PostsRouteRouteWithChildren,
166+
ExploreRoute: ExploreRoute,
167+
HowItWorksRoute: HowItWorksRoute,
168+
}
169+
export const routeTree = rootRouteImport
170+
._addFileChildren(rootRouteChildren)
171+
._addFileTypes<FileRouteTypes>()

0 commit comments

Comments
 (0)