Skip to content

Commit 648b133

Browse files
schiller-manuelautofix-ci[bot]
andauthoredFeb 1, 2025··
tests: stabilize e2e tests to not be dependent on external host (#3302)
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
1 parent c1d9751 commit 648b133

File tree

15 files changed

+133
-36
lines changed

15 files changed

+133
-36
lines changed
 

Diff for: ‎e2e/e2e-utils/src/index.ts

+1
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
11
export { derivePort } from './derivePort'
2+
export { localDummyServer } from './localDummyServer'

Diff for: ‎e2e/e2e-utils/src/localDummyServer.ts

+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
import http from 'node:http'
2+
3+
// some tests redirect to an external host
4+
// however, in CI this is unstable due to network conditions
5+
// so here we spawn a local server to simulate the external host
6+
7+
export async function localDummyServer(port: number) {
8+
const server = http.createServer((req, res) => {
9+
res.writeHead(200, { 'Content-Type': 'text/html' })
10+
res.end('Hello World')
11+
})
12+
const promise = new Promise<void>((resolve) => {
13+
server.listen(port, 'localhost', () => {
14+
console.log(`local dummy server running at http://localhost:${port}`)
15+
resolve()
16+
})
17+
})
18+
await promise
19+
20+
return server
21+
}

Diff for: ‎e2e/react-router/basic-file-based/src/routes/redirect/$target.tsx

+5-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { createFileRoute } from '@tanstack/react-router'
1+
import { createFileRoute, retainSearchParams } from '@tanstack/react-router'
22
import z from 'zod'
33

44
export const Route = createFileRoute('/redirect/$target')({
@@ -13,5 +13,9 @@ export const Route = createFileRoute('/redirect/$target')({
1313
validateSearch: z.object({
1414
reloadDocument: z.boolean().optional(),
1515
preload: z.literal(false).optional(),
16+
externalHost: z.string().optional(),
1617
}),
18+
search: {
19+
middlewares: [retainSearchParams(['externalHost'])],
20+
},
1721
})

Diff for: ‎e2e/react-router/basic-file-based/src/routes/redirect/$target/via-beforeLoad.tsx

+6-2
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,16 @@
11
import { createFileRoute, redirect } from '@tanstack/react-router'
22

33
export const Route = createFileRoute('/redirect/$target/via-beforeLoad')({
4-
beforeLoad: ({ params: { target }, search: { reloadDocument } }) => {
4+
beforeLoad: ({
5+
params: { target },
6+
search: { reloadDocument, externalHost },
7+
}) => {
58
switch (target) {
69
case 'internal':
710
throw redirect({ to: '/posts', reloadDocument })
811
case 'external':
9-
throw redirect({ href: 'http://example.com' })
12+
const href = externalHost ?? 'http://example.com'
13+
throw redirect({ href })
1014
}
1115
},
1216
component: () => <div>{Route.fullPath}</div>,

Diff for: ‎e2e/react-router/basic-file-based/src/routes/redirect/$target/via-loader.tsx

+7-3
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,17 @@
11
import { createFileRoute, redirect } from '@tanstack/react-router'
22

33
export const Route = createFileRoute('/redirect/$target/via-loader')({
4-
loaderDeps: ({ search: { reloadDocument } }) => ({ reloadDocument }),
5-
loader: ({ params: { target }, deps: { reloadDocument } }) => {
4+
loaderDeps: ({ search: { reloadDocument, externalHost } }) => ({
5+
reloadDocument,
6+
externalHost,
7+
}),
8+
loader: ({ params: { target }, deps: { externalHost, reloadDocument } }) => {
69
switch (target) {
710
case 'internal':
811
throw redirect({ to: '/posts', reloadDocument })
912
case 'external':
10-
throw redirect({ href: 'http://example.com' })
13+
const href = externalHost ?? 'http://example.com'
14+
throw redirect({ href })
1115
}
1216
},
1317
component: () => <div>{Route.fullPath}</div>,

Diff for: ‎e2e/react-router/basic-file-based/tests/redirect.spec.ts

+18-4
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,25 @@
11
import { expect, test } from '@playwright/test'
22
import combinateImport from 'combinate'
3-
import { derivePort } from '@tanstack/router-e2e-utils'
3+
import { derivePort, localDummyServer } from '@tanstack/router-e2e-utils'
44
import packageJson from '../package.json' with { type: 'json' }
5+
import { Server } from 'node:http'
6+
import queryString from 'node:querystring'
57

68
// somehow playwright does not correctly import default exports
79
const combinate = (combinateImport as any).default as typeof combinateImport
810

911
const PORT = derivePort(packageJson.name)
12+
const EXTERNAL_HOST_PORT = derivePort(`${packageJson.name}-external`)
1013

1114
test.describe('redirects', () => {
15+
let server: Server
16+
test.beforeAll(async () => {
17+
server = await localDummyServer(EXTERNAL_HOST_PORT)
18+
})
19+
test.afterAll(async () => {
20+
server.close()
21+
})
22+
1223
const internalNavigationTestMatrix = combinate({
1324
thrower: ['beforeLoad', 'loader'] as const,
1425
reloadDocument: [false, true] as const,
@@ -98,14 +109,17 @@ test.describe('redirects', () => {
98109
}) => {
99110
await page.waitForLoadState('networkidle')
100111

112+
let q = queryString.stringify({
113+
externalHost: `http://localhost:${EXTERNAL_HOST_PORT}/`,
114+
})
101115
if (scenario === 'navigate') {
102-
await page.goto(`/redirect/external`)
116+
await page.goto(`/redirect/external?${q}`)
103117
await page.getByTestId(`via-${thrower}`).click()
104118
} else {
105-
await page.goto(`/redirect/external/via-${thrower}`)
119+
await page.goto(`/redirect/external/via-${thrower}?${q}`)
106120
}
107121

108-
const url = 'http://example.com/'
122+
const url = `http://localhost:${EXTERNAL_HOST_PORT}/`
109123

110124
await page.waitForURL(url)
111125
expect(page.url()).toBe(url)

Diff for: ‎e2e/start/basic/app/components/RedirectOnClick.tsx

+5-1
Original file line numberDiff line numberDiff line change
@@ -4,17 +4,21 @@ import { throwRedirect } from './throwRedirect'
44
interface RedirectOnClickProps {
55
target: 'internal' | 'external'
66
reloadDocument?: boolean
7+
externalHost?: string
78
}
89

910
export function RedirectOnClick({
1011
target,
1112
reloadDocument,
13+
externalHost,
1214
}: RedirectOnClickProps) {
1315
const execute = useServerFn(throwRedirect)
1416
return (
1517
<button
1618
data-testid="redirect-on-click"
17-
onClick={() => execute({ data: { target, reloadDocument } })}
19+
onClick={() =>
20+
execute({ data: { target, reloadDocument, externalHost } })
21+
}
1822
>
1923
click me
2024
</button>

Diff for: ‎e2e/start/basic/app/components/throwRedirect.ts

+7-3
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,18 @@ import { createServerFn } from '@tanstack/start'
33

44
export const throwRedirect = createServerFn()
55
.validator(
6-
(opts: { target: 'internal' | 'external'; reloadDocument?: boolean }) =>
7-
opts,
6+
(opts: {
7+
target: 'internal' | 'external'
8+
reloadDocument?: boolean
9+
externalHost?: string
10+
}) => opts,
811
)
912
.handler((ctx) => {
1013
if (ctx.data.target === 'internal') {
1114
throw redirect({ to: '/posts', reloadDocument: ctx.data.reloadDocument })
1215
}
16+
const href = ctx.data.externalHost ?? 'http://example.com'
1317
throw redirect({
14-
href: 'http://example.com',
18+
href,
1519
})
1620
})

Diff for: ‎e2e/start/basic/app/routes/redirect/$target.tsx

+5-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { createFileRoute } from '@tanstack/react-router'
1+
import { createFileRoute, retainSearchParams } from '@tanstack/react-router'
22
import z from 'zod'
33

44
export const Route = createFileRoute('/redirect/$target')({
@@ -13,5 +13,9 @@ export const Route = createFileRoute('/redirect/$target')({
1313
validateSearch: z.object({
1414
reloadDocument: z.boolean().optional(),
1515
preload: z.literal(false).optional(),
16+
externalHost: z.string().optional(),
1617
}),
18+
search: {
19+
middlewares: [retainSearchParams(['externalHost'])],
20+
},
1721
})

Diff for: ‎e2e/start/basic/app/routes/redirect/$target/serverFn/via-beforeLoad.tsx

+4-2
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,9 @@ import { throwRedirect } from '~/components/throwRedirect'
44
export const Route = createFileRoute(
55
'/redirect/$target/serverFn/via-beforeLoad',
66
)({
7-
beforeLoad: ({ params: { target }, search: { reloadDocument } }) =>
8-
throwRedirect({ data: { target, reloadDocument } }),
7+
beforeLoad: ({
8+
params: { target },
9+
search: { reloadDocument, externalHost },
10+
}) => throwRedirect({ data: { target, reloadDocument, externalHost } }),
911
component: () => <div>{Route.fullPath}</div>,
1012
})

Diff for: ‎e2e/start/basic/app/routes/redirect/$target/serverFn/via-loader.tsx

+6-3
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,11 @@ import { createFileRoute } from '@tanstack/react-router'
22
import { throwRedirect } from '~/components/throwRedirect'
33

44
export const Route = createFileRoute('/redirect/$target/serverFn/via-loader')({
5-
loaderDeps: ({ search: { reloadDocument } }) => ({ reloadDocument }),
6-
loader: ({ params: { target }, deps: { reloadDocument } }) =>
7-
throwRedirect({ data: { target, reloadDocument } }),
5+
loaderDeps: ({ search: { reloadDocument, externalHost } }) => ({
6+
reloadDocument,
7+
externalHost,
8+
}),
9+
loader: ({ params: { target }, deps: { reloadDocument, externalHost } }) =>
10+
throwRedirect({ data: { target, reloadDocument, externalHost } }),
811
component: () => <div>{Route.fullPath}</div>,
912
})

Diff for: ‎e2e/start/basic/app/routes/redirect/$target/serverFn/via-useServerFn.tsx

+8-2
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,13 @@ export const Route = createFileRoute(
66
)({
77
component: () => {
88
const { target } = Route.useParams()
9-
const { reloadDocument } = Route.useSearch()
10-
return <RedirectOnClick target={target} reloadDocument={reloadDocument} />
9+
const { reloadDocument, externalHost } = Route.useSearch()
10+
return (
11+
<RedirectOnClick
12+
target={target}
13+
reloadDocument={reloadDocument}
14+
externalHost={externalHost}
15+
/>
16+
)
1117
},
1218
})

Diff for: ‎e2e/start/basic/app/routes/redirect/$target/via-beforeLoad.tsx

+6-2
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,16 @@
11
import { createFileRoute, redirect } from '@tanstack/react-router'
22

33
export const Route = createFileRoute('/redirect/$target/via-beforeLoad')({
4-
beforeLoad: ({ params: { target }, search: { reloadDocument } }) => {
4+
beforeLoad: ({
5+
params: { target },
6+
search: { reloadDocument, externalHost },
7+
}) => {
58
switch (target) {
69
case 'internal':
710
throw redirect({ to: '/posts', reloadDocument })
811
case 'external':
9-
throw redirect({ href: 'http://example.com' })
12+
const href = externalHost ?? 'http://example.com'
13+
throw redirect({ href })
1014
}
1115
},
1216
component: () => <div>{Route.fullPath}</div>,

Diff for: ‎e2e/start/basic/app/routes/redirect/$target/via-loader.tsx

+7-3
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,17 @@
11
import { createFileRoute, redirect } from '@tanstack/react-router'
22

33
export const Route = createFileRoute('/redirect/$target/via-loader')({
4-
loaderDeps: ({ search: { reloadDocument } }) => ({ reloadDocument }),
5-
loader: ({ params: { target }, deps: { reloadDocument } }) => {
4+
loaderDeps: ({ search: { reloadDocument, externalHost } }) => ({
5+
reloadDocument,
6+
externalHost,
7+
}),
8+
loader: ({ params: { target }, deps: { externalHost, reloadDocument } }) => {
69
switch (target) {
710
case 'internal':
811
throw redirect({ to: '/posts', reloadDocument })
912
case 'external':
10-
throw redirect({ href: 'http://example.com' })
13+
const href = externalHost ?? 'http://example.com'
14+
throw redirect({ href })
1115
}
1216
},
1317
component: () => <div>{Route.fullPath}</div>,

Diff for: ‎e2e/start/basic/tests/redirect.spec.ts

+27-9
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,26 @@
11
import { expect } from '@playwright/test'
22
import combinateImport from 'combinate'
3-
import { derivePort } from '@tanstack/router-e2e-utils'
3+
import { derivePort, localDummyServer } from '@tanstack/router-e2e-utils'
44
import packageJson from '../package.json' with { type: 'json' }
55
import { test } from './fixture'
6+
import { Server } from 'node:http'
7+
import queryString from 'node:querystring'
68

79
// somehow playwright does not correctly import default exports
810
const combinate = (combinateImport as any).default as typeof combinateImport
911

1012
const PORT = derivePort(packageJson.name)
13+
const EXTERNAL_HOST_PORT = derivePort(`${packageJson.name}-external`)
1114

1215
test.describe('redirects', () => {
16+
let server: Server
17+
test.beforeAll(async () => {
18+
server = await localDummyServer(EXTERNAL_HOST_PORT)
19+
})
20+
test.afterAll(async () => {
21+
server.close()
22+
})
23+
1324
const internalNavigationTestMatrix = combinate({
1425
thrower: ['beforeLoad', 'loader'] as const,
1526
reloadDocument: [false, true] as const,
@@ -93,17 +104,21 @@ test.describe('redirects', () => {
93104
test(`external target: scenario: ${scenario}, thrower: ${thrower}`, async ({
94105
page,
95106
}) => {
107+
let q = queryString.stringify({
108+
externalHost: `http://localhost:${EXTERNAL_HOST_PORT}/`,
109+
})
110+
96111
if (scenario === 'navigate') {
97-
await page.goto(`/redirect/external`)
112+
await page.goto(`/redirect/external?${q}`)
98113
await page.waitForLoadState('networkidle')
99114
const link = page.getByTestId(`via-${thrower}`)
100115
await link.focus()
101116
await link.click()
102117
} else {
103-
await page.goto(`/redirect/external/via-${thrower}`)
118+
await page.goto(`/redirect/external/via-${thrower}?${q}`)
104119
}
105120

106-
const url = 'http://example.com/'
121+
const url = `http://localhost:${EXTERNAL_HOST_PORT}/`
107122

108123
await page.waitForURL(url)
109124
expect(page.url()).toBe(url)
@@ -123,8 +138,13 @@ test.describe('redirects', () => {
123138
page,
124139
}) => {
125140
let fullPageLoad = false
141+
let q = queryString.stringify({
142+
externalHost: `http://localhost:${EXTERNAL_HOST_PORT}/`,
143+
reloadDocument,
144+
})
145+
126146
if (scenario === 'navigate') {
127-
await page.goto(`/redirect/${target}/serverFn`)
147+
await page.goto(`/redirect/${target}/serverFn?${q}`)
128148
await page.waitForLoadState('networkidle')
129149
const link = page.getByTestId(
130150
`via-${thrower}${reloadDocument ? '-reloadDocument' : ''}`,
@@ -135,15 +155,13 @@ test.describe('redirects', () => {
135155
await link.focus()
136156
await link.click()
137157
} else {
138-
await page.goto(
139-
`/redirect/${target}/serverFn/via-${thrower}${reloadDocument ? '?reloadDocument=true' : ''}`,
140-
)
158+
await page.goto(`/redirect/${target}/serverFn/via-${thrower}?${q}`)
141159
}
142160

143161
const url =
144162
target === 'internal'
145163
? `http://localhost:${PORT}/posts`
146-
: 'http://example.com/'
164+
: `http://localhost:${EXTERNAL_HOST_PORT}/`
147165
await page.waitForURL(url)
148166
expect(page.url()).toBe(url)
149167
if (target === 'internal' && scenario === 'navigate') {

0 commit comments

Comments
 (0)
Please sign in to comment.