-
-
Notifications
You must be signed in to change notification settings - Fork 1.3k
test(solid-router): generator-cli-only e2e suite #5625
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,11 @@ | ||
| node_modules | ||
| .DS_Store | ||
| dist | ||
| dist-hash | ||
| dist-ssr | ||
| *.local | ||
|
|
||
| /test-results/ | ||
| /playwright-report/ | ||
| /blob-report/ | ||
| /playwright/.cache/ |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,12 @@ | ||
| <!doctype html> | ||
| <html lang="en"> | ||
| <head> | ||
| <meta charset="UTF-8" /> | ||
| <meta name="viewport" content="width=device-width, initial-scale=1.0" /> | ||
| <title>Vite App</title> | ||
| </head> | ||
| <body> | ||
| <div id="app"></div> | ||
| <script type="module" src="/src/main.tsx"></script> | ||
| </body> | ||
| </html> |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,29 @@ | ||
| { | ||
| "name": "tanstack-solid-router-e2e-react-generator-cli-only", | ||
| "private": true, | ||
| "type": "module", | ||
| "scripts": { | ||
| "dev": "tsr generate && vite --port 3000", | ||
| "dev:e2e": "tsr generate && vite", | ||
| "build": "tsr generate && vite build && tsc --noEmit", | ||
| "serve": "vite preview", | ||
| "start": "tsr generate && vite", | ||
| "test:e2e": "rm -rf port*.txt; playwright test --project=chromium" | ||
| }, | ||
| "dependencies": { | ||
| "@tailwindcss/postcss": "^4.1.15", | ||
| "@tanstack/solid-router": "workspace:^", | ||
| "@tanstack/solid-router-devtools": "workspace:^", | ||
| "@tanstack/router-cli": "workspace:^", | ||
| "postcss": "^8.5.1", | ||
| "solid-js": "^1.9.9", | ||
| "redaxios": "^0.5.1", | ||
| "tailwindcss": "^4.1.15" | ||
| }, | ||
| "devDependencies": { | ||
| "@playwright/test": "^1.50.1", | ||
| "@tanstack/router-e2e-utils": "workspace:^", | ||
| "vite-plugin-solid": "^2.11.10", | ||
| "vite": "^7.1.7" | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,41 @@ | ||
| import { defineConfig, devices } from '@playwright/test' | ||
| import { | ||
| getDummyServerPort, | ||
| getTestServerPort, | ||
| } from '@tanstack/router-e2e-utils' | ||
| import packageJson from './package.json' with { type: 'json' } | ||
|
|
||
| const PORT = await getTestServerPort(packageJson.name) | ||
| const EXTERNAL_PORT = await getDummyServerPort(packageJson.name) | ||
| const baseURL = `http://localhost:${PORT}` | ||
| /** | ||
| * See https://playwright.dev/docs/test-configuration. | ||
| */ | ||
| export default defineConfig({ | ||
| testDir: './tests', | ||
| workers: 1, | ||
|
|
||
| reporter: [['line']], | ||
|
|
||
| globalSetup: './tests/setup/global.setup.ts', | ||
| globalTeardown: './tests/setup/global.teardown.ts', | ||
|
|
||
| use: { | ||
| /* Base URL to use in actions like `await page.goto('/')`. */ | ||
| baseURL, | ||
| }, | ||
|
|
||
| webServer: { | ||
| command: `VITE_NODE_ENV="test" VITE_EXTERNAL_PORT=${EXTERNAL_PORT} VITE_SERVER_PORT=${PORT} pnpm build && VITE_SERVER_PORT=${PORT} pnpm serve --port ${PORT}`, | ||
| url: baseURL, | ||
| reuseExistingServer: !process.env.CI, | ||
| stdout: 'pipe', | ||
| }, | ||
|
|
||
| projects: [ | ||
| { | ||
| name: 'chromium', | ||
| use: { ...devices['Desktop Chrome'] }, | ||
| }, | ||
| ], | ||
| }) |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,5 @@ | ||
| export default { | ||
| plugins: { | ||
| '@tailwindcss/postcss': {}, | ||
| }, | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,25 @@ | ||
| import { RouterProvider, createRouter } from '@tanstack/solid-router' | ||
| import { render } from 'solid-js/web' | ||
| import { routeTree } from './routeTree.gen' | ||
| import './styles.css' | ||
|
|
||
| // Set up a Router instance | ||
| const router = createRouter({ | ||
| routeTree, | ||
| defaultPreload: 'intent', | ||
| defaultStaleTime: 5000, | ||
| scrollRestoration: true, | ||
| }) | ||
|
|
||
| // Register things for typesafety | ||
| declare module '@tanstack/solid-router' { | ||
| interface Register { | ||
| router: typeof router | ||
| } | ||
| } | ||
|
|
||
| const rootElement = document.getElementById('app')! | ||
|
|
||
| if (!rootElement.innerHTML) { | ||
| render(() => <RouterProvider router={router} />, rootElement) | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,36 @@ | ||
| import axios from 'redaxios' | ||
|
|
||
| export class NotFoundError extends Error {} | ||
|
|
||
| type PostType = { | ||
| id: string | ||
| title: string | ||
| body: string | ||
| } | ||
|
|
||
| let queryURL = 'https://jsonplaceholder.typicode.com' | ||
|
|
||
| if (import.meta.env.VITE_NODE_ENV === 'test') { | ||
| queryURL = `http://localhost:${import.meta.env.VITE_EXTERNAL_PORT}` | ||
| } | ||
|
|
||
| export const fetchPosts = async () => { | ||
| console.info('Fetching posts...') | ||
| return axios | ||
| .get<Array<PostType>>(`${queryURL}/posts`) | ||
| .then((r) => r.data.slice(0, 10)) | ||
| } | ||
|
|
||
| export const fetchPost = async (postId: string) => { | ||
| console.info(`Fetching post with id ${postId}...`) | ||
| const post = await axios | ||
| .get<PostType>(`${queryURL}/posts/${postId}`) | ||
| .then((r) => r.data) | ||
|
|
||
| // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition | ||
| if (!post) { | ||
| throw new NotFoundError(`Post with id "${postId}" not found!`) | ||
| } | ||
|
|
||
| return post | ||
| } | ||
|
Comment on lines
+24
to
+36
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Map 404s (and empty payloads) to NotFoundError so route notFoundComponent renders. As written, axios 404 rejects before Apply this diff: -export const fetchPost = async (postId: string) => {
- console.info(`Fetching post with id ${postId}...`)
- const post = await axios
- .get<PostType>(`${queryURL}/posts/${postId}`)
- .then((r) => r.data)
-
- // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
- if (!post) {
- throw new NotFoundError(`Post with id "${postId}" not found!`)
- }
-
- return post
-}
+export const fetchPost = async (postId: string) => {
+ console.info(`Fetching post with id ${postId}...`)
+ try {
+ const post = await axios
+ .get<PostType | null>(`${queryURL}/posts/${encodeURIComponent(postId)}`)
+ .then((r) => r.data)
+
+ // Treat null/empty payloads as not found
+ // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
+ if (!post || !(post as any).id) {
+ throw new NotFoundError(`Post with id "${postId}" not found!`)
+ }
+ return post
+ } catch (err: any) {
+ const status = err?.response?.status ?? err?.status
+ if (status === 404) {
+ throw new NotFoundError(`Post with id "${postId}" not found!`)
+ }
+ throw err
+ }
+}🤖 Prompt for AI Agents |
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,230 @@ | ||
| /* eslint-disable */ | ||
|
|
||
| // @ts-nocheck | ||
|
|
||
| // noinspection JSUnusedGlobalSymbols | ||
|
|
||
| // This file was automatically generated by TanStack Router. | ||
| // You should NOT make any changes in this file as it will be overwritten. | ||
| // Additionally, you should also exclude this file from your linter and/or formatter to prevent it from being checked or modified. | ||
|
|
||
| import { Route as rootRouteImport } from './routes/__root' | ||
| import { Route as PathlessLayoutRouteImport } from './routes/_pathlessLayout' | ||
| import { Route as PostsRouteRouteImport } from './routes/posts.route' | ||
| import { Route as IndexRouteImport } from './routes/index' | ||
| import { Route as PostsIndexRouteImport } from './routes/posts.index' | ||
| import { Route as PostsPostIdRouteImport } from './routes/posts.$postId' | ||
| import { Route as PathlessLayoutNestedLayoutRouteImport } from './routes/_pathlessLayout/_nested-layout' | ||
| import { Route as PathlessLayoutNestedLayoutRouteBRouteImport } from './routes/_pathlessLayout/_nested-layout/route-b' | ||
| import { Route as PathlessLayoutNestedLayoutRouteARouteImport } from './routes/_pathlessLayout/_nested-layout/route-a' | ||
|
|
||
| const PathlessLayoutRoute = PathlessLayoutRouteImport.update({ | ||
| id: '/_pathlessLayout', | ||
| getParentRoute: () => rootRouteImport, | ||
| } as any) | ||
| const PostsRouteRoute = PostsRouteRouteImport.update({ | ||
| id: '/posts', | ||
| path: '/posts', | ||
| getParentRoute: () => rootRouteImport, | ||
| } as any) | ||
| const IndexRoute = IndexRouteImport.update({ | ||
| id: '/', | ||
| path: '/', | ||
| getParentRoute: () => rootRouteImport, | ||
| } as any) | ||
| const PostsIndexRoute = PostsIndexRouteImport.update({ | ||
| id: '/', | ||
| path: '/', | ||
| getParentRoute: () => PostsRouteRoute, | ||
| } as any) | ||
| const PostsPostIdRoute = PostsPostIdRouteImport.update({ | ||
| id: '/$postId', | ||
| path: '/$postId', | ||
| getParentRoute: () => PostsRouteRoute, | ||
| } as any) | ||
| const PathlessLayoutNestedLayoutRoute = | ||
| PathlessLayoutNestedLayoutRouteImport.update({ | ||
| id: '/_nested-layout', | ||
| getParentRoute: () => PathlessLayoutRoute, | ||
| } as any) | ||
| const PathlessLayoutNestedLayoutRouteBRoute = | ||
| PathlessLayoutNestedLayoutRouteBRouteImport.update({ | ||
| id: '/route-b', | ||
| path: '/route-b', | ||
| getParentRoute: () => PathlessLayoutNestedLayoutRoute, | ||
| } as any) | ||
| const PathlessLayoutNestedLayoutRouteARoute = | ||
| PathlessLayoutNestedLayoutRouteARouteImport.update({ | ||
| id: '/route-a', | ||
| path: '/route-a', | ||
| getParentRoute: () => PathlessLayoutNestedLayoutRoute, | ||
| } as any) | ||
|
|
||
| export interface FileRoutesByFullPath { | ||
| '/': typeof IndexRoute | ||
| '/posts': typeof PostsRouteRouteWithChildren | ||
| '/posts/$postId': typeof PostsPostIdRoute | ||
| '/posts/': typeof PostsIndexRoute | ||
| '/route-a': typeof PathlessLayoutNestedLayoutRouteARoute | ||
| '/route-b': typeof PathlessLayoutNestedLayoutRouteBRoute | ||
| } | ||
| export interface FileRoutesByTo { | ||
| '/': typeof IndexRoute | ||
| '/posts/$postId': typeof PostsPostIdRoute | ||
| '/posts': typeof PostsIndexRoute | ||
| '/route-a': typeof PathlessLayoutNestedLayoutRouteARoute | ||
| '/route-b': typeof PathlessLayoutNestedLayoutRouteBRoute | ||
| } | ||
| export interface FileRoutesById { | ||
| __root__: typeof rootRouteImport | ||
| '/': typeof IndexRoute | ||
| '/posts': typeof PostsRouteRouteWithChildren | ||
| '/_pathlessLayout': typeof PathlessLayoutRouteWithChildren | ||
| '/_pathlessLayout/_nested-layout': typeof PathlessLayoutNestedLayoutRouteWithChildren | ||
| '/posts/$postId': typeof PostsPostIdRoute | ||
| '/posts/': typeof PostsIndexRoute | ||
| '/_pathlessLayout/_nested-layout/route-a': typeof PathlessLayoutNestedLayoutRouteARoute | ||
| '/_pathlessLayout/_nested-layout/route-b': typeof PathlessLayoutNestedLayoutRouteBRoute | ||
| } | ||
| export interface FileRouteTypes { | ||
| fileRoutesByFullPath: FileRoutesByFullPath | ||
| fullPaths: | ||
| | '/' | ||
| | '/posts' | ||
| | '/posts/$postId' | ||
| | '/posts/' | ||
| | '/route-a' | ||
| | '/route-b' | ||
| fileRoutesByTo: FileRoutesByTo | ||
| to: '/' | '/posts/$postId' | '/posts' | '/route-a' | '/route-b' | ||
| id: | ||
| | '__root__' | ||
| | '/' | ||
| | '/posts' | ||
| | '/_pathlessLayout' | ||
| | '/_pathlessLayout/_nested-layout' | ||
| | '/posts/$postId' | ||
| | '/posts/' | ||
| | '/_pathlessLayout/_nested-layout/route-a' | ||
| | '/_pathlessLayout/_nested-layout/route-b' | ||
| fileRoutesById: FileRoutesById | ||
| } | ||
| export interface RootRouteChildren { | ||
| IndexRoute: typeof IndexRoute | ||
| PostsRouteRoute: typeof PostsRouteRouteWithChildren | ||
| PathlessLayoutRoute: typeof PathlessLayoutRouteWithChildren | ||
| } | ||
|
|
||
| declare module '@tanstack/solid-router' { | ||
| interface FileRoutesByPath { | ||
| '/_pathlessLayout': { | ||
| id: '/_pathlessLayout' | ||
| path: '' | ||
| fullPath: '' | ||
| preLoaderRoute: typeof PathlessLayoutRouteImport | ||
| parentRoute: typeof rootRouteImport | ||
| } | ||
| '/posts': { | ||
| id: '/posts' | ||
| path: '/posts' | ||
| fullPath: '/posts' | ||
| preLoaderRoute: typeof PostsRouteRouteImport | ||
| parentRoute: typeof rootRouteImport | ||
| } | ||
| '/': { | ||
| id: '/' | ||
| path: '/' | ||
| fullPath: '/' | ||
| preLoaderRoute: typeof IndexRouteImport | ||
| parentRoute: typeof rootRouteImport | ||
| } | ||
| '/posts/': { | ||
| id: '/posts/' | ||
| path: '/' | ||
| fullPath: '/posts/' | ||
| preLoaderRoute: typeof PostsIndexRouteImport | ||
| parentRoute: typeof PostsRouteRoute | ||
| } | ||
| '/posts/$postId': { | ||
| id: '/posts/$postId' | ||
| path: '/$postId' | ||
| fullPath: '/posts/$postId' | ||
| preLoaderRoute: typeof PostsPostIdRouteImport | ||
| parentRoute: typeof PostsRouteRoute | ||
| } | ||
| '/_pathlessLayout/_nested-layout': { | ||
| id: '/_pathlessLayout/_nested-layout' | ||
| path: '' | ||
| fullPath: '' | ||
| preLoaderRoute: typeof PathlessLayoutNestedLayoutRouteImport | ||
| parentRoute: typeof PathlessLayoutRoute | ||
| } | ||
| '/_pathlessLayout/_nested-layout/route-b': { | ||
| id: '/_pathlessLayout/_nested-layout/route-b' | ||
| path: '/route-b' | ||
| fullPath: '/route-b' | ||
| preLoaderRoute: typeof PathlessLayoutNestedLayoutRouteBRouteImport | ||
| parentRoute: typeof PathlessLayoutNestedLayoutRoute | ||
| } | ||
| '/_pathlessLayout/_nested-layout/route-a': { | ||
| id: '/_pathlessLayout/_nested-layout/route-a' | ||
| path: '/route-a' | ||
| fullPath: '/route-a' | ||
| preLoaderRoute: typeof PathlessLayoutNestedLayoutRouteARouteImport | ||
| parentRoute: typeof PathlessLayoutNestedLayoutRoute | ||
| } | ||
| } | ||
| } | ||
|
|
||
| interface PostsRouteRouteChildren { | ||
| PostsPostIdRoute: typeof PostsPostIdRoute | ||
| PostsIndexRoute: typeof PostsIndexRoute | ||
| } | ||
|
|
||
| const PostsRouteRouteChildren: PostsRouteRouteChildren = { | ||
| PostsPostIdRoute: PostsPostIdRoute, | ||
| PostsIndexRoute: PostsIndexRoute, | ||
| } | ||
|
|
||
| const PostsRouteRouteWithChildren = PostsRouteRoute._addFileChildren( | ||
| PostsRouteRouteChildren, | ||
| ) | ||
|
|
||
| interface PathlessLayoutNestedLayoutRouteChildren { | ||
| PathlessLayoutNestedLayoutRouteARoute: typeof PathlessLayoutNestedLayoutRouteARoute | ||
| PathlessLayoutNestedLayoutRouteBRoute: typeof PathlessLayoutNestedLayoutRouteBRoute | ||
| } | ||
|
|
||
| const PathlessLayoutNestedLayoutRouteChildren: PathlessLayoutNestedLayoutRouteChildren = | ||
| { | ||
| PathlessLayoutNestedLayoutRouteARoute: | ||
| PathlessLayoutNestedLayoutRouteARoute, | ||
| PathlessLayoutNestedLayoutRouteBRoute: | ||
| PathlessLayoutNestedLayoutRouteBRoute, | ||
| } | ||
|
|
||
| const PathlessLayoutNestedLayoutRouteWithChildren = | ||
| PathlessLayoutNestedLayoutRoute._addFileChildren( | ||
| PathlessLayoutNestedLayoutRouteChildren, | ||
| ) | ||
|
|
||
| interface PathlessLayoutRouteChildren { | ||
| PathlessLayoutNestedLayoutRoute: typeof PathlessLayoutNestedLayoutRouteWithChildren | ||
| } | ||
|
|
||
| const PathlessLayoutRouteChildren: PathlessLayoutRouteChildren = { | ||
| PathlessLayoutNestedLayoutRoute: PathlessLayoutNestedLayoutRouteWithChildren, | ||
| } | ||
|
|
||
| const PathlessLayoutRouteWithChildren = PathlessLayoutRoute._addFileChildren( | ||
| PathlessLayoutRouteChildren, | ||
| ) | ||
|
|
||
| const rootRouteChildren: RootRouteChildren = { | ||
| IndexRoute: IndexRoute, | ||
| PostsRouteRoute: PostsRouteRouteWithChildren, | ||
| PathlessLayoutRoute: PathlessLayoutRouteWithChildren, | ||
| } | ||
| export const routeTree = rootRouteImport | ||
| ._addFileChildren(rootRouteChildren) | ||
| ._addFileTypes<FileRouteTypes>() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fix incorrect package name reference.
The package name contains "react" but this is a Solid Router e2e test suite. The name should reflect that it's for Solid, not React.
Apply this diff to correct the package name:
📝 Committable suggestion
🤖 Prompt for AI Agents