Skip to content

fix: ensure child routes receive context returned from parent routes' beforeLoad functions, even if invalidate() is called during another load #4306

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

Closed
Closed
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/react-router/basic-auth/.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/
12 changes: 12 additions & 0 deletions e2e/react-router/basic-auth/index.html
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>
31 changes: 31 additions & 0 deletions e2e/react-router/basic-auth/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
{
"name": "tanstack-router-e2e-react-basic-auth",
"private": true,
"type": "module",
"scripts": {
"dev": "vite --port 3000",
"dev:e2e": "vite",
"build": "vite build && tsc --noEmit",
"serve": "vite preview",
"start": "vite",
"test:e2e": "playwright test --project=chromium"
},
"dependencies": {
"@tanstack/react-router": "workspace:^",
"@tanstack/react-router-devtools": "workspace:^",
"react": "^19.0.0",
"react-dom": "^19.0.0",
"redaxios": "^0.5.1",
"postcss": "^8.5.1",
"autoprefixer": "^10.4.20",
"tailwindcss": "^3.4.17"
},
"devDependencies": {
"@playwright/test": "^1.50.1",
"@tanstack/router-e2e-utils": "workspace:^",
"@types/react": "^19.0.8",
"@types/react-dom": "^19.0.3",
"@vitejs/plugin-react": "^4.3.4",
"vite": "^6.1.0"
}
}
34 changes: 34 additions & 0 deletions e2e/react-router/basic-auth/playwright.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import { defineConfig, devices } from '@playwright/test'
import { derivePort } from '@tanstack/router-e2e-utils'
import packageJson from './package.json' with { type: 'json' }

const PORT = derivePort(packageJson.name)
const baseURL = `http://localhost:${PORT}`
/**
* See https://playwright.dev/docs/test-configuration.
*/
export default defineConfig({
testDir: './tests',
workers: 1,

reporter: [['line']],

use: {
/* Base URL to use in actions like `await page.goto('/')`. */
baseURL,
},

webServer: {
command: `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'] },
},
],
})
6 changes: 6 additions & 0 deletions e2e/react-router/basic-auth/postcss.config.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
export default {
plugins: {
tailwindcss: {},
autoprefixer: {},
},
}
77 changes: 77 additions & 0 deletions e2e/react-router/basic-auth/src/auth.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
import { useMemo, useState } from 'react'

export type AuthUser = {
id: string
name: string
}

export type AuthState =
| {
type: 'authenticated'
user: AuthUser
}
| {
type: 'unauthenticated'
hasLoggedIn: boolean
}

export type AuthHandle = {
login: (userId: string) => Promise<void>
logout: () => Promise<void>
}

export const initialAuthState: AuthState = {
type: 'unauthenticated',
hasLoggedIn: false,
}

export const initialAuthHandle: AuthHandle = {
login: () => Promise.resolve(),
logout: () => Promise.resolve(),
}

export function useAuth(): readonly [AuthState, AuthHandle] {
const [authState, setAuthState] = useState<AuthState>({
type: 'unauthenticated',
hasLoggedIn: false,
})

const login = async (userId: string) =>
new Promise<void>((resolve) => {
// Delay the login with a setTimeout to simulate a network request,
// and make sure any actions in response are not batched as part of
// a React request handler.
setTimeout(() => {
setAuthState({
type: 'authenticated',
user: {
id: userId,
name: `User #${userId}`,
},
})
resolve()
}, 500)
})

const logout = async () =>
new Promise<void>((resolve) => {
setTimeout(() => {
setAuthState((currentState) => ({
type: 'unauthenticated',
hasLoggedIn:
currentState.type === 'authenticated' || currentState.hasLoggedIn,
}))
resolve()
})
})

const authHandle = useMemo<AuthHandle>(
() => ({
login,
logout,
}),
[login, logout],
)

return [authState, authHandle]
}
Loading
Loading