-
-
Notifications
You must be signed in to change notification settings - Fork 1.5k
test(vue-router): basic-esbuild-file-based #6115
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
8959719
1878cde
b95ab7f
39eaad3
b8a4b0c
9376105
086b8c2
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,49 @@ | ||
| import js from '@eslint/js' | ||
| import typescript from '@typescript-eslint/eslint-plugin' | ||
| import typescriptParser from '@typescript-eslint/parser' | ||
| import vue from 'eslint-plugin-vue' | ||
| import vueParser from 'vue-eslint-parser' | ||
|
|
||
| export default [ | ||
| js.configs.recommended, | ||
| ...vue.configs['flat/recommended'], | ||
| { | ||
| files: ['**/*.{js,jsx,ts,tsx,vue}'], | ||
| languageOptions: { | ||
| parser: vueParser, | ||
| parserOptions: { | ||
| parser: typescriptParser, | ||
| ecmaVersion: 'latest', | ||
| sourceType: 'module', | ||
| ecmaFeatures: { | ||
| jsx: true, | ||
| }, | ||
| }, | ||
| }, | ||
| plugins: { | ||
| '@typescript-eslint': typescript, | ||
| vue, | ||
| }, | ||
| rules: { | ||
| // Vue specific rules | ||
| 'vue/multi-word-component-names': 'off', | ||
| 'vue/no-unused-vars': 'error', | ||
|
|
||
| // TypeScript rules | ||
| '@typescript-eslint/no-unused-vars': 'error', | ||
| '@typescript-eslint/no-explicit-any': 'warn', | ||
|
|
||
| // General rules | ||
| 'no-unused-vars': 'off', // Let TypeScript handle this | ||
| }, | ||
| }, | ||
| { | ||
| files: ['**/*.vue'], | ||
| languageOptions: { | ||
| parser: vueParser, | ||
| parserOptions: { | ||
| parser: typescriptParser, | ||
| }, | ||
| }, | ||
| }, | ||
| ] | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,24 @@ | ||
| <!doctype html> | ||
| <html lang="en"> | ||
| <head> | ||
| <meta charset="UTF-8" /> | ||
| <meta name="viewport" content="width=device-width, initial-scale=1.0" /> | ||
|
|
||
| <script src="https://unpkg.com/@tailwindcss/browser@4"></script> | ||
| <style type="text/tailwindcss"> | ||
| html { | ||
| color-scheme: light dark; | ||
| } | ||
| * { | ||
| @apply border-gray-200 dark:border-gray-800; | ||
| } | ||
| body { | ||
| @apply bg-gray-50 text-gray-950 dark:bg-gray-900 dark:text-gray-200; | ||
| } | ||
| </style> | ||
| </head> | ||
| <body> | ||
| <div id="app"></div> | ||
| <script type="module" src="/dist/main.js"></script> | ||
| </body> | ||
| </html> |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,35 @@ | ||
| { | ||
| "name": "tanstack-router-e2e-vue-basic-esbuild-file-based", | ||
| "private": true, | ||
| "type": "module", | ||
| "scripts": { | ||
| "dev": "node src/esbuild.run.js dev --port 5601", | ||
| "build": "node src/esbuild.run.js build && vue-tsc --noEmit", | ||
| "preview": "node src/esbuild.run.js preview", | ||
| "start": "pnpm dev", | ||
| "test:e2e": "rm -rf port*.txt; playwright test --project=chromium" | ||
| }, | ||
| "dependencies": { | ||
| "@tanstack/router-plugin": "workspace:^", | ||
| "@tanstack/vue-router": "workspace:^", | ||
| "@tanstack/vue-router-devtools": "workspace:^", | ||
| "@tanstack/zod-adapter": "workspace:^", | ||
|
Comment on lines
+13
to
+16
Contributor
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. 🛠️ Refactor suggestion | 🟠 Major Use As per coding guidelines, internal dependencies should use the Apply this diff to align with the coding guidelines: "dependencies": {
- "@tanstack/router-plugin": "workspace:^",
- "@tanstack/vue-router": "workspace:^",
- "@tanstack/vue-router-devtools": "workspace:^",
- "@tanstack/zod-adapter": "workspace:^",
+ "@tanstack/router-plugin": "workspace:*",
+ "@tanstack/vue-router": "workspace:*",
+ "@tanstack/vue-router-devtools": "workspace:*",
+ "@tanstack/zod-adapter": "workspace:*",
"postcss": "^8.5.1",
"redaxios": "^0.5.1",
"vue": "^3.5.16",
"zod": "^3.24.2"
},
"devDependencies": {
"@eslint/js": "^9.36.0",
"@playwright/test": "^1.50.1",
- "@tanstack/router-e2e-utils": "workspace:^",
+ "@tanstack/router-e2e-utils": "workspace:*",Also applies to: 25-25 🤖 Prompt for AI Agents |
||
| "postcss": "^8.5.1", | ||
| "redaxios": "^0.5.1", | ||
| "vue": "^3.5.16", | ||
| "zod": "^3.24.2" | ||
| }, | ||
| "devDependencies": { | ||
| "@eslint/js": "^9.36.0", | ||
| "@playwright/test": "^1.50.1", | ||
| "@tanstack/router-e2e-utils": "workspace:^", | ||
| "@typescript-eslint/eslint-plugin": "^8.44.1", | ||
| "@typescript-eslint/parser": "^8.44.1", | ||
| "esbuild": "^0.25.0", | ||
| "esbuild-plugin-vue3": "^0.5.1", | ||
| "eslint-plugin-vue": "^9.33.0", | ||
| "typescript": "^5.8.3", | ||
| "vue-eslint-parser": "^9.4.3", | ||
| "vue-tsc": "^3.1.5" | ||
| } | ||
|
Comment on lines
+22
to
+34
Contributor
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. 🛠️ Refactor suggestion | 🟠 Major Add router ESLint plugin to devDependencies. As per coding guidelines, ESLint rules for router best practices should be implemented using the ESLint plugin router. The corresponding package needs to be added to devDependencies. Add the router ESLint plugin to devDependencies: "devDependencies": {
"@eslint/js": "^9.36.0",
"@playwright/test": "^1.50.1",
"@tanstack/router-e2e-utils": "workspace:*",
+ "@tanstack/eslint-plugin-router": "workspace:*",
"@typescript-eslint/eslint-plugin": "^8.44.1",
🤖 Prompt for AI Agents |
||
| } | ||
| 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_NODE_ENV="test" VITE_EXTERNAL_PORT=${EXTERNAL_PORT} VITE_SERVER_PORT=${PORT} pnpm preview --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,41 @@ | ||
| import { ref, defineComponent } from 'vue' | ||
| import { useBlocker, useNavigate } from '@tanstack/vue-router' | ||
|
|
||
| export const EditingAComponent = defineComponent({ | ||
| setup() { | ||
| const navigate = useNavigate() | ||
| const input = ref('') | ||
|
|
||
| const blocker = useBlocker({ | ||
| shouldBlockFn: ({ next }) => { | ||
| if (next.fullPath === '/editing-b' && input.value.length > 0) { | ||
| return true | ||
| } | ||
| return false | ||
| }, | ||
| withResolver: true, | ||
| }) | ||
|
|
||
| return () => ( | ||
| <div> | ||
| <h1>Editing A</h1> | ||
| <label> | ||
| Enter your name: | ||
| <input | ||
| name="input" | ||
| value={input.value} | ||
| onInput={(e) => | ||
| (input.value = (e.target as HTMLInputElement).value) | ||
| } | ||
| /> | ||
| </label> | ||
| <button onClick={() => navigate({ to: '/editing-b' })}> | ||
| Go to next step | ||
| </button> | ||
| {blocker.value.status === 'blocked' && ( | ||
| <button onClick={() => blocker.value.proceed?.()}>Proceed</button> | ||
| )} | ||
| </div> | ||
| ) | ||
| }, | ||
| }) |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,34 @@ | ||
| import { ref, toValue, defineComponent } from 'vue' | ||
| import { useBlocker, useNavigate } from '@tanstack/vue-router' | ||
|
|
||
| export const EditingBComponent = defineComponent({ | ||
| setup() { | ||
| const navigate = useNavigate() | ||
| const input = ref('') | ||
|
|
||
| const blocker = useBlocker({ | ||
| shouldBlockFn: () => !!toValue(input), | ||
| withResolver: true, | ||
| }) | ||
|
|
||
| return () => ( | ||
| <div> | ||
| <h1>Editing B</h1> | ||
| <label> | ||
| Enter your name: | ||
| <input | ||
| name="input" | ||
| value={input.value} | ||
| onInput={(e) => | ||
| (input.value = (e.target as HTMLInputElement).value) | ||
| } | ||
| /> | ||
| </label> | ||
| <button onClick={() => navigate({ to: '/editing-a' })}>Go back</button> | ||
| {blocker.value.status === 'blocked' && ( | ||
| <button onClick={() => blocker.value.proceed?.()}>Proceed</button> | ||
| )} | ||
| </div> | ||
| ) | ||
| }, | ||
| }) |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,10 @@ | ||
| <script setup lang="ts"> | ||
| import { Link } from '@tanstack/vue-router' | ||
| </script> | ||
|
|
||
| <template> | ||
| <div> | ||
| <p>This is the notFoundComponent configured on root route</p> | ||
| <Link to="/">Start Over</Link> | ||
| </div> | ||
| </template> |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,37 @@ | ||
| import { ref, onMounted, defineComponent } from 'vue' | ||
| import { useSearch, useNavigate } from '@tanstack/vue-router' | ||
|
|
||
| export const NotRemountDepsComponent = defineComponent({ | ||
| setup() { | ||
| // Component-scoped ref - will be recreated on component remount | ||
| const mounts = ref(0) | ||
| const search = useSearch({ from: '/notRemountDeps' }) | ||
| const navigate = useNavigate() | ||
|
|
||
| onMounted(() => { | ||
| mounts.value++ | ||
| }) | ||
|
|
||
| return () => ( | ||
| <div class="p-2"> | ||
| <button | ||
| onClick={() => | ||
| navigate({ | ||
| to: '/notRemountDeps', | ||
| search: { | ||
| searchParam: Math.random().toString(36).substring(2, 8), | ||
| }, | ||
| }) | ||
| } | ||
| > | ||
| Regenerate search param | ||
| </button> | ||
|
|
||
| <div>Search: {search.value.searchParam}</div> | ||
| <div data-testid="component-mounts"> | ||
| Page component mounts: {mounts.value} | ||
| </div> | ||
| </div> | ||
| ) | ||
| }, | ||
| }) |
| Original file line number | Diff line number | Diff line change | ||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,11 @@ | ||||||||||||||
| <script setup lang="ts"> | ||||||||||||||
| import { ErrorComponent } from '@tanstack/vue-router' | ||||||||||||||
| defineProps<{ | ||||||||||||||
| error: any | ||||||||||||||
| }>() | ||||||||||||||
|
Comment on lines
+4
to
+6
Contributor
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. 🛠️ Refactor suggestion | 🟠 Major Use a more specific type instead of The As per coding guidelines, TypeScript strict mode with extensive type safety should be used. Apply this diff: defineProps<{
- error: any
+ error: Error | unknown
}>()📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||
| </script> | ||||||||||||||
|
|
||||||||||||||
| <template> | ||||||||||||||
| <ErrorComponent :error="error" /> | ||||||||||||||
| </template> | ||||||||||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,38 @@ | ||
| import { ref, onMounted, defineComponent } from 'vue' | ||
| import { useSearch, useNavigate } from '@tanstack/vue-router' | ||
|
|
||
| // Module-scoped ref to persist across component remounts | ||
| const mounts = ref(0) | ||
|
|
||
| export const RemountDepsComponent = defineComponent({ | ||
| setup() { | ||
| const search = useSearch({ from: '/remountDeps' }) | ||
| const navigate = useNavigate() | ||
|
|
||
| onMounted(() => { | ||
| mounts.value++ | ||
| }) | ||
|
|
||
| return () => ( | ||
| <div class="p-2"> | ||
| <button | ||
| onClick={() => | ||
| navigate({ | ||
| to: '/remountDeps', | ||
| search: { | ||
| searchParam: Math.random().toString(36).substring(2, 8), | ||
| }, | ||
| }) | ||
| } | ||
| > | ||
| Regenerate search param | ||
| </button> | ||
|
|
||
| <div>Search: {search.value.searchParam}</div> | ||
| <div data-testid="component-mounts"> | ||
| Page component mounts: {mounts.value} | ||
| </div> | ||
| </div> | ||
| ) | ||
| }, | ||
| }) |
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.
🛠️ Refactor suggestion | 🟠 Major
Add router ESLint plugin configuration.
As per coding guidelines, files matching
**/*.{js,ts,tsx}should implement ESLint rules for router best practices using the ESLint plugin router. This ESLint configuration is missing the TanStack router ESLint plugin.Based on coding guidelines, the ESLint plugin for TanStack Router should be added:
First, ensure the router ESLint plugin package is added to
package.jsondevDependencies (see comment on package.json).Then, import and configure it in this file:
🤖 Prompt for AI Agents