From acf0082dbabf13fe391ba870c5428fabe9e73024 Mon Sep 17 00:00:00 2001
From: Onur Temizkan
Date: Thu, 16 Jan 2025 22:14:32 +0000
Subject: [PATCH 1/3] fix(react): Wait for lazy-loaded pages on navigation
---
.../.gitignore | 29 +++++++
.../.npmrc | 2 +
.../package.json | 40 ++++++++++
.../playwright.config.mjs | 7 ++
.../public/index.html | 24 ++++++
.../src/globals.d.ts | 5 ++
.../src/index.tsx | 71 +++++++++++++++++
.../src/pages/Index.tsx | 23 ++++++
.../src/pages/User.tsx | 8 ++
.../src/react-app-env.d.ts | 1 +
.../start-event-proxy.mjs | 6 ++
.../tests/errors.test.ts | 30 +++++++
.../tests/transactions.test.ts | 78 +++++++++++++++++++
.../tsconfig.json | 20 +++++
.../react/src/reactrouterv6-compat-utils.tsx | 19 +++--
15 files changed, 356 insertions(+), 7 deletions(-)
create mode 100644 dev-packages/e2e-tests/test-applications/react-create-browser-router-lazy-routes/.gitignore
create mode 100644 dev-packages/e2e-tests/test-applications/react-create-browser-router-lazy-routes/.npmrc
create mode 100644 dev-packages/e2e-tests/test-applications/react-create-browser-router-lazy-routes/package.json
create mode 100644 dev-packages/e2e-tests/test-applications/react-create-browser-router-lazy-routes/playwright.config.mjs
create mode 100644 dev-packages/e2e-tests/test-applications/react-create-browser-router-lazy-routes/public/index.html
create mode 100644 dev-packages/e2e-tests/test-applications/react-create-browser-router-lazy-routes/src/globals.d.ts
create mode 100644 dev-packages/e2e-tests/test-applications/react-create-browser-router-lazy-routes/src/index.tsx
create mode 100644 dev-packages/e2e-tests/test-applications/react-create-browser-router-lazy-routes/src/pages/Index.tsx
create mode 100644 dev-packages/e2e-tests/test-applications/react-create-browser-router-lazy-routes/src/pages/User.tsx
create mode 100644 dev-packages/e2e-tests/test-applications/react-create-browser-router-lazy-routes/src/react-app-env.d.ts
create mode 100644 dev-packages/e2e-tests/test-applications/react-create-browser-router-lazy-routes/start-event-proxy.mjs
create mode 100644 dev-packages/e2e-tests/test-applications/react-create-browser-router-lazy-routes/tests/errors.test.ts
create mode 100644 dev-packages/e2e-tests/test-applications/react-create-browser-router-lazy-routes/tests/transactions.test.ts
create mode 100644 dev-packages/e2e-tests/test-applications/react-create-browser-router-lazy-routes/tsconfig.json
diff --git a/dev-packages/e2e-tests/test-applications/react-create-browser-router-lazy-routes/.gitignore b/dev-packages/e2e-tests/test-applications/react-create-browser-router-lazy-routes/.gitignore
new file mode 100644
index 000000000000..84634c973eeb
--- /dev/null
+++ b/dev-packages/e2e-tests/test-applications/react-create-browser-router-lazy-routes/.gitignore
@@ -0,0 +1,29 @@
+# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
+
+# dependencies
+/node_modules
+/.pnp
+.pnp.js
+
+# testing
+/coverage
+
+# production
+/build
+
+# misc
+.DS_Store
+.env.local
+.env.development.local
+.env.test.local
+.env.production.local
+
+npm-debug.log*
+yarn-debug.log*
+yarn-error.log*
+
+/test-results/
+/playwright-report/
+/playwright/.cache/
+
+!*.d.ts
diff --git a/dev-packages/e2e-tests/test-applications/react-create-browser-router-lazy-routes/.npmrc b/dev-packages/e2e-tests/test-applications/react-create-browser-router-lazy-routes/.npmrc
new file mode 100644
index 000000000000..070f80f05092
--- /dev/null
+++ b/dev-packages/e2e-tests/test-applications/react-create-browser-router-lazy-routes/.npmrc
@@ -0,0 +1,2 @@
+@sentry:registry=http://127.0.0.1:4873
+@sentry-internal:registry=http://127.0.0.1:4873
diff --git a/dev-packages/e2e-tests/test-applications/react-create-browser-router-lazy-routes/package.json b/dev-packages/e2e-tests/test-applications/react-create-browser-router-lazy-routes/package.json
new file mode 100644
index 000000000000..1e061d9bc267
--- /dev/null
+++ b/dev-packages/e2e-tests/test-applications/react-create-browser-router-lazy-routes/package.json
@@ -0,0 +1,40 @@
+{
+ "name": "react-create-browser-router-lazy-routes-test",
+ "version": "0.1.0",
+ "private": true,
+ "dependencies": {
+ "@sentry/react": "latest || *",
+ "@types/node": "^18.19.1",
+ "@types/react": "18.0.0",
+ "@types/react-dom": "18.0.0",
+ "react": "18.2.0",
+ "react-dom": "18.2.0",
+ "react-router-dom": "^6.4.1",
+ "react-scripts": "5.0.1",
+ "typescript": "~5.0.0"
+ },
+ "scripts": {
+ "build": "react-scripts build",
+ "start": "serve -s build",
+ "test": "playwright test",
+ "clean": "npx rimraf node_modules pnpm-lock.yaml",
+ "test:build": "pnpm install && pnpm build",
+ "test:build-canary": "pnpm install && pnpm add react@canary react-dom@canary && pnpm build",
+ "test:assert": "pnpm test"
+ },
+ "eslintConfig": {
+ "extends": ["react-app", "react-app/jest"]
+ },
+ "browserslist": {
+ "production": [">0.2%", "not dead", "not op_mini all"],
+ "development": ["last 1 chrome version", "last 1 firefox version", "last 1 safari version"]
+ },
+ "devDependencies": {
+ "@playwright/test": "^1.44.1",
+ "@sentry-internal/test-utils": "link:../../../test-utils",
+ "serve": "14.0.1"
+ },
+ "volta": {
+ "extends": "../../package.json"
+ }
+}
diff --git a/dev-packages/e2e-tests/test-applications/react-create-browser-router-lazy-routes/playwright.config.mjs b/dev-packages/e2e-tests/test-applications/react-create-browser-router-lazy-routes/playwright.config.mjs
new file mode 100644
index 000000000000..31f2b913b58b
--- /dev/null
+++ b/dev-packages/e2e-tests/test-applications/react-create-browser-router-lazy-routes/playwright.config.mjs
@@ -0,0 +1,7 @@
+import { getPlaywrightConfig } from '@sentry-internal/test-utils';
+
+const config = getPlaywrightConfig({
+ startCommand: `pnpm start`,
+});
+
+export default config;
diff --git a/dev-packages/e2e-tests/test-applications/react-create-browser-router-lazy-routes/public/index.html b/dev-packages/e2e-tests/test-applications/react-create-browser-router-lazy-routes/public/index.html
new file mode 100644
index 000000000000..39da76522bea
--- /dev/null
+++ b/dev-packages/e2e-tests/test-applications/react-create-browser-router-lazy-routes/public/index.html
@@ -0,0 +1,24 @@
+
+
+
+
+
+
+
+ React App
+
+
+
+
+
+
+
diff --git a/dev-packages/e2e-tests/test-applications/react-create-browser-router-lazy-routes/src/globals.d.ts b/dev-packages/e2e-tests/test-applications/react-create-browser-router-lazy-routes/src/globals.d.ts
new file mode 100644
index 000000000000..ffa61ca49acc
--- /dev/null
+++ b/dev-packages/e2e-tests/test-applications/react-create-browser-router-lazy-routes/src/globals.d.ts
@@ -0,0 +1,5 @@
+interface Window {
+ recordedTransactions?: string[];
+ capturedExceptionId?: string;
+ sentryReplayId?: string;
+}
diff --git a/dev-packages/e2e-tests/test-applications/react-create-browser-router-lazy-routes/src/index.tsx b/dev-packages/e2e-tests/test-applications/react-create-browser-router-lazy-routes/src/index.tsx
new file mode 100644
index 000000000000..b89e00b43f51
--- /dev/null
+++ b/dev-packages/e2e-tests/test-applications/react-create-browser-router-lazy-routes/src/index.tsx
@@ -0,0 +1,71 @@
+import * as Sentry from '@sentry/react';
+import React, { Suspense, lazy } from 'react';
+import ReactDOM from 'react-dom/client';
+import {
+ RouterProvider,
+ createBrowserRouter,
+ createRoutesFromChildren,
+ matchRoutes,
+ useLocation,
+ useNavigationType,
+} from 'react-router-dom';
+import Index from './pages/Index';
+
+const replay = Sentry.replayIntegration();
+
+Sentry.init({
+ // environment: 'qa', // dynamic sampling bias to keep transactions
+ dsn: process.env.REACT_APP_E2E_TEST_DSN,
+ integrations: [
+ Sentry.reactRouterV6BrowserTracingIntegration({
+ useEffect: React.useEffect,
+ useLocation,
+ useNavigationType,
+ createRoutesFromChildren,
+ matchRoutes,
+ }),
+ replay,
+ ],
+ // We recommend adjusting this value in production, or using tracesSampler
+ // for finer control
+ tracesSampleRate: 1.0,
+ release: 'e2e-test',
+
+ tunnel: 'http://localhost:3031',
+
+ // Always capture replays, so we can test this properly
+ replaysSessionSampleRate: 1.0,
+ replaysOnErrorSampleRate: 0.0,
+
+ debug: !!process.env.DEBUG,
+});
+
+const sentryCreateBrowserRouter = Sentry.wrapCreateBrowserRouterV6(createBrowserRouter);
+
+const User = lazy(() => import('./pages/User'));
+
+const router = sentryCreateBrowserRouter(
+ [
+ {
+ path: '/',
+ element: ,
+ },
+ {
+ element: (
+ Loading...}>
+
+
+ ),
+ path: '/user/:id',
+ },
+ ],
+ {
+ // We're testing whether this option is avoided in the integration
+ // We expect this to be ignored
+ initialEntries: ['/user/1'],
+ },
+);
+
+const root = ReactDOM.createRoot(document.getElementById('root') as HTMLElement);
+
+root.render();
diff --git a/dev-packages/e2e-tests/test-applications/react-create-browser-router-lazy-routes/src/pages/Index.tsx b/dev-packages/e2e-tests/test-applications/react-create-browser-router-lazy-routes/src/pages/Index.tsx
new file mode 100644
index 000000000000..d6b71a1d1279
--- /dev/null
+++ b/dev-packages/e2e-tests/test-applications/react-create-browser-router-lazy-routes/src/pages/Index.tsx
@@ -0,0 +1,23 @@
+// biome-ignore lint/nursery/noUnusedImports: Need React import for JSX
+import * as React from 'react';
+import { Link } from 'react-router-dom';
+
+const Index = () => {
+ return (
+ <>
+ {
+ throw new Error('I am an error!');
+ }}
+ />
+
+ navigate
+
+ >
+ );
+};
+
+export default Index;
diff --git a/dev-packages/e2e-tests/test-applications/react-create-browser-router-lazy-routes/src/pages/User.tsx b/dev-packages/e2e-tests/test-applications/react-create-browser-router-lazy-routes/src/pages/User.tsx
new file mode 100644
index 000000000000..62f0c2d17533
--- /dev/null
+++ b/dev-packages/e2e-tests/test-applications/react-create-browser-router-lazy-routes/src/pages/User.tsx
@@ -0,0 +1,8 @@
+// biome-ignore lint/nursery/noUnusedImports: Need React import for JSX
+import * as React from 'react';
+
+const User = () => {
+ return I am a blank page :)
;
+};
+
+export default User;
diff --git a/dev-packages/e2e-tests/test-applications/react-create-browser-router-lazy-routes/src/react-app-env.d.ts b/dev-packages/e2e-tests/test-applications/react-create-browser-router-lazy-routes/src/react-app-env.d.ts
new file mode 100644
index 000000000000..6431bc5fc6b2
--- /dev/null
+++ b/dev-packages/e2e-tests/test-applications/react-create-browser-router-lazy-routes/src/react-app-env.d.ts
@@ -0,0 +1 @@
+///
diff --git a/dev-packages/e2e-tests/test-applications/react-create-browser-router-lazy-routes/start-event-proxy.mjs b/dev-packages/e2e-tests/test-applications/react-create-browser-router-lazy-routes/start-event-proxy.mjs
new file mode 100644
index 000000000000..be93e129284f
--- /dev/null
+++ b/dev-packages/e2e-tests/test-applications/react-create-browser-router-lazy-routes/start-event-proxy.mjs
@@ -0,0 +1,6 @@
+import { startEventProxyServer } from '@sentry-internal/test-utils';
+
+startEventProxyServer({
+ port: 3031,
+ proxyServerName: 'react-create-browser-router',
+});
diff --git a/dev-packages/e2e-tests/test-applications/react-create-browser-router-lazy-routes/tests/errors.test.ts b/dev-packages/e2e-tests/test-applications/react-create-browser-router-lazy-routes/tests/errors.test.ts
new file mode 100644
index 000000000000..4a11f07410ab
--- /dev/null
+++ b/dev-packages/e2e-tests/test-applications/react-create-browser-router-lazy-routes/tests/errors.test.ts
@@ -0,0 +1,30 @@
+import { expect, test } from '@playwright/test';
+import { waitForError } from '@sentry-internal/test-utils';
+
+test('Captures exception correctly', async ({ page }) => {
+ const errorEventPromise = waitForError('react-create-browser-router', event => {
+ return !event.type && event.exception?.values?.[0]?.value === 'I am an error!';
+ });
+
+ await page.goto('/');
+
+ const exceptionButton = page.locator('id=exception-button');
+ await exceptionButton.click();
+
+ const errorEvent = await errorEventPromise;
+
+ expect(errorEvent.exception?.values).toHaveLength(1);
+ expect(errorEvent.exception?.values?.[0]?.value).toBe('I am an error!');
+
+ expect(errorEvent.request).toEqual({
+ headers: expect.any(Object),
+ url: 'http://localhost:3030/',
+ });
+
+ expect(errorEvent.transaction).toEqual('/');
+
+ expect(errorEvent.contexts?.trace).toEqual({
+ trace_id: expect.stringMatching(/[a-f0-9]{32}/),
+ span_id: expect.stringMatching(/[a-f0-9]{16}/),
+ });
+});
diff --git a/dev-packages/e2e-tests/test-applications/react-create-browser-router-lazy-routes/tests/transactions.test.ts b/dev-packages/e2e-tests/test-applications/react-create-browser-router-lazy-routes/tests/transactions.test.ts
new file mode 100644
index 000000000000..5ecd098daf94
--- /dev/null
+++ b/dev-packages/e2e-tests/test-applications/react-create-browser-router-lazy-routes/tests/transactions.test.ts
@@ -0,0 +1,78 @@
+import { expect, test } from '@playwright/test';
+import { waitForTransaction } from '@sentry-internal/test-utils';
+
+test('Captures a pageload transaction', async ({ page }) => {
+ const transactionEventPromise = waitForTransaction('react-create-browser-router', event => {
+ return event.contexts?.trace?.op === 'pageload';
+ });
+
+ await page.goto('/');
+
+ const transactionEvent = await transactionEventPromise;
+
+ expect(transactionEvent).toEqual(
+ expect.objectContaining({
+ transaction: '/',
+ type: 'transaction',
+ transaction_info: {
+ source: 'route',
+ },
+ }),
+ );
+
+ expect(transactionEvent.contexts?.trace).toEqual(
+ expect.objectContaining({
+ data: expect.objectContaining({
+ deviceMemory: expect.any(String),
+ effectiveConnectionType: expect.any(String),
+ hardwareConcurrency: expect.any(String),
+ 'sentry.idle_span_finish_reason': 'idleTimeout',
+ 'sentry.op': 'pageload',
+ 'sentry.origin': 'auto.pageload.react.reactrouter_v6',
+ 'sentry.sample_rate': 1,
+ 'sentry.source': 'route',
+ }),
+ op: 'pageload',
+ span_id: expect.stringMatching(/[a-f0-9]{16}/),
+ trace_id: expect.stringMatching(/[a-f0-9]{32}/),
+ origin: 'auto.pageload.react.reactrouter_v6',
+ }),
+ );
+});
+
+test('Captures a navigation transaction', async ({ page }) => {
+ const transactionEventPromise = waitForTransaction('react-create-browser-router', event => {
+ return event.contexts?.trace?.op === 'navigation';
+ });
+
+ await page.goto('/');
+ const linkElement = page.locator('id=navigation');
+ await linkElement.click();
+
+ const transactionEvent = await transactionEventPromise;
+ expect(transactionEvent.contexts?.trace).toEqual({
+ data: expect.objectContaining({
+ 'sentry.idle_span_finish_reason': 'idleTimeout',
+ 'sentry.op': 'navigation',
+ 'sentry.origin': 'auto.navigation.react.reactrouter_v6',
+ 'sentry.sample_rate': 1,
+ 'sentry.source': 'route',
+ }),
+ op: 'navigation',
+ span_id: expect.stringMatching(/[a-f0-9]{16}/),
+ trace_id: expect.stringMatching(/[a-f0-9]{32}/),
+ origin: 'auto.navigation.react.reactrouter_v6',
+ });
+
+ expect(transactionEvent).toEqual(
+ expect.objectContaining({
+ transaction: '/user/:id',
+ type: 'transaction',
+ transaction_info: {
+ source: 'route',
+ },
+ }),
+ );
+
+ expect(transactionEvent.spans).toEqual([]);
+});
diff --git a/dev-packages/e2e-tests/test-applications/react-create-browser-router-lazy-routes/tsconfig.json b/dev-packages/e2e-tests/test-applications/react-create-browser-router-lazy-routes/tsconfig.json
new file mode 100644
index 000000000000..4cc95dc2689a
--- /dev/null
+++ b/dev-packages/e2e-tests/test-applications/react-create-browser-router-lazy-routes/tsconfig.json
@@ -0,0 +1,20 @@
+{
+ "compilerOptions": {
+ "target": "es2018",
+ "lib": ["dom", "dom.iterable", "esnext"],
+ "allowJs": true,
+ "skipLibCheck": true,
+ "esModuleInterop": true,
+ "allowSyntheticDefaultImports": true,
+ "strict": true,
+ "forceConsistentCasingInFileNames": true,
+ "noFallthroughCasesInSwitch": true,
+ "module": "esnext",
+ "moduleResolution": "node",
+ "resolveJsonModule": true,
+ "isolatedModules": true,
+ "noEmit": true,
+ "jsx": "react"
+ },
+ "include": ["src", "tests"]
+}
diff --git a/packages/react/src/reactrouterv6-compat-utils.tsx b/packages/react/src/reactrouterv6-compat-utils.tsx
index 9ca96a03b0de..2dc4bf29bd20 100644
--- a/packages/react/src/reactrouterv6-compat-utils.tsx
+++ b/packages/react/src/reactrouterv6-compat-utils.tsx
@@ -94,14 +94,19 @@ export function createV6CompatibleWrapCreateBrowserRouter<
}
router.subscribe((state: RouterState) => {
- const location = state.location;
if (state.historyAction === 'PUSH' || state.historyAction === 'POP') {
- handleNavigation({
- location,
- routes,
- navigationType: state.historyAction,
- version,
- basename,
+ // Use requestAnimationFrame to wait for Suspense boundaries to settle
+ requestAnimationFrame(() => {
+ // Small delay to allow for Suspense transitions
+ setTimeout(() => {
+ handleNavigation({
+ location: state.location,
+ routes,
+ navigationType: state.historyAction,
+ version,
+ basename,
+ });
+ }, 100);
});
}
});
From 1e023fb3b8d16b3df3e9e4255cfcafc91c6ccebb Mon Sep 17 00:00:00 2001
From: Onur Temizkan
Date: Fri, 17 Jan 2025 12:49:07 +0000
Subject: [PATCH 2/3] Conditionally wait for the render depending on the router
state.
---
.../tests/transactions.test.ts | 7 ++++++-
.../react/src/reactrouterv6-compat-utils.tsx | 19 +++++++++++++------
packages/react/src/types.ts | 11 ++++++++---
3 files changed, 27 insertions(+), 10 deletions(-)
diff --git a/dev-packages/e2e-tests/test-applications/react-create-browser-router-lazy-routes/tests/transactions.test.ts b/dev-packages/e2e-tests/test-applications/react-create-browser-router-lazy-routes/tests/transactions.test.ts
index 5ecd098daf94..5843d94abac8 100644
--- a/dev-packages/e2e-tests/test-applications/react-create-browser-router-lazy-routes/tests/transactions.test.ts
+++ b/dev-packages/e2e-tests/test-applications/react-create-browser-router-lazy-routes/tests/transactions.test.ts
@@ -74,5 +74,10 @@ test('Captures a navigation transaction', async ({ page }) => {
}),
);
- expect(transactionEvent.spans).toEqual([]);
+ expect(transactionEvent.spans).toEqual([
+ expect.objectContaining({
+ op: 'resource.script',
+ origin: 'auto.resource.browser.metrics',
+ }),
+ ]);
});
diff --git a/packages/react/src/reactrouterv6-compat-utils.tsx b/packages/react/src/reactrouterv6-compat-utils.tsx
index 2dc4bf29bd20..07249caa33db 100644
--- a/packages/react/src/reactrouterv6-compat-utils.tsx
+++ b/packages/react/src/reactrouterv6-compat-utils.tsx
@@ -95,10 +95,9 @@ export function createV6CompatibleWrapCreateBrowserRouter<
router.subscribe((state: RouterState) => {
if (state.historyAction === 'PUSH' || state.historyAction === 'POP') {
- // Use requestAnimationFrame to wait for Suspense boundaries to settle
- requestAnimationFrame(() => {
- // Small delay to allow for Suspense transitions
- setTimeout(() => {
+ // Wait for the next render if loading an unsettled route
+ if (state.navigation.state !== 'idle') {
+ requestAnimationFrame(() => {
handleNavigation({
location: state.location,
routes,
@@ -106,8 +105,16 @@ export function createV6CompatibleWrapCreateBrowserRouter<
version,
basename,
});
- }, 100);
- });
+ });
+ } else {
+ handleNavigation({
+ location: state.location,
+ routes,
+ navigationType: state.historyAction,
+ version,
+ basename,
+ });
+ }
}
});
diff --git a/packages/react/src/types.ts b/packages/react/src/types.ts
index 1a40ec4fce91..ce6f1b50e0c8 100644
--- a/packages/react/src/types.ts
+++ b/packages/react/src/types.ts
@@ -182,10 +182,14 @@ export interface RouterInit {
hydrationData?: HydrationState;
}
+export type NavigationState = {
+ state: 'idle' | 'loading' | 'submitting';
+}
+
export type NavigationStates = {
- Idle: any;
- Loading: any;
- Submitting: any;
+ Idle: NavigationState;
+ Loading: NavigationState;
+ Submitting: NavigationState;
};
export type Navigation = NavigationStates[keyof NavigationStates];
@@ -202,6 +206,7 @@ export declare enum HistoryAction {
export interface RouterState {
historyAction: Action | HistoryAction | any;
location: Location;
+ navigation: Navigation;
}
export interface Router {
state: TState;
From 2a8888d981ef7d582ae50b34730b5dda77b98218 Mon Sep 17 00:00:00 2001
From: Onur Temizkan
Date: Mon, 20 Jan 2025 16:42:10 +0000
Subject: [PATCH 3/3] Support lazy-loaded descendant routes.
---
.../.gitignore | 29 -----
.../.npmrc | 2 -
.../package.json | 40 -------
.../playwright.config.mjs | 7 --
.../public/index.html | 24 -----
.../src/globals.d.ts | 5 -
.../src/index.tsx | 71 ------------
.../src/pages/Index.tsx | 23 ----
.../src/pages/User.tsx | 8 --
.../src/react-app-env.d.ts | 1 -
.../start-event-proxy.mjs | 6 --
.../tests/errors.test.ts | 30 ------
.../tests/transactions.test.ts | 83 --------------
.../tsconfig.json | 20 ----
.../react-create-browser-router/src/index.tsx | 11 +-
.../src/pages/Index.tsx | 3 +
.../src/pages/LazyLoadedInnerRoute.tsx | 14 +++
.../src/pages/LazyLoadedUser.tsx | 23 ++++
.../tests/transactions.test.ts | 102 ++++++++++++++++++
.../react/src/reactrouterv6-compat-utils.tsx | 34 ++++--
packages/react/src/types.ts | 2 +-
21 files changed, 178 insertions(+), 360 deletions(-)
delete mode 100644 dev-packages/e2e-tests/test-applications/react-create-browser-router-lazy-routes/.gitignore
delete mode 100644 dev-packages/e2e-tests/test-applications/react-create-browser-router-lazy-routes/.npmrc
delete mode 100644 dev-packages/e2e-tests/test-applications/react-create-browser-router-lazy-routes/package.json
delete mode 100644 dev-packages/e2e-tests/test-applications/react-create-browser-router-lazy-routes/playwright.config.mjs
delete mode 100644 dev-packages/e2e-tests/test-applications/react-create-browser-router-lazy-routes/public/index.html
delete mode 100644 dev-packages/e2e-tests/test-applications/react-create-browser-router-lazy-routes/src/globals.d.ts
delete mode 100644 dev-packages/e2e-tests/test-applications/react-create-browser-router-lazy-routes/src/index.tsx
delete mode 100644 dev-packages/e2e-tests/test-applications/react-create-browser-router-lazy-routes/src/pages/Index.tsx
delete mode 100644 dev-packages/e2e-tests/test-applications/react-create-browser-router-lazy-routes/src/pages/User.tsx
delete mode 100644 dev-packages/e2e-tests/test-applications/react-create-browser-router-lazy-routes/src/react-app-env.d.ts
delete mode 100644 dev-packages/e2e-tests/test-applications/react-create-browser-router-lazy-routes/start-event-proxy.mjs
delete mode 100644 dev-packages/e2e-tests/test-applications/react-create-browser-router-lazy-routes/tests/errors.test.ts
delete mode 100644 dev-packages/e2e-tests/test-applications/react-create-browser-router-lazy-routes/tests/transactions.test.ts
delete mode 100644 dev-packages/e2e-tests/test-applications/react-create-browser-router-lazy-routes/tsconfig.json
create mode 100644 dev-packages/e2e-tests/test-applications/react-create-browser-router/src/pages/LazyLoadedInnerRoute.tsx
create mode 100644 dev-packages/e2e-tests/test-applications/react-create-browser-router/src/pages/LazyLoadedUser.tsx
diff --git a/dev-packages/e2e-tests/test-applications/react-create-browser-router-lazy-routes/.gitignore b/dev-packages/e2e-tests/test-applications/react-create-browser-router-lazy-routes/.gitignore
deleted file mode 100644
index 84634c973eeb..000000000000
--- a/dev-packages/e2e-tests/test-applications/react-create-browser-router-lazy-routes/.gitignore
+++ /dev/null
@@ -1,29 +0,0 @@
-# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
-
-# dependencies
-/node_modules
-/.pnp
-.pnp.js
-
-# testing
-/coverage
-
-# production
-/build
-
-# misc
-.DS_Store
-.env.local
-.env.development.local
-.env.test.local
-.env.production.local
-
-npm-debug.log*
-yarn-debug.log*
-yarn-error.log*
-
-/test-results/
-/playwright-report/
-/playwright/.cache/
-
-!*.d.ts
diff --git a/dev-packages/e2e-tests/test-applications/react-create-browser-router-lazy-routes/.npmrc b/dev-packages/e2e-tests/test-applications/react-create-browser-router-lazy-routes/.npmrc
deleted file mode 100644
index 070f80f05092..000000000000
--- a/dev-packages/e2e-tests/test-applications/react-create-browser-router-lazy-routes/.npmrc
+++ /dev/null
@@ -1,2 +0,0 @@
-@sentry:registry=http://127.0.0.1:4873
-@sentry-internal:registry=http://127.0.0.1:4873
diff --git a/dev-packages/e2e-tests/test-applications/react-create-browser-router-lazy-routes/package.json b/dev-packages/e2e-tests/test-applications/react-create-browser-router-lazy-routes/package.json
deleted file mode 100644
index 1e061d9bc267..000000000000
--- a/dev-packages/e2e-tests/test-applications/react-create-browser-router-lazy-routes/package.json
+++ /dev/null
@@ -1,40 +0,0 @@
-{
- "name": "react-create-browser-router-lazy-routes-test",
- "version": "0.1.0",
- "private": true,
- "dependencies": {
- "@sentry/react": "latest || *",
- "@types/node": "^18.19.1",
- "@types/react": "18.0.0",
- "@types/react-dom": "18.0.0",
- "react": "18.2.0",
- "react-dom": "18.2.0",
- "react-router-dom": "^6.4.1",
- "react-scripts": "5.0.1",
- "typescript": "~5.0.0"
- },
- "scripts": {
- "build": "react-scripts build",
- "start": "serve -s build",
- "test": "playwright test",
- "clean": "npx rimraf node_modules pnpm-lock.yaml",
- "test:build": "pnpm install && pnpm build",
- "test:build-canary": "pnpm install && pnpm add react@canary react-dom@canary && pnpm build",
- "test:assert": "pnpm test"
- },
- "eslintConfig": {
- "extends": ["react-app", "react-app/jest"]
- },
- "browserslist": {
- "production": [">0.2%", "not dead", "not op_mini all"],
- "development": ["last 1 chrome version", "last 1 firefox version", "last 1 safari version"]
- },
- "devDependencies": {
- "@playwright/test": "^1.44.1",
- "@sentry-internal/test-utils": "link:../../../test-utils",
- "serve": "14.0.1"
- },
- "volta": {
- "extends": "../../package.json"
- }
-}
diff --git a/dev-packages/e2e-tests/test-applications/react-create-browser-router-lazy-routes/playwright.config.mjs b/dev-packages/e2e-tests/test-applications/react-create-browser-router-lazy-routes/playwright.config.mjs
deleted file mode 100644
index 31f2b913b58b..000000000000
--- a/dev-packages/e2e-tests/test-applications/react-create-browser-router-lazy-routes/playwright.config.mjs
+++ /dev/null
@@ -1,7 +0,0 @@
-import { getPlaywrightConfig } from '@sentry-internal/test-utils';
-
-const config = getPlaywrightConfig({
- startCommand: `pnpm start`,
-});
-
-export default config;
diff --git a/dev-packages/e2e-tests/test-applications/react-create-browser-router-lazy-routes/public/index.html b/dev-packages/e2e-tests/test-applications/react-create-browser-router-lazy-routes/public/index.html
deleted file mode 100644
index 39da76522bea..000000000000
--- a/dev-packages/e2e-tests/test-applications/react-create-browser-router-lazy-routes/public/index.html
+++ /dev/null
@@ -1,24 +0,0 @@
-
-
-
-
-
-
-
- React App
-
-
-
-
-
-
-
diff --git a/dev-packages/e2e-tests/test-applications/react-create-browser-router-lazy-routes/src/globals.d.ts b/dev-packages/e2e-tests/test-applications/react-create-browser-router-lazy-routes/src/globals.d.ts
deleted file mode 100644
index ffa61ca49acc..000000000000
--- a/dev-packages/e2e-tests/test-applications/react-create-browser-router-lazy-routes/src/globals.d.ts
+++ /dev/null
@@ -1,5 +0,0 @@
-interface Window {
- recordedTransactions?: string[];
- capturedExceptionId?: string;
- sentryReplayId?: string;
-}
diff --git a/dev-packages/e2e-tests/test-applications/react-create-browser-router-lazy-routes/src/index.tsx b/dev-packages/e2e-tests/test-applications/react-create-browser-router-lazy-routes/src/index.tsx
deleted file mode 100644
index b89e00b43f51..000000000000
--- a/dev-packages/e2e-tests/test-applications/react-create-browser-router-lazy-routes/src/index.tsx
+++ /dev/null
@@ -1,71 +0,0 @@
-import * as Sentry from '@sentry/react';
-import React, { Suspense, lazy } from 'react';
-import ReactDOM from 'react-dom/client';
-import {
- RouterProvider,
- createBrowserRouter,
- createRoutesFromChildren,
- matchRoutes,
- useLocation,
- useNavigationType,
-} from 'react-router-dom';
-import Index from './pages/Index';
-
-const replay = Sentry.replayIntegration();
-
-Sentry.init({
- // environment: 'qa', // dynamic sampling bias to keep transactions
- dsn: process.env.REACT_APP_E2E_TEST_DSN,
- integrations: [
- Sentry.reactRouterV6BrowserTracingIntegration({
- useEffect: React.useEffect,
- useLocation,
- useNavigationType,
- createRoutesFromChildren,
- matchRoutes,
- }),
- replay,
- ],
- // We recommend adjusting this value in production, or using tracesSampler
- // for finer control
- tracesSampleRate: 1.0,
- release: 'e2e-test',
-
- tunnel: 'http://localhost:3031',
-
- // Always capture replays, so we can test this properly
- replaysSessionSampleRate: 1.0,
- replaysOnErrorSampleRate: 0.0,
-
- debug: !!process.env.DEBUG,
-});
-
-const sentryCreateBrowserRouter = Sentry.wrapCreateBrowserRouterV6(createBrowserRouter);
-
-const User = lazy(() => import('./pages/User'));
-
-const router = sentryCreateBrowserRouter(
- [
- {
- path: '/',
- element: ,
- },
- {
- element: (
- Loading...}>
-
-
- ),
- path: '/user/:id',
- },
- ],
- {
- // We're testing whether this option is avoided in the integration
- // We expect this to be ignored
- initialEntries: ['/user/1'],
- },
-);
-
-const root = ReactDOM.createRoot(document.getElementById('root') as HTMLElement);
-
-root.render();
diff --git a/dev-packages/e2e-tests/test-applications/react-create-browser-router-lazy-routes/src/pages/Index.tsx b/dev-packages/e2e-tests/test-applications/react-create-browser-router-lazy-routes/src/pages/Index.tsx
deleted file mode 100644
index d6b71a1d1279..000000000000
--- a/dev-packages/e2e-tests/test-applications/react-create-browser-router-lazy-routes/src/pages/Index.tsx
+++ /dev/null
@@ -1,23 +0,0 @@
-// biome-ignore lint/nursery/noUnusedImports: Need React import for JSX
-import * as React from 'react';
-import { Link } from 'react-router-dom';
-
-const Index = () => {
- return (
- <>
- {
- throw new Error('I am an error!');
- }}
- />
-
- navigate
-
- >
- );
-};
-
-export default Index;
diff --git a/dev-packages/e2e-tests/test-applications/react-create-browser-router-lazy-routes/src/pages/User.tsx b/dev-packages/e2e-tests/test-applications/react-create-browser-router-lazy-routes/src/pages/User.tsx
deleted file mode 100644
index 62f0c2d17533..000000000000
--- a/dev-packages/e2e-tests/test-applications/react-create-browser-router-lazy-routes/src/pages/User.tsx
+++ /dev/null
@@ -1,8 +0,0 @@
-// biome-ignore lint/nursery/noUnusedImports: Need React import for JSX
-import * as React from 'react';
-
-const User = () => {
- return I am a blank page :)
;
-};
-
-export default User;
diff --git a/dev-packages/e2e-tests/test-applications/react-create-browser-router-lazy-routes/src/react-app-env.d.ts b/dev-packages/e2e-tests/test-applications/react-create-browser-router-lazy-routes/src/react-app-env.d.ts
deleted file mode 100644
index 6431bc5fc6b2..000000000000
--- a/dev-packages/e2e-tests/test-applications/react-create-browser-router-lazy-routes/src/react-app-env.d.ts
+++ /dev/null
@@ -1 +0,0 @@
-///
diff --git a/dev-packages/e2e-tests/test-applications/react-create-browser-router-lazy-routes/start-event-proxy.mjs b/dev-packages/e2e-tests/test-applications/react-create-browser-router-lazy-routes/start-event-proxy.mjs
deleted file mode 100644
index be93e129284f..000000000000
--- a/dev-packages/e2e-tests/test-applications/react-create-browser-router-lazy-routes/start-event-proxy.mjs
+++ /dev/null
@@ -1,6 +0,0 @@
-import { startEventProxyServer } from '@sentry-internal/test-utils';
-
-startEventProxyServer({
- port: 3031,
- proxyServerName: 'react-create-browser-router',
-});
diff --git a/dev-packages/e2e-tests/test-applications/react-create-browser-router-lazy-routes/tests/errors.test.ts b/dev-packages/e2e-tests/test-applications/react-create-browser-router-lazy-routes/tests/errors.test.ts
deleted file mode 100644
index 4a11f07410ab..000000000000
--- a/dev-packages/e2e-tests/test-applications/react-create-browser-router-lazy-routes/tests/errors.test.ts
+++ /dev/null
@@ -1,30 +0,0 @@
-import { expect, test } from '@playwright/test';
-import { waitForError } from '@sentry-internal/test-utils';
-
-test('Captures exception correctly', async ({ page }) => {
- const errorEventPromise = waitForError('react-create-browser-router', event => {
- return !event.type && event.exception?.values?.[0]?.value === 'I am an error!';
- });
-
- await page.goto('/');
-
- const exceptionButton = page.locator('id=exception-button');
- await exceptionButton.click();
-
- const errorEvent = await errorEventPromise;
-
- expect(errorEvent.exception?.values).toHaveLength(1);
- expect(errorEvent.exception?.values?.[0]?.value).toBe('I am an error!');
-
- expect(errorEvent.request).toEqual({
- headers: expect.any(Object),
- url: 'http://localhost:3030/',
- });
-
- expect(errorEvent.transaction).toEqual('/');
-
- expect(errorEvent.contexts?.trace).toEqual({
- trace_id: expect.stringMatching(/[a-f0-9]{32}/),
- span_id: expect.stringMatching(/[a-f0-9]{16}/),
- });
-});
diff --git a/dev-packages/e2e-tests/test-applications/react-create-browser-router-lazy-routes/tests/transactions.test.ts b/dev-packages/e2e-tests/test-applications/react-create-browser-router-lazy-routes/tests/transactions.test.ts
deleted file mode 100644
index 5843d94abac8..000000000000
--- a/dev-packages/e2e-tests/test-applications/react-create-browser-router-lazy-routes/tests/transactions.test.ts
+++ /dev/null
@@ -1,83 +0,0 @@
-import { expect, test } from '@playwright/test';
-import { waitForTransaction } from '@sentry-internal/test-utils';
-
-test('Captures a pageload transaction', async ({ page }) => {
- const transactionEventPromise = waitForTransaction('react-create-browser-router', event => {
- return event.contexts?.trace?.op === 'pageload';
- });
-
- await page.goto('/');
-
- const transactionEvent = await transactionEventPromise;
-
- expect(transactionEvent).toEqual(
- expect.objectContaining({
- transaction: '/',
- type: 'transaction',
- transaction_info: {
- source: 'route',
- },
- }),
- );
-
- expect(transactionEvent.contexts?.trace).toEqual(
- expect.objectContaining({
- data: expect.objectContaining({
- deviceMemory: expect.any(String),
- effectiveConnectionType: expect.any(String),
- hardwareConcurrency: expect.any(String),
- 'sentry.idle_span_finish_reason': 'idleTimeout',
- 'sentry.op': 'pageload',
- 'sentry.origin': 'auto.pageload.react.reactrouter_v6',
- 'sentry.sample_rate': 1,
- 'sentry.source': 'route',
- }),
- op: 'pageload',
- span_id: expect.stringMatching(/[a-f0-9]{16}/),
- trace_id: expect.stringMatching(/[a-f0-9]{32}/),
- origin: 'auto.pageload.react.reactrouter_v6',
- }),
- );
-});
-
-test('Captures a navigation transaction', async ({ page }) => {
- const transactionEventPromise = waitForTransaction('react-create-browser-router', event => {
- return event.contexts?.trace?.op === 'navigation';
- });
-
- await page.goto('/');
- const linkElement = page.locator('id=navigation');
- await linkElement.click();
-
- const transactionEvent = await transactionEventPromise;
- expect(transactionEvent.contexts?.trace).toEqual({
- data: expect.objectContaining({
- 'sentry.idle_span_finish_reason': 'idleTimeout',
- 'sentry.op': 'navigation',
- 'sentry.origin': 'auto.navigation.react.reactrouter_v6',
- 'sentry.sample_rate': 1,
- 'sentry.source': 'route',
- }),
- op: 'navigation',
- span_id: expect.stringMatching(/[a-f0-9]{16}/),
- trace_id: expect.stringMatching(/[a-f0-9]{32}/),
- origin: 'auto.navigation.react.reactrouter_v6',
- });
-
- expect(transactionEvent).toEqual(
- expect.objectContaining({
- transaction: '/user/:id',
- type: 'transaction',
- transaction_info: {
- source: 'route',
- },
- }),
- );
-
- expect(transactionEvent.spans).toEqual([
- expect.objectContaining({
- op: 'resource.script',
- origin: 'auto.resource.browser.metrics',
- }),
- ]);
-});
diff --git a/dev-packages/e2e-tests/test-applications/react-create-browser-router-lazy-routes/tsconfig.json b/dev-packages/e2e-tests/test-applications/react-create-browser-router-lazy-routes/tsconfig.json
deleted file mode 100644
index 4cc95dc2689a..000000000000
--- a/dev-packages/e2e-tests/test-applications/react-create-browser-router-lazy-routes/tsconfig.json
+++ /dev/null
@@ -1,20 +0,0 @@
-{
- "compilerOptions": {
- "target": "es2018",
- "lib": ["dom", "dom.iterable", "esnext"],
- "allowJs": true,
- "skipLibCheck": true,
- "esModuleInterop": true,
- "allowSyntheticDefaultImports": true,
- "strict": true,
- "forceConsistentCasingInFileNames": true,
- "noFallthroughCasesInSwitch": true,
- "module": "esnext",
- "moduleResolution": "node",
- "resolveJsonModule": true,
- "isolatedModules": true,
- "noEmit": true,
- "jsx": "react"
- },
- "include": ["src", "tests"]
-}
diff --git a/dev-packages/e2e-tests/test-applications/react-create-browser-router/src/index.tsx b/dev-packages/e2e-tests/test-applications/react-create-browser-router/src/index.tsx
index 88f8cfa502ec..c7ad16eebcf7 100644
--- a/dev-packages/e2e-tests/test-applications/react-create-browser-router/src/index.tsx
+++ b/dev-packages/e2e-tests/test-applications/react-create-browser-router/src/index.tsx
@@ -1,5 +1,5 @@
import * as Sentry from '@sentry/react';
-import React from 'react';
+import React, { lazy, Suspense } from 'react';
import ReactDOM from 'react-dom/client';
import {
RouterProvider,
@@ -42,6 +42,7 @@ Sentry.init({
});
const sentryCreateBrowserRouter = Sentry.wrapCreateBrowserRouterV6(createBrowserRouter);
+const LazyLoadedUser = lazy(() => import('./pages/LazyLoadedUser'));
const router = sentryCreateBrowserRouter(
[
@@ -49,6 +50,14 @@ const router = sentryCreateBrowserRouter(
path: '/',
element: ,
},
+ {
+ path: '/lazy-loaded-user/*',
+ element: (
+ Loading...}>
+
+
+ ),
+ },
{
path: '/user/:id',
element: ,
diff --git a/dev-packages/e2e-tests/test-applications/react-create-browser-router/src/pages/Index.tsx b/dev-packages/e2e-tests/test-applications/react-create-browser-router/src/pages/Index.tsx
index d6b71a1d1279..12bfb12ec3a9 100644
--- a/dev-packages/e2e-tests/test-applications/react-create-browser-router/src/pages/Index.tsx
+++ b/dev-packages/e2e-tests/test-applications/react-create-browser-router/src/pages/Index.tsx
@@ -16,6 +16,9 @@ const Index = () => {
navigate
+
+ lazy navigate
+
>
);
};
diff --git a/dev-packages/e2e-tests/test-applications/react-create-browser-router/src/pages/LazyLoadedInnerRoute.tsx b/dev-packages/e2e-tests/test-applications/react-create-browser-router/src/pages/LazyLoadedInnerRoute.tsx
new file mode 100644
index 000000000000..1410df69124b
--- /dev/null
+++ b/dev-packages/e2e-tests/test-applications/react-create-browser-router/src/pages/LazyLoadedInnerRoute.tsx
@@ -0,0 +1,14 @@
+import * as Sentry from '@sentry/react';
+// biome-ignore lint/nursery/noUnusedImports: Need React import for JSX
+import * as React from 'react';
+import { Route, Routes } from 'react-router-dom';
+
+const SentryRoutes = Sentry.withSentryReactRouterV6Routing(Routes);
+
+const InnerRoute = () => (
+
+ I am a lazy loaded user
} />
+
+);
+
+export default InnerRoute;
diff --git a/dev-packages/e2e-tests/test-applications/react-create-browser-router/src/pages/LazyLoadedUser.tsx b/dev-packages/e2e-tests/test-applications/react-create-browser-router/src/pages/LazyLoadedUser.tsx
new file mode 100644
index 000000000000..636f99d9c8cb
--- /dev/null
+++ b/dev-packages/e2e-tests/test-applications/react-create-browser-router/src/pages/LazyLoadedUser.tsx
@@ -0,0 +1,23 @@
+import * as Sentry from '@sentry/react';
+import * as React from 'react';
+import { Route, Routes } from 'react-router-dom';
+
+const SentryRoutes = Sentry.withSentryReactRouterV6Routing(Routes);
+const InnerRoute = React.lazy(() => import('./LazyLoadedInnerRoute'));
+
+const LazyLoadedUser = () => {
+ return (
+
+ Loading...}>
+
+
+ }
+ />
+
+ );
+};
+
+export default LazyLoadedUser;
diff --git a/dev-packages/e2e-tests/test-applications/react-create-browser-router/tests/transactions.test.ts b/dev-packages/e2e-tests/test-applications/react-create-browser-router/tests/transactions.test.ts
index 5ecd098daf94..c35d731915d6 100644
--- a/dev-packages/e2e-tests/test-applications/react-create-browser-router/tests/transactions.test.ts
+++ b/dev-packages/e2e-tests/test-applications/react-create-browser-router/tests/transactions.test.ts
@@ -76,3 +76,105 @@ test('Captures a navigation transaction', async ({ page }) => {
expect(transactionEvent.spans).toEqual([]);
});
+
+test('Captures a lazy pageload transaction', async ({ page }) => {
+ const transactionEventPromise = waitForTransaction('react-create-browser-router', event => {
+ return event.contexts?.trace?.op === 'pageload';
+ });
+
+ await page.goto('/lazy-loaded-user/5/foo');
+
+ const transactionEvent = await transactionEventPromise;
+ expect(transactionEvent.contexts?.trace).toEqual({
+ data: expect.objectContaining({
+ 'sentry.idle_span_finish_reason': 'idleTimeout',
+ 'sentry.op': 'pageload',
+ 'sentry.origin': 'auto.pageload.react.reactrouter_v6',
+ 'sentry.sample_rate': 1,
+ 'sentry.source': 'route',
+ }),
+ op: 'pageload',
+ span_id: expect.stringMatching(/[a-f0-9]{16}/),
+ trace_id: expect.stringMatching(/[a-f0-9]{32}/),
+ origin: 'auto.pageload.react.reactrouter_v6',
+ });
+
+ expect(transactionEvent).toEqual(
+ expect.objectContaining({
+ transaction: '/lazy-loaded-user/:id/:innerId',
+ type: 'transaction',
+ transaction_info: {
+ source: 'route',
+ },
+ }),
+ );
+
+ expect(await page.innerText('id=content')).toContain('I am a lazy loaded user');
+
+ expect(transactionEvent.spans).toEqual(
+ expect.arrayContaining([
+ // This one is the outer lazy route
+ expect.objectContaining({
+ op: 'resource.script',
+ origin: 'auto.resource.browser.metrics',
+ }),
+ // This one is the inner lazy route
+ expect.objectContaining({
+ op: 'resource.script',
+ origin: 'auto.resource.browser.metrics',
+ }),
+ ]),
+ );
+});
+
+test('Captures a lazy navigation transaction', async ({ page }) => {
+ const transactionEventPromise = waitForTransaction('react-create-browser-router', event => {
+ return event.contexts?.trace?.op === 'navigation';
+ });
+
+ await page.goto('/');
+ const linkElement = page.locator('id=lazy-navigation');
+ await linkElement.click();
+
+ const transactionEvent = await transactionEventPromise;
+ expect(transactionEvent.contexts?.trace).toEqual({
+ data: expect.objectContaining({
+ 'sentry.idle_span_finish_reason': 'idleTimeout',
+ 'sentry.op': 'navigation',
+ 'sentry.origin': 'auto.navigation.react.reactrouter_v6',
+ 'sentry.sample_rate': 1,
+ 'sentry.source': 'route',
+ }),
+ op: 'navigation',
+ span_id: expect.stringMatching(/[a-f0-9]{16}/),
+ trace_id: expect.stringMatching(/[a-f0-9]{32}/),
+ origin: 'auto.navigation.react.reactrouter_v6',
+ });
+
+ expect(transactionEvent).toEqual(
+ expect.objectContaining({
+ transaction: '/lazy-loaded-user/:id/:innerId',
+ type: 'transaction',
+ transaction_info: {
+ source: 'route',
+ },
+ }),
+ );
+
+ expect(await page.innerText('id=content')).toContain('I am a lazy loaded user');
+
+ expect(transactionEvent.spans).toEqual(
+ expect.arrayContaining([
+ // This one is the outer lazy route
+ expect.objectContaining({
+ op: 'resource.script',
+ origin: 'auto.resource.browser.metrics',
+ }),
+ // This one is the inner lazy route
+ expect.objectContaining({
+ op: 'resource.script',
+ origin: 'auto.resource.browser.metrics',
+ }),
+ ]),
+ );
+});
diff --git a/packages/react/src/reactrouterv6-compat-utils.tsx b/packages/react/src/reactrouterv6-compat-utils.tsx
index 07249caa33db..db65c32cee99 100644
--- a/packages/react/src/reactrouterv6-compat-utils.tsx
+++ b/packages/react/src/reactrouterv6-compat-utils.tsx
@@ -61,6 +61,9 @@ export interface ReactRouterOptions {
type V6CompatibleVersion = '6' | '7';
+// Keeping as a global variable for cross-usage in multiple functions
+const allRoutes = new Set();
+
/**
* Creates a wrapCreateBrowserRouter function that can be used with all React Router v6 compatible versions.
*/
@@ -81,6 +84,10 @@ export function createV6CompatibleWrapCreateBrowserRouter<
}
return function (routes: RouteObject[], opts?: Record & { basename?: string }): TRouter {
+ routes.forEach(route => {
+ allRoutes.add(route);
+ });
+
const router = createRouterFunction(routes, opts);
const basename = opts?.basename;
@@ -90,7 +97,14 @@ export function createV6CompatibleWrapCreateBrowserRouter<
// This is the earliest convenient time to update the transaction name.
// Callbacks to `router.subscribe` are not called for the initial load.
if (router.state.historyAction === 'POP' && activeRootSpan) {
- updatePageloadTransaction(activeRootSpan, router.state.location, routes, undefined, basename);
+ updatePageloadTransaction(
+ activeRootSpan,
+ router.state.location,
+ routes,
+ undefined,
+ basename,
+ Array.from(allRoutes),
+ );
}
router.subscribe((state: RouterState) => {
@@ -104,6 +118,7 @@ export function createV6CompatibleWrapCreateBrowserRouter<
navigationType: state.historyAction,
version,
basename,
+ allRoutes: Array.from(allRoutes),
});
});
} else {
@@ -113,6 +128,7 @@ export function createV6CompatibleWrapCreateBrowserRouter<
navigationType: state.historyAction,
version,
basename,
+ allRoutes: Array.from(allRoutes),
});
}
}
@@ -149,6 +165,10 @@ export function createV6CompatibleWrapCreateMemoryRouter<
initialIndex?: number;
},
): TRouter {
+ routes.forEach(route => {
+ allRoutes.add(route);
+ });
+
const router = createRouterFunction(routes, opts);
const basename = opts?.basename;
@@ -174,7 +194,7 @@ export function createV6CompatibleWrapCreateMemoryRouter<
: router.state.location;
if (router.state.historyAction === 'POP' && activeRootSpan) {
- updatePageloadTransaction(activeRootSpan, location, routes, undefined, basename);
+ updatePageloadTransaction(activeRootSpan, location, routes, undefined, basename, Array.from(allRoutes));
}
router.subscribe((state: RouterState) => {
@@ -186,6 +206,7 @@ export function createV6CompatibleWrapCreateMemoryRouter<
navigationType: state.historyAction,
version,
basename,
+ allRoutes: Array.from(allRoutes),
});
}
});
@@ -260,8 +281,6 @@ export function createV6CompatibleWrapUseRoutes(origUseRoutes: UseRoutes, versio
return origUseRoutes;
}
- const allRoutes: Set = new Set();
-
const SentryRoutes: React.FC<{
children?: React.ReactNode;
routes: RouteObject[];
@@ -331,7 +350,6 @@ export function handleNavigation(opts: {
allRoutes?: RouteObject[];
}): void {
const { location, routes, navigationType, version, matches, basename, allRoutes } = opts;
-
const branches = Array.isArray(matches) ? matches : _matchRoutes(routes, location, basename);
const client = getClient();
@@ -565,7 +583,7 @@ function updatePageloadTransaction(
): void {
const branches = Array.isArray(matches)
? matches
- : (_matchRoutes(routes, location, basename) as unknown as RouteMatch[]);
+ : (_matchRoutes(allRoutes || routes, location, basename) as unknown as RouteMatch[]);
if (branches) {
let name,
@@ -581,7 +599,7 @@ function updatePageloadTransaction(
[name, source] = getNormalizedName(routes, location, branches, basename);
}
- getCurrentScope().setTransactionName(name);
+ getCurrentScope().setTransactionName(name || '/');
if (activeRootSpan) {
activeRootSpan.updateName(name);
@@ -604,8 +622,6 @@ export function createV6CompatibleWithSentryReactRouterRouting = new Set();
-
const SentryRoutes: React.FC
= (props: P) => {
const isMountRenderPass = React.useRef(true);
diff --git a/packages/react/src/types.ts b/packages/react/src/types.ts
index ce6f1b50e0c8..b29a2dbd1cad 100644
--- a/packages/react/src/types.ts
+++ b/packages/react/src/types.ts
@@ -184,7 +184,7 @@ export interface RouterInit {
export type NavigationState = {
state: 'idle' | 'loading' | 'submitting';
-}
+};
export type NavigationStates = {
Idle: NavigationState;