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
11 changes: 11 additions & 0 deletions e2e/vue-router/basepath-file-based/.gitignore
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/
11 changes: 11 additions & 0 deletions e2e/vue-router/basepath-file-based/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
</head>
<body>
<div id="app"></div>
<script type="module" src="/src/main.tsx"></script>
</body>
</html>
34 changes: 34 additions & 0 deletions e2e/vue-router/basepath-file-based/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
{
"name": "tanstack-router-e2e-vue-basepath-file-based",
"private": true,
"type": "module",
"scripts": {
"dev": "vite --port 3000",
"dev:e2e": "vite",
"build": "vite build && tsc --noEmit",
"preview": "vite preview",
"start": "vite",
"test:e2e": "rm -rf port*.txt; playwright test --project=chromium"
},
"dependencies": {
"@tailwindcss/postcss": "^4.1.15",
"@tanstack/router-plugin": "workspace:^",
"@tanstack/vue-query": "^5.90.0",
"@tanstack/vue-query-devtools": "^6.1.2",
"@tanstack/vue-router": "workspace:^",
"@tanstack/vue-router-devtools": "workspace:^",
"postcss": "^8.5.1",
"redaxios": "^0.5.1",
"tailwindcss": "^4.1.17",
"vue": "^3.5.16"
},
"devDependencies": {
"@playwright/test": "^1.50.1",
"@tanstack/router-e2e-utils": "workspace:^",
"@vitejs/plugin-vue": "^5.2.3",
"@vitejs/plugin-vue-jsx": "^4.1.2",
"typescript": "~5.8.3",
"vite": "^7.1.7",
"vue-tsc": "^3.1.5"
}
}
41 changes: 41 additions & 0 deletions e2e/vue-router/basepath-file-based/playwright.config.ts
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_SERVER_PORT=${PORT} VITE_EXTERNAL_PORT=${EXTERNAL_PORT} pnpm build && VITE_SERVER_PORT=${PORT} pnpm preview --port ${PORT}`,
url: baseURL,
reuseExistingServer: !process.env.CI,
stdout: 'pipe',
},

projects: [
{
name: 'chromium',
use: { ...devices['Desktop Chrome'] },
},
],
})
30 changes: 30 additions & 0 deletions e2e/vue-router/basepath-file-based/src/main.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import { createApp, h } from 'vue'
import { RouterProvider, createRouter } from '@tanstack/vue-router'
import { routeTree } from './routeTree.gen'

// Set up a Router instance
const router = createRouter({
routeTree,
defaultPreload: 'intent',
defaultStaleTime: 5000,
scrollRestoration: true,
basepath: '/app/',
})

// Register things for typesafety
declare module '@tanstack/vue-router' {
interface Register {
router: typeof router
}
}

const rootElement = document.getElementById('app')!
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Non-null assertion could fail if element is missing.

The non-null assertion (!) assumes the #app element always exists. If the element is missing from the HTML, this will cause a runtime error.

Consider adding a null check:

-const rootElement = document.getElementById('app')!
+const rootElement = document.getElementById('app')
+if (!rootElement) {
+  throw new Error('Failed to find #app element')
+}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const rootElement = document.getElementById('app')!
const rootElement = document.getElementById('app')
if (!rootElement) {
throw new Error('Failed to find #app element')
}
🤖 Prompt for AI Agents
In e2e/vue-router/basepath-file-based/src/main.tsx around line 21, remove the
non-null assertion on document.getElementById('app') as it can throw at runtime
if the element is absent; instead get the element into a nullable variable,
check for null and handle it (for example throw a clear error like "Root element
#app not found" or create/append a fallback DIV with id "app" before mounting),
then use the verified element for mounting.


if (!rootElement.innerHTML) {
const app = createApp({
setup() {
return () => h(RouterProvider, { router })
},
})
app.mount('#app')
}
77 changes: 77 additions & 0 deletions e2e/vue-router/basepath-file-based/src/routeTree.gen.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
/* 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 AboutRouteImport } from './routes/about'
import { Route as IndexRouteImport } from './routes/index'

const AboutRoute = AboutRouteImport.update({
id: '/about',
path: '/about',
getParentRoute: () => rootRouteImport,
} as any)
const IndexRoute = IndexRouteImport.update({
id: '/',
path: '/',
getParentRoute: () => rootRouteImport,
} as any)

export interface FileRoutesByFullPath {
'/': typeof IndexRoute
'/about': typeof AboutRoute
}
export interface FileRoutesByTo {
'/': typeof IndexRoute
'/about': typeof AboutRoute
}
export interface FileRoutesById {
__root__: typeof rootRouteImport
'/': typeof IndexRoute
'/about': typeof AboutRoute
}
export interface FileRouteTypes {
fileRoutesByFullPath: FileRoutesByFullPath
fullPaths: '/' | '/about'
fileRoutesByTo: FileRoutesByTo
to: '/' | '/about'
id: '__root__' | '/' | '/about'
fileRoutesById: FileRoutesById
}
export interface RootRouteChildren {
IndexRoute: typeof IndexRoute
AboutRoute: typeof AboutRoute
}

declare module '@tanstack/vue-router' {
interface FileRoutesByPath {
'/about': {
id: '/about'
path: '/about'
fullPath: '/about'
preLoaderRoute: typeof AboutRouteImport
parentRoute: typeof rootRouteImport
}
'/': {
id: '/'
path: '/'
fullPath: '/'
preLoaderRoute: typeof IndexRouteImport
parentRoute: typeof rootRouteImport
}
}
}

const rootRouteChildren: RootRouteChildren = {
IndexRoute: IndexRoute,
AboutRoute: AboutRoute,
}
export const routeTree = rootRouteImport
._addFileChildren(rootRouteChildren)
._addFileTypes<FileRouteTypes>()
3 changes: 3 additions & 0 deletions e2e/vue-router/basepath-file-based/src/routes/__root.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import { createRootRoute } from '@tanstack/vue-router'

export const Route = createRootRoute()
Comment on lines +1 to +3
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Missing Outlet component in root route.

The root route lacks a component with an Outlet, which is required to render child routes (index and about). Without it, navigation will fail and the e2e tests will not work correctly.

Apply this diff to add the required component with Outlet:

-import { createRootRoute } from '@tanstack/vue-router'
+import { createRootRoute, Outlet } from '@tanstack/vue-router'

-export const Route = createRootRoute()
+export const Route = createRootRoute({
+  component: () => <Outlet />,
+})
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
import { createRootRoute } from '@tanstack/vue-router'
export const Route = createRootRoute()
import { createRootRoute, Outlet } from '@tanstack/vue-router'
export const Route = createRootRoute({
component: () => <Outlet />,
})
🤖 Prompt for AI Agents
In e2e/vue-router/basepath-file-based/src/routes/__root.tsx lines 1-3, the root
route is created without a component that renders an Outlet, so child routes
can't render; import the Outlet component from '@tanstack/vue-router', define a
small root component that returns the Outlet (e.g., a functional component or
setup component that renders Outlet), and pass that component into
createRootRoute({ component: YourRootComponent }) before exporting Route so
child routes can mount.

20 changes: 20 additions & 0 deletions e2e/vue-router/basepath-file-based/src/routes/about.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { createFileRoute } from '@tanstack/vue-router'

export const Route = createFileRoute('/about')({
component: RouteComponent,
})

function RouteComponent() {
const navigate = Route.useNavigate()

return (
<div data-testid="about-component">
<button
onClick={() => navigate({ to: '/', reloadDocument: true })}
data-testid="to-home-btn"
>
Navigate to / with document reload
</button>
</div>
)
}
25 changes: 25 additions & 0 deletions e2e/vue-router/basepath-file-based/src/routes/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { createFileRoute } from '@tanstack/vue-router'

export const Route = createFileRoute('/')({
component: App,
})

function App() {
const navigate = Route.useNavigate()

return (
<div data-testid="home-component">
<button
data-testid="to-about-btn"
onClick={() =>
navigate({
to: '/about',
reloadDocument: true,
})
}
>
Navigate to /about with document reload
</button>
</div>
)
}
18 changes: 18 additions & 0 deletions e2e/vue-router/basepath-file-based/tests/reload-document.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { expect, test } from '@playwright/test'

test('navigate() respects basepath for when reloadDocument=true', async ({
page,
}) => {
await page.goto(`/app/`)
await expect(page.getByTestId(`home-component`)).toBeInViewport()

const aboutBtn = page.getByTestId(`to-about-btn`)
await aboutBtn.click()
await page.waitForURL('/app/about')
await expect(page.getByTestId(`about-component`)).toBeInViewport()

const homeBtn = page.getByTestId(`to-home-btn`)
await homeBtn.click()
await page.waitForURL('/app/')
await expect(page.getByTestId(`home-component`)).toBeInViewport()
})
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import { e2eStartDummyServer } from '@tanstack/router-e2e-utils'
import packageJson from '../../package.json' with { type: 'json' }

export default async function setup() {
await e2eStartDummyServer(packageJson.name)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import { e2eStopDummyServer } from '@tanstack/router-e2e-utils'
import packageJson from '../../package.json' with { type: 'json' }

export default async function teardown() {
await e2eStopDummyServer(packageJson.name)
}
16 changes: 16 additions & 0 deletions e2e/vue-router/basepath-file-based/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
{
"compilerOptions": {
"strict": true,
"esModuleInterop": true,
"jsx": "preserve",
"jsxImportSource": "vue",
"target": "ESNext",
"moduleResolution": "Bundler",
"module": "ESNext",
"skipLibCheck": true,
"resolveJsonModule": true,
"allowJs": true,
"types": ["vite/client"]
},
"exclude": ["node_modules", "dist"]
}
17 changes: 17 additions & 0 deletions e2e/vue-router/basepath-file-based/vite.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import vueJsx from '@vitejs/plugin-vue-jsx'
import { tanstackRouter } from '@tanstack/router-plugin/vite'

// https://vitejs.dev/config/
export default defineConfig({
base: '/app/',
plugins: [
tanstackRouter({
target: 'vue',
autoCodeSplitting: true,
}),
vue(),
vueJsx(),
],
})
55 changes: 55 additions & 0 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading