From aea5f22fe2b4dfa7e8e131e89a90794c389c0785 Mon Sep 17 00:00:00 2001 From: s1gr1d Date: Tue, 21 Oct 2025 15:17:29 +0200 Subject: [PATCH 1/9] fix(react-router): Don't add child routes to Set on root-level --- .../instrumentation.tsx | 14 +- .../instrumentation.test.tsx | 244 ++++++++++++++++++ 2 files changed, 251 insertions(+), 7 deletions(-) diff --git a/packages/react/src/reactrouter-compat-utils/instrumentation.tsx b/packages/react/src/reactrouter-compat-utils/instrumentation.tsx index bf57fdbd74dc..81526a4749a6 100644 --- a/packages/react/src/reactrouter-compat-utils/instrumentation.tsx +++ b/packages/react/src/reactrouter-compat-utils/instrumentation.tsx @@ -106,7 +106,8 @@ export interface ReactRouterOptions { type V6CompatibleVersion = '6' | '7'; // Keeping as a global variable for cross-usage in multiple functions -const allRoutes = new Set(); +// only exported for testing purposes +export const allRoutes = new Set(); /** * Processes resolved routes by adding them to allRoutes and checking for nested async handlers. @@ -679,13 +680,12 @@ export function handleNavigation(opts: { } } -function addRoutesToAllRoutes(routes: RouteObject[]): void { +/* Only exported for testing purposes */ +export function addRoutesToAllRoutes(routes: RouteObject[]): void { routes.forEach(route => { - const extractedChildRoutes = getChildRoutesRecursively(route); - - extractedChildRoutes.forEach(r => { - allRoutes.add(r); - }); + if (!allRoutes.has(route)) { + allRoutes.add(route); + } }); } diff --git a/packages/react/test/reactrouter-compat-utils/instrumentation.test.tsx b/packages/react/test/reactrouter-compat-utils/instrumentation.test.tsx index 0eeeeb342287..9e4072e1b21d 100644 --- a/packages/react/test/reactrouter-compat-utils/instrumentation.test.tsx +++ b/packages/react/test/reactrouter-compat-utils/instrumentation.test.tsx @@ -10,6 +10,7 @@ import { createReactRouterV6CompatibleTracingIntegration, updateNavigationSpan, } from '../../src/reactrouter-compat-utils'; +import { addRoutesToAllRoutes, allRoutes } from '../../src/reactrouter-compat-utils/instrumentation'; import type { Location, RouteObject } from '../../src/types'; const mockUpdateName = vi.fn(); @@ -141,3 +142,246 @@ describe('reactrouter-compat-utils/instrumentation', () => { }); }); }); + +describe('addRoutesToAllRoutes', () => { + beforeEach(() => { + vi.clearAllMocks(); + vi.resetModules(); + allRoutes.clear(); + }); + + it('should add simple routes without nesting', () => { + const routes = [ + { path: '/', element:
}, + { path: '/user/:id', element:
}, + { path: '/group/:group/:user?', element:
}, + ]; + + addRoutesToAllRoutes(routes); + const allRoutesArr = Array.from(allRoutes); + + expect(allRoutesArr).toHaveLength(3); + expect(allRoutesArr).toEqual( + expect.arrayContaining([ + expect.objectContaining({ path: '/' }), + expect.objectContaining({ path: '/user/:id' }), + expect.objectContaining({ path: '/group/:group/:user?' }), + ]), + ); + + // Verify exact structure matches manual testing results + allRoutesArr.forEach(route => { + expect(route).toHaveProperty('element'); + expect(route.element).toHaveProperty('props'); + }); + }); + + it('should preserve nested children on parent routes without creating duplicate top-level entries', () => { + const routes = [ + { path: '/', element:
}, + { path: '/user/:id', element:
}, + { path: '/group/:group/:user?', element:
}, + { + path: '/v2/post/:post', + element:
, + children: [ + { index: true, element:
}, + { path: 'featured', element:
}, + { path: '/v2/post/:post/related', element:
}, + ], + }, + ]; + + addRoutesToAllRoutes(routes); + const allRoutesArr = Array.from(allRoutes); + + // Should have all routes flattened (parent + all descendants) + expect(allRoutesArr.length).toBeGreaterThanOrEqual(4); + + // Find the parent route + const parentRoute = allRoutesArr.find((r: any) => r.path === '/v2/post/:post'); + expect(parentRoute).toBeTruthy(); + expect(parentRoute!.children).toBeDefined(); + expect(Array.isArray(parentRoute!.children)).toBe(true); + expect(parentRoute!.children).toHaveLength(3); + + // Verify children structure is preserved + expect(parentRoute!.children).toEqual( + expect.arrayContaining([ + expect.objectContaining({ index: true }), + expect.objectContaining({ path: 'featured' }), + expect.objectContaining({ path: '/v2/post/:post/related' }), + ]), + ); + + // Verify that children are NOT in allRoutes as individual entries + expect(allRoutesArr).not.toEqual( + expect.arrayContaining([ + expect.objectContaining({ index: true }), + expect.objectContaining({ path: 'featured' }), + expect.objectContaining({ path: '/v2/post/:post/related' }), + ]), + ); + }); + + it('should handle complex nested routes with multiple levels', () => { + const routes = [ + { path: '/', element:
}, + { path: '/user/:id', element:
}, + { path: '/group/:group/:user?', element:
}, + { + path: '/v1/post/:post', + element:
, + children: [ + { path: 'featured', element:
}, + { path: '/v1/post/:post/related', element:
}, + { + element:
More Nested Children
, + children: [{ path: 'edit', element:
Edit Post
}], + }, + ], + }, + { + path: '/v2/post/:post', + element:
, + children: [ + { index: true, element:
}, + { path: 'featured', element:
}, + { path: '/v2/post/:post/related', element:
}, + ], + }, + ]; + + addRoutesToAllRoutes(routes); + const allRoutesArr = Array.from(allRoutes); + + expect(allRoutesArr).toEqual([ + { + path: '/', + element: expect.objectContaining({ type: 'div', props: {} }), + }, + { + path: '/user/:id', + element: expect.objectContaining({ type: 'div', props: {} }), + }, + { + path: '/group/:group/:user?', + element: expect.objectContaining({ type: 'div', props: {} }), + }, + { + path: '/v1/post/:post', + element: expect.objectContaining({ type: 'div', props: {} }), + children: [ + { element:
, path: 'featured' }, + { element:
, path: '/v1/post/:post/related' }, + { children: [{ element:
Edit Post
, path: 'edit' }], element:
More Nested Children
}, + ], + }, + { + path: '/v2/post/:post', + element: expect.objectContaining({ type: 'div', props: {} }), + children: [ + { element:
, index: true }, + { element:
, path: 'featured' }, + { element:
, path: '/v2/post/:post/related' }, + ], + }, + ]); + }); + + it('should handle routes with nested index routes', () => { + const routes = [ + { + path: '/dashboard', + element:
, + children: [ + { index: true, element:
Dashboard Index
}, + { path: 'settings', element:
Settings
}, + ], + }, + ]; + + addRoutesToAllRoutes(routes); + const allRoutesArr = Array.from(allRoutes); + + expect(allRoutesArr).toEqual([ + { + path: '/dashboard', + element: expect.objectContaining({ type: 'div' }), + children: [ + { element:
Dashboard Index
, index: true }, + { element:
Settings
, path: 'settings' }, + ], + }, + ]); + }); + + it('should handle deeply nested routes with layout wrappers', () => { + const routes = [ + { + path: '/', + element:
Root
, + children: [ + { path: 'contact', element:
Contact
}, + { path: 'dashboard', element:
Dashboard
}, + { + element:
AuthLayout
, + children: [ + { path: 'login', element:
Login
}, + { path: 'logout', element:
Logout
}, + ], + }, + ], + }, + ]; + + addRoutesToAllRoutes(routes); + const allRoutesArr = Array.from(allRoutes); + + // Verify all routes are flattened into allRoutes + expect(allRoutesArr).toEqual([ + { + path: '/', + element: expect.objectContaining({ type: 'div', props: { children: 'Root' } }), + children: [ + { + path: 'contact', + element: expect.objectContaining({ type: 'div', props: { children: 'Contact' } }), + }, + { + path: 'dashboard', + element: expect.objectContaining({ type: 'div', props: { children: 'Dashboard' } }), + }, + { + element: expect.objectContaining({ type: 'div', props: { children: 'AuthLayout' } }), + children: [ + { + path: 'login', + element: expect.objectContaining({ type: 'div', props: { children: 'Login' } }), + }, + { + path: 'logout', + element: expect.objectContaining({ type: 'div', props: { children: 'Logout' } }), + }, + ], + }, + ], + }, + ]); + }); + + it('should not duplicate routes when called multiple times', () => { + const routes = [ + { path: '/', element:
}, + { path: '/about', element:
}, + ]; + + addRoutesToAllRoutes(routes); + const firstCount = allRoutes.size; + + addRoutesToAllRoutes(routes); + const secondCount = allRoutes.size; + + expect(firstCount).toBe(secondCount); + }); +}); From f2a158111eb8bfd5048dae06a35711098975acb7 Mon Sep 17 00:00:00 2001 From: s1gr1d Date: Tue, 21 Oct 2025 15:23:03 +0200 Subject: [PATCH 2/9] remove duplicate test --- .../instrumentation.test.tsx | 49 ------------------- 1 file changed, 49 deletions(-) diff --git a/packages/react/test/reactrouter-compat-utils/instrumentation.test.tsx b/packages/react/test/reactrouter-compat-utils/instrumentation.test.tsx index 9e4072e1b21d..01b921b4132c 100644 --- a/packages/react/test/reactrouter-compat-utils/instrumentation.test.tsx +++ b/packages/react/test/reactrouter-compat-utils/instrumentation.test.tsx @@ -176,54 +176,6 @@ describe('addRoutesToAllRoutes', () => { }); }); - it('should preserve nested children on parent routes without creating duplicate top-level entries', () => { - const routes = [ - { path: '/', element:
}, - { path: '/user/:id', element:
}, - { path: '/group/:group/:user?', element:
}, - { - path: '/v2/post/:post', - element:
, - children: [ - { index: true, element:
}, - { path: 'featured', element:
}, - { path: '/v2/post/:post/related', element:
}, - ], - }, - ]; - - addRoutesToAllRoutes(routes); - const allRoutesArr = Array.from(allRoutes); - - // Should have all routes flattened (parent + all descendants) - expect(allRoutesArr.length).toBeGreaterThanOrEqual(4); - - // Find the parent route - const parentRoute = allRoutesArr.find((r: any) => r.path === '/v2/post/:post'); - expect(parentRoute).toBeTruthy(); - expect(parentRoute!.children).toBeDefined(); - expect(Array.isArray(parentRoute!.children)).toBe(true); - expect(parentRoute!.children).toHaveLength(3); - - // Verify children structure is preserved - expect(parentRoute!.children).toEqual( - expect.arrayContaining([ - expect.objectContaining({ index: true }), - expect.objectContaining({ path: 'featured' }), - expect.objectContaining({ path: '/v2/post/:post/related' }), - ]), - ); - - // Verify that children are NOT in allRoutes as individual entries - expect(allRoutesArr).not.toEqual( - expect.arrayContaining([ - expect.objectContaining({ index: true }), - expect.objectContaining({ path: 'featured' }), - expect.objectContaining({ path: '/v2/post/:post/related' }), - ]), - ); - }); - it('should handle complex nested routes with multiple levels', () => { const routes = [ { path: '/', element:
}, @@ -338,7 +290,6 @@ describe('addRoutesToAllRoutes', () => { addRoutesToAllRoutes(routes); const allRoutesArr = Array.from(allRoutes); - // Verify all routes are flattened into allRoutes expect(allRoutesArr).toEqual([ { path: '/', From d0af0d4f3a7e1152cd5a342070db7977688c07bb Mon Sep 17 00:00:00 2001 From: s1gr1d Date: Wed, 22 Oct 2025 12:53:33 +0200 Subject: [PATCH 3/9] debug rr routing (children index route) --- .../react-router-7-cross-usage/src/index.tsx | 10 + .../tests/transactions.test.ts | 2 + .../1-noRecursion-noIndexRoute.md | 258 +++++++++++++++++ .../2-noRecursion-withIndexRoute.md | 260 ++++++++++++++++++ .../3-withRecursion-noIndexRoute.md | 99 +++++++ .../4-withRecursion-withIndexRoute.md | 187 +++++++++++++ .../instrumentation.tsx | 27 +- .../src/reactrouter-compat-utils/utils.ts | 21 +- .../instrumentation.test.tsx | 1 + 9 files changed, 860 insertions(+), 5 deletions(-) create mode 100644 packages/react/src/reactrouter-compat-utils/1-noRecursion-noIndexRoute.md create mode 100644 packages/react/src/reactrouter-compat-utils/2-noRecursion-withIndexRoute.md create mode 100644 packages/react/src/reactrouter-compat-utils/3-withRecursion-noIndexRoute.md create mode 100644 packages/react/src/reactrouter-compat-utils/4-withRecursion-withIndexRoute.md diff --git a/dev-packages/e2e-tests/test-applications/react-router-7-cross-usage/src/index.tsx b/dev-packages/e2e-tests/test-applications/react-router-7-cross-usage/src/index.tsx index bfcc527ded1b..b70ece337b54 100644 --- a/dev-packages/e2e-tests/test-applications/react-router-7-cross-usage/src/index.tsx +++ b/dev-packages/e2e-tests/test-applications/react-router-7-cross-usage/src/index.tsx @@ -89,6 +89,14 @@ const ProjectsRoutes = () => ( ); const router = sentryCreateBrowserRouter([ + { + path: '/post/:post', + element:
Post
, + children: [ + { index: true, element:
Post Index
}, + { path: '/post/:post/related', element:
Related Posts
}, + ], + }, { children: [ { @@ -103,5 +111,7 @@ const router = sentryCreateBrowserRouter([ }, ]); +console.log('router::', router); + const root = ReactDOM.createRoot(document.getElementById('root') as HTMLElement); root.render(); diff --git a/dev-packages/e2e-tests/test-applications/react-router-7-cross-usage/tests/transactions.test.ts b/dev-packages/e2e-tests/test-applications/react-router-7-cross-usage/tests/transactions.test.ts index 1b521964f770..f5107a40c967 100644 --- a/dev-packages/e2e-tests/test-applications/react-router-7-cross-usage/tests/transactions.test.ts +++ b/dev-packages/e2e-tests/test-applications/react-router-7-cross-usage/tests/transactions.test.ts @@ -2,6 +2,8 @@ import { expect, test } from '@playwright/test'; import { waitForTransaction } from '@sentry-internal/test-utils'; test('sends a pageload transaction with a parameterized URL', async ({ page }) => { + page.on('console', msg => console.log(msg.text())); + const transactionPromise = waitForTransaction('react-router-7-cross-usage', async transactionEvent => { return !!transactionEvent?.transaction && transactionEvent.contexts?.trace?.op === 'pageload'; }); diff --git a/packages/react/src/reactrouter-compat-utils/1-noRecursion-noIndexRoute.md b/packages/react/src/reactrouter-compat-utils/1-noRecursion-noIndexRoute.md new file mode 100644 index 000000000000..9c6c672c5e83 --- /dev/null +++ b/packages/react/src/reactrouter-compat-utils/1-noRecursion-noIndexRoute.md @@ -0,0 +1,258 @@ +## Expected Outcome + +It should work with index routes inside the children routes + +## Scenario + +- no recursion + ```ts + export function addRoutesToAllRoutes(routes: RouteObject[]): void { + console.log('routes to add to allRoutes::', JSON.stringify(routes)); + routes.forEach(route => { + if (!allRoutes.has(route)) { + allRoutes.add(route); + } + }); + } + ``` +- no index route + ```ts + { + path: '/post/:post', + element:
Post
, + children: [ + // { index: true, element:
Post Index
}, + { path: '/post/:post/related', element:
Related Posts
}, + ], + }, + ``` + +## Logs during E2E test + +Running 4 tests using 1 worker + +createV6CompatibleWrapCreateBrowserRouter - routes:: [{"path":"/post/:post","element":{"type":"div","key":null,"ref":null,"props":{"children":"Post"},"\_owner":null},"children":[{"path":"/post/:post/related","element":{"type":"div","key":null,"ref":null,"props":{"children":"Related Posts"},"_owner":null}}]},{"children":[{"path":"/","element":{"key":null,"ref":null,"props":{},"_owner":null}},{"path":"/*","element":{"key":null,"ref":null,"props":{},"_owner":null}}]}] + +routes to add to allRoutes:: [{"path":"/post/:post","element":{"type":"div","key":null,"ref":null,"props":{"children":"Post"},"\_owner":null},"children":[{"path":"/post/:post/related","element":{"type":"div","key":null,"ref":null,"props":{"children":"Related Posts"},"_owner":null}}]},{"children":[{"path":"/","element":{"key":null,"ref":null,"props":{},"_owner":null}},{"path":"/*","element":{"key":null,"ref":null,"props":{},"_owner":null}}]}] + +updatePageloadTransaction:: + +isInDescendantRoute:: true + +rebuildRoutePathFromAllRoutes matched: {pathname: /projects/123/views/234/567, search: , hash: , state: null, key: default} [{"params":{"_":"projects/123/views/234/567"},"pathname":"/","pathnameBase":"/","route":{"children":[{"path":"/","element":{"key":null,"ref":null,"props":{},"\_owner":null}},{"path":"/_","element":{"key":null,"ref":null,"props":{},"\_owner":null}}]}},{"params":{"_":"projects/123/views/234/567"},"pathname":"/projects/123/views/234/567","pathnameBase":"/","route":{"path":"/_","element":{"key":null,"ref":null,"props":{},"\_owner":null}}}] + +desc: name:: /projects/123/views/234/567 source:: route + +router:: {basename: undefined, future: undefined, state: undefined, routes: undefined, window: undefined} + +routes to add to allRoutes:: [{"path":":detailId","element":{"type":"div","key":null,"ref":null,"props":{"id":"details","children":"Details"},"_owner":null}}] + +updatePageloadTransaction:: + +isInDescendantRoute:: true + +rebuildRoutePathFromAllRoutes matched: {pathname: /projects/123/views/234/567, search: , hash: , state: null, key: default} [{"params":{"_":"projects/123/views/234/567"},"pathname":"/","pathnameBase":"/","route":{"children":[{"path":"/","element":{"key":null,"ref":null,"props":{},"\_owner":null}},{"path":"/_","element":{"key":null,"ref":null,"props":{},"\_owner":null}}]}},{"params":{"_":"projects/123/views/234/567"},"pathname":"/projects/123/views/234/567","pathnameBase":"/","route":{"path":"/_","element":{"key":null,"ref":null,"props":{},"\_owner":null}}}] +desc: name:: /projects/123/views/234/567 source:: route + +routes to add to allRoutes:: [{"index":true,"element":{"type":"div","key":null,"ref":null,"props":{"id":"views","children":"Views"},"_owner":null}},{"path":"views/:viewId/*","element":{"key":null,"ref":null,"props":{},"_owner":null}},{"path":"old-views/:viewId/*","element":{"key":null,"ref":null,"props":{},"_owner":null}}] + +updatePageloadTransaction:: +isInDescendantRoute:: true +rebuildRoutePathFromAllRoutes matched: {pathname: /projects/123/views/234/567, search: , hash: , state: null, key: default} [{"params":{"_":"projects/123/views/234/567"},"pathname":"/","pathnameBase":"/","route":{"children":[{"path":"/","element":{"key":null,"ref":null,"props":{},"\_owner":null}},{"path":"/_","element":{"key":null,"ref":null,"props":{},"\_owner":null}}]}},{"params":{"_":"projects/123/views/234/567"},"pathname":"/projects/123/views/234/567","pathnameBase":"/","route":{"path":"/_","element":{"key":null,"ref":null,"props":{},"\_owner":null}}}] + +desc: name:: /projects/123/views/234/567 source:: route + +routesFromChildren:: [{"id":"0","element":{"key":null,"ref":null,"props":{},"\_owner":null},"path":"projects","hasErrorBoundary":false,"children":[{"id":"0-0","element":{"type":"div","key":null,"ref":null,"props":{"children":"Project Page Root"},"\_owner":null},"index":true,"hasErrorBoundary":false},{"id":"0-1","element":{"key":null,"ref":null,"props":{},"\_owner":null},"path":"_","hasErrorBoundary":false,"children":[{"id":"0-1-0","element":{"key":null,"ref":null,"props":{},"\_owner":null},"path":":projectId/_","hasErrorBoundary":false}]}]}] + +routes to add to allRoutes:: [{"id":"0","element":{"key":null,"ref":null,"props":{},"\_owner":null},"path":"projects","hasErrorBoundary":false,"children":[{"id":"0-0","element":{"type":"div","key":null,"ref":null,"props":{"children":"Project Page Root"},"\_owner":null},"index":true,"hasErrorBoundary":false},{"id":"0-1","element":{"key":null,"ref":null,"props":{},"\_owner":null},"path":"_","hasErrorBoundary":false,"children":[{"id":"0-1-0","element":{"key":null,"ref":null,"props":{},"\_owner":null},"path":":projectId/_","hasErrorBoundary":false}]}]}] +updatePageloadTransaction:: + +isInDescendantRoute:: true + +rebuildRoutePathFromAllRoutes matched: {pathname: /projects/123/views/234/567, search: , hash: , state: null, key: default} [{"params":{"_":"views/234/567","projectId":"123"},"pathname":"/projects","pathnameBase":"/projects","route":{"id":"0","element":{"key":null,"ref":null,"props":{},"\_owner":null},"path":"projects","hasErrorBoundary":false,"children":[{"id":"0-0","element":{"type":"div","key":null,"ref":null,"props":{"children":"Project Page Root"},"\_owner":null},"index":true,"hasErrorBoundary":false},{"id":"0-1","element":{"key":null,"ref":null,"props":{},"\_owner":null},"path":"_","hasErrorBoundary":false,"children":[{"id":"0-1-0","element":{"key":null,"ref":null,"props":{},"_owner":null},"path":":projectId/*","hasErrorBoundary":false}]}]}},{"params":{"_":"views/234/567","projectId":"123"},"pathname":"/projects/123/views/234/567","pathnameBase":"/projects","route":{"id":"0-1","element":{"key":null,"ref":null,"props":{},"\_owner":null},"path":"_","hasErrorBoundary":false,"children":[{"id":"0-1-0","element":{"key":null,"ref":null,"props":{},"_owner":null},"path":":projectId/*","hasErrorBoundary":false}]}},{"params":{"_":"views/234/567","projectId":"123"},"pathname":"/projects/123/views/234/567","pathnameBase":"/projects/123","route":{"id":"0-1-0","element":{"key":null,"ref":null,"props":{},"\_owner":null},"path":":projectId/_","hasErrorBoundary":false}}] + +rebuildRoutePathFromAllRoutes matched: {pathname: /123/views/234/567} [{"params":{"_":"123/views/234/567"},"pathname":"/","pathnameBase":"/","route":{"children":[{"path":"/","element":{"key":null,"ref":null,"props":{},"\_owner":null}},{"path":"/_","element":{"key":null,"ref":null,"props":{},"\_owner":null}}]}},{"params":{"_":"123/views/234/567"},"pathname":"/123/views/234/567","pathnameBase":"/","route":{"path":"/_","element":{"key":null,"ref":null,"props":{},"\_owner":null}}}] + +desc: name:: /projects/123/views/234/567 source:: route + +✘ 1 [chromium] › tests/transactions.test.ts:4:5 › sends a pageload transaction with a parameterized URL (1.3s) + +✘ 2 [chromium] › tests/transactions.test.ts:30:5 › sends a pageload transaction with a parameterized URL - alternative route (1.2s) + +✘ 3 [chromium] › tests/transactions.test.ts:54:5 › sends a navigation transaction with a parameterized URL (2.3s) + +✘ 4 [chromium] › tests/transactions.test.ts:98:5 › sends a navigation transaction with a parameterized URL - alternative route (2.3s) + +1. [chromium] › tests/transactions.test.ts:4:5 › sends a pageload transaction with a parameterized URL + + Error: expect(received).toMatchObject(expected) + - Expected - 1 + * Received + 1 + + @@ -3,10 +3,10 @@ + "trace": Object { + "op": "pageload", + "origin": "auto.pageload.react.reactrouter_v7", + }, + }, + - "transaction": "/projects/:projectId/views/:viewId/:detailId", + * "transaction": "/projects/123/views/234/567", + "transaction_info": Object { + "source": "route", + }, + } + + 14 | + 15 | expect((await page.innerHTML('#root')).includes('Details')).toBe(true); + + > 16 | expect(rootSpan).toMatchObject({ + + | ^ + + 17 | contexts: { + 18 | trace: { + 19 | op: 'pageload', + at /private/var/folders/zg/3jx06d797kv5pggvpxj2bc540000gn/T/sentry-e2e-tests-react-router-7-cross-usage-mSvGiI/tests/transactions.test.ts:16:20 + + Error Context: test-results/transactions-sends-a-pagel-84aa8-on-with-a-parameterized-URL-chromium/error-context.md + + attachment #2: trace (application/zip) ───────────────────────────────────────────────────────── + test-results/transactions-sends-a-pagel-84aa8-on-with-a-parameterized-URL-chromium/trace.zip + Usage: + + pnpm exec playwright show-trace test-results/transactions-sends-a-pagel-84aa8-on-with-a-parameterized-URL-chromium/trace.zip + + ──────────────────────────────────────────────────────────────────────────────────────────────── + +2. [chromium] › tests/transactions.test.ts:30:5 › sends a pageload transaction with a parameterized URL - alternative route + + Error: expect(received).toMatchObject(expected) + - Expected - 1 + * Received + 1 + + @@ -3,10 +3,10 @@ + "trace": Object { + "op": "pageload", + "origin": "auto.pageload.react.reactrouter_v7", + }, + }, + - "transaction": "/projects/:projectId/old-views/:viewId/:detailId", + * "transaction": "/projects/234/old-views/234/567", + "transaction_info": Object { + "source": "route", + }, + } + + 38 | + 39 | expect((await page.innerHTML('#root')).includes('Details')).toBe(true); + + > 40 | expect(rootSpan).toMatchObject({ + + | ^ + + 41 | contexts: { + 42 | trace: { + 43 | op: 'pageload', + at /private/var/folders/zg/3jx06d797kv5pggvpxj2bc540000gn/T/sentry-e2e-tests-react-router-7-cross-usage-mSvGiI/tests/transactions.test.ts:40:20 + + Error Context: test-results/transactions-sends-a-pagel-f5147-zed-URL---alternative-route-chromium/error-context.md + + attachment #2: trace (application/zip) ───────────────────────────────────────────────────────── + test-results/transactions-sends-a-pagel-f5147-zed-URL---alternative-route-chromium/trace.zip + Usage: + + pnpm exec playwright show-trace test-results/transactions-sends-a-pagel-f5147-zed-URL---alternative-route-chromium/trace.zip + + ──────────────────────────────────────────────────────────────────────────────────────────────── + +3. [chromium] › tests/transactions.test.ts:54:5 › sends a navigation transaction with a parameterized URL + + Error: expect(received).toMatchObject(expected) + - Expected - 1 + * Received + 1 + + @@ -3,10 +3,10 @@ + "trace": Object { + "op": "navigation", + "origin": "auto.navigation.react.reactrouter_v7", + }, + }, + - "transaction": "/projects/:projectId/views/:viewId/:detailId", + * "transaction": "/projects/123/views/456/789", + "transaction_info": Object { + "source": "route", + }, + } + + 82 | + 83 | expect((await page.innerHTML('#root')).includes('Details')).toBe(true); + + > 84 | expect(navigationTxn).toMatchObject({ + + | ^ + + 85 | contexts: { + 86 | trace: { + 87 | op: 'navigation', + at /private/var/folders/zg/3jx06d797kv5pggvpxj2bc540000gn/T/sentry-e2e-tests-react-router-7-cross-usage-mSvGiI/tests/transactions.test.ts:84:25 + + Error Context: test-results/transactions-sends-a-navig-fc4a1-on-with-a-parameterized-URL-chromium/error-context.md + + attachment #2: trace (application/zip) ───────────────────────────────────────────────────────── + test-results/transactions-sends-a-navig-fc4a1-on-with-a-parameterized-URL-chromium/trace.zip + Usage: + + pnpm exec playwright show-trace test-results/transactions-sends-a-navig-fc4a1-on-with-a-parameterized-URL-chromium/trace.zip + + ──────────────────────────────────────────────────────────────────────────────────────────────── + +4. [chromium] › tests/transactions.test.ts:98:5 › sends a navigation transaction with a parameterized URL - alternative route + + Error: expect(received).toMatchObject(expected) + - Expected - 1 + * Received + 1 + + @@ -3,10 +3,10 @@ + "trace": Object { + "op": "navigation", + "origin": "auto.navigation.react.reactrouter_v7", + }, + }, + - "transaction": "/projects/:projectId/old-views/:viewId/:detailId", + * "transaction": "/projects/123/old-views/345/654", + "transaction_info": Object { + "source": "route", + }, + } + + 126 | + 127 | expect((await page.innerHTML('#root')).includes('Details')).toBe(true); + + > 128 | expect(navigationTxn).toMatchObject({ + + | ^ + + 129 | contexts: { + 130 | trace: { + 131 | op: 'navigation', + at /private/var/folders/zg/3jx06d797kv5pggvpxj2bc540000gn/T/sentry-e2e-tests-react-router-7-cross-usage-mSvGiI/tests/transactions.test.ts:128:25 + + Error Context: test-results/transactions-sends-a-navig-c389f-zed-URL---alternative-route-chromium/error-context.md + + attachment #2: trace (application/zip) ───────────────────────────────────────────────────────── + test-results/transactions-sends-a-navig-c389f-zed-URL---alternative-route-chromium/trace.zip + Usage: + + pnpm exec playwright show-trace test-results/transactions-sends-a-navig-c389f-zed-URL---alternative-route-chromium/trace.zip + + ──────────────────────────────────────────────────────────────────────────────────────────────── + +4 failed +[chromium] › tests/transactions.test.ts:4:5 › sends a pageload transaction with a parameterized URL +[chromium] › tests/transactions.test.ts:30:5 › sends a pageload transaction with a parameterized URL - alternative route +[chromium] › tests/transactions.test.ts:54:5 › sends a navigation transaction with a parameterized URL +[chromium] › tests/transactions.test.ts:98:5 › sends a navigation transaction with a parameterized URL - alternative route + +ELIFECYCLE  Test failed. See above for more details. + +ELIFECYCLE  Command failed with exit code 1. diff --git a/packages/react/src/reactrouter-compat-utils/2-noRecursion-withIndexRoute.md b/packages/react/src/reactrouter-compat-utils/2-noRecursion-withIndexRoute.md new file mode 100644 index 000000000000..b2f1aa466473 --- /dev/null +++ b/packages/react/src/reactrouter-compat-utils/2-noRecursion-withIndexRoute.md @@ -0,0 +1,260 @@ +## Expected Outcome + +It should work with index routes inside the children routes + +## Scenario + +- no recursion + ```ts + export function addRoutesToAllRoutes(routes: RouteObject[]): void { + console.log('routes to add to allRoutes::', JSON.stringify(routes)); + routes.forEach(route => { + if (!allRoutes.has(route)) { + allRoutes.add(route); + } + }); + } + ``` +- with index route + ```ts + { + path: '/post/:post', + element:
Post
, + children: [ + { index: true, element:
Post Index
}, + { path: '/post/:post/related', element:
Related Posts
}, + ], + }, + ``` + +## Logs during E2E test + +Running 4 tests using 1 worker + +createV6CompatibleWrapCreateBrowserRouter - routes:: [{"path":"/post/:post","element":{"type":"div","key":null,"ref":null,"props":{"children":"Post"},"\_owner":null},"children":[{"index":true,"element":{"type":"div","key":null,"ref":null,"props":{"children":"Post Index"},"_owner":null}},{"path":"/post/:post/related","element":{"type":"div","key":null,"ref":null,"props":{"children":"Related Posts"},"_owner":null}}]},{"children":[{"path":"/","element":{"key":null,"ref":null,"props":{},"_owner":null}},{"path":"/*","element":{"key":null,"ref":null,"props":{},"_owner":null}}]}] + +routes to add to allRoutes:: [{"path":"/post/:post","element":{"type":"div","key":null,"ref":null,"props":{"children":"Post"},"\_owner":null},"children":[{"index":true,"element":{"type":"div","key":null,"ref":null,"props":{"children":"Post Index"},"_owner":null}},{"path":"/post/:post/related","element":{"type":"div","key":null,"ref":null,"props":{"children":"Related Posts"},"_owner":null}}]},{"children":[{"path":"/","element":{"key":null,"ref":null,"props":{},"_owner":null}},{"path":"/*","element":{"key":null,"ref":null,"props":{},"_owner":null}}]}] + +updatePageloadTransaction:: + +isInDescendantRoute:: true + +rebuildRoutePathFromAllRoutes matched: {pathname: /projects/123/views/234/567, search: , hash: , state: null, key: default} [{"params":{"_":"projects/123/views/234/567"},"pathname":"/","pathnameBase":"/","route":{"children":[{"path":"/","element":{"key":null,"ref":null,"props":{},"\_owner":null}},{"path":"/_","element":{"key":null,"ref":null,"props":{},"\_owner":null}}]}},{"params":{"_":"projects/123/views/234/567"},"pathname":"/projects/123/views/234/567","pathnameBase":"/","route":{"path":"/_","element":{"key":null,"ref":null,"props":{},"\_owner":null}}}] + +desc: name:: /projects/123/views/234/567 source:: route + +router:: {basename: undefined, future: undefined, state: undefined, routes: undefined, window: undefined} + +routes to add to allRoutes:: [{"path":":detailId","element":{"type":"div","key":null,"ref":null,"props":{"id":"details","children":"Details"},"_owner":null}}] + +updatePageloadTransaction:: + +isInDescendantRoute:: true + +rebuildRoutePathFromAllRoutes matched: {pathname: /projects/123/views/234/567, search: , hash: , state: null, key: default} [{"params":{"_":"projects/123/views/234/567"},"pathname":"/","pathnameBase":"/","route":{"children":[{"path":"/","element":{"key":null,"ref":null,"props":{},"\_owner":null}},{"path":"/_","element":{"key":null,"ref":null,"props":{},"\_owner":null}}]}},{"params":{"_":"projects/123/views/234/567"},"pathname":"/projects/123/views/234/567","pathnameBase":"/","route":{"path":"/_","element":{"key":null,"ref":null,"props":{},"\_owner":null}}}] + +desc: name:: /projects/123/views/234/567 source:: route + +routes to add to allRoutes:: [{"index":true,"element":{"type":"div","key":null,"ref":null,"props":{"id":"views","children":"Views"},"_owner":null}},{"path":"views/:viewId/*","element":{"key":null,"ref":null,"props":{},"_owner":null}},{"path":"old-views/:viewId/*","element":{"key":null,"ref":null,"props":{},"_owner":null}}] +updatePageloadTransaction:: + +isInDescendantRoute:: true + +rebuildRoutePathFromAllRoutes matched: {pathname: /projects/123/views/234/567, search: , hash: , state: null, key: default} [{"params":{"_":"projects/123/views/234/567"},"pathname":"/","pathnameBase":"/","route":{"children":[{"path":"/","element":{"key":null,"ref":null,"props":{},"\_owner":null}},{"path":"/_","element":{"key":null,"ref":null,"props":{},"\_owner":null}}]}},{"params":{"_":"projects/123/views/234/567"},"pathname":"/projects/123/views/234/567","pathnameBase":"/","route":{"path":"/_","element":{"key":null,"ref":null,"props":{},"\_owner":null}}}] + +desc: name:: /projects/123/views/234/567 source:: route +routesFromChildren:: [{"id":"0","element":{"key":null,"ref":null,"props":{},"\_owner":null},"path":"projects","hasErrorBoundary":false,"children":[{"id":"0-0","element":{"type":"div","key":null,"ref":null,"props":{"children":"Project Page Root"},"\_owner":null},"index":true,"hasErrorBoundary":false},{"id":"0-1","element":{"key":null,"ref":null,"props":{},"\_owner":null},"path":"_","hasErrorBoundary":false,"children":[{"id":"0-1-0","element":{"key":null,"ref":null,"props":{},"\_owner":null},"path":":projectId/_","hasErrorBoundary":false}]}]}] + +routes to add to allRoutes:: [{"id":"0","element":{"key":null,"ref":null,"props":{},"\_owner":null},"path":"projects","hasErrorBoundary":false,"children":[{"id":"0-0","element":{"type":"div","key":null,"ref":null,"props":{"children":"Project Page Root"},"\_owner":null},"index":true,"hasErrorBoundary":false},{"id":"0-1","element":{"key":null,"ref":null,"props":{},"\_owner":null},"path":"_","hasErrorBoundary":false,"children":[{"id":"0-1-0","element":{"key":null,"ref":null,"props":{},"\_owner":null},"path":":projectId/_","hasErrorBoundary":false}]}]}] + +updatePageloadTransaction:: + +isInDescendantRoute:: true + +rebuildRoutePathFromAllRoutes matched: {pathname: /projects/123/views/234/567, search: , hash: , state: null, key: default} [{"params":{"_":"views/234/567","projectId":"123"},"pathname":"/projects","pathnameBase":"/projects","route":{"id":"0","element":{"key":null,"ref":null,"props":{},"\_owner":null},"path":"projects","hasErrorBoundary":false,"children":[{"id":"0-0","element":{"type":"div","key":null,"ref":null,"props":{"children":"Project Page Root"},"\_owner":null},"index":true,"hasErrorBoundary":false},{"id":"0-1","element":{"key":null,"ref":null,"props":{},"\_owner":null},"path":"_","hasErrorBoundary":false,"children":[{"id":"0-1-0","element":{"key":null,"ref":null,"props":{},"_owner":null},"path":":projectId/*","hasErrorBoundary":false}]}]}},{"params":{"_":"views/234/567","projectId":"123"},"pathname":"/projects/123/views/234/567","pathnameBase":"/projects","route":{"id":"0-1","element":{"key":null,"ref":null,"props":{},"\_owner":null},"path":"_","hasErrorBoundary":false,"children":[{"id":"0-1-0","element":{"key":null,"ref":null,"props":{},"_owner":null},"path":":projectId/*","hasErrorBoundary":false}]}},{"params":{"_":"views/234/567","projectId":"123"},"pathname":"/projects/123/views/234/567","pathnameBase":"/projects/123","route":{"id":"0-1-0","element":{"key":null,"ref":null,"props":{},"\_owner":null},"path":":projectId/_","hasErrorBoundary":false}}] + +rebuildRoutePathFromAllRoutes matched: {pathname: /123/views/234/567} [{"params":{"_":"123/views/234/567"},"pathname":"/","pathnameBase":"/","route":{"children":[{"path":"/","element":{"key":null,"ref":null,"props":{},"\_owner":null}},{"path":"/_","element":{"key":null,"ref":null,"props":{},"\_owner":null}}]}},{"params":{"_":"123/views/234/567"},"pathname":"/123/views/234/567","pathnameBase":"/","route":{"path":"/_","element":{"key":null,"ref":null,"props":{},"\_owner":null}}}] + +desc: name:: /projects/123/views/234/567 source:: route + +✘ 1 [chromium] › tests/transactions.test.ts:4:5 › sends a pageload transaction with a parameterized URL (1.3s) + +✘ 2 [chromium] › tests/transactions.test.ts:30:5 › sends a pageload transaction with a parameterized URL - alternative route (1.2s) + +✘ 3 [chromium] › tests/transactions.test.ts:54:5 › sends a navigation transaction with a parameterized URL (2.3s) + +✘ 4 [chromium] › tests/transactions.test.ts:98:5 › sends a navigation transaction with a parameterized URL - alternative route (2.3s) + +1. [chromium] › tests/transactions.test.ts:4:5 › sends a pageload transaction with a parameterized URL + + Error: expect(received).toMatchObject(expected) + - Expected - 1 + * Received + 1 + + @@ -3,10 +3,10 @@ + "trace": Object { + "op": "pageload", + "origin": "auto.pageload.react.reactrouter_v7", + }, + }, + - "transaction": "/projects/:projectId/views/:viewId/:detailId", + * "transaction": "/projects/123/views/234/567", + "transaction_info": Object { + "source": "route", + }, + } + + 14 | + 15 | expect((await page.innerHTML('#root')).includes('Details')).toBe(true); + + > 16 | expect(rootSpan).toMatchObject({ + + | ^ + + 17 | contexts: { + 18 | trace: { + 19 | op: 'pageload', + at /private/var/folders/zg/3jx06d797kv5pggvpxj2bc540000gn/T/sentry-e2e-tests-react-router-7-cross-usage-KjJeCW/tests/transactions.test.ts:16:20 + + Error Context: test-results/transactions-sends-a-pagel-84aa8-on-with-a-parameterized-URL-chromium/error-context.md + + attachment #2: trace (application/zip) ───────────────────────────────────────────────────────── + test-results/transactions-sends-a-pagel-84aa8-on-with-a-parameterized-URL-chromium/trace.zip + Usage: + + pnpm exec playwright show-trace test-results/transactions-sends-a-pagel-84aa8-on-with-a-parameterized-URL-chromium/trace.zip + + ──────────────────────────────────────────────────────────────────────────────────────────────── + +2. [chromium] › tests/transactions.test.ts:30:5 › sends a pageload transaction with a parameterized URL - alternative route + + Error: expect(received).toMatchObject(expected) + - Expected - 1 + * Received + 1 + + @@ -3,10 +3,10 @@ + "trace": Object { + "op": "pageload", + "origin": "auto.pageload.react.reactrouter_v7", + }, + }, + - "transaction": "/projects/:projectId/old-views/:viewId/:detailId", + * "transaction": "/projects/234/old-views/234/567", + "transaction_info": Object { + "source": "route", + }, + } + + 38 | + 39 | expect((await page.innerHTML('#root')).includes('Details')).toBe(true); + + > 40 | expect(rootSpan).toMatchObject({ + + | ^ + + 41 | contexts: { + 42 | trace: { + 43 | op: 'pageload', + at /private/var/folders/zg/3jx06d797kv5pggvpxj2bc540000gn/T/sentry-e2e-tests-react-router-7-cross-usage-KjJeCW/tests/transactions.test.ts:40:20 + + Error Context: test-results/transactions-sends-a-pagel-f5147-zed-URL---alternative-route-chromium/error-context.md + + attachment #2: trace (application/zip) ───────────────────────────────────────────────────────── + test-results/transactions-sends-a-pagel-f5147-zed-URL---alternative-route-chromium/trace.zip + Usage: + + pnpm exec playwright show-trace test-results/transactions-sends-a-pagel-f5147-zed-URL---alternative-route-chromium/trace.zip + + ──────────────────────────────────────────────────────────────────────────────────────────────── + +3. [chromium] › tests/transactions.test.ts:54:5 › sends a navigation transaction with a parameterized URL + + Error: expect(received).toMatchObject(expected) + - Expected - 1 + * Received + 1 + + @@ -3,10 +3,10 @@ + "trace": Object { + "op": "navigation", + "origin": "auto.navigation.react.reactrouter_v7", + }, + }, + - "transaction": "/projects/:projectId/views/:viewId/:detailId", + * "transaction": "/projects/123/views/456/789", + "transaction_info": Object { + "source": "route", + }, + } + + 82 | + 83 | expect((await page.innerHTML('#root')).includes('Details')).toBe(true); + + > 84 | expect(navigationTxn).toMatchObject({ + + | ^ + + 85 | contexts: { + 86 | trace: { + 87 | op: 'navigation', + at /private/var/folders/zg/3jx06d797kv5pggvpxj2bc540000gn/T/sentry-e2e-tests-react-router-7-cross-usage-KjJeCW/tests/transactions.test.ts:84:25 + + Error Context: test-results/transactions-sends-a-navig-fc4a1-on-with-a-parameterized-URL-chromium/error-context.md + + attachment #2: trace (application/zip) ───────────────────────────────────────────────────────── + test-results/transactions-sends-a-navig-fc4a1-on-with-a-parameterized-URL-chromium/trace.zip + Usage: + + pnpm exec playwright show-trace test-results/transactions-sends-a-navig-fc4a1-on-with-a-parameterized-URL-chromium/trace.zip + + ──────────────────────────────────────────────────────────────────────────────────────────────── + +4. [chromium] › tests/transactions.test.ts:98:5 › sends a navigation transaction with a parameterized URL - alternative route + + Error: expect(received).toMatchObject(expected) + - Expected - 1 + * Received + 1 + + @@ -3,10 +3,10 @@ + "trace": Object { + "op": "navigation", + "origin": "auto.navigation.react.reactrouter_v7", + }, + }, + - "transaction": "/projects/:projectId/old-views/:viewId/:detailId", + * "transaction": "/projects/123/old-views/345/654", + "transaction_info": Object { + "source": "route", + }, + } + + 126 | + 127 | expect((await page.innerHTML('#root')).includes('Details')).toBe(true); + + > 128 | expect(navigationTxn).toMatchObject({ + + | ^ + + 129 | contexts: { + 130 | trace: { + 131 | op: 'navigation', + at /private/var/folders/zg/3jx06d797kv5pggvpxj2bc540000gn/T/sentry-e2e-tests-react-router-7-cross-usage-KjJeCW/tests/transactions.test.ts:128:25 + + Error Context: test-results/transactions-sends-a-navig-c389f-zed-URL---alternative-route-chromium/error-context.md + + attachment #2: trace (application/zip) ───────────────────────────────────────────────────────── + test-results/transactions-sends-a-navig-c389f-zed-URL---alternative-route-chromium/trace.zip + Usage: + + pnpm exec playwright show-trace test-results/transactions-sends-a-navig-c389f-zed-URL---alternative-route-chromium/trace.zip + + ──────────────────────────────────────────────────────────────────────────────────────────────── + +4 failed +[chromium] › tests/transactions.test.ts:4:5 › sends a pageload transaction with a parameterized URL +[chromium] › tests/transactions.test.ts:30:5 › sends a pageload transaction with a parameterized URL - alternative route +[chromium] › tests/transactions.test.ts:54:5 › sends a navigation transaction with a parameterized URL +[chromium] › tests/transactions.test.ts:98:5 › sends a navigation transaction with a parameterized URL - alternative route + +ELIFECYCLE  Test failed. See above for more details. + +ELIFECYCLE  Command failed with exit code 1. diff --git a/packages/react/src/reactrouter-compat-utils/3-withRecursion-noIndexRoute.md b/packages/react/src/reactrouter-compat-utils/3-withRecursion-noIndexRoute.md new file mode 100644 index 000000000000..dd7d5e04ed5b --- /dev/null +++ b/packages/react/src/reactrouter-compat-utils/3-withRecursion-noIndexRoute.md @@ -0,0 +1,99 @@ +## Expected Outcome + +It should work with index routes inside the children routes + +## Scenario + +- with recursion + + ```ts + export function addRoutesToAllRoutes(routes: RouteObject[]): void { + console.log('routes to add to allRoutes::', JSON.stringify(routes)); + routes.forEach(route => { + const extractedChildRoutes = getChildRoutesRecursively(route); + + extractedChildRoutes.forEach(r => { + allRoutes.add(r); + }); + }); + } + ``` + +- no index route + ```ts + { + path: '/post/:post', + element:
Post
, + children: [ + // { index: true, element:
Post Index
}, + { path: '/post/:post/related', element:
Related Posts
}, + ], + }, + ``` + +## Logs during E2E test + +Running 4 tests using 1 worker + +createV6CompatibleWrapCreateBrowserRouter - routes:: [{"path":"/post/:post","element":{"type":"div","key":null,"ref":null,"props":{"children":"Post"},"\_owner":null},"children":[{"path":"/post/:post/related","element":{"type":"div","key":null,"ref":null,"props":{"children":"Related Posts"},"_owner":null}}]},{"children":[{"path":"/","element":{"key":null,"ref":null,"props":{},"_owner":null}},{"path":"/*","element":{"key":null,"ref":null,"props":{},"_owner":null}}]}] + +routes to add to allRoutes:: [{"path":"/post/:post","element":{"type":"div","key":null,"ref":null,"props":{"children":"Post"},"\_owner":null},"children":[{"path":"/post/:post/related","element":{"type":"div","key":null,"ref":null,"props":{"children":"Related Posts"},"_owner":null}}]},{"children":[{"path":"/","element":{"key":null,"ref":null,"props":{},"_owner":null}},{"path":"/*","element":{"key":null,"ref":null,"props":{},"_owner":null}}]}] + +updatePageloadTransaction:: + +isInDescendantRoute:: true + +rebuildRoutePathFromAllRoutes matched: {pathname: /projects/123/views/234/567, search: , hash: , state: null, key: default} [{"params":{"_":"projects/123/views/234/567"},"pathname":"/","pathnameBase":"/","route":{"children":[{"path":"/","element":{"key":null,"ref":null,"props":{},"\_owner":null}},{"path":"/_","element":{"key":null,"ref":null,"props":{},"\_owner":null}}]}},{"params":{"_":"projects/123/views/234/567"},"pathname":"/projects/123/views/234/567","pathnameBase":"/","route":{"path":"/_","element":{"key":null,"ref":null,"props":{},"\_owner":null}}}] + +desc: name:: /projects/123/views/234/567 source:: route + +router:: {basename: undefined, future: undefined, state: undefined, routes: undefined, window: undefined} + +routes to add to allRoutes:: [{"path":":detailId","element":{"type":"div","key":null,"ref":null,"props":{"id":"details","children":"Details"},"_owner":null}}] + +updatePageloadTransaction:: + +isInDescendantRoute:: true + +rebuildRoutePathFromAllRoutes matched: {pathname: /projects/123/views/234/567, search: , hash: , state: null, key: default} [{"params":{"_":"projects/123/views/234/567"},"pathname":"/","pathnameBase":"/","route":{"children":[{"path":"/","element":{"key":null,"ref":null,"props":{},"\_owner":null}},{"path":"/_","element":{"key":null,"ref":null,"props":{},"\_owner":null}}]}},{"params":{"_":"projects/123/views/234/567"},"pathname":"/projects/123/views/234/567","pathnameBase":"/","route":{"path":"/_","element":{"key":null,"ref":null,"props":{},"\_owner":null}}}] + +desc: name:: /projects/123/views/234/567 source:: route +routes to add to allRoutes:: [{"index":true,"element":{"type":"div","key":null,"ref":null,"props":{"id":"views","children":"Views"},"_owner":null}},{"path":"views/:viewId/*","element":{"key":null,"ref":null,"props":{},"_owner":null}},{"path":"old-views/:viewId/*","element":{"key":null,"ref":null,"props":{},"_owner":null}}] +updatePageloadTransaction:: + +isInDescendantRoute:: true + +rebuildRoutePathFromAllRoutes matched: {pathname: /projects/123/views/234/567, search: , hash: , state: null, key: default} [{"params":{"_":"projects/123/views/234/567"},"pathname":"/","pathnameBase":"/","route":{"children":[{"path":"/","element":{"key":null,"ref":null,"props":{},"\_owner":null}},{"path":"/_","element":{"key":null,"ref":null,"props":{},"\_owner":null}}]}},{"params":{"_":"projects/123/views/234/567"},"pathname":"/projects/123/views/234/567","pathnameBase":"/","route":{"path":"/_","element":{"key":null,"ref":null,"props":{},"\_owner":null}}}] + +desc: name:: /projects/123/views/234/567 source:: route + +routesFromChildren:: [{"id":"0","element":{"key":null,"ref":null,"props":{},"\_owner":null},"path":"projects","hasErrorBoundary":false,"children":[{"id":"0-0","element":{"type":"div","key":null,"ref":null,"props":{"children":"Project Page Root"},"\_owner":null},"index":true,"hasErrorBoundary":false},{"id":"0-1","element":{"key":null,"ref":null,"props":{},"\_owner":null},"path":"_","hasErrorBoundary":false,"children":[{"id":"0-1-0","element":{"key":null,"ref":null,"props":{},"\_owner":null},"path":":projectId/_","hasErrorBoundary":false}]}]}] +routes to add to allRoutes:: [{"id":"0","element":{"key":null,"ref":null,"props":{},"\_owner":null},"path":"projects","hasErrorBoundary":false,"children":[{"id":"0-0","element":{"type":"div","key":null,"ref":null,"props":{"children":"Project Page Root"},"\_owner":null},"index":true,"hasErrorBoundary":false},{"id":"0-1","element":{"key":null,"ref":null,"props":{},"\_owner":null},"path":"_","hasErrorBoundary":false,"children":[{"id":"0-1-0","element":{"key":null,"ref":null,"props":{},"\_owner":null},"path":":projectId/_","hasErrorBoundary":false}]}]}] + +updatePageloadTransaction:: + +isInDescendantRoute:: true + +rebuildRoutePathFromAllRoutes matched: {pathname: /projects/123/views/234/567, search: , hash: , state: null, key: default} [{"params":{"_":"views/234/567","projectId":"123"},"pathname":"/projects","pathnameBase":"/projects","route":{"id":"0","element":{"key":null,"ref":null,"props":{},"\_owner":null},"path":"projects","hasErrorBoundary":false,"children":[{"id":"0-0","element":{"type":"div","key":null,"ref":null,"props":{"children":"Project Page Root"},"\_owner":null},"index":true,"hasErrorBoundary":false},{"id":"0-1","element":{"key":null,"ref":null,"props":{},"\_owner":null},"path":"_","hasErrorBoundary":false,"children":[{"id":"0-1-0","element":{"key":null,"ref":null,"props":{},"_owner":null},"path":":projectId/*","hasErrorBoundary":false}]}]}},{"params":{"_":"views/234/567","projectId":"123"},"pathname":"/projects/123/views/234/567","pathnameBase":"/projects","route":{"id":"0-1","element":{"key":null,"ref":null,"props":{},"\_owner":null},"path":"_","hasErrorBoundary":false,"children":[{"id":"0-1-0","element":{"key":null,"ref":null,"props":{},"_owner":null},"path":":projectId/*","hasErrorBoundary":false}]}},{"params":{"_":"views/234/567","projectId":"123"},"pathname":"/projects/123/views/234/567","pathnameBase":"/projects/123","route":{"id":"0-1-0","element":{"key":null,"ref":null,"props":{},"\_owner":null},"path":":projectId/_","hasErrorBoundary":false}}] + +rebuildRoutePathFromAllRoutes matched: {pathname: /123/views/234/567} [{"params":{"_":"views/234/567","projectId":"123"},"pathname":"/123/views/234/567","pathnameBase":"/","route":{"id":"0-1","element":{"key":null,"ref":null,"props":{},"\_owner":null},"path":"_","hasErrorBoundary":false,"children":[{"id":"0-1-0","element":{"key":null,"ref":null,"props":{},"_owner":null},"path":":projectId/*","hasErrorBoundary":false}]}},{"params":{"_":"views/234/567","projectId":"123"},"pathname":"/123/views/234/567","pathnameBase":"/123","route":{"id":"0-1-0","element":{"key":null,"ref":null,"props":{},"\_owner":null},"path":":projectId/_","hasErrorBoundary":false}}] + +rebuildRoutePathFromAllRoutes matched: {pathname: /views/234/567} [{"params":{"viewId":"234","*":"567"},"pathname":"/views/234/567","pathnameBase":"/views/234","route":{"path":"views/:viewId/*","element":{"key":null,"ref":null,"props":{},"_owner":null}}}] + +rebuildRoutePathFromAllRoutes matched: {pathname: /567} [{"params":{"detailId":"567"},"pathname":"/567","pathnameBase":"/567","route":{"path":":detailId","element":{"type":"div","key":null,"ref":null,"props":{"id":"details","children":"Details"},"_owner":null}}}] + +rebuildRoutePathFromAllRoutes matched: {pathname: /} [{"params":{},"pathname":"/","pathnameBase":"/","route":{"index":true,"element":{"type":"div","key":null,"ref":null,"props":{"id":"views","children":"Views"},"_owner":null}}}] + +desc: name:: /projects/:projectId/views/:viewId/:detailId source:: route + +✓ 1 [chromium] › tests/transactions.test.ts:4:5 › sends a pageload transaction with a parameterized URL (1.3s) + +✓ 2 [chromium] › tests/transactions.test.ts:30:5 › sends a pageload transaction with a parameterized URL - alternative route (1.1s) + +✓ 3 [chromium] › tests/transactions.test.ts:54:5 › sends a navigation transaction with a parameterized URL (2.2s) + +✓ 4 [chromium] › tests/transactions.test.ts:98:5 › sends a navigation transaction with a parameterized URL - alternative route (2.2s) + +4 passed (9.9s) + +✨ Done in 47.87s. diff --git a/packages/react/src/reactrouter-compat-utils/4-withRecursion-withIndexRoute.md b/packages/react/src/reactrouter-compat-utils/4-withRecursion-withIndexRoute.md new file mode 100644 index 000000000000..c4195886bc77 --- /dev/null +++ b/packages/react/src/reactrouter-compat-utils/4-withRecursion-withIndexRoute.md @@ -0,0 +1,187 @@ +## Expected Outcome + +It should work with index routes inside the children routes + +## Scenario + +- with recursion + + ```ts + export function addRoutesToAllRoutes(routes: RouteObject[]): void { + console.log('routes to add to allRoutes::', JSON.stringify(routes)); + routes.forEach(route => { + const extractedChildRoutes = getChildRoutesRecursively(route); + + extractedChildRoutes.forEach(r => { + allRoutes.add(r); + }); + }); + } + ``` + +- with index route + ```ts + { + path: '/post/:post', + element:
Post
, + children: [ + { index: true, element:
Post Index
}, + { path: '/post/:post/related', element:
Related Posts
}, + ], + }, + ``` + +## Logs during E2E test + +Running 4 tests using 1 worker + +createV6CompatibleWrapCreateBrowserRouter - routes:: [{"path":"/post/:post","element":{"type":"div","key":null,"ref":null,"props":{"children":"Post"},"\_owner":null},"children":[{"index":true,"element":{"type":"div","key":null,"ref":null,"props":{"children":"Post Index"},"_owner":null}},{"path":"/post/:post/related","element":{"type":"div","key":null,"ref":null,"props":{"children":"Related Posts"},"_owner":null}}]},{"children":[{"path":"/","element":{"key":null,"ref":null,"props":{},"_owner":null}},{"path":"/*","element":{"key":null,"ref":null,"props":{},"_owner":null}}]}] + +routes to add to allRoutes:: [{"path":"/post/:post","element":{"type":"div","key":null,"ref":null,"props":{"children":"Post"},"\_owner":null},"children":[{"index":true,"element":{"type":"div","key":null,"ref":null,"props":{"children":"Post Index"},"_owner":null}},{"path":"/post/:post/related","element":{"type":"div","key":null,"ref":null,"props":{"children":"Related Posts"},"_owner":null}}]},{"children":[{"path":"/","element":{"key":null,"ref":null,"props":{},"_owner":null}},{"path":"/*","element":{"key":null,"ref":null,"props":{},"_owner":null}}]}] + +updatePageloadTransaction:: + +isInDescendantRoute:: true + +rebuildRoutePathFromAllRoutes matched: {pathname: /projects/123/views/234/567, search: , hash: , state: null, key: default} [{"params":{"_":"projects/123/views/234/567"},"pathname":"/","pathnameBase":"/","route":{"children":[{"path":"/","element":{"key":null,"ref":null,"props":{},"\_owner":null}},{"path":"/_","element":{"key":null,"ref":null,"props":{},"\_owner":null}}]}},{"params":{"_":"projects/123/views/234/567"},"pathname":"/projects/123/views/234/567","pathnameBase":"/","route":{"path":"/_","element":{"key":null,"ref":null,"props":{},"\_owner":null}}}] + +desc: name:: /projects/123/views/234/567 source:: route + +router:: {basename: undefined, future: undefined, state: undefined, routes: undefined, window: undefined} + +routes to add to allRoutes:: [{"path":":detailId","element":{"type":"div","key":null,"ref":null,"props":{"id":"details","children":"Details"},"_owner":null}}] + +updatePageloadTransaction:: + +isInDescendantRoute:: true + +rebuildRoutePathFromAllRoutes matched: {pathname: /projects/123/views/234/567, search: , hash: , state: null, key: default} [{"params":{"_":"projects/123/views/234/567"},"pathname":"/","pathnameBase":"/","route":{"children":[{"path":"/","element":{"key":null,"ref":null,"props":{},"\_owner":null}},{"path":"/_","element":{"key":null,"ref":null,"props":{},"\_owner":null}}]}},{"params":{"_":"projects/123/views/234/567"},"pathname":"/projects/123/views/234/567","pathnameBase":"/","route":{"path":"/_","element":{"key":null,"ref":null,"props":{},"\_owner":null}}}] + +desc: name:: /projects/123/views/234/567 source:: route +routes to add to allRoutes:: [{"index":true,"element":{"type":"div","key":null,"ref":null,"props":{"id":"views","children":"Views"},"_owner":null}},{"path":"views/:viewId/*","element":{"key":null,"ref":null,"props":{},"_owner":null}},{"path":"old-views/:viewId/*","element":{"key":null,"ref":null,"props":{},"_owner":null}}] + +updatePageloadTransaction:: + +isInDescendantRoute:: true + +rebuildRoutePathFromAllRoutes matched: {pathname: /projects/123/views/234/567, search: , hash: , state: null, key: default} [{"params":{"_":"projects/123/views/234/567"},"pathname":"/","pathnameBase":"/","route":{"children":[{"path":"/","element":{"key":null,"ref":null,"props":{},"\_owner":null}},{"path":"/_","element":{"key":null,"ref":null,"props":{},"\_owner":null}}]}},{"params":{"_":"projects/123/views/234/567"},"pathname":"/projects/123/views/234/567","pathnameBase":"/","route":{"path":"/_","element":{"key":null,"ref":null,"props":{},"\_owner":null}}}] + +desc: name:: /projects/123/views/234/567 source:: route + +routesFromChildren:: [{"id":"0","element":{"key":null,"ref":null,"props":{},"\_owner":null},"path":"projects","hasErrorBoundary":false,"children":[{"id":"0-0","element":{"type":"div","key":null,"ref":null,"props":{"children":"Project Page Root"},"\_owner":null},"index":true,"hasErrorBoundary":false},{"id":"0-1","element":{"key":null,"ref":null,"props":{},"\_owner":null},"path":"_","hasErrorBoundary":false,"children":[{"id":"0-1-0","element":{"key":null,"ref":null,"props":{},"\_owner":null},"path":":projectId/_","hasErrorBoundary":false}]}]}] + +routes to add to allRoutes:: [{"id":"0","element":{"key":null,"ref":null,"props":{},"\_owner":null},"path":"projects","hasErrorBoundary":false,"children":[{"id":"0-0","element":{"type":"div","key":null,"ref":null,"props":{"children":"Project Page Root"},"\_owner":null},"index":true,"hasErrorBoundary":false},{"id":"0-1","element":{"key":null,"ref":null,"props":{},"\_owner":null},"path":"_","hasErrorBoundary":false,"children":[{"id":"0-1-0","element":{"key":null,"ref":null,"props":{},"\_owner":null},"path":":projectId/_","hasErrorBoundary":false}]}]}] +updatePageloadTransaction:: + +isInDescendantRoute:: true + +rebuildRoutePathFromAllRoutes matched: {pathname: /projects/123/views/234/567, search: , hash: , state: null, key: default} [{"params":{"_":"views/234/567","projectId":"123"},"pathname":"/projects","pathnameBase":"/projects","route":{"id":"0","element":{"key":null,"ref":null,"props":{},"\_owner":null},"path":"projects","hasErrorBoundary":false,"children":[{"id":"0-0","element":{"type":"div","key":null,"ref":null,"props":{"children":"Project Page Root"},"\_owner":null},"index":true,"hasErrorBoundary":false},{"id":"0-1","element":{"key":null,"ref":null,"props":{},"\_owner":null},"path":"_","hasErrorBoundary":false,"children":[{"id":"0-1-0","element":{"key":null,"ref":null,"props":{},"_owner":null},"path":":projectId/*","hasErrorBoundary":false}]}]}},{"params":{"_":"views/234/567","projectId":"123"},"pathname":"/projects/123/views/234/567","pathnameBase":"/projects","route":{"id":"0-1","element":{"key":null,"ref":null,"props":{},"\_owner":null},"path":"_","hasErrorBoundary":false,"children":[{"id":"0-1-0","element":{"key":null,"ref":null,"props":{},"_owner":null},"path":":projectId/*","hasErrorBoundary":false}]}},{"params":{"_":"views/234/567","projectId":"123"},"pathname":"/projects/123/views/234/567","pathnameBase":"/projects/123","route":{"id":"0-1-0","element":{"key":null,"ref":null,"props":{},"\_owner":null},"path":":projectId/_","hasErrorBoundary":false}}] + +rebuildRoutePathFromAllRoutes matched: {pathname: /123/views/234/567} [{"params":{"_":"views/234/567","projectId":"123"},"pathname":"/123/views/234/567","pathnameBase":"/","route":{"id":"0-1","element":{"key":null,"ref":null,"props":{},"\_owner":null},"path":"_","hasErrorBoundary":false,"children":[{"id":"0-1-0","element":{"key":null,"ref":null,"props":{},"_owner":null},"path":":projectId/*","hasErrorBoundary":false}]}},{"params":{"_":"views/234/567","projectId":"123"},"pathname":"/123/views/234/567","pathnameBase":"/123","route":{"id":"0-1-0","element":{"key":null,"ref":null,"props":{},"\_owner":null},"path":":projectId/_","hasErrorBoundary":false}}] + +rebuildRoutePathFromAllRoutes matched: {pathname: /views/234/567} [{"params":{"viewId":"234","*":"567"},"pathname":"/views/234/567","pathnameBase":"/views/234","route":{"path":"views/:viewId/*","element":{"key":null,"ref":null,"props":{},"_owner":null}}}] + +rebuildRoutePathFromAllRoutes matched: {pathname: /567} [{"params":{"detailId":"567"},"pathname":"/567","pathnameBase":"/567","route":{"path":":detailId","element":{"type":"div","key":null,"ref":null,"props":{"id":"details","children":"Details"},"_owner":null}}}] + +rebuildRoutePathFromAllRoutes matched: {pathname: /} [{"params":{},"pathname":"/","pathnameBase":"/","route":{"index":true,"element":{"type":"div","key":null,"ref":null,"props":{"children":"Post Index"},"_owner":null}}}] + +desc: name:: /projects/:projectId/views/:viewId/:detailId source:: route + +✓ 1 [chromium] › tests/transactions.test.ts:4:5 › sends a pageload transaction with a parameterized URL (1.3s) + +✓ 2 [chromium] › tests/transactions.test.ts:30:5 › sends a pageload transaction with a parameterized URL - alternative route (1.2s) + +✘ 3 [chromium] › tests/transactions.test.ts:54:5 › sends a navigation transaction with a parameterized URL (1.2s) + +✘ 4 [chromium] › tests/transactions.test.ts:98:5 › sends a navigation transaction with a parameterized URL - alternative route (1.2s) + +1. [chromium] › tests/transactions.test.ts:54:5 › sends a navigation transaction with a parameterized URL + + Error: expect(received).toMatchObject(expected) + - Expected - 1 + * Received + 1 + + @@ -3,10 +3,10 @@ + "trace": Object { + "op": "pageload", + "origin": "auto.pageload.react.reactrouter_v7", + }, + }, + - "transaction": "/", + * "transaction": "", + "transaction_info": Object { + "source": "route", + }, + } + + 64 | const pageloadTxn = await pageloadTxnPromise; + 65 | + + > 66 | expect(pageloadTxn).toMatchObject({ + + | ^ + + 67 | contexts: { + 68 | trace: { + 69 | op: 'pageload', + at /private/var/folders/zg/3jx06d797kv5pggvpxj2bc540000gn/T/sentry-e2e-tests-react-router-7-cross-usage-Gan5my/tests/transactions.test.ts:66:23 + + Error Context: test-results/transactions-sends-a-navig-fc4a1-on-with-a-parameterized-URL-chromium/error-context.md + + attachment #2: trace (application/zip) ───────────────────────────────────────────────────────── + test-results/transactions-sends-a-navig-fc4a1-on-with-a-parameterized-URL-chromium/trace.zip + Usage: + + pnpm exec playwright show-trace test-results/transactions-sends-a-navig-fc4a1-on-with-a-parameterized-URL-chromium/trace.zip + + ──────────────────────────────────────────────────────────────────────────────────────────────── + +2. [chromium] › tests/transactions.test.ts:98:5 › sends a navigation transaction with a parameterized URL - alternative route + + Error: expect(received).toMatchObject(expected) + - Expected - 1 + * Received + 1 + + @@ -3,10 +3,10 @@ + "trace": Object { + "op": "pageload", + "origin": "auto.pageload.react.reactrouter_v7", + }, + }, + - "transaction": "/", + * "transaction": "", + "transaction_info": Object { + "source": "route", + }, + } + + 108 | const pageloadTxn = await pageloadTxnPromise; + 109 | + + > 110 | expect(pageloadTxn).toMatchObject({ + + | ^ + + 111 | contexts: { + 112 | trace: { + 113 | op: 'pageload', + at /private/var/folders/zg/3jx06d797kv5pggvpxj2bc540000gn/T/sentry-e2e-tests-react-router-7-cross-usage-Gan5my/tests/transactions.test.ts:110:23 + + Error Context: test-results/transactions-sends-a-navig-c389f-zed-URL---alternative-route-chromium/error-context.md + + attachment #2: trace (application/zip) ───────────────────────────────────────────────────────── + test-results/transactions-sends-a-navig-c389f-zed-URL---alternative-route-chromium/trace.zip + Usage: + + pnpm exec playwright show-trace test-results/transactions-sends-a-navig-c389f-zed-URL---alternative-route-chromium/trace.zip + + ──────────────────────────────────────────────────────────────────────────────────────────────── + +2 failed +[chromium] › tests/transactions.test.ts:54:5 › sends a navigation transaction with a parameterized URL +[chromium] › tests/transactions.test.ts:98:5 › sends a navigation transaction with a parameterized URL - alternative route +2 passed (8.4s) + +ELIFECYCLE  Test failed. See above for more details. + +ELIFECYCLE  Command failed with exit code 1. diff --git a/packages/react/src/reactrouter-compat-utils/instrumentation.tsx b/packages/react/src/reactrouter-compat-utils/instrumentation.tsx index 81526a4749a6..09470a96716e 100644 --- a/packages/react/src/reactrouter-compat-utils/instrumentation.tsx +++ b/packages/react/src/reactrouter-compat-utils/instrumentation.tsx @@ -48,6 +48,7 @@ import { prefixWithSlash, rebuildRoutePathFromAllRoutes, resolveRouteNameAndSource, + routeIsDescendant, } from './utils'; let _useEffect: UseEffect; @@ -225,6 +226,7 @@ export function createV6CompatibleWrapCreateBrowserRouter< } return function (routes: RouteObject[], opts?: Record & { basename?: string }): TRouter { + console.log('createV6CompatibleWrapCreateBrowserRouter - routes::', JSON.stringify(routes)); addRoutesToAllRoutes(routes); // Check for async handlers that might contain sub-route declarations (only if enabled) @@ -242,6 +244,8 @@ export function createV6CompatibleWrapCreateBrowserRouter< const activeRootSpan = getActiveRootSpan(); + // console.log('activeroo', activeRootSpan); + // Track whether we've completed the initial pageload to properly distinguish // between POPs that occur during pageload vs. legitimate back/forward navigation. let isInitialPageloadComplete = false; @@ -654,10 +658,11 @@ export function handleNavigation(opts: { } if ((navigationType === 'PUSH' || navigationType === 'POP') && branches) { + console.log('allRoutes::', allRoutes); const [name, source] = resolveRouteNameAndSource( location, routes, - allRoutes || routes, + allRoutes?.slice(0, -3) || routes, branches as RouteMatch[], basename, ); @@ -666,6 +671,8 @@ export function handleNavigation(opts: { const spanJson = activeSpan && spanToJSON(activeSpan); const isAlreadyInNavigationSpan = spanJson?.op === 'navigation'; + console.log('name::', name); + // Cross usage can result in multiple navigation spans being created without this check if (!isAlreadyInNavigationSpan) { startBrowserTracingNavigationSpan(client, { @@ -682,10 +689,15 @@ export function handleNavigation(opts: { /* Only exported for testing purposes */ export function addRoutesToAllRoutes(routes: RouteObject[]): void { + console.log('routes to add to allRoutes::', JSON.stringify(routes)); + + // fixme: this maybe has a bug routes.forEach(route => { - if (!allRoutes.has(route)) { - allRoutes.add(route); - } + const extractedChildRoutes = getChildRoutesRecursively(route); + + extractedChildRoutes.forEach(r => { + allRoutes.add(r); + }); }); } @@ -722,6 +734,7 @@ function updatePageloadTransaction({ basename?: string; allRoutes?: RouteObject[]; }): void { + console.log('updatePageloadTransaction::'); const branches = Array.isArray(matches) ? matches : (_matchRoutes(allRoutes || routes, location, basename) as unknown as RouteMatch[]); @@ -731,14 +744,18 @@ function updatePageloadTransaction({ source: TransactionSource = 'url'; const isInDescendantRoute = locationIsInsideDescendantRoute(location, allRoutes || routes); + console.log('isInDescendantRoute::', isInDescendantRoute); if (isInDescendantRoute) { name = prefixWithSlash(rebuildRoutePathFromAllRoutes(allRoutes || routes, location)); source = 'route'; + console.log('desc: name::', name, 'source::', source); } if (!isInDescendantRoute || !name) { [name, source] = getNormalizedName(routes, location, branches, basename); + + console.log('name::', name, 'source::', source); } getCurrentScope().setTransactionName(name || '/'); @@ -774,6 +791,8 @@ export function createV6CompatibleWithSentryReactRouterRouting

{ const routes = _createRoutesFromChildren(props.children) as RouteObject[]; + console.log('routesFromChildren::', JSON.stringify(routes)); + if (isMountRenderPass.current) { addRoutesToAllRoutes(routes); diff --git a/packages/react/src/reactrouter-compat-utils/utils.ts b/packages/react/src/reactrouter-compat-utils/utils.ts index c0750c17c57c..1c8424b69a72 100644 --- a/packages/react/src/reactrouter-compat-utils/utils.ts +++ b/packages/react/src/reactrouter-compat-utils/utils.ts @@ -45,13 +45,22 @@ export function pathIsWildcardAndHasChildren(path: string, branch: RouteMatch ({ initializeRouterUtils: vi.fn(), getGlobalLocation: vi.fn(() => ({ pathname: '/test', search: '', hash: '' })), getGlobalPathname: vi.fn(() => '/test'), + routeIsDescendant: vi.fn(() => false), })); vi.mock('../../src/reactrouter-compat-utils/lazy-routes', () => ({ From dabe09f7c1bd936e207f51cdd70143cad1431694 Mon Sep 17 00:00:00 2001 From: s1gr1d Date: Wed, 22 Oct 2025 13:46:53 +0200 Subject: [PATCH 4/9] fix route naming --- .../1-noRecursion-noIndexRoute.md | 258 ----------------- .../2-noRecursion-withIndexRoute.md | 260 ------------------ .../3-withRecursion-noIndexRoute.md | 99 ------- .../4-withRecursion-withIndexRoute.md | 187 ------------- .../instrumentation.tsx | 16 -- .../src/reactrouter-compat-utils/utils.ts | 37 +-- .../instrumentation.test.tsx | 48 ++-- 7 files changed, 36 insertions(+), 869 deletions(-) delete mode 100644 packages/react/src/reactrouter-compat-utils/1-noRecursion-noIndexRoute.md delete mode 100644 packages/react/src/reactrouter-compat-utils/2-noRecursion-withIndexRoute.md delete mode 100644 packages/react/src/reactrouter-compat-utils/3-withRecursion-noIndexRoute.md delete mode 100644 packages/react/src/reactrouter-compat-utils/4-withRecursion-withIndexRoute.md diff --git a/packages/react/src/reactrouter-compat-utils/1-noRecursion-noIndexRoute.md b/packages/react/src/reactrouter-compat-utils/1-noRecursion-noIndexRoute.md deleted file mode 100644 index 9c6c672c5e83..000000000000 --- a/packages/react/src/reactrouter-compat-utils/1-noRecursion-noIndexRoute.md +++ /dev/null @@ -1,258 +0,0 @@ -## Expected Outcome - -It should work with index routes inside the children routes - -## Scenario - -- no recursion - ```ts - export function addRoutesToAllRoutes(routes: RouteObject[]): void { - console.log('routes to add to allRoutes::', JSON.stringify(routes)); - routes.forEach(route => { - if (!allRoutes.has(route)) { - allRoutes.add(route); - } - }); - } - ``` -- no index route - ```ts - { - path: '/post/:post', - element:

Post
, - children: [ - // { index: true, element:
Post Index
}, - { path: '/post/:post/related', element:
Related Posts
}, - ], - }, - ``` - -## Logs during E2E test - -Running 4 tests using 1 worker - -createV6CompatibleWrapCreateBrowserRouter - routes:: [{"path":"/post/:post","element":{"type":"div","key":null,"ref":null,"props":{"children":"Post"},"\_owner":null},"children":[{"path":"/post/:post/related","element":{"type":"div","key":null,"ref":null,"props":{"children":"Related Posts"},"_owner":null}}]},{"children":[{"path":"/","element":{"key":null,"ref":null,"props":{},"_owner":null}},{"path":"/*","element":{"key":null,"ref":null,"props":{},"_owner":null}}]}] - -routes to add to allRoutes:: [{"path":"/post/:post","element":{"type":"div","key":null,"ref":null,"props":{"children":"Post"},"\_owner":null},"children":[{"path":"/post/:post/related","element":{"type":"div","key":null,"ref":null,"props":{"children":"Related Posts"},"_owner":null}}]},{"children":[{"path":"/","element":{"key":null,"ref":null,"props":{},"_owner":null}},{"path":"/*","element":{"key":null,"ref":null,"props":{},"_owner":null}}]}] - -updatePageloadTransaction:: - -isInDescendantRoute:: true - -rebuildRoutePathFromAllRoutes matched: {pathname: /projects/123/views/234/567, search: , hash: , state: null, key: default} [{"params":{"_":"projects/123/views/234/567"},"pathname":"/","pathnameBase":"/","route":{"children":[{"path":"/","element":{"key":null,"ref":null,"props":{},"\_owner":null}},{"path":"/_","element":{"key":null,"ref":null,"props":{},"\_owner":null}}]}},{"params":{"_":"projects/123/views/234/567"},"pathname":"/projects/123/views/234/567","pathnameBase":"/","route":{"path":"/_","element":{"key":null,"ref":null,"props":{},"\_owner":null}}}] - -desc: name:: /projects/123/views/234/567 source:: route - -router:: {basename: undefined, future: undefined, state: undefined, routes: undefined, window: undefined} - -routes to add to allRoutes:: [{"path":":detailId","element":{"type":"div","key":null,"ref":null,"props":{"id":"details","children":"Details"},"_owner":null}}] - -updatePageloadTransaction:: - -isInDescendantRoute:: true - -rebuildRoutePathFromAllRoutes matched: {pathname: /projects/123/views/234/567, search: , hash: , state: null, key: default} [{"params":{"_":"projects/123/views/234/567"},"pathname":"/","pathnameBase":"/","route":{"children":[{"path":"/","element":{"key":null,"ref":null,"props":{},"\_owner":null}},{"path":"/_","element":{"key":null,"ref":null,"props":{},"\_owner":null}}]}},{"params":{"_":"projects/123/views/234/567"},"pathname":"/projects/123/views/234/567","pathnameBase":"/","route":{"path":"/_","element":{"key":null,"ref":null,"props":{},"\_owner":null}}}] -desc: name:: /projects/123/views/234/567 source:: route - -routes to add to allRoutes:: [{"index":true,"element":{"type":"div","key":null,"ref":null,"props":{"id":"views","children":"Views"},"_owner":null}},{"path":"views/:viewId/*","element":{"key":null,"ref":null,"props":{},"_owner":null}},{"path":"old-views/:viewId/*","element":{"key":null,"ref":null,"props":{},"_owner":null}}] - -updatePageloadTransaction:: -isInDescendantRoute:: true -rebuildRoutePathFromAllRoutes matched: {pathname: /projects/123/views/234/567, search: , hash: , state: null, key: default} [{"params":{"_":"projects/123/views/234/567"},"pathname":"/","pathnameBase":"/","route":{"children":[{"path":"/","element":{"key":null,"ref":null,"props":{},"\_owner":null}},{"path":"/_","element":{"key":null,"ref":null,"props":{},"\_owner":null}}]}},{"params":{"_":"projects/123/views/234/567"},"pathname":"/projects/123/views/234/567","pathnameBase":"/","route":{"path":"/_","element":{"key":null,"ref":null,"props":{},"\_owner":null}}}] - -desc: name:: /projects/123/views/234/567 source:: route - -routesFromChildren:: [{"id":"0","element":{"key":null,"ref":null,"props":{},"\_owner":null},"path":"projects","hasErrorBoundary":false,"children":[{"id":"0-0","element":{"type":"div","key":null,"ref":null,"props":{"children":"Project Page Root"},"\_owner":null},"index":true,"hasErrorBoundary":false},{"id":"0-1","element":{"key":null,"ref":null,"props":{},"\_owner":null},"path":"_","hasErrorBoundary":false,"children":[{"id":"0-1-0","element":{"key":null,"ref":null,"props":{},"\_owner":null},"path":":projectId/_","hasErrorBoundary":false}]}]}] - -routes to add to allRoutes:: [{"id":"0","element":{"key":null,"ref":null,"props":{},"\_owner":null},"path":"projects","hasErrorBoundary":false,"children":[{"id":"0-0","element":{"type":"div","key":null,"ref":null,"props":{"children":"Project Page Root"},"\_owner":null},"index":true,"hasErrorBoundary":false},{"id":"0-1","element":{"key":null,"ref":null,"props":{},"\_owner":null},"path":"_","hasErrorBoundary":false,"children":[{"id":"0-1-0","element":{"key":null,"ref":null,"props":{},"\_owner":null},"path":":projectId/_","hasErrorBoundary":false}]}]}] -updatePageloadTransaction:: - -isInDescendantRoute:: true - -rebuildRoutePathFromAllRoutes matched: {pathname: /projects/123/views/234/567, search: , hash: , state: null, key: default} [{"params":{"_":"views/234/567","projectId":"123"},"pathname":"/projects","pathnameBase":"/projects","route":{"id":"0","element":{"key":null,"ref":null,"props":{},"\_owner":null},"path":"projects","hasErrorBoundary":false,"children":[{"id":"0-0","element":{"type":"div","key":null,"ref":null,"props":{"children":"Project Page Root"},"\_owner":null},"index":true,"hasErrorBoundary":false},{"id":"0-1","element":{"key":null,"ref":null,"props":{},"\_owner":null},"path":"_","hasErrorBoundary":false,"children":[{"id":"0-1-0","element":{"key":null,"ref":null,"props":{},"_owner":null},"path":":projectId/*","hasErrorBoundary":false}]}]}},{"params":{"_":"views/234/567","projectId":"123"},"pathname":"/projects/123/views/234/567","pathnameBase":"/projects","route":{"id":"0-1","element":{"key":null,"ref":null,"props":{},"\_owner":null},"path":"_","hasErrorBoundary":false,"children":[{"id":"0-1-0","element":{"key":null,"ref":null,"props":{},"_owner":null},"path":":projectId/*","hasErrorBoundary":false}]}},{"params":{"_":"views/234/567","projectId":"123"},"pathname":"/projects/123/views/234/567","pathnameBase":"/projects/123","route":{"id":"0-1-0","element":{"key":null,"ref":null,"props":{},"\_owner":null},"path":":projectId/_","hasErrorBoundary":false}}] - -rebuildRoutePathFromAllRoutes matched: {pathname: /123/views/234/567} [{"params":{"_":"123/views/234/567"},"pathname":"/","pathnameBase":"/","route":{"children":[{"path":"/","element":{"key":null,"ref":null,"props":{},"\_owner":null}},{"path":"/_","element":{"key":null,"ref":null,"props":{},"\_owner":null}}]}},{"params":{"_":"123/views/234/567"},"pathname":"/123/views/234/567","pathnameBase":"/","route":{"path":"/_","element":{"key":null,"ref":null,"props":{},"\_owner":null}}}] - -desc: name:: /projects/123/views/234/567 source:: route - -✘ 1 [chromium] › tests/transactions.test.ts:4:5 › sends a pageload transaction with a parameterized URL (1.3s) - -✘ 2 [chromium] › tests/transactions.test.ts:30:5 › sends a pageload transaction with a parameterized URL - alternative route (1.2s) - -✘ 3 [chromium] › tests/transactions.test.ts:54:5 › sends a navigation transaction with a parameterized URL (2.3s) - -✘ 4 [chromium] › tests/transactions.test.ts:98:5 › sends a navigation transaction with a parameterized URL - alternative route (2.3s) - -1. [chromium] › tests/transactions.test.ts:4:5 › sends a pageload transaction with a parameterized URL - - Error: expect(received).toMatchObject(expected) - - Expected - 1 - * Received + 1 - - @@ -3,10 +3,10 @@ - "trace": Object { - "op": "pageload", - "origin": "auto.pageload.react.reactrouter_v7", - }, - }, - - "transaction": "/projects/:projectId/views/:viewId/:detailId", - * "transaction": "/projects/123/views/234/567", - "transaction_info": Object { - "source": "route", - }, - } - - 14 | - 15 | expect((await page.innerHTML('#root')).includes('Details')).toBe(true); - - > 16 | expect(rootSpan).toMatchObject({ - - | ^ - - 17 | contexts: { - 18 | trace: { - 19 | op: 'pageload', - at /private/var/folders/zg/3jx06d797kv5pggvpxj2bc540000gn/T/sentry-e2e-tests-react-router-7-cross-usage-mSvGiI/tests/transactions.test.ts:16:20 - - Error Context: test-results/transactions-sends-a-pagel-84aa8-on-with-a-parameterized-URL-chromium/error-context.md - - attachment #2: trace (application/zip) ───────────────────────────────────────────────────────── - test-results/transactions-sends-a-pagel-84aa8-on-with-a-parameterized-URL-chromium/trace.zip - Usage: - - pnpm exec playwright show-trace test-results/transactions-sends-a-pagel-84aa8-on-with-a-parameterized-URL-chromium/trace.zip - - ──────────────────────────────────────────────────────────────────────────────────────────────── - -2. [chromium] › tests/transactions.test.ts:30:5 › sends a pageload transaction with a parameterized URL - alternative route - - Error: expect(received).toMatchObject(expected) - - Expected - 1 - * Received + 1 - - @@ -3,10 +3,10 @@ - "trace": Object { - "op": "pageload", - "origin": "auto.pageload.react.reactrouter_v7", - }, - }, - - "transaction": "/projects/:projectId/old-views/:viewId/:detailId", - * "transaction": "/projects/234/old-views/234/567", - "transaction_info": Object { - "source": "route", - }, - } - - 38 | - 39 | expect((await page.innerHTML('#root')).includes('Details')).toBe(true); - - > 40 | expect(rootSpan).toMatchObject({ - - | ^ - - 41 | contexts: { - 42 | trace: { - 43 | op: 'pageload', - at /private/var/folders/zg/3jx06d797kv5pggvpxj2bc540000gn/T/sentry-e2e-tests-react-router-7-cross-usage-mSvGiI/tests/transactions.test.ts:40:20 - - Error Context: test-results/transactions-sends-a-pagel-f5147-zed-URL---alternative-route-chromium/error-context.md - - attachment #2: trace (application/zip) ───────────────────────────────────────────────────────── - test-results/transactions-sends-a-pagel-f5147-zed-URL---alternative-route-chromium/trace.zip - Usage: - - pnpm exec playwright show-trace test-results/transactions-sends-a-pagel-f5147-zed-URL---alternative-route-chromium/trace.zip - - ──────────────────────────────────────────────────────────────────────────────────────────────── - -3. [chromium] › tests/transactions.test.ts:54:5 › sends a navigation transaction with a parameterized URL - - Error: expect(received).toMatchObject(expected) - - Expected - 1 - * Received + 1 - - @@ -3,10 +3,10 @@ - "trace": Object { - "op": "navigation", - "origin": "auto.navigation.react.reactrouter_v7", - }, - }, - - "transaction": "/projects/:projectId/views/:viewId/:detailId", - * "transaction": "/projects/123/views/456/789", - "transaction_info": Object { - "source": "route", - }, - } - - 82 | - 83 | expect((await page.innerHTML('#root')).includes('Details')).toBe(true); - - > 84 | expect(navigationTxn).toMatchObject({ - - | ^ - - 85 | contexts: { - 86 | trace: { - 87 | op: 'navigation', - at /private/var/folders/zg/3jx06d797kv5pggvpxj2bc540000gn/T/sentry-e2e-tests-react-router-7-cross-usage-mSvGiI/tests/transactions.test.ts:84:25 - - Error Context: test-results/transactions-sends-a-navig-fc4a1-on-with-a-parameterized-URL-chromium/error-context.md - - attachment #2: trace (application/zip) ───────────────────────────────────────────────────────── - test-results/transactions-sends-a-navig-fc4a1-on-with-a-parameterized-URL-chromium/trace.zip - Usage: - - pnpm exec playwright show-trace test-results/transactions-sends-a-navig-fc4a1-on-with-a-parameterized-URL-chromium/trace.zip - - ──────────────────────────────────────────────────────────────────────────────────────────────── - -4. [chromium] › tests/transactions.test.ts:98:5 › sends a navigation transaction with a parameterized URL - alternative route - - Error: expect(received).toMatchObject(expected) - - Expected - 1 - * Received + 1 - - @@ -3,10 +3,10 @@ - "trace": Object { - "op": "navigation", - "origin": "auto.navigation.react.reactrouter_v7", - }, - }, - - "transaction": "/projects/:projectId/old-views/:viewId/:detailId", - * "transaction": "/projects/123/old-views/345/654", - "transaction_info": Object { - "source": "route", - }, - } - - 126 | - 127 | expect((await page.innerHTML('#root')).includes('Details')).toBe(true); - - > 128 | expect(navigationTxn).toMatchObject({ - - | ^ - - 129 | contexts: { - 130 | trace: { - 131 | op: 'navigation', - at /private/var/folders/zg/3jx06d797kv5pggvpxj2bc540000gn/T/sentry-e2e-tests-react-router-7-cross-usage-mSvGiI/tests/transactions.test.ts:128:25 - - Error Context: test-results/transactions-sends-a-navig-c389f-zed-URL---alternative-route-chromium/error-context.md - - attachment #2: trace (application/zip) ───────────────────────────────────────────────────────── - test-results/transactions-sends-a-navig-c389f-zed-URL---alternative-route-chromium/trace.zip - Usage: - - pnpm exec playwright show-trace test-results/transactions-sends-a-navig-c389f-zed-URL---alternative-route-chromium/trace.zip - - ──────────────────────────────────────────────────────────────────────────────────────────────── - -4 failed -[chromium] › tests/transactions.test.ts:4:5 › sends a pageload transaction with a parameterized URL -[chromium] › tests/transactions.test.ts:30:5 › sends a pageload transaction with a parameterized URL - alternative route -[chromium] › tests/transactions.test.ts:54:5 › sends a navigation transaction with a parameterized URL -[chromium] › tests/transactions.test.ts:98:5 › sends a navigation transaction with a parameterized URL - alternative route - -ELIFECYCLE  Test failed. See above for more details. - -ELIFECYCLE  Command failed with exit code 1. diff --git a/packages/react/src/reactrouter-compat-utils/2-noRecursion-withIndexRoute.md b/packages/react/src/reactrouter-compat-utils/2-noRecursion-withIndexRoute.md deleted file mode 100644 index b2f1aa466473..000000000000 --- a/packages/react/src/reactrouter-compat-utils/2-noRecursion-withIndexRoute.md +++ /dev/null @@ -1,260 +0,0 @@ -## Expected Outcome - -It should work with index routes inside the children routes - -## Scenario - -- no recursion - ```ts - export function addRoutesToAllRoutes(routes: RouteObject[]): void { - console.log('routes to add to allRoutes::', JSON.stringify(routes)); - routes.forEach(route => { - if (!allRoutes.has(route)) { - allRoutes.add(route); - } - }); - } - ``` -- with index route - ```ts - { - path: '/post/:post', - element:
Post
, - children: [ - { index: true, element:
Post Index
}, - { path: '/post/:post/related', element:
Related Posts
}, - ], - }, - ``` - -## Logs during E2E test - -Running 4 tests using 1 worker - -createV6CompatibleWrapCreateBrowserRouter - routes:: [{"path":"/post/:post","element":{"type":"div","key":null,"ref":null,"props":{"children":"Post"},"\_owner":null},"children":[{"index":true,"element":{"type":"div","key":null,"ref":null,"props":{"children":"Post Index"},"_owner":null}},{"path":"/post/:post/related","element":{"type":"div","key":null,"ref":null,"props":{"children":"Related Posts"},"_owner":null}}]},{"children":[{"path":"/","element":{"key":null,"ref":null,"props":{},"_owner":null}},{"path":"/*","element":{"key":null,"ref":null,"props":{},"_owner":null}}]}] - -routes to add to allRoutes:: [{"path":"/post/:post","element":{"type":"div","key":null,"ref":null,"props":{"children":"Post"},"\_owner":null},"children":[{"index":true,"element":{"type":"div","key":null,"ref":null,"props":{"children":"Post Index"},"_owner":null}},{"path":"/post/:post/related","element":{"type":"div","key":null,"ref":null,"props":{"children":"Related Posts"},"_owner":null}}]},{"children":[{"path":"/","element":{"key":null,"ref":null,"props":{},"_owner":null}},{"path":"/*","element":{"key":null,"ref":null,"props":{},"_owner":null}}]}] - -updatePageloadTransaction:: - -isInDescendantRoute:: true - -rebuildRoutePathFromAllRoutes matched: {pathname: /projects/123/views/234/567, search: , hash: , state: null, key: default} [{"params":{"_":"projects/123/views/234/567"},"pathname":"/","pathnameBase":"/","route":{"children":[{"path":"/","element":{"key":null,"ref":null,"props":{},"\_owner":null}},{"path":"/_","element":{"key":null,"ref":null,"props":{},"\_owner":null}}]}},{"params":{"_":"projects/123/views/234/567"},"pathname":"/projects/123/views/234/567","pathnameBase":"/","route":{"path":"/_","element":{"key":null,"ref":null,"props":{},"\_owner":null}}}] - -desc: name:: /projects/123/views/234/567 source:: route - -router:: {basename: undefined, future: undefined, state: undefined, routes: undefined, window: undefined} - -routes to add to allRoutes:: [{"path":":detailId","element":{"type":"div","key":null,"ref":null,"props":{"id":"details","children":"Details"},"_owner":null}}] - -updatePageloadTransaction:: - -isInDescendantRoute:: true - -rebuildRoutePathFromAllRoutes matched: {pathname: /projects/123/views/234/567, search: , hash: , state: null, key: default} [{"params":{"_":"projects/123/views/234/567"},"pathname":"/","pathnameBase":"/","route":{"children":[{"path":"/","element":{"key":null,"ref":null,"props":{},"\_owner":null}},{"path":"/_","element":{"key":null,"ref":null,"props":{},"\_owner":null}}]}},{"params":{"_":"projects/123/views/234/567"},"pathname":"/projects/123/views/234/567","pathnameBase":"/","route":{"path":"/_","element":{"key":null,"ref":null,"props":{},"\_owner":null}}}] - -desc: name:: /projects/123/views/234/567 source:: route - -routes to add to allRoutes:: [{"index":true,"element":{"type":"div","key":null,"ref":null,"props":{"id":"views","children":"Views"},"_owner":null}},{"path":"views/:viewId/*","element":{"key":null,"ref":null,"props":{},"_owner":null}},{"path":"old-views/:viewId/*","element":{"key":null,"ref":null,"props":{},"_owner":null}}] -updatePageloadTransaction:: - -isInDescendantRoute:: true - -rebuildRoutePathFromAllRoutes matched: {pathname: /projects/123/views/234/567, search: , hash: , state: null, key: default} [{"params":{"_":"projects/123/views/234/567"},"pathname":"/","pathnameBase":"/","route":{"children":[{"path":"/","element":{"key":null,"ref":null,"props":{},"\_owner":null}},{"path":"/_","element":{"key":null,"ref":null,"props":{},"\_owner":null}}]}},{"params":{"_":"projects/123/views/234/567"},"pathname":"/projects/123/views/234/567","pathnameBase":"/","route":{"path":"/_","element":{"key":null,"ref":null,"props":{},"\_owner":null}}}] - -desc: name:: /projects/123/views/234/567 source:: route -routesFromChildren:: [{"id":"0","element":{"key":null,"ref":null,"props":{},"\_owner":null},"path":"projects","hasErrorBoundary":false,"children":[{"id":"0-0","element":{"type":"div","key":null,"ref":null,"props":{"children":"Project Page Root"},"\_owner":null},"index":true,"hasErrorBoundary":false},{"id":"0-1","element":{"key":null,"ref":null,"props":{},"\_owner":null},"path":"_","hasErrorBoundary":false,"children":[{"id":"0-1-0","element":{"key":null,"ref":null,"props":{},"\_owner":null},"path":":projectId/_","hasErrorBoundary":false}]}]}] - -routes to add to allRoutes:: [{"id":"0","element":{"key":null,"ref":null,"props":{},"\_owner":null},"path":"projects","hasErrorBoundary":false,"children":[{"id":"0-0","element":{"type":"div","key":null,"ref":null,"props":{"children":"Project Page Root"},"\_owner":null},"index":true,"hasErrorBoundary":false},{"id":"0-1","element":{"key":null,"ref":null,"props":{},"\_owner":null},"path":"_","hasErrorBoundary":false,"children":[{"id":"0-1-0","element":{"key":null,"ref":null,"props":{},"\_owner":null},"path":":projectId/_","hasErrorBoundary":false}]}]}] - -updatePageloadTransaction:: - -isInDescendantRoute:: true - -rebuildRoutePathFromAllRoutes matched: {pathname: /projects/123/views/234/567, search: , hash: , state: null, key: default} [{"params":{"_":"views/234/567","projectId":"123"},"pathname":"/projects","pathnameBase":"/projects","route":{"id":"0","element":{"key":null,"ref":null,"props":{},"\_owner":null},"path":"projects","hasErrorBoundary":false,"children":[{"id":"0-0","element":{"type":"div","key":null,"ref":null,"props":{"children":"Project Page Root"},"\_owner":null},"index":true,"hasErrorBoundary":false},{"id":"0-1","element":{"key":null,"ref":null,"props":{},"\_owner":null},"path":"_","hasErrorBoundary":false,"children":[{"id":"0-1-0","element":{"key":null,"ref":null,"props":{},"_owner":null},"path":":projectId/*","hasErrorBoundary":false}]}]}},{"params":{"_":"views/234/567","projectId":"123"},"pathname":"/projects/123/views/234/567","pathnameBase":"/projects","route":{"id":"0-1","element":{"key":null,"ref":null,"props":{},"\_owner":null},"path":"_","hasErrorBoundary":false,"children":[{"id":"0-1-0","element":{"key":null,"ref":null,"props":{},"_owner":null},"path":":projectId/*","hasErrorBoundary":false}]}},{"params":{"_":"views/234/567","projectId":"123"},"pathname":"/projects/123/views/234/567","pathnameBase":"/projects/123","route":{"id":"0-1-0","element":{"key":null,"ref":null,"props":{},"\_owner":null},"path":":projectId/_","hasErrorBoundary":false}}] - -rebuildRoutePathFromAllRoutes matched: {pathname: /123/views/234/567} [{"params":{"_":"123/views/234/567"},"pathname":"/","pathnameBase":"/","route":{"children":[{"path":"/","element":{"key":null,"ref":null,"props":{},"\_owner":null}},{"path":"/_","element":{"key":null,"ref":null,"props":{},"\_owner":null}}]}},{"params":{"_":"123/views/234/567"},"pathname":"/123/views/234/567","pathnameBase":"/","route":{"path":"/_","element":{"key":null,"ref":null,"props":{},"\_owner":null}}}] - -desc: name:: /projects/123/views/234/567 source:: route - -✘ 1 [chromium] › tests/transactions.test.ts:4:5 › sends a pageload transaction with a parameterized URL (1.3s) - -✘ 2 [chromium] › tests/transactions.test.ts:30:5 › sends a pageload transaction with a parameterized URL - alternative route (1.2s) - -✘ 3 [chromium] › tests/transactions.test.ts:54:5 › sends a navigation transaction with a parameterized URL (2.3s) - -✘ 4 [chromium] › tests/transactions.test.ts:98:5 › sends a navigation transaction with a parameterized URL - alternative route (2.3s) - -1. [chromium] › tests/transactions.test.ts:4:5 › sends a pageload transaction with a parameterized URL - - Error: expect(received).toMatchObject(expected) - - Expected - 1 - * Received + 1 - - @@ -3,10 +3,10 @@ - "trace": Object { - "op": "pageload", - "origin": "auto.pageload.react.reactrouter_v7", - }, - }, - - "transaction": "/projects/:projectId/views/:viewId/:detailId", - * "transaction": "/projects/123/views/234/567", - "transaction_info": Object { - "source": "route", - }, - } - - 14 | - 15 | expect((await page.innerHTML('#root')).includes('Details')).toBe(true); - - > 16 | expect(rootSpan).toMatchObject({ - - | ^ - - 17 | contexts: { - 18 | trace: { - 19 | op: 'pageload', - at /private/var/folders/zg/3jx06d797kv5pggvpxj2bc540000gn/T/sentry-e2e-tests-react-router-7-cross-usage-KjJeCW/tests/transactions.test.ts:16:20 - - Error Context: test-results/transactions-sends-a-pagel-84aa8-on-with-a-parameterized-URL-chromium/error-context.md - - attachment #2: trace (application/zip) ───────────────────────────────────────────────────────── - test-results/transactions-sends-a-pagel-84aa8-on-with-a-parameterized-URL-chromium/trace.zip - Usage: - - pnpm exec playwright show-trace test-results/transactions-sends-a-pagel-84aa8-on-with-a-parameterized-URL-chromium/trace.zip - - ──────────────────────────────────────────────────────────────────────────────────────────────── - -2. [chromium] › tests/transactions.test.ts:30:5 › sends a pageload transaction with a parameterized URL - alternative route - - Error: expect(received).toMatchObject(expected) - - Expected - 1 - * Received + 1 - - @@ -3,10 +3,10 @@ - "trace": Object { - "op": "pageload", - "origin": "auto.pageload.react.reactrouter_v7", - }, - }, - - "transaction": "/projects/:projectId/old-views/:viewId/:detailId", - * "transaction": "/projects/234/old-views/234/567", - "transaction_info": Object { - "source": "route", - }, - } - - 38 | - 39 | expect((await page.innerHTML('#root')).includes('Details')).toBe(true); - - > 40 | expect(rootSpan).toMatchObject({ - - | ^ - - 41 | contexts: { - 42 | trace: { - 43 | op: 'pageload', - at /private/var/folders/zg/3jx06d797kv5pggvpxj2bc540000gn/T/sentry-e2e-tests-react-router-7-cross-usage-KjJeCW/tests/transactions.test.ts:40:20 - - Error Context: test-results/transactions-sends-a-pagel-f5147-zed-URL---alternative-route-chromium/error-context.md - - attachment #2: trace (application/zip) ───────────────────────────────────────────────────────── - test-results/transactions-sends-a-pagel-f5147-zed-URL---alternative-route-chromium/trace.zip - Usage: - - pnpm exec playwright show-trace test-results/transactions-sends-a-pagel-f5147-zed-URL---alternative-route-chromium/trace.zip - - ──────────────────────────────────────────────────────────────────────────────────────────────── - -3. [chromium] › tests/transactions.test.ts:54:5 › sends a navigation transaction with a parameterized URL - - Error: expect(received).toMatchObject(expected) - - Expected - 1 - * Received + 1 - - @@ -3,10 +3,10 @@ - "trace": Object { - "op": "navigation", - "origin": "auto.navigation.react.reactrouter_v7", - }, - }, - - "transaction": "/projects/:projectId/views/:viewId/:detailId", - * "transaction": "/projects/123/views/456/789", - "transaction_info": Object { - "source": "route", - }, - } - - 82 | - 83 | expect((await page.innerHTML('#root')).includes('Details')).toBe(true); - - > 84 | expect(navigationTxn).toMatchObject({ - - | ^ - - 85 | contexts: { - 86 | trace: { - 87 | op: 'navigation', - at /private/var/folders/zg/3jx06d797kv5pggvpxj2bc540000gn/T/sentry-e2e-tests-react-router-7-cross-usage-KjJeCW/tests/transactions.test.ts:84:25 - - Error Context: test-results/transactions-sends-a-navig-fc4a1-on-with-a-parameterized-URL-chromium/error-context.md - - attachment #2: trace (application/zip) ───────────────────────────────────────────────────────── - test-results/transactions-sends-a-navig-fc4a1-on-with-a-parameterized-URL-chromium/trace.zip - Usage: - - pnpm exec playwright show-trace test-results/transactions-sends-a-navig-fc4a1-on-with-a-parameterized-URL-chromium/trace.zip - - ──────────────────────────────────────────────────────────────────────────────────────────────── - -4. [chromium] › tests/transactions.test.ts:98:5 › sends a navigation transaction with a parameterized URL - alternative route - - Error: expect(received).toMatchObject(expected) - - Expected - 1 - * Received + 1 - - @@ -3,10 +3,10 @@ - "trace": Object { - "op": "navigation", - "origin": "auto.navigation.react.reactrouter_v7", - }, - }, - - "transaction": "/projects/:projectId/old-views/:viewId/:detailId", - * "transaction": "/projects/123/old-views/345/654", - "transaction_info": Object { - "source": "route", - }, - } - - 126 | - 127 | expect((await page.innerHTML('#root')).includes('Details')).toBe(true); - - > 128 | expect(navigationTxn).toMatchObject({ - - | ^ - - 129 | contexts: { - 130 | trace: { - 131 | op: 'navigation', - at /private/var/folders/zg/3jx06d797kv5pggvpxj2bc540000gn/T/sentry-e2e-tests-react-router-7-cross-usage-KjJeCW/tests/transactions.test.ts:128:25 - - Error Context: test-results/transactions-sends-a-navig-c389f-zed-URL---alternative-route-chromium/error-context.md - - attachment #2: trace (application/zip) ───────────────────────────────────────────────────────── - test-results/transactions-sends-a-navig-c389f-zed-URL---alternative-route-chromium/trace.zip - Usage: - - pnpm exec playwright show-trace test-results/transactions-sends-a-navig-c389f-zed-URL---alternative-route-chromium/trace.zip - - ──────────────────────────────────────────────────────────────────────────────────────────────── - -4 failed -[chromium] › tests/transactions.test.ts:4:5 › sends a pageload transaction with a parameterized URL -[chromium] › tests/transactions.test.ts:30:5 › sends a pageload transaction with a parameterized URL - alternative route -[chromium] › tests/transactions.test.ts:54:5 › sends a navigation transaction with a parameterized URL -[chromium] › tests/transactions.test.ts:98:5 › sends a navigation transaction with a parameterized URL - alternative route - -ELIFECYCLE  Test failed. See above for more details. - -ELIFECYCLE  Command failed with exit code 1. diff --git a/packages/react/src/reactrouter-compat-utils/3-withRecursion-noIndexRoute.md b/packages/react/src/reactrouter-compat-utils/3-withRecursion-noIndexRoute.md deleted file mode 100644 index dd7d5e04ed5b..000000000000 --- a/packages/react/src/reactrouter-compat-utils/3-withRecursion-noIndexRoute.md +++ /dev/null @@ -1,99 +0,0 @@ -## Expected Outcome - -It should work with index routes inside the children routes - -## Scenario - -- with recursion - - ```ts - export function addRoutesToAllRoutes(routes: RouteObject[]): void { - console.log('routes to add to allRoutes::', JSON.stringify(routes)); - routes.forEach(route => { - const extractedChildRoutes = getChildRoutesRecursively(route); - - extractedChildRoutes.forEach(r => { - allRoutes.add(r); - }); - }); - } - ``` - -- no index route - ```ts - { - path: '/post/:post', - element:
Post
, - children: [ - // { index: true, element:
Post Index
}, - { path: '/post/:post/related', element:
Related Posts
}, - ], - }, - ``` - -## Logs during E2E test - -Running 4 tests using 1 worker - -createV6CompatibleWrapCreateBrowserRouter - routes:: [{"path":"/post/:post","element":{"type":"div","key":null,"ref":null,"props":{"children":"Post"},"\_owner":null},"children":[{"path":"/post/:post/related","element":{"type":"div","key":null,"ref":null,"props":{"children":"Related Posts"},"_owner":null}}]},{"children":[{"path":"/","element":{"key":null,"ref":null,"props":{},"_owner":null}},{"path":"/*","element":{"key":null,"ref":null,"props":{},"_owner":null}}]}] - -routes to add to allRoutes:: [{"path":"/post/:post","element":{"type":"div","key":null,"ref":null,"props":{"children":"Post"},"\_owner":null},"children":[{"path":"/post/:post/related","element":{"type":"div","key":null,"ref":null,"props":{"children":"Related Posts"},"_owner":null}}]},{"children":[{"path":"/","element":{"key":null,"ref":null,"props":{},"_owner":null}},{"path":"/*","element":{"key":null,"ref":null,"props":{},"_owner":null}}]}] - -updatePageloadTransaction:: - -isInDescendantRoute:: true - -rebuildRoutePathFromAllRoutes matched: {pathname: /projects/123/views/234/567, search: , hash: , state: null, key: default} [{"params":{"_":"projects/123/views/234/567"},"pathname":"/","pathnameBase":"/","route":{"children":[{"path":"/","element":{"key":null,"ref":null,"props":{},"\_owner":null}},{"path":"/_","element":{"key":null,"ref":null,"props":{},"\_owner":null}}]}},{"params":{"_":"projects/123/views/234/567"},"pathname":"/projects/123/views/234/567","pathnameBase":"/","route":{"path":"/_","element":{"key":null,"ref":null,"props":{},"\_owner":null}}}] - -desc: name:: /projects/123/views/234/567 source:: route - -router:: {basename: undefined, future: undefined, state: undefined, routes: undefined, window: undefined} - -routes to add to allRoutes:: [{"path":":detailId","element":{"type":"div","key":null,"ref":null,"props":{"id":"details","children":"Details"},"_owner":null}}] - -updatePageloadTransaction:: - -isInDescendantRoute:: true - -rebuildRoutePathFromAllRoutes matched: {pathname: /projects/123/views/234/567, search: , hash: , state: null, key: default} [{"params":{"_":"projects/123/views/234/567"},"pathname":"/","pathnameBase":"/","route":{"children":[{"path":"/","element":{"key":null,"ref":null,"props":{},"\_owner":null}},{"path":"/_","element":{"key":null,"ref":null,"props":{},"\_owner":null}}]}},{"params":{"_":"projects/123/views/234/567"},"pathname":"/projects/123/views/234/567","pathnameBase":"/","route":{"path":"/_","element":{"key":null,"ref":null,"props":{},"\_owner":null}}}] - -desc: name:: /projects/123/views/234/567 source:: route -routes to add to allRoutes:: [{"index":true,"element":{"type":"div","key":null,"ref":null,"props":{"id":"views","children":"Views"},"_owner":null}},{"path":"views/:viewId/*","element":{"key":null,"ref":null,"props":{},"_owner":null}},{"path":"old-views/:viewId/*","element":{"key":null,"ref":null,"props":{},"_owner":null}}] -updatePageloadTransaction:: - -isInDescendantRoute:: true - -rebuildRoutePathFromAllRoutes matched: {pathname: /projects/123/views/234/567, search: , hash: , state: null, key: default} [{"params":{"_":"projects/123/views/234/567"},"pathname":"/","pathnameBase":"/","route":{"children":[{"path":"/","element":{"key":null,"ref":null,"props":{},"\_owner":null}},{"path":"/_","element":{"key":null,"ref":null,"props":{},"\_owner":null}}]}},{"params":{"_":"projects/123/views/234/567"},"pathname":"/projects/123/views/234/567","pathnameBase":"/","route":{"path":"/_","element":{"key":null,"ref":null,"props":{},"\_owner":null}}}] - -desc: name:: /projects/123/views/234/567 source:: route - -routesFromChildren:: [{"id":"0","element":{"key":null,"ref":null,"props":{},"\_owner":null},"path":"projects","hasErrorBoundary":false,"children":[{"id":"0-0","element":{"type":"div","key":null,"ref":null,"props":{"children":"Project Page Root"},"\_owner":null},"index":true,"hasErrorBoundary":false},{"id":"0-1","element":{"key":null,"ref":null,"props":{},"\_owner":null},"path":"_","hasErrorBoundary":false,"children":[{"id":"0-1-0","element":{"key":null,"ref":null,"props":{},"\_owner":null},"path":":projectId/_","hasErrorBoundary":false}]}]}] -routes to add to allRoutes:: [{"id":"0","element":{"key":null,"ref":null,"props":{},"\_owner":null},"path":"projects","hasErrorBoundary":false,"children":[{"id":"0-0","element":{"type":"div","key":null,"ref":null,"props":{"children":"Project Page Root"},"\_owner":null},"index":true,"hasErrorBoundary":false},{"id":"0-1","element":{"key":null,"ref":null,"props":{},"\_owner":null},"path":"_","hasErrorBoundary":false,"children":[{"id":"0-1-0","element":{"key":null,"ref":null,"props":{},"\_owner":null},"path":":projectId/_","hasErrorBoundary":false}]}]}] - -updatePageloadTransaction:: - -isInDescendantRoute:: true - -rebuildRoutePathFromAllRoutes matched: {pathname: /projects/123/views/234/567, search: , hash: , state: null, key: default} [{"params":{"_":"views/234/567","projectId":"123"},"pathname":"/projects","pathnameBase":"/projects","route":{"id":"0","element":{"key":null,"ref":null,"props":{},"\_owner":null},"path":"projects","hasErrorBoundary":false,"children":[{"id":"0-0","element":{"type":"div","key":null,"ref":null,"props":{"children":"Project Page Root"},"\_owner":null},"index":true,"hasErrorBoundary":false},{"id":"0-1","element":{"key":null,"ref":null,"props":{},"\_owner":null},"path":"_","hasErrorBoundary":false,"children":[{"id":"0-1-0","element":{"key":null,"ref":null,"props":{},"_owner":null},"path":":projectId/*","hasErrorBoundary":false}]}]}},{"params":{"_":"views/234/567","projectId":"123"},"pathname":"/projects/123/views/234/567","pathnameBase":"/projects","route":{"id":"0-1","element":{"key":null,"ref":null,"props":{},"\_owner":null},"path":"_","hasErrorBoundary":false,"children":[{"id":"0-1-0","element":{"key":null,"ref":null,"props":{},"_owner":null},"path":":projectId/*","hasErrorBoundary":false}]}},{"params":{"_":"views/234/567","projectId":"123"},"pathname":"/projects/123/views/234/567","pathnameBase":"/projects/123","route":{"id":"0-1-0","element":{"key":null,"ref":null,"props":{},"\_owner":null},"path":":projectId/_","hasErrorBoundary":false}}] - -rebuildRoutePathFromAllRoutes matched: {pathname: /123/views/234/567} [{"params":{"_":"views/234/567","projectId":"123"},"pathname":"/123/views/234/567","pathnameBase":"/","route":{"id":"0-1","element":{"key":null,"ref":null,"props":{},"\_owner":null},"path":"_","hasErrorBoundary":false,"children":[{"id":"0-1-0","element":{"key":null,"ref":null,"props":{},"_owner":null},"path":":projectId/*","hasErrorBoundary":false}]}},{"params":{"_":"views/234/567","projectId":"123"},"pathname":"/123/views/234/567","pathnameBase":"/123","route":{"id":"0-1-0","element":{"key":null,"ref":null,"props":{},"\_owner":null},"path":":projectId/_","hasErrorBoundary":false}}] - -rebuildRoutePathFromAllRoutes matched: {pathname: /views/234/567} [{"params":{"viewId":"234","*":"567"},"pathname":"/views/234/567","pathnameBase":"/views/234","route":{"path":"views/:viewId/*","element":{"key":null,"ref":null,"props":{},"_owner":null}}}] - -rebuildRoutePathFromAllRoutes matched: {pathname: /567} [{"params":{"detailId":"567"},"pathname":"/567","pathnameBase":"/567","route":{"path":":detailId","element":{"type":"div","key":null,"ref":null,"props":{"id":"details","children":"Details"},"_owner":null}}}] - -rebuildRoutePathFromAllRoutes matched: {pathname: /} [{"params":{},"pathname":"/","pathnameBase":"/","route":{"index":true,"element":{"type":"div","key":null,"ref":null,"props":{"id":"views","children":"Views"},"_owner":null}}}] - -desc: name:: /projects/:projectId/views/:viewId/:detailId source:: route - -✓ 1 [chromium] › tests/transactions.test.ts:4:5 › sends a pageload transaction with a parameterized URL (1.3s) - -✓ 2 [chromium] › tests/transactions.test.ts:30:5 › sends a pageload transaction with a parameterized URL - alternative route (1.1s) - -✓ 3 [chromium] › tests/transactions.test.ts:54:5 › sends a navigation transaction with a parameterized URL (2.2s) - -✓ 4 [chromium] › tests/transactions.test.ts:98:5 › sends a navigation transaction with a parameterized URL - alternative route (2.2s) - -4 passed (9.9s) - -✨ Done in 47.87s. diff --git a/packages/react/src/reactrouter-compat-utils/4-withRecursion-withIndexRoute.md b/packages/react/src/reactrouter-compat-utils/4-withRecursion-withIndexRoute.md deleted file mode 100644 index c4195886bc77..000000000000 --- a/packages/react/src/reactrouter-compat-utils/4-withRecursion-withIndexRoute.md +++ /dev/null @@ -1,187 +0,0 @@ -## Expected Outcome - -It should work with index routes inside the children routes - -## Scenario - -- with recursion - - ```ts - export function addRoutesToAllRoutes(routes: RouteObject[]): void { - console.log('routes to add to allRoutes::', JSON.stringify(routes)); - routes.forEach(route => { - const extractedChildRoutes = getChildRoutesRecursively(route); - - extractedChildRoutes.forEach(r => { - allRoutes.add(r); - }); - }); - } - ``` - -- with index route - ```ts - { - path: '/post/:post', - element:
Post
, - children: [ - { index: true, element:
Post Index
}, - { path: '/post/:post/related', element:
Related Posts
}, - ], - }, - ``` - -## Logs during E2E test - -Running 4 tests using 1 worker - -createV6CompatibleWrapCreateBrowserRouter - routes:: [{"path":"/post/:post","element":{"type":"div","key":null,"ref":null,"props":{"children":"Post"},"\_owner":null},"children":[{"index":true,"element":{"type":"div","key":null,"ref":null,"props":{"children":"Post Index"},"_owner":null}},{"path":"/post/:post/related","element":{"type":"div","key":null,"ref":null,"props":{"children":"Related Posts"},"_owner":null}}]},{"children":[{"path":"/","element":{"key":null,"ref":null,"props":{},"_owner":null}},{"path":"/*","element":{"key":null,"ref":null,"props":{},"_owner":null}}]}] - -routes to add to allRoutes:: [{"path":"/post/:post","element":{"type":"div","key":null,"ref":null,"props":{"children":"Post"},"\_owner":null},"children":[{"index":true,"element":{"type":"div","key":null,"ref":null,"props":{"children":"Post Index"},"_owner":null}},{"path":"/post/:post/related","element":{"type":"div","key":null,"ref":null,"props":{"children":"Related Posts"},"_owner":null}}]},{"children":[{"path":"/","element":{"key":null,"ref":null,"props":{},"_owner":null}},{"path":"/*","element":{"key":null,"ref":null,"props":{},"_owner":null}}]}] - -updatePageloadTransaction:: - -isInDescendantRoute:: true - -rebuildRoutePathFromAllRoutes matched: {pathname: /projects/123/views/234/567, search: , hash: , state: null, key: default} [{"params":{"_":"projects/123/views/234/567"},"pathname":"/","pathnameBase":"/","route":{"children":[{"path":"/","element":{"key":null,"ref":null,"props":{},"\_owner":null}},{"path":"/_","element":{"key":null,"ref":null,"props":{},"\_owner":null}}]}},{"params":{"_":"projects/123/views/234/567"},"pathname":"/projects/123/views/234/567","pathnameBase":"/","route":{"path":"/_","element":{"key":null,"ref":null,"props":{},"\_owner":null}}}] - -desc: name:: /projects/123/views/234/567 source:: route - -router:: {basename: undefined, future: undefined, state: undefined, routes: undefined, window: undefined} - -routes to add to allRoutes:: [{"path":":detailId","element":{"type":"div","key":null,"ref":null,"props":{"id":"details","children":"Details"},"_owner":null}}] - -updatePageloadTransaction:: - -isInDescendantRoute:: true - -rebuildRoutePathFromAllRoutes matched: {pathname: /projects/123/views/234/567, search: , hash: , state: null, key: default} [{"params":{"_":"projects/123/views/234/567"},"pathname":"/","pathnameBase":"/","route":{"children":[{"path":"/","element":{"key":null,"ref":null,"props":{},"\_owner":null}},{"path":"/_","element":{"key":null,"ref":null,"props":{},"\_owner":null}}]}},{"params":{"_":"projects/123/views/234/567"},"pathname":"/projects/123/views/234/567","pathnameBase":"/","route":{"path":"/_","element":{"key":null,"ref":null,"props":{},"\_owner":null}}}] - -desc: name:: /projects/123/views/234/567 source:: route -routes to add to allRoutes:: [{"index":true,"element":{"type":"div","key":null,"ref":null,"props":{"id":"views","children":"Views"},"_owner":null}},{"path":"views/:viewId/*","element":{"key":null,"ref":null,"props":{},"_owner":null}},{"path":"old-views/:viewId/*","element":{"key":null,"ref":null,"props":{},"_owner":null}}] - -updatePageloadTransaction:: - -isInDescendantRoute:: true - -rebuildRoutePathFromAllRoutes matched: {pathname: /projects/123/views/234/567, search: , hash: , state: null, key: default} [{"params":{"_":"projects/123/views/234/567"},"pathname":"/","pathnameBase":"/","route":{"children":[{"path":"/","element":{"key":null,"ref":null,"props":{},"\_owner":null}},{"path":"/_","element":{"key":null,"ref":null,"props":{},"\_owner":null}}]}},{"params":{"_":"projects/123/views/234/567"},"pathname":"/projects/123/views/234/567","pathnameBase":"/","route":{"path":"/_","element":{"key":null,"ref":null,"props":{},"\_owner":null}}}] - -desc: name:: /projects/123/views/234/567 source:: route - -routesFromChildren:: [{"id":"0","element":{"key":null,"ref":null,"props":{},"\_owner":null},"path":"projects","hasErrorBoundary":false,"children":[{"id":"0-0","element":{"type":"div","key":null,"ref":null,"props":{"children":"Project Page Root"},"\_owner":null},"index":true,"hasErrorBoundary":false},{"id":"0-1","element":{"key":null,"ref":null,"props":{},"\_owner":null},"path":"_","hasErrorBoundary":false,"children":[{"id":"0-1-0","element":{"key":null,"ref":null,"props":{},"\_owner":null},"path":":projectId/_","hasErrorBoundary":false}]}]}] - -routes to add to allRoutes:: [{"id":"0","element":{"key":null,"ref":null,"props":{},"\_owner":null},"path":"projects","hasErrorBoundary":false,"children":[{"id":"0-0","element":{"type":"div","key":null,"ref":null,"props":{"children":"Project Page Root"},"\_owner":null},"index":true,"hasErrorBoundary":false},{"id":"0-1","element":{"key":null,"ref":null,"props":{},"\_owner":null},"path":"_","hasErrorBoundary":false,"children":[{"id":"0-1-0","element":{"key":null,"ref":null,"props":{},"\_owner":null},"path":":projectId/_","hasErrorBoundary":false}]}]}] -updatePageloadTransaction:: - -isInDescendantRoute:: true - -rebuildRoutePathFromAllRoutes matched: {pathname: /projects/123/views/234/567, search: , hash: , state: null, key: default} [{"params":{"_":"views/234/567","projectId":"123"},"pathname":"/projects","pathnameBase":"/projects","route":{"id":"0","element":{"key":null,"ref":null,"props":{},"\_owner":null},"path":"projects","hasErrorBoundary":false,"children":[{"id":"0-0","element":{"type":"div","key":null,"ref":null,"props":{"children":"Project Page Root"},"\_owner":null},"index":true,"hasErrorBoundary":false},{"id":"0-1","element":{"key":null,"ref":null,"props":{},"\_owner":null},"path":"_","hasErrorBoundary":false,"children":[{"id":"0-1-0","element":{"key":null,"ref":null,"props":{},"_owner":null},"path":":projectId/*","hasErrorBoundary":false}]}]}},{"params":{"_":"views/234/567","projectId":"123"},"pathname":"/projects/123/views/234/567","pathnameBase":"/projects","route":{"id":"0-1","element":{"key":null,"ref":null,"props":{},"\_owner":null},"path":"_","hasErrorBoundary":false,"children":[{"id":"0-1-0","element":{"key":null,"ref":null,"props":{},"_owner":null},"path":":projectId/*","hasErrorBoundary":false}]}},{"params":{"_":"views/234/567","projectId":"123"},"pathname":"/projects/123/views/234/567","pathnameBase":"/projects/123","route":{"id":"0-1-0","element":{"key":null,"ref":null,"props":{},"\_owner":null},"path":":projectId/_","hasErrorBoundary":false}}] - -rebuildRoutePathFromAllRoutes matched: {pathname: /123/views/234/567} [{"params":{"_":"views/234/567","projectId":"123"},"pathname":"/123/views/234/567","pathnameBase":"/","route":{"id":"0-1","element":{"key":null,"ref":null,"props":{},"\_owner":null},"path":"_","hasErrorBoundary":false,"children":[{"id":"0-1-0","element":{"key":null,"ref":null,"props":{},"_owner":null},"path":":projectId/*","hasErrorBoundary":false}]}},{"params":{"_":"views/234/567","projectId":"123"},"pathname":"/123/views/234/567","pathnameBase":"/123","route":{"id":"0-1-0","element":{"key":null,"ref":null,"props":{},"\_owner":null},"path":":projectId/_","hasErrorBoundary":false}}] - -rebuildRoutePathFromAllRoutes matched: {pathname: /views/234/567} [{"params":{"viewId":"234","*":"567"},"pathname":"/views/234/567","pathnameBase":"/views/234","route":{"path":"views/:viewId/*","element":{"key":null,"ref":null,"props":{},"_owner":null}}}] - -rebuildRoutePathFromAllRoutes matched: {pathname: /567} [{"params":{"detailId":"567"},"pathname":"/567","pathnameBase":"/567","route":{"path":":detailId","element":{"type":"div","key":null,"ref":null,"props":{"id":"details","children":"Details"},"_owner":null}}}] - -rebuildRoutePathFromAllRoutes matched: {pathname: /} [{"params":{},"pathname":"/","pathnameBase":"/","route":{"index":true,"element":{"type":"div","key":null,"ref":null,"props":{"children":"Post Index"},"_owner":null}}}] - -desc: name:: /projects/:projectId/views/:viewId/:detailId source:: route - -✓ 1 [chromium] › tests/transactions.test.ts:4:5 › sends a pageload transaction with a parameterized URL (1.3s) - -✓ 2 [chromium] › tests/transactions.test.ts:30:5 › sends a pageload transaction with a parameterized URL - alternative route (1.2s) - -✘ 3 [chromium] › tests/transactions.test.ts:54:5 › sends a navigation transaction with a parameterized URL (1.2s) - -✘ 4 [chromium] › tests/transactions.test.ts:98:5 › sends a navigation transaction with a parameterized URL - alternative route (1.2s) - -1. [chromium] › tests/transactions.test.ts:54:5 › sends a navigation transaction with a parameterized URL - - Error: expect(received).toMatchObject(expected) - - Expected - 1 - * Received + 1 - - @@ -3,10 +3,10 @@ - "trace": Object { - "op": "pageload", - "origin": "auto.pageload.react.reactrouter_v7", - }, - }, - - "transaction": "/", - * "transaction": "", - "transaction_info": Object { - "source": "route", - }, - } - - 64 | const pageloadTxn = await pageloadTxnPromise; - 65 | - - > 66 | expect(pageloadTxn).toMatchObject({ - - | ^ - - 67 | contexts: { - 68 | trace: { - 69 | op: 'pageload', - at /private/var/folders/zg/3jx06d797kv5pggvpxj2bc540000gn/T/sentry-e2e-tests-react-router-7-cross-usage-Gan5my/tests/transactions.test.ts:66:23 - - Error Context: test-results/transactions-sends-a-navig-fc4a1-on-with-a-parameterized-URL-chromium/error-context.md - - attachment #2: trace (application/zip) ───────────────────────────────────────────────────────── - test-results/transactions-sends-a-navig-fc4a1-on-with-a-parameterized-URL-chromium/trace.zip - Usage: - - pnpm exec playwright show-trace test-results/transactions-sends-a-navig-fc4a1-on-with-a-parameterized-URL-chromium/trace.zip - - ──────────────────────────────────────────────────────────────────────────────────────────────── - -2. [chromium] › tests/transactions.test.ts:98:5 › sends a navigation transaction with a parameterized URL - alternative route - - Error: expect(received).toMatchObject(expected) - - Expected - 1 - * Received + 1 - - @@ -3,10 +3,10 @@ - "trace": Object { - "op": "pageload", - "origin": "auto.pageload.react.reactrouter_v7", - }, - }, - - "transaction": "/", - * "transaction": "", - "transaction_info": Object { - "source": "route", - }, - } - - 108 | const pageloadTxn = await pageloadTxnPromise; - 109 | - - > 110 | expect(pageloadTxn).toMatchObject({ - - | ^ - - 111 | contexts: { - 112 | trace: { - 113 | op: 'pageload', - at /private/var/folders/zg/3jx06d797kv5pggvpxj2bc540000gn/T/sentry-e2e-tests-react-router-7-cross-usage-Gan5my/tests/transactions.test.ts:110:23 - - Error Context: test-results/transactions-sends-a-navig-c389f-zed-URL---alternative-route-chromium/error-context.md - - attachment #2: trace (application/zip) ───────────────────────────────────────────────────────── - test-results/transactions-sends-a-navig-c389f-zed-URL---alternative-route-chromium/trace.zip - Usage: - - pnpm exec playwright show-trace test-results/transactions-sends-a-navig-c389f-zed-URL---alternative-route-chromium/trace.zip - - ──────────────────────────────────────────────────────────────────────────────────────────────── - -2 failed -[chromium] › tests/transactions.test.ts:54:5 › sends a navigation transaction with a parameterized URL -[chromium] › tests/transactions.test.ts:98:5 › sends a navigation transaction with a parameterized URL - alternative route -2 passed (8.4s) - -ELIFECYCLE  Test failed. See above for more details. - -ELIFECYCLE  Command failed with exit code 1. diff --git a/packages/react/src/reactrouter-compat-utils/instrumentation.tsx b/packages/react/src/reactrouter-compat-utils/instrumentation.tsx index 09470a96716e..7489f31aae9a 100644 --- a/packages/react/src/reactrouter-compat-utils/instrumentation.tsx +++ b/packages/react/src/reactrouter-compat-utils/instrumentation.tsx @@ -226,7 +226,6 @@ export function createV6CompatibleWrapCreateBrowserRouter< } return function (routes: RouteObject[], opts?: Record & { basename?: string }): TRouter { - console.log('createV6CompatibleWrapCreateBrowserRouter - routes::', JSON.stringify(routes)); addRoutesToAllRoutes(routes); // Check for async handlers that might contain sub-route declarations (only if enabled) @@ -244,8 +243,6 @@ export function createV6CompatibleWrapCreateBrowserRouter< const activeRootSpan = getActiveRootSpan(); - // console.log('activeroo', activeRootSpan); - // Track whether we've completed the initial pageload to properly distinguish // between POPs that occur during pageload vs. legitimate back/forward navigation. let isInitialPageloadComplete = false; @@ -658,7 +655,6 @@ export function handleNavigation(opts: { } if ((navigationType === 'PUSH' || navigationType === 'POP') && branches) { - console.log('allRoutes::', allRoutes); const [name, source] = resolveRouteNameAndSource( location, routes, @@ -671,8 +667,6 @@ export function handleNavigation(opts: { const spanJson = activeSpan && spanToJSON(activeSpan); const isAlreadyInNavigationSpan = spanJson?.op === 'navigation'; - console.log('name::', name); - // Cross usage can result in multiple navigation spans being created without this check if (!isAlreadyInNavigationSpan) { startBrowserTracingNavigationSpan(client, { @@ -689,9 +683,6 @@ export function handleNavigation(opts: { /* Only exported for testing purposes */ export function addRoutesToAllRoutes(routes: RouteObject[]): void { - console.log('routes to add to allRoutes::', JSON.stringify(routes)); - - // fixme: this maybe has a bug routes.forEach(route => { const extractedChildRoutes = getChildRoutesRecursively(route); @@ -734,7 +725,6 @@ function updatePageloadTransaction({ basename?: string; allRoutes?: RouteObject[]; }): void { - console.log('updatePageloadTransaction::'); const branches = Array.isArray(matches) ? matches : (_matchRoutes(allRoutes || routes, location, basename) as unknown as RouteMatch[]); @@ -744,18 +734,14 @@ function updatePageloadTransaction({ source: TransactionSource = 'url'; const isInDescendantRoute = locationIsInsideDescendantRoute(location, allRoutes || routes); - console.log('isInDescendantRoute::', isInDescendantRoute); if (isInDescendantRoute) { name = prefixWithSlash(rebuildRoutePathFromAllRoutes(allRoutes || routes, location)); source = 'route'; - console.log('desc: name::', name, 'source::', source); } if (!isInDescendantRoute || !name) { [name, source] = getNormalizedName(routes, location, branches, basename); - - console.log('name::', name, 'source::', source); } getCurrentScope().setTransactionName(name || '/'); @@ -791,8 +777,6 @@ export function createV6CompatibleWithSentryReactRouterRouting

{ const routes = _createRoutesFromChildren(props.children) as RouteObject[]; - console.log('routesFromChildren::', JSON.stringify(routes)); - if (isMountRenderPass.current) { addRoutesToAllRoutes(routes); diff --git a/packages/react/src/reactrouter-compat-utils/utils.ts b/packages/react/src/reactrouter-compat-utils/utils.ts index 1c8424b69a72..35ef464f96eb 100644 --- a/packages/react/src/reactrouter-compat-utils/utils.ts +++ b/packages/react/src/reactrouter-compat-utils/utils.ts @@ -53,25 +53,22 @@ export function routeIsDescendant(route: RouteObject): boolean { } function sendIndexPath(pathBuilder: string, pathname: string, basename: string): [string, TransactionSource] { - const reconstructedPath = pathBuilder || _stripBasename ? stripBasenameFromPathname(pathname, basename) : pathname; - - if (reconstructedPath === '/') { - // return ['/', 'route']; + const reconstructedPath = + pathBuilder && pathBuilder.length > 0 + ? pathBuilder + : _stripBasename + ? stripBasenameFromPathname(pathname, basename) + : pathname; + + let formattedPath = + // If the path ends with a wildcard suffix, remove both the slash and the asterisk + reconstructedPath.slice(-2) === '/*' ? reconstructedPath.slice(0, -2) : reconstructedPath; + + // If the path ends with a slash, remove it (but keep single '/') + if (formattedPath.length > 1 && formattedPath[formattedPath.length - 1] === '/') { + formattedPath = formattedPath.slice(0, -1); } - console.log('reconstructedPath for index route:', reconstructedPath); - - const formattedPath = - // If the path ends with a slash, remove it - reconstructedPath[reconstructedPath.length - 1] === '/' - ? reconstructedPath.slice(0, -1) - : // If the path ends with a wildcard, remove it - reconstructedPath.slice(-2) === '/*' - ? reconstructedPath.slice(0, -1) - : reconstructedPath; - - console.log('formattedPath for index route:', formattedPath); - return [formattedPath, 'route']; } @@ -129,8 +126,6 @@ export function prefixWithSlash(path: string): string { export function rebuildRoutePathFromAllRoutes(allRoutes: RouteObject[], location: Location): string { const matchedRoutes = _matchRoutes(allRoutes, location) as RouteMatch[]; - console.log('rebuildRoutePathFromAllRoutes matched: ', location, JSON.stringify(matchedRoutes)); - if (!matchedRoutes || matchedRoutes.length === 0) { return ''; } @@ -195,16 +190,12 @@ export function getNormalizedName( let pathBuilder = ''; - // console.log('branches:: ', JSON.stringify(branches, null, 2)); - if (branches) { for (const branch of branches) { const route = branch.route; - console.log('branch.route', JSON.stringify(branch.route, null, 2)); if (route) { // Early return if index route if (route.index) { - console.log('index route', pathBuilder, branch.pathname, basename); return sendIndexPath(pathBuilder, branch.pathname, basename); } const path = route.path; diff --git a/packages/react/test/reactrouter-compat-utils/instrumentation.test.tsx b/packages/react/test/reactrouter-compat-utils/instrumentation.test.tsx index c05cd1c4c166..4785849f1192 100644 --- a/packages/react/test/reactrouter-compat-utils/instrumentation.test.tsx +++ b/packages/react/test/reactrouter-compat-utils/instrumentation.test.tsx @@ -209,27 +209,24 @@ describe('addRoutesToAllRoutes', () => { const allRoutesArr = Array.from(allRoutes); expect(allRoutesArr).toEqual([ - { - path: '/', - element: expect.objectContaining({ type: 'div', props: {} }), - }, - { - path: '/user/:id', - element: expect.objectContaining({ type: 'div', props: {} }), - }, - { - path: '/group/:group/:user?', - element: expect.objectContaining({ type: 'div', props: {} }), - }, + { path: '/', element:

}, + { path: '/user/:id', element:
}, + { path: '/group/:group/:user?', element:
}, + // v1 routes ---- { path: '/v1/post/:post', - element: expect.objectContaining({ type: 'div', props: {} }), + element:
, children: [ { element:
, path: 'featured' }, { element:
, path: '/v1/post/:post/related' }, { children: [{ element:
Edit Post
, path: 'edit' }], element:
More Nested Children
}, ], }, + { element:
, path: 'featured' }, + { element:
, path: '/v1/post/:post/related' }, + { children: [{ element:
Edit Post
, path: 'edit' }], element:
More Nested Children
}, + { element:
Edit Post
, path: 'edit' }, + // v2 routes --- { path: '/v2/post/:post', element: expect.objectContaining({ type: 'div', props: {} }), @@ -239,6 +236,9 @@ describe('addRoutesToAllRoutes', () => { { element:
, path: '/v2/post/:post/related' }, ], }, + { element:
, index: true }, + { element:
, path: 'featured' }, + { element:
, path: '/v2/post/:post/related' }, ]); }); @@ -266,6 +266,8 @@ describe('addRoutesToAllRoutes', () => { { element:
Settings
, path: 'settings' }, ], }, + { element:
Dashboard Index
, index: true }, + { element:
Settings
, path: 'settings' }, ]); }); @@ -275,14 +277,10 @@ describe('addRoutesToAllRoutes', () => { path: '/', element:
Root
, children: [ - { path: 'contact', element:
Contact
}, { path: 'dashboard', element:
Dashboard
}, { element:
AuthLayout
, - children: [ - { path: 'login', element:
Login
}, - { path: 'logout', element:
Logout
}, - ], + children: [{ path: 'login', element:
Login
}], }, ], }, @@ -296,10 +294,6 @@ describe('addRoutesToAllRoutes', () => { path: '/', element: expect.objectContaining({ type: 'div', props: { children: 'Root' } }), children: [ - { - path: 'contact', - element: expect.objectContaining({ type: 'div', props: { children: 'Contact' } }), - }, { path: 'dashboard', element: expect.objectContaining({ type: 'div', props: { children: 'Dashboard' } }), @@ -311,14 +305,16 @@ describe('addRoutesToAllRoutes', () => { path: 'login', element: expect.objectContaining({ type: 'div', props: { children: 'Login' } }), }, - { - path: 'logout', - element: expect.objectContaining({ type: 'div', props: { children: 'Logout' } }), - }, ], }, ], }, + { element:
Dashboard
, path: 'dashboard' }, + { + children: [{ element:
Login
, path: 'login' }], + element:
AuthLayout
, + }, + { element:
Login
, path: 'login' }, ]); }); From 324ff7c2a5dab6b43716fb42938b7b784aa990f4 Mon Sep 17 00:00:00 2001 From: s1gr1d Date: Wed, 22 Oct 2025 13:50:15 +0200 Subject: [PATCH 5/9] fix comments --- .../react/src/reactrouter-compat-utils/instrumentation.tsx | 1 - packages/react/src/reactrouter-compat-utils/utils.ts | 6 +----- 2 files changed, 1 insertion(+), 6 deletions(-) diff --git a/packages/react/src/reactrouter-compat-utils/instrumentation.tsx b/packages/react/src/reactrouter-compat-utils/instrumentation.tsx index 7489f31aae9a..d0676226b18a 100644 --- a/packages/react/src/reactrouter-compat-utils/instrumentation.tsx +++ b/packages/react/src/reactrouter-compat-utils/instrumentation.tsx @@ -48,7 +48,6 @@ import { prefixWithSlash, rebuildRoutePathFromAllRoutes, resolveRouteNameAndSource, - routeIsDescendant, } from './utils'; let _useEffect: UseEffect; diff --git a/packages/react/src/reactrouter-compat-utils/utils.ts b/packages/react/src/reactrouter-compat-utils/utils.ts index 35ef464f96eb..d6501d0e4dbf 100644 --- a/packages/react/src/reactrouter-compat-utils/utils.ts +++ b/packages/react/src/reactrouter-compat-utils/utils.ts @@ -45,9 +45,7 @@ export function pathIsWildcardAndHasChildren(path: string, branch: RouteMatch within ) */ export function routeIsDescendant(route: RouteObject): boolean { return !!(!route.children && route.element && route.path?.endsWith('/*')); } @@ -130,8 +128,6 @@ export function rebuildRoutePathFromAllRoutes(allRoutes: RouteObject[], location return ''; } - // fixme: this maybe has a bug - for (const match of matchedRoutes) { if (match.route.path && match.route.path !== '*') { const path = pickPath(match); From b4041439ba269778d4bcbdbb7f3d45cb64e023a3 Mon Sep 17 00:00:00 2001 From: s1gr1d Date: Wed, 22 Oct 2025 13:54:31 +0200 Subject: [PATCH 6/9] revert debug change --- packages/react/src/reactrouter-compat-utils/instrumentation.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/react/src/reactrouter-compat-utils/instrumentation.tsx b/packages/react/src/reactrouter-compat-utils/instrumentation.tsx index d0676226b18a..a72c4fd05378 100644 --- a/packages/react/src/reactrouter-compat-utils/instrumentation.tsx +++ b/packages/react/src/reactrouter-compat-utils/instrumentation.tsx @@ -657,7 +657,7 @@ export function handleNavigation(opts: { const [name, source] = resolveRouteNameAndSource( location, routes, - allRoutes?.slice(0, -3) || routes, + allRoutes || routes, branches as RouteMatch[], basename, ); From 506d6c10b41a03c779c324716634a4516609f7fc Mon Sep 17 00:00:00 2001 From: s1gr1d Date: Wed, 22 Oct 2025 14:00:20 +0200 Subject: [PATCH 7/9] remove on console message --- .../react-router-7-cross-usage/tests/transactions.test.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/dev-packages/e2e-tests/test-applications/react-router-7-cross-usage/tests/transactions.test.ts b/dev-packages/e2e-tests/test-applications/react-router-7-cross-usage/tests/transactions.test.ts index f5107a40c967..1b521964f770 100644 --- a/dev-packages/e2e-tests/test-applications/react-router-7-cross-usage/tests/transactions.test.ts +++ b/dev-packages/e2e-tests/test-applications/react-router-7-cross-usage/tests/transactions.test.ts @@ -2,8 +2,6 @@ import { expect, test } from '@playwright/test'; import { waitForTransaction } from '@sentry-internal/test-utils'; test('sends a pageload transaction with a parameterized URL', async ({ page }) => { - page.on('console', msg => console.log(msg.text())); - const transactionPromise = waitForTransaction('react-router-7-cross-usage', async transactionEvent => { return !!transactionEvent?.transaction && transactionEvent.contexts?.trace?.op === 'pageload'; }); From c6964ee0d9b785bed7d993cfdb834f83856c4ff6 Mon Sep 17 00:00:00 2001 From: s1gr1d Date: Wed, 22 Oct 2025 14:01:02 +0200 Subject: [PATCH 8/9] remove log --- .../test-applications/react-router-7-cross-usage/src/index.tsx | 2 -- 1 file changed, 2 deletions(-) diff --git a/dev-packages/e2e-tests/test-applications/react-router-7-cross-usage/src/index.tsx b/dev-packages/e2e-tests/test-applications/react-router-7-cross-usage/src/index.tsx index b70ece337b54..089b27ab974a 100644 --- a/dev-packages/e2e-tests/test-applications/react-router-7-cross-usage/src/index.tsx +++ b/dev-packages/e2e-tests/test-applications/react-router-7-cross-usage/src/index.tsx @@ -111,7 +111,5 @@ const router = sentryCreateBrowserRouter([ }, ]); -console.log('router::', router); - const root = ReactDOM.createRoot(document.getElementById('root') as HTMLElement); root.render(); From 6af0854f27a83e82ede0064a3d1dbf8cac9edda9 Mon Sep 17 00:00:00 2001 From: s1gr1d Date: Thu, 23 Oct 2025 14:58:22 +0200 Subject: [PATCH 9/9] fix test --- packages/react/test/reactrouter-compat-utils/utils.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/react/test/reactrouter-compat-utils/utils.test.ts b/packages/react/test/reactrouter-compat-utils/utils.test.ts index 91885940db31..9ff48e7450bc 100644 --- a/packages/react/test/reactrouter-compat-utils/utils.test.ts +++ b/packages/react/test/reactrouter-compat-utils/utils.test.ts @@ -436,7 +436,7 @@ describe('reactrouter-compat-utils/utils', () => { ]; const result = getNormalizedName(routes, location, branches, ''); - expect(result).toEqual(['', 'route']); + expect(result).toEqual(['/', 'route']); }); it('should handle simple route path', () => {