Skip to content

Commit 430f268

Browse files
authored
test(start): test that multiple Set-Cookie headers are not lost during redirect (#5978)
1 parent 672f6ca commit 430f268

File tree

11 files changed

+257
-0
lines changed

11 files changed

+257
-0
lines changed

e2e/react-start/basic/package.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
"@tanstack/react-start": "workspace:^",
2727
"express": "^5.1.0",
2828
"http-proxy-middleware": "^3.0.5",
29+
"js-cookie": "^3.0.5",
2930
"react": "^19.0.0",
3031
"react-dom": "^19.0.0",
3132
"redaxios": "^0.5.1",
@@ -35,6 +36,7 @@
3536
"@playwright/test": "^1.50.1",
3637
"@tailwindcss/postcss": "^4.1.15",
3738
"@tanstack/router-e2e-utils": "workspace:^",
39+
"@types/js-cookie": "^3.0.6",
3840
"@types/node": "^22.10.2",
3941
"@types/react": "^19.0.8",
4042
"@types/react-dom": "^19.0.3",

e2e/react-start/basic/src/routeTree.gen.ts

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ import { Route as SearchParamsIndexRouteImport } from './routes/search-params/in
2828
import { Route as RedirectIndexRouteImport } from './routes/redirect/index'
2929
import { Route as PostsIndexRouteImport } from './routes/posts.index'
3030
import { Route as NotFoundIndexRouteImport } from './routes/not-found/index'
31+
import { Route as MultiCookieRedirectIndexRouteImport } from './routes/multi-cookie-redirect/index'
3132
import { Route as UsersUserIdRouteImport } from './routes/users.$userId'
3233
import { Route as SearchParamsLoaderThrowsRedirectRouteImport } from './routes/search-params/loader-throws-redirect'
3334
import { Route as SearchParamsDefaultRouteImport } from './routes/search-params/default'
@@ -36,6 +37,7 @@ import { Route as PostsPostIdRouteImport } from './routes/posts.$postId'
3637
import { Route as NotFoundViaLoaderRouteImport } from './routes/not-found/via-loader'
3738
import { Route as NotFoundViaHeadRouteImport } from './routes/not-found/via-head'
3839
import { Route as NotFoundViaBeforeLoadRouteImport } from './routes/not-found/via-beforeLoad'
40+
import { Route as MultiCookieRedirectTargetRouteImport } from './routes/multi-cookie-redirect/target'
3941
import { Route as ApiUsersRouteImport } from './routes/api.users'
4042
import { Route as LayoutLayout2RouteImport } from './routes/_layout/_layout-2'
4143
import { Route as RedirectTargetIndexRouteImport } from './routes/redirect/$target/index'
@@ -139,6 +141,12 @@ const NotFoundIndexRoute = NotFoundIndexRouteImport.update({
139141
path: '/',
140142
getParentRoute: () => NotFoundRouteRoute,
141143
} as any)
144+
const MultiCookieRedirectIndexRoute =
145+
MultiCookieRedirectIndexRouteImport.update({
146+
id: '/multi-cookie-redirect/',
147+
path: '/multi-cookie-redirect/',
148+
getParentRoute: () => rootRouteImport,
149+
} as any)
142150
const UsersUserIdRoute = UsersUserIdRouteImport.update({
143151
id: '/$userId',
144152
path: '/$userId',
@@ -180,6 +188,12 @@ const NotFoundViaBeforeLoadRoute = NotFoundViaBeforeLoadRouteImport.update({
180188
path: '/via-beforeLoad',
181189
getParentRoute: () => NotFoundRouteRoute,
182190
} as any)
191+
const MultiCookieRedirectTargetRoute =
192+
MultiCookieRedirectTargetRouteImport.update({
193+
id: '/multi-cookie-redirect/target',
194+
path: '/multi-cookie-redirect/target',
195+
getParentRoute: () => rootRouteImport,
196+
} as any)
183197
const ApiUsersRoute = ApiUsersRouteImport.update({
184198
id: '/api/users',
185199
path: '/api/users',
@@ -277,6 +291,7 @@ export interface FileRoutesByFullPath {
277291
'/users': typeof UsersRouteWithChildren
278292
'/대한민국': typeof Char45824Char54620Char48124Char44397Route
279293
'/api/users': typeof ApiUsersRouteWithChildren
294+
'/multi-cookie-redirect/target': typeof MultiCookieRedirectTargetRoute
280295
'/not-found/via-beforeLoad': typeof NotFoundViaBeforeLoadRoute
281296
'/not-found/via-head': typeof NotFoundViaHeadRoute
282297
'/not-found/via-loader': typeof NotFoundViaLoaderRoute
@@ -285,6 +300,7 @@ export interface FileRoutesByFullPath {
285300
'/search-params/default': typeof SearchParamsDefaultRoute
286301
'/search-params/loader-throws-redirect': typeof SearchParamsLoaderThrowsRedirectRoute
287302
'/users/$userId': typeof UsersUserIdRoute
303+
'/multi-cookie-redirect': typeof MultiCookieRedirectIndexRoute
288304
'/not-found/': typeof NotFoundIndexRoute
289305
'/posts/': typeof PostsIndexRoute
290306
'/redirect': typeof RedirectIndexRoute
@@ -313,13 +329,15 @@ export interface FileRoutesByTo {
313329
'/stream': typeof StreamRoute
314330
'/대한민국': typeof Char45824Char54620Char48124Char44397Route
315331
'/api/users': typeof ApiUsersRouteWithChildren
332+
'/multi-cookie-redirect/target': typeof MultiCookieRedirectTargetRoute
316333
'/not-found/via-beforeLoad': typeof NotFoundViaBeforeLoadRoute
317334
'/not-found/via-head': typeof NotFoundViaHeadRoute
318335
'/not-found/via-loader': typeof NotFoundViaLoaderRoute
319336
'/posts/$postId': typeof PostsPostIdRoute
320337
'/search-params/default': typeof SearchParamsDefaultRoute
321338
'/search-params/loader-throws-redirect': typeof SearchParamsLoaderThrowsRedirectRoute
322339
'/users/$userId': typeof UsersUserIdRoute
340+
'/multi-cookie-redirect': typeof MultiCookieRedirectIndexRoute
323341
'/not-found': typeof NotFoundIndexRoute
324342
'/posts': typeof PostsIndexRoute
325343
'/redirect': typeof RedirectIndexRoute
@@ -354,6 +372,7 @@ export interface FileRoutesById {
354372
'/대한민국': typeof Char45824Char54620Char48124Char44397Route
355373
'/_layout/_layout-2': typeof LayoutLayout2RouteWithChildren
356374
'/api/users': typeof ApiUsersRouteWithChildren
375+
'/multi-cookie-redirect/target': typeof MultiCookieRedirectTargetRoute
357376
'/not-found/via-beforeLoad': typeof NotFoundViaBeforeLoadRoute
358377
'/not-found/via-head': typeof NotFoundViaHeadRoute
359378
'/not-found/via-loader': typeof NotFoundViaLoaderRoute
@@ -362,6 +381,7 @@ export interface FileRoutesById {
362381
'/search-params/default': typeof SearchParamsDefaultRoute
363382
'/search-params/loader-throws-redirect': typeof SearchParamsLoaderThrowsRedirectRoute
364383
'/users/$userId': typeof UsersUserIdRoute
384+
'/multi-cookie-redirect/': typeof MultiCookieRedirectIndexRoute
365385
'/not-found/': typeof NotFoundIndexRoute
366386
'/posts/': typeof PostsIndexRoute
367387
'/redirect/': typeof RedirectIndexRoute
@@ -397,6 +417,7 @@ export interface FileRouteTypes {
397417
| '/users'
398418
| '/대한민국'
399419
| '/api/users'
420+
| '/multi-cookie-redirect/target'
400421
| '/not-found/via-beforeLoad'
401422
| '/not-found/via-head'
402423
| '/not-found/via-loader'
@@ -405,6 +426,7 @@ export interface FileRouteTypes {
405426
| '/search-params/default'
406427
| '/search-params/loader-throws-redirect'
407428
| '/users/$userId'
429+
| '/multi-cookie-redirect'
408430
| '/not-found/'
409431
| '/posts/'
410432
| '/redirect'
@@ -433,13 +455,15 @@ export interface FileRouteTypes {
433455
| '/stream'
434456
| '/대한민국'
435457
| '/api/users'
458+
| '/multi-cookie-redirect/target'
436459
| '/not-found/via-beforeLoad'
437460
| '/not-found/via-head'
438461
| '/not-found/via-loader'
439462
| '/posts/$postId'
440463
| '/search-params/default'
441464
| '/search-params/loader-throws-redirect'
442465
| '/users/$userId'
466+
| '/multi-cookie-redirect'
443467
| '/not-found'
444468
| '/posts'
445469
| '/redirect'
@@ -473,6 +497,7 @@ export interface FileRouteTypes {
473497
| '/대한민국'
474498
| '/_layout/_layout-2'
475499
| '/api/users'
500+
| '/multi-cookie-redirect/target'
476501
| '/not-found/via-beforeLoad'
477502
| '/not-found/via-head'
478503
| '/not-found/via-loader'
@@ -481,6 +506,7 @@ export interface FileRouteTypes {
481506
| '/search-params/default'
482507
| '/search-params/loader-throws-redirect'
483508
| '/users/$userId'
509+
| '/multi-cookie-redirect/'
484510
| '/not-found/'
485511
| '/posts/'
486512
| '/redirect/'
@@ -516,7 +542,9 @@ export interface RootRouteChildren {
516542
UsersRoute: typeof UsersRouteWithChildren
517543
Char45824Char54620Char48124Char44397Route: typeof Char45824Char54620Char48124Char44397Route
518544
ApiUsersRoute: typeof ApiUsersRouteWithChildren
545+
MultiCookieRedirectTargetRoute: typeof MultiCookieRedirectTargetRoute
519546
RedirectTargetRoute: typeof RedirectTargetRouteWithChildren
547+
MultiCookieRedirectIndexRoute: typeof MultiCookieRedirectIndexRoute
520548
RedirectIndexRoute: typeof RedirectIndexRoute
521549
PostsPostIdDeepRoute: typeof PostsPostIdDeepRoute
522550
FooBarQuxRoute: typeof FooBarQuxRouteWithChildren
@@ -643,6 +671,13 @@ declare module '@tanstack/react-router' {
643671
preLoaderRoute: typeof NotFoundIndexRouteImport
644672
parentRoute: typeof NotFoundRouteRoute
645673
}
674+
'/multi-cookie-redirect/': {
675+
id: '/multi-cookie-redirect/'
676+
path: '/multi-cookie-redirect'
677+
fullPath: '/multi-cookie-redirect'
678+
preLoaderRoute: typeof MultiCookieRedirectIndexRouteImport
679+
parentRoute: typeof rootRouteImport
680+
}
646681
'/users/$userId': {
647682
id: '/users/$userId'
648683
path: '/$userId'
@@ -699,6 +734,13 @@ declare module '@tanstack/react-router' {
699734
preLoaderRoute: typeof NotFoundViaBeforeLoadRouteImport
700735
parentRoute: typeof NotFoundRouteRoute
701736
}
737+
'/multi-cookie-redirect/target': {
738+
id: '/multi-cookie-redirect/target'
739+
path: '/multi-cookie-redirect/target'
740+
fullPath: '/multi-cookie-redirect/target'
741+
preLoaderRoute: typeof MultiCookieRedirectTargetRouteImport
742+
parentRoute: typeof rootRouteImport
743+
}
702744
'/api/users': {
703745
id: '/api/users'
704746
path: '/api/users'
@@ -973,7 +1015,9 @@ const rootRouteChildren: RootRouteChildren = {
9731015
Char45824Char54620Char48124Char44397Route:
9741016
Char45824Char54620Char48124Char44397Route,
9751017
ApiUsersRoute: ApiUsersRouteWithChildren,
1018+
MultiCookieRedirectTargetRoute: MultiCookieRedirectTargetRoute,
9761019
RedirectTargetRoute: RedirectTargetRouteWithChildren,
1020+
MultiCookieRedirectIndexRoute: MultiCookieRedirectIndexRoute,
9771021
RedirectIndexRoute: RedirectIndexRoute,
9781022
PostsPostIdDeepRoute: PostsPostIdDeepRoute,
9791023
FooBarQuxRoute: FooBarQuxRouteWithChildren,
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
import { createFileRoute, redirect } from '@tanstack/react-router'
2+
import { createServerFn } from '@tanstack/react-start'
3+
import { setCookie } from '@tanstack/react-start/server'
4+
5+
const setMultipleCookiesAndRedirect = createServerFn().handler(() => {
6+
// Set multiple cookies before redirecting
7+
// This tests that multiple Set-Cookie headers are preserved during redirect
8+
setCookie('session', 'session-value', { path: '/' })
9+
setCookie('csrf', 'csrf-token-value', { path: '/' })
10+
setCookie('theme', 'dark', { path: '/' })
11+
12+
throw redirect({ to: '/multi-cookie-redirect/target' })
13+
})
14+
15+
export const Route = createFileRoute('/multi-cookie-redirect/')({
16+
loader: () => setMultipleCookiesAndRedirect(),
17+
component: () => null,
18+
})
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
import { createFileRoute } from '@tanstack/react-router'
2+
import Cookies from 'js-cookie'
3+
import React, { useEffect } from 'react'
4+
5+
export const Route = createFileRoute('/multi-cookie-redirect/target')({
6+
component: RouteComponent,
7+
})
8+
9+
function RouteComponent() {
10+
const [cookies, setCookies] = React.useState<Record<string, string>>({})
11+
12+
useEffect(() => {
13+
setCookies({
14+
session: Cookies.get('session') || '',
15+
csrf: Cookies.get('csrf') || '',
16+
theme: Cookies.get('theme') || '',
17+
})
18+
}, [])
19+
20+
return (
21+
<div>
22+
<h1 data-testid="multi-cookie-redirect-target">
23+
Multi Cookie Redirect Target
24+
</h1>
25+
<div>
26+
<p>
27+
Session cookie:{' '}
28+
<span data-testid="cookie-session">{cookies.session}</span>
29+
</p>
30+
<p>
31+
CSRF cookie: <span data-testid="cookie-csrf">{cookies.csrf}</span>
32+
</p>
33+
<p>
34+
Theme cookie: <span data-testid="cookie-theme">{cookies.theme}</span>
35+
</p>
36+
</div>
37+
</div>
38+
)
39+
}

e2e/react-start/basic/tests/redirect.spec.ts

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -228,4 +228,23 @@ test.describe('redirects', () => {
228228
})
229229
})
230230
})
231+
232+
test('multiple Set-Cookie headers are preserved on redirect', async ({
233+
page,
234+
}) => {
235+
// This test verifies that multiple Set-Cookie headers are not lost during redirect
236+
await page.goto('/multi-cookie-redirect')
237+
238+
// Wait for redirect to complete
239+
await page.waitForURL(/\/multi-cookie-redirect\/target/)
240+
241+
// Should redirect to target page
242+
await expect(page.getByTestId('multi-cookie-redirect-target')).toBeVisible()
243+
expect(page.url()).toContain('/multi-cookie-redirect/target')
244+
245+
// Verify all three cookies were preserved during the redirect
246+
await expect(page.getByTestId('cookie-session')).toHaveText('session-value')
247+
await expect(page.getByTestId('cookie-csrf')).toHaveText('csrf-token-value')
248+
await expect(page.getByTestId('cookie-theme')).toHaveText('dark')
249+
})
231250
})

e2e/solid-start/basic/package.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
"@tanstack/solid-start": "workspace:^",
2727
"express": "^5.1.0",
2828
"http-proxy-middleware": "^3.0.5",
29+
"js-cookie": "^3.0.5",
2930
"redaxios": "^0.5.1",
3031
"solid-js": "^1.9.10",
3132
"tailwind-merge": "^2.6.0",
@@ -36,6 +37,7 @@
3637
"@playwright/test": "^1.50.1",
3738
"@tailwindcss/postcss": "^4.1.15",
3839
"@tanstack/router-e2e-utils": "workspace:^",
40+
"@types/js-cookie": "^3.0.6",
3941
"@types/node": "^22.10.2",
4042
"combinate": "^1.1.11",
4143
"postcss": "^8.5.1",

0 commit comments

Comments
 (0)