Skip to content

Commit 62096ee

Browse files
authored
test(tanstackstart): Add TanStack Start on Cloudflare Workers test app (#20649)
Adds an e2e test application for TanStack Start deployed to Cloudflare Workers using `@sentry/cloudflare`. The setup uses a custom server entry point that wraps the TanStack Start handler with `Sentry.withSentry`. Unfortunately the type safety is not great yet with `Sentry.withSentry` and TanStack Start, so this needs to be adapted in another PR (or in v11 if it is not yet possible). Closes #18669
1 parent 51bb32c commit 62096ee

19 files changed

Lines changed: 562 additions & 0 deletions

File tree

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
node_modules
2+
dist
3+
.output
4+
.wrangler
5+
.tanstack
6+
src/routeTree.gen.ts
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
{
2+
"name": "tanstackstart-react-cloudflare",
3+
"private": true,
4+
"version": "0.0.1",
5+
"type": "module",
6+
"scripts": {
7+
"dev": "vite dev",
8+
"build": "vite build",
9+
"preview": "wrangler dev --var E2E_TEST_DSN:$E2E_TEST_DSN --log-level=$(test $CI && echo 'none' || echo 'log')",
10+
"test": "playwright test",
11+
"typecheck": "tsc --noEmit",
12+
"test:build": "pnpm install && pnpm build",
13+
"test:assert": "pnpm typecheck && pnpm test"
14+
},
15+
"dependencies": {
16+
"@sentry/browser": "file:../../packed/sentry-browser-packed.tgz",
17+
"@sentry/cloudflare": "file:../../packed/sentry-cloudflare-packed.tgz",
18+
"@sentry/tanstackstart-react": "file:../../packed/sentry-tanstackstart-react-packed.tgz",
19+
"@tanstack/react-start": "^1.136.0",
20+
"@tanstack/react-router": "^1.136.0",
21+
"react": "^19.2.0",
22+
"react-dom": "^19.2.0"
23+
},
24+
"devDependencies": {
25+
"@cloudflare/vite-plugin": "^1.35.0",
26+
"@cloudflare/workers-types": "^4.20260504.0",
27+
"@playwright/test": "~1.56.0",
28+
"@sentry-internal/test-utils": "link:../../../test-utils",
29+
"@types/react": "^19.2.0",
30+
"@types/react-dom": "^19.2.0",
31+
"@vitejs/plugin-react": "^4.5.0",
32+
"typescript": "^5.9.0",
33+
"vite": "7.3.1",
34+
"vite-tsconfig-paths": "^5.1.4",
35+
"wrangler": "^4.68.1"
36+
},
37+
"volta": {
38+
"node": "24.15.0",
39+
"extends": "../../package.json"
40+
},
41+
"sentryTest": {
42+
"optional": true
43+
}
44+
}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
import { getPlaywrightConfig } from '@sentry-internal/test-utils';
2+
3+
export default getPlaywrightConfig({
4+
startCommand: 'pnpm preview',
5+
port: 8787,
6+
});
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
interface Env {
2+
E2E_TEST_DSN: string;
3+
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
import * as Sentry from '@sentry/browser';
2+
import { createRouter } from '@tanstack/react-router';
3+
import { routeTree } from './routeTree.gen';
4+
5+
export const getRouter = () => {
6+
const router = createRouter({
7+
routeTree,
8+
scrollRestoration: true,
9+
});
10+
11+
if (!router.isServer) {
12+
Sentry.init({
13+
environment: 'qa',
14+
dsn: 'https://public@dsn.ingest.sentry.io/1337',
15+
integrations: [Sentry.browserTracingIntegration()],
16+
tracesSampleRate: 1.0,
17+
release: 'e2e-test',
18+
tunnel: 'http://localhost:3031/',
19+
});
20+
}
21+
22+
return router;
23+
};
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
import type { ReactNode } from 'react';
2+
import { Outlet, createRootRoute, HeadContent, Scripts } from '@tanstack/react-router';
3+
import { getTraceData } from '@sentry/tanstackstart-react';
4+
5+
export const Route = createRootRoute({
6+
head: () => {
7+
const traceData = getTraceData();
8+
const sentryMeta = Object.entries(traceData).map(([key, value]) => ({
9+
name: key,
10+
content: value,
11+
}));
12+
13+
return {
14+
meta: [
15+
{
16+
charSet: 'utf-8',
17+
},
18+
{
19+
name: 'viewport',
20+
content: 'width=device-width, initial-scale=1',
21+
},
22+
{
23+
title: 'TanStack Start Cloudflare E2E Test',
24+
},
25+
...sentryMeta,
26+
],
27+
};
28+
},
29+
component: RootComponent,
30+
});
31+
32+
function RootComponent() {
33+
return (
34+
<RootDocument>
35+
<Outlet />
36+
</RootDocument>
37+
);
38+
}
39+
40+
function RootDocument({ children }: Readonly<{ children: ReactNode }>) {
41+
return (
42+
<html>
43+
<head>
44+
<HeadContent />
45+
</head>
46+
<body>
47+
{children}
48+
<Scripts />
49+
</body>
50+
</html>
51+
);
52+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
import { createFileRoute } from '@tanstack/react-router';
2+
3+
export const Route = createFileRoute('/api/error')({
4+
server: {
5+
handlers: {
6+
GET: async () => {
7+
throw new Error('Sentry API Route Test Error');
8+
},
9+
},
10+
},
11+
});
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import { createFileRoute } from '@tanstack/react-router';
2+
import { flush } from '@sentry/cloudflare';
3+
4+
export const Route = createFileRoute('/api/flush')({
5+
server: {
6+
handlers: {
7+
GET: async () => {
8+
await flush();
9+
return new Response('ok');
10+
},
11+
},
12+
},
13+
});
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
import { createFileRoute } from '@tanstack/react-router';
2+
import { createServerFn } from '@tanstack/react-start';
3+
4+
const throwServerError = createServerFn().handler(async () => {
5+
throw new Error('Sentry Server Function Test Error');
6+
});
7+
8+
export const Route = createFileRoute('/')({
9+
component: Home,
10+
});
11+
12+
function Home() {
13+
return (
14+
<div>
15+
<h1>TanStack Start Cloudflare E2E Test</h1>
16+
<button
17+
id="client-error-btn"
18+
type="button"
19+
onClick={() => {
20+
throw new Error('Sentry Client Test Error');
21+
}}
22+
>
23+
Break the client
24+
</button>
25+
<button
26+
id="throw-server-fn-btn"
27+
type="button"
28+
onClick={async () => {
29+
await throwServerError();
30+
}}
31+
>
32+
Break server function
33+
</button>
34+
<button
35+
id="api-error-btn"
36+
type="button"
37+
onClick={async () => {
38+
await fetch('/api/error');
39+
}}
40+
>
41+
Break API route
42+
</button>
43+
</div>
44+
);
45+
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
import { createFileRoute } from '@tanstack/react-router';
2+
3+
export const Route = createFileRoute('/ssr-error')({
4+
loader: () => {
5+
throw new Error('Sentry SSR Test Error');
6+
},
7+
component: () => <div>SSR Error Page</div>,
8+
});

0 commit comments

Comments
 (0)