8000 test(react-router): Add e2e tests for react router framework SPA mode… · getsentry/sentry-javascript@e6a8594 · GitHub
[go: up one dir, main page]

Skip to content

Commit e6a8594

Browse files
authored
test(react-router): Add e2e tests for react router framework SPA mode (#16390)
Adding e2e tests for SPA mode for react router framework. It was confirmed that navigation and client pageload instrumentations work. closes getsentry/sentry-docs#13856
1 parent 6e61f82 commit e6a8594

25 files changed

+2995
-0
lines changed
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2+
3+
# dependencies
4+
/node_modules
5+
/.pnp
6+
.pnp.js
7+
8+
# testing
9+
/coverage
10+
11+
# production
12+
/build
13+
14+
# misc
15+
.DS_Store
16+
.env.local
17+
.env.development.local
18+
.env.test.local
19+
.env.production.local
20+
21+
npm-debug.log*
22+
yarn-debug.log*
23+
yarn-error.log*
24+
25+
/test-results/
26+
/playwright-report/
27+
/playwright/.cache/
28+
29+
!*.d.ts
30+
31+
# react router
32+
.react-router
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
@sentry:registry=http://127.0.0.1:4873
2+
@sentry-internal:registry=http://127.0.0.1:4873
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
@import 'tailwindcss';
2+
3+
@theme {
4+
--font-sans: 'Inter', ui-sans-serif, system-ui, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol',
5+
'Noto Color Emoji';
6+
}
7+
8+
html,
9+
body {
10+
@apply bg-white dark:bg-gray-950;
11+
12+
@media (prefers-color-scheme: dark) {
13+
color-scheme: dark;
14+
}
15+
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
import * as Sentry from '@sentry/react-router';
2+
import { StrictMode, startTransition } from 'react';
3+
import { hydrateRoot } from 'react-dom/client';
4+
import { HydratedRouter } from 'react-router/dom';
5+
6+
Sentry.init({
7+
environment: 'qa', // dynamic sampling bias to keep transactions
8+
// todo: get this from env
9+
dsn: 'https://username@domain/123',
10+
integrations: [Sentry.reactRouterTracingIntegration()],
11+
tracesSampleRate: 1.0,
12+
tunnel: `http://localhost:3031/`, // proxy server
13+
tracePropagationTargets: [/^\//],
14+
});
15+
16+
startTransition(() => {
17+
hydrateRoot(
18+
document,
19+
<StrictMode>
20+
<HydratedRouter />
21+
</StrictMode>,
22+
);
23+
});
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
import {
2+
isRouteErrorResponse,
3+
Links,
4+
Meta,
5+
Outlet,
6+
Scripts,
7+
ScrollRestoration,
8+
} from "react-router";
9+
10+
import type { Route } from "./+types/root";
11+
import "./app.css";
12+
import * as Sentry from '@sentry/react-router';
13+
14+
export function Layout({ children }: { children: React.ReactNode }) {
15+
return (
16+
<html lang="en">
17+
<head>
18+
<meta charSet="utf-8" />
19+
<meta name="viewport" content="width=device-width, initial-scale=1" />
20+
<Meta />
21+
<Links />
22+
</head>
23+
<body>
24+
{children}
25+
<ScrollRestoration />
26+
<Scripts />
27+
</body>
28+
</html>
29+
);
30+
}
31+
32+
export default function App() {
33+
return <Outlet />;
34+
}
35+
36+
export function ErrorBoundary({ error }: Route.ErrorBoundaryProps) {
37+
let message = "Oops!";
38+
let details = "An unexpected error occurred.";
39+
let stack: string | undefined;
40+
41+
if (isRouteErrorResponse(error)) {
42+
message = error.status === 404 ? "404" : "Error";
43+
details =
44+
error.status === 404
45+
? "The requested page could not be found."
46+
: error.statusText || details;
47+
} else if (error && error instanceof Error) {
48+
Sentry.captureException(error);
49+
50+
details = error.message;
51+
stack = error.stack;
52+
}
53+
54+
return (
55+
<main className="pt-16 p-4 container mx-auto">
56+
<h1>{message}</h1>
57+
<p>{details}</p>
58+
{stack && (
59+
<pre className="w-full p-4 overflow-x-auto">
60+
<code>{stack}</code>
61+
</pre>
62+
)}
63+
</main>
64+
);
65+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
import { type RouteConfig, index, prefix, route } from '@react-router/dev/routes';
2+
3+
export default [
4+
index('routes/home.tsx'),
5+
...prefix('errors', [
6+
route('client', 'routes/errors/client.tsx'),
7+
route('client/:client-param', 'routes/errors/client-param.tsx'),
8+
route('client-loader', 'routes/errors/client-loader.tsx'),
9+
route('client-action', 'routes/errors/client-action.tsx'),
10+
]),
11+
...prefix('performance', [
12+
index('routes/performance/index.tsx'),
13+
route('with/:param', 'routes/performance/dynamic-param.tsx'),
14+
]),
15+
] satisfies RouteConfig;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
import { Form } from 'react-router';
2+
3+
export function clientAction() {
4+
throw new Error('Madonna mia! Che casino nella Client Action!');
5+
}
6+
7+
export default function ClientActionErrorPage() {
8+
return (
9+
<div>
10+
<h1>Client Error Action Page</h1>
11+
<Form method="post">
12+
<button id="submit" type="submit">
13+
Submit
14+
</button>
15+
</Form>
16+
</div>
17+
);
18+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
import type { Route } from './+types/client-loader';
2+
3+
export function clientLoader() {
4+
throw new Error('¡Madre mía del client loader!');
5+
}
6+
7+
export default function ClientLoaderErrorPage({ loaderData }: Route.ComponentProps) {
8+
const { data } = loaderData ?? { data: 'sad' };
9+
return (
10+
<div>
11+
<h1>Client Loader Error Page</h1>
12+
<div>{data}</div>
13+
</div>
14+
);
15+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
import type { Route } from './+types/client-param';
2+
3+
export default function ClientErrorParamPage({ params }: Route.ComponentProps) {
4+
return (
5+
<div>
6+
<h1>Client Error Param Page</h1>
7+
<button
8+
id="throw-on-click"
9+
onClick={() => {
10+
throw new Error(`¡Madre mía de ${params['client-param']}!`);
11+
}}
12+
>
13+
Throw Error
14+
</button>
15+
</div>
16+
);
17+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
export default function ClientErrorPage() {
2+
return (
3+
<div>
4+
<h1>Client Error Page</h1>
5+
<button
6+
id="throw-on-click"
7+
onClick={() => {
8+
throw new Error('¡Madre mía!');
9+
}}
10+
>
11+
Throw Error
12+
</button>
13+
</div>
14+
);
15+
}

0 commit comments

Comments
 (0)
0