diff --git a/dev-packages/e2e-tests/test-applications/standard-frontend-react-tracing-import/src/index.tsx b/dev-packages/e2e-tests/test-applications/standard-frontend-react-tracing-import/src/index.tsx index 9a26b58079e1..319290d010ce 100644 --- a/dev-packages/e2e-tests/test-applications/standard-frontend-react-tracing-import/src/index.tsx +++ b/dev-packages/e2e-tests/test-applications/standard-frontend-react-tracing-import/src/index.tsx @@ -1,5 +1,4 @@ import * as Sentry from '@sentry/react'; -import { BrowserTracing } from '@sentry/tracing'; import React from 'react'; import ReactDOM from 'react-dom/client'; import { @@ -18,14 +17,12 @@ Sentry.init({ environment: 'qa', // dynamic sampling bias to keep transactions dsn: process.env.REACT_APP_E2E_TEST_DSN, integrations: [ - new BrowserTracing({ - routingInstrumentation: Sentry.reactRouterV6Instrumentation( - React.useEffect, - useLocation, - useNavigationType, - createRoutesFromChildren, - matchRoutes, - ), + Sentry.reactRouterV6BrowserTracingIntegration({ + useEffect: React.useEffect, + useLocation, + useNavigationType, + createRoutesFromChildren, + matchRoutes, }), ], // We recommend adjusting this value in production, or using tracesSampler diff --git a/packages/react/src/index.ts b/packages/react/src/index.ts index 2fa3e32e67d6..ef6627cf0c5e 100644 --- a/packages/react/src/index.ts +++ b/packages/react/src/index.ts @@ -5,23 +5,13 @@ export { Profiler, withProfiler, useProfiler } from './profiler'; export type { ErrorBoundaryProps, FallbackRender } from './errorboundary'; export { ErrorBoundary, withErrorBoundary } from './errorboundary'; export { createReduxEnhancer } from './redux'; +export { reactRouterV3BrowserTracingIntegration } from './reactrouterv3'; export { - // eslint-disable-next-line deprecation/deprecation - reactRouterV3Instrumentation, - reactRouterV3BrowserTracingIntegration, -} from './reactrouterv3'; -export { - // eslint-disable-next-line deprecation/deprecation - reactRouterV4Instrumentation, - // eslint-disable-next-line deprecation/deprecation - reactRouterV5Instrumentation, withSentryRouting, reactRouterV4BrowserTracingIntegration, reactRouterV5BrowserTracingIntegration, } from './reactrouter'; export { - // eslint-disable-next-line deprecation/deprecation - reactRouterV6Instrumentation, reactRouterV6BrowserTracingIntegration, withSentryReactRouterV6Routing, wrapUseRoutes, diff --git a/packages/react/src/reactrouter.tsx b/packages/react/src/reactrouter.tsx index ba6fc523ee58..fba359e75e79 100644 --- a/packages/react/src/reactrouter.tsx +++ b/packages/react/src/reactrouter.tsx @@ -75,8 +75,7 @@ export function reactRouterV4BrowserTracingIntegration( return undefined; }; - // eslint-disable-next-line deprecation/deprecation - const instrumentation = reactRouterV4Instrumentation(history, routes, matchPath); + const instrumentation = createReactRouterInstrumentation(history, 'reactrouter_v4', routes, matchPath); // Now instrument page load & navigation with correct settings instrumentation(startPageloadCallback, instrumentPageLoad, false); @@ -115,8 +114,7 @@ export function reactRouterV5BrowserTracingIntegration( return undefined; }; - // eslint-disable-next-line deprecation/deprecation - const instrumentation = reactRouterV5Instrumentation(history, routes, matchPath); + const instrumentation = createReactRouterInstrumentation(history, 'reactrouter_v5', routes, matchPath); // Now instrument page load & navigation with correct settings instrumentation(startPageloadCallback, options.instrumentPageLoad, false); @@ -125,28 +123,6 @@ export function reactRouterV5BrowserTracingIntegration( }; } -/** - * @deprecated Use `browserTracingReactRouterV4()` instead. - */ -export function reactRouterV4Instrumentation( - history: RouterHistory, - routes?: RouteConfig[], - matchPath?: MatchPath, -): ReactRouterInstrumentation { - return createReactRouterInstrumentation(history, 'reactrouter_v4', routes, matchPath); -} - -/** - * @deprecated Use `browserTracingReactRouterV5()` instead. - */ -export function reactRouterV5Instrumentation( - history: RouterHistory, - routes?: RouteConfig[], - matchPath?: MatchPath, -): ReactRouterInstrumentation { - return createReactRouterInstrumentation(history, 'reactrouter_v5', routes, matchPath); -} - function createReactRouterInstrumentation( history: RouterHistory, instrumentationName: string, diff --git a/packages/react/src/reactrouterv3.ts b/packages/react/src/reactrouterv3.ts index 905ebec13897..459589d91b93 100644 --- a/packages/react/src/reactrouterv3.ts +++ b/packages/react/src/reactrouterv3.ts @@ -74,7 +74,6 @@ export function reactRouterV3BrowserTracingIntegration( return undefined; }; - // eslint-disable-next-line deprecation/deprecation const instrumentation = reactRouterV3Instrumentation(history, routes, match); // Now instrument page load & navigation with correct settings @@ -91,14 +90,8 @@ export function reactRouterV3BrowserTracingIntegration( * @param history object from the `history` library * @param routes a list of all routes, should be * @param match `Router.match` utility - * - * @deprecated Use `reactRouterV3BrowserTracingIntegration()` instead */ -export function reactRouterV3Instrumentation( - history: HistoryV3, - routes: Route[], - match: Match, -): ReactRouterInstrumentation { +function reactRouterV3Instrumentation(history: HistoryV3, routes: Route[], match: Match): ReactRouterInstrumentation { return ( startTransaction: (context: TransactionContext) => Transaction | undefined, startTransactionOnPageLoad: boolean = true, diff --git a/packages/react/test/reactrouterv3.test.tsx b/packages/react/test/reactrouterv3.test.tsx index c9926567cea4..964fe7e47b3d 100644 --- a/packages/react/test/reactrouterv3.test.tsx +++ b/packages/react/test/reactrouterv3.test.tsx @@ -13,7 +13,6 @@ import { IndexRoute, Route, Router, createMemoryHistory, createRoutes, match } f import type { Match, Route as RouteType } from '../src/reactrouterv3'; import { reactRouterV3BrowserTracingIntegration } from '../src/reactrouterv3'; -import { reactRouterV3Instrumentation } from '../src/reactrouterv3'; // Have to manually set types because we are using package-alias declare module 'react-router-3' { @@ -26,219 +25,6 @@ declare module 'react-router-3' { export const createRoutes: (routes: any) => RouteType[]; } -function createMockStartTransaction(opts: { finish?: jest.FunctionLike; setMetadata?: jest.FunctionLike } = {}) { - const { finish = jest.fn(), setMetadata = jest.fn() } = opts; - return jest.fn().mockReturnValue({ - end: finish, - setMetadata, - }); -} - -describe('reactRouterV3Instrumentation', () => { - const routes = ( -
{children}
}> -
Home
} /> -
About
} /> -
Features
} /> - }) =>
{params.userid}
} - /> - -
OrgId
} /> -
Team
} /> -
-
- ); - const history = createMemoryHistory(); - - const instrumentationRoutes = createRoutes(routes); - // eslint-disable-next-line deprecation/deprecation - const instrumentation = reactRouterV3Instrumentation(history, instrumentationRoutes, match); - - it('starts a pageload transaction when instrumentation is started', () => { - const mockStartTransaction = createMockStartTransaction(); - instrumentation(mockStartTransaction); - expect(mockStartTransaction).toHaveBeenCalledTimes(1); - expect(mockStartTransaction).toHaveBeenLastCalledWith({ - name: '/', - attributes: { - [SEMANTIC_ATTRIBUTE_SENTRY_SOURCE]: 'route', - [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.pageload.react.reactrouter_v3', - [SEMANTIC_ATTRIBUTE_SENTRY_OP]: 'pageload', - }, - }); - }); - - it('does not start pageload transaction if option is false', () => { - const mockStartTransaction = createMockStartTransaction(); - instrumentation(mockStartTransaction, false); - expect(mockStartTransaction).toHaveBeenCalledTimes(0); - }); - - it('starts a navigation transaction', () => { - const mockStartTransaction = createMockStartTransaction(); - instrumentation(mockStartTransaction); - render({routes}); - - act(() => { - history.push('/about'); - }); - expect(mockStartTransaction).toHaveBeenCalledTimes(2); - expect(mockStartTransaction).toHaveBeenLastCalledWith({ - name: '/about', - attributes: { - [SEMANTIC_ATTRIBUTE_SENTRY_SOURCE]: 'route', - [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.navigation.react.reactrouter_v3', - [SEMANTIC_ATTRIBUTE_SENTRY_OP]: 'navigation', - }, - }); - - act(() => { - history.push('/features'); - }); - expect(mockStartTransaction).toHaveBeenCalledTimes(3); - expect(mockStartTransaction).toHaveBeenLastCalledWith({ - name: '/features', - attributes: { - [SEMANTIC_ATTRIBUTE_SENTRY_SOURCE]: 'route', - [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.navigation.react.reactrouter_v3', - [SEMANTIC_ATTRIBUTE_SENTRY_OP]: 'navigation', - }, - }); - }); - - it('does not start a transaction if option is false', () => { - const mockStartTransaction = createMockStartTransaction(); - instrumentation(mockStartTransaction, true, false); - render({routes}); - expect(mockStartTransaction).toHaveBeenCalledTimes(1); - }); - - it('only starts a navigation transaction on push', () => { - const mockStartTransaction = createMockStartTransaction(); - instrumentation(mockStartTransaction); - render({routes}); - - act(() => { - history.replace('hello'); - }); - expect(mockStartTransaction).toHaveBeenCalledTimes(1); - }); - - it('finishes a transaction on navigation', () => { - const mockFinish = jest.fn(); - const mockStartTransaction = createMockStartTransaction({ finish: mockFinish }); - instrumentation(mockStartTransaction); - render({routes}); - expect(mockStartTransaction).toHaveBeenCalledTimes(1); - - act(() => { - history.push('/features'); - }); - expect(mockFinish).toHaveBeenCalledTimes(1); - expect(mockStartTransaction).toHaveBeenCalledTimes(2); - }); - - it('normalizes transaction names', () => { - const mockStartTransaction = createMockStartTransaction(); - instrumentation(mockStartTransaction); - const { container } = render({routes}); - - act(() => { - history.push('/users/123'); - }); - expect(container.innerHTML).toContain('123'); - - expect(mockStartTransaction).toHaveBeenCalledTimes(2); - expect(mockStartTransaction).toHaveBeenLastCalledWith({ - name: '/users/:userid', - attributes: { - [SEMANTIC_ATTRIBUTE_SENTRY_SOURCE]: 'route', - [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.navigation.react.reactrouter_v3', - [SEMANTIC_ATTRIBUTE_SENTRY_OP]: 'navigation', - }, - }); - }); - - it('normalizes nested transaction names', () => { - const mockStartTransaction = createMockStartTransaction(); - instrumentation(mockStartTransaction); - const { container } = render({routes}); - - act(() => { - history.push('/organizations/1234/v1/758'); - }); - expect(container.innerHTML).toContain('Team'); - - expect(mockStartTransaction).toHaveBeenCalledTimes(2); - expect(mockStartTransaction).toHaveBeenLastCalledWith({ - name: '/organizations/:orgid/v1/:teamid', - attributes: { - [SEMANTIC_ATTRIBUTE_SENTRY_SOURCE]: 'route', - [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.navigation.react.reactrouter_v3', - [SEMANTIC_ATTRIBUTE_SENTRY_OP]: 'navigation', - }, - }); - - act(() => { - history.push('/organizations/543'); - }); - expect(container.innerHTML).toContain('OrgId'); - - expect(mockStartTransaction).toHaveBeenCalledTimes(3); - expect(mockStartTransaction).toHaveBeenLastCalledWith({ - name: '/organizations/:orgid', - attributes: { - [SEMANTIC_ATTRIBUTE_SENTRY_SOURCE]: 'route', - [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.navigation.react.reactrouter_v3', - [SEMANTIC_ATTRIBUTE_SENTRY_OP]: 'navigation', - }, - }); - }); - - it('sets metadata to url if on an unknown route', () => { - const mockStartTransaction = createMockStartTransaction(); - instrumentation(mockStartTransaction); - render({routes}); - - act(() => { - history.push('/organizations/1234/some/other/route'); - }); - - expect(mockStartTransaction).toHaveBeenCalledTimes(2); - expect(mockStartTransaction).toHaveBeenLastCalledWith({ - name: '/organizations/1234/some/other/route', - attributes: { - [SEMANTIC_ATTRIBUTE_SENTRY_SOURCE]: 'url', - [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.navigation.react.reactrouter_v3', - [SEMANTIC_ATTRIBUTE_SENTRY_OP]: 'navigation', - }, - }); - }); - - it('sets metadata to url if no routes are provided', () => { - const fakeRoutes =
hello
; - const mockStartTransaction = createMockStartTransaction(); - // eslint-disable-next-line deprecation/deprecation - const mockInstrumentation = reactRouterV3Instrumentation(history, createRoutes(fakeRoutes), match); - mockInstrumentation(mockStartTransaction); - // We render here with `routes` instead of `fakeRoutes` from above to validate the case - // where users provided the instrumentation with a bad set of routes. - render({routes}); - - expect(mockStartTransaction).toHaveBeenCalledTimes(1); - expect(mockStartTransaction).toHaveBeenLastCalledWith({ - name: '/', - attributes: { - [SEMANTIC_ATTRIBUTE_SENTRY_SOURCE]: 'url', - [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.pageload.react.reactrouter_v3', - [SEMANTIC_ATTRIBUTE_SENTRY_OP]: 'pageload', - }, - }); - }); -}); - const mockStartBrowserTracingPageLoadSpan = jest.fn(); const mockStartBrowserTracingNavigationSpan = jest.fn(); diff --git a/packages/react/test/reactrouterv4.test.tsx b/packages/react/test/reactrouterv4.test.tsx index 973bda75d273..1028131eb0b3 100644 --- a/packages/react/test/reactrouterv4.test.tsx +++ b/packages/react/test/reactrouterv4.test.tsx @@ -12,309 +12,9 @@ import { createMemoryHistory } from 'history-4'; import * as React from 'react'; import { Route, Router, Switch, matchPath } from 'react-router-4'; -import { - BrowserClient, - reactRouterV4BrowserTracingIntegration, - reactRouterV4Instrumentation, - withSentryRouting, -} from '../src'; +import { BrowserClient, reactRouterV4BrowserTracingIntegration, withSentryRouting } from '../src'; import type { RouteConfig } from '../src/reactrouter'; -describe('reactRouterV4Instrumentation', () => { - function createInstrumentation(_opts?: { - startTransactionOnPageLoad?: boolean; - startTransactionOnLocationChange?: boolean; - routes?: RouteConfig[]; - }): [jest.Mock, any, { mockUpdateName: jest.Mock; mockFinish: jest.Mock; mockSetAttribute: jest.Mock }] { - const options = { - matchPath: _opts && _opts.routes !== undefined ? matchPath : undefined, - routes: undefined, - startTransactionOnLocationChange: true, - startTransactionOnPageLoad: true, - ..._opts, - }; - const history = createMemoryHistory(); - const mockFinish = jest.fn(); - const mockUpdateName = jest.fn(); - const mockSetAttribute = jest.fn(); - const mockStartTransaction = jest - .fn() - .mockReturnValue({ updateName: mockUpdateName, end: mockFinish, setAttribute: mockSetAttribute }); - // eslint-disable-next-line deprecation/deprecation - reactRouterV4Instrumentation(history, options.routes, options.matchPath)( - mockStartTransaction, - options.startTransactionOnPageLoad, - options.startTransactionOnLocationChange, - ); - return [mockStartTransaction, history, { mockUpdateName, mockFinish, mockSetAttribute }]; - } - - it('starts a pageload transaction when instrumentation is started', () => { - const [mockStartTransaction] = createInstrumentation(); - expect(mockStartTransaction).toHaveBeenCalledTimes(1); - expect(mockStartTransaction).toHaveBeenLastCalledWith({ - name: '/', - attributes: { - [SEMANTIC_ATTRIBUTE_SENTRY_SOURCE]: 'url', - [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.pageload.react.reactrouter_v4', - [SEMANTIC_ATTRIBUTE_SENTRY_OP]: 'pageload', - }, - }); - }); - - it('does not start pageload transaction if option is false', () => { - const [mockStartTransaction] = createInstrumentation({ startTransactionOnPageLoad: false }); - expect(mockStartTransaction).toHaveBeenCalledTimes(0); - }); - - it('starts a navigation transaction', () => { - const [mockStartTransaction, history] = createInstrumentation(); - render( - - -
Features
} /> -
About
} /> -
Home
} /> -
-
, - ); - - act(() => { - history.push('/about'); - }); - expect(mockStartTransaction).toHaveBeenCalledTimes(2); - expect(mockStartTransaction).toHaveBeenLastCalledWith({ - name: '/about', - attributes: { - [SEMANTIC_ATTRIBUTE_SENTRY_SOURCE]: 'url', - [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.navigation.react.reactrouter_v4', - [SEMANTIC_ATTRIBUTE_SENTRY_OP]: 'navigation', - }, - }); - - act(() => { - history.push('/features'); - }); - expect(mockStartTransaction).toHaveBeenCalledTimes(3); - expect(mockStartTransaction).toHaveBeenLastCalledWith({ - name: '/features', - attributes: { - [SEMANTIC_ATTRIBUTE_SENTRY_SOURCE]: 'url', - [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.navigation.react.reactrouter_v4', - [SEMANTIC_ATTRIBUTE_SENTRY_OP]: 'navigation', - }, - }); - }); - - it('does not start a transaction if option is false', () => { - const [mockStartTransaction, history] = createInstrumentation({ startTransactionOnLocationChange: false }); - render( - - -
Features
} /> -
About
} /> -
Home
} /> -
-
, - ); - expect(mockStartTransaction).toHaveBeenCalledTimes(1); - }); - - it('only starts a navigation transaction on push', () => { - const [mockStartTransaction, history] = createInstrumentation(); - render( - - -
Features
} /> -
About
} /> -
Home
} /> -
-
, - ); - - act(() => { - history.replace('hello'); - }); - expect(mockStartTransaction).toHaveBeenCalledTimes(1); - }); - - it('finishes a transaction on navigation', () => { - const [mockStartTransaction, history, { mockFinish }] = createInstrumentation(); - render( - - -
Features
} /> -
About
} /> -
Home
} /> -
-
, - ); - expect(mockStartTransaction).toHaveBeenCalledTimes(1); - - act(() => { - history.push('/features'); - }); - expect(mockFinish).toHaveBeenCalledTimes(1); - expect(mockStartTransaction).toHaveBeenCalledTimes(2); - }); - - it('does not normalize transaction name ', () => { - const [mockStartTransaction, history] = createInstrumentation(); - const { getByText } = render( - - -
UserId
} /> -
Users
} /> -
Home
} /> -
-
, - ); - - act(() => { - history.push('/users/123'); - }); - getByText('UserId'); - - expect(mockStartTransaction).toHaveBeenCalledTimes(2); - expect(mockStartTransaction).toHaveBeenLastCalledWith({ - name: '/users/123', - attributes: { - [SEMANTIC_ATTRIBUTE_SENTRY_SOURCE]: 'url', - [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.navigation.react.reactrouter_v4', - [SEMANTIC_ATTRIBUTE_SENTRY_OP]: 'navigation', - }, - }); - }); - - it('normalizes transaction name with custom Route', () => { - const [mockStartTransaction, history, { mockUpdateName, mockSetAttribute }] = createInstrumentation(); - const SentryRoute = withSentryRouting(Route); - const { getByText } = render( - - -
UserId
} /> -
Users
} /> -
Home
} /> -
-
, - ); - - act(() => { - history.push('/users/123'); - }); - getByText('UserId'); - - expect(mockStartTransaction).toHaveBeenCalledTimes(2); - expect(mockStartTransaction).toHaveBeenLastCalledWith({ - name: '/users/123', - attributes: { - [SEMANTIC_ATTRIBUTE_SENTRY_SOURCE]: 'url', - [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.navigation.react.reactrouter_v4', - [SEMANTIC_ATTRIBUTE_SENTRY_OP]: 'navigation', - }, - }); - expect(mockUpdateName).toHaveBeenCalledTimes(2); - expect(mockUpdateName).toHaveBeenLastCalledWith('/users/:userid'); - expect(mockSetAttribute).toHaveBeenCalledWith(SEMANTIC_ATTRIBUTE_SENTRY_SOURCE, 'route'); - }); - - it('normalizes nested transaction names with custom Route', () => { - const [mockStartTransaction, history, { mockUpdateName, mockSetAttribute }] = createInstrumentation(); - const SentryRoute = withSentryRouting(Route); - const { getByText } = render( - - -
Team
} /> -
OrgId
} /> -
Home
} /> -
-
, - ); - - act(() => { - history.push('/organizations/1234/v1/758'); - }); - getByText('Team'); - - expect(mockStartTransaction).toHaveBeenCalledTimes(2); - expect(mockStartTransaction).toHaveBeenLastCalledWith({ - name: '/organizations/1234/v1/758', - attributes: { - [SEMANTIC_ATTRIBUTE_SENTRY_SOURCE]: 'url', - [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.navigation.react.reactrouter_v4', - [SEMANTIC_ATTRIBUTE_SENTRY_OP]: 'navigation', - }, - }); - expect(mockUpdateName).toHaveBeenCalledTimes(2); - expect(mockUpdateName).toHaveBeenLastCalledWith('/organizations/:orgid/v1/:teamid'); - expect(mockSetAttribute).toHaveBeenLastCalledWith(SEMANTIC_ATTRIBUTE_SENTRY_SOURCE, 'route'); - - act(() => { - history.push('/organizations/543'); - }); - getByText('OrgId'); - - expect(mockStartTransaction).toHaveBeenCalledTimes(3); - expect(mockStartTransaction).toHaveBeenLastCalledWith({ - name: '/organizations/543', - attributes: { - [SEMANTIC_ATTRIBUTE_SENTRY_SOURCE]: 'url', - [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.navigation.react.reactrouter_v4', - [SEMANTIC_ATTRIBUTE_SENTRY_OP]: 'navigation', - }, - }); - expect(mockUpdateName).toHaveBeenCalledTimes(3); - expect(mockUpdateName).toHaveBeenLastCalledWith('/organizations/:orgid'); - expect(mockSetAttribute).toHaveBeenLastCalledWith(SEMANTIC_ATTRIBUTE_SENTRY_SOURCE, 'route'); - }); - - it('matches with route object', () => { - const routes: RouteConfig[] = [ - { - path: '/organizations/:orgid/v1/:teamid', - }, - { path: '/organizations/:orgid' }, - { path: '/' }, - ]; - const [mockStartTransaction, history] = createInstrumentation({ routes }); - render( - - -
Team
} /> -
OrgId
} /> -
Home
} /> -
-
, - ); - - act(() => { - history.push('/organizations/1234/v1/758'); - }); - expect(mockStartTransaction).toHaveBeenCalledTimes(2); - expect(mockStartTransaction).toHaveBeenLastCalledWith({ - name: '/organizations/:orgid/v1/:teamid', - attributes: { - [SEMANTIC_ATTRIBUTE_SENTRY_SOURCE]: 'route', - [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.navigation.react.reactrouter_v4', - [SEMANTIC_ATTRIBUTE_SENTRY_OP]: 'navigation', - }, - }); - - act(() => { - history.push('/organizations/1234'); - }); - expect(mockStartTransaction).toHaveBeenCalledTimes(3); - expect(mockStartTransaction).toHaveBeenLastCalledWith({ - name: '/organizations/:orgid', - attributes: { - [SEMANTIC_ATTRIBUTE_SENTRY_SOURCE]: 'route', - [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.navigation.react.reactrouter_v4', - [SEMANTIC_ATTRIBUTE_SENTRY_OP]: 'navigation', - }, - }); - }); -}); - const mockStartBrowserTracingPageLoadSpan = jest.fn(); const mockStartBrowserTracingNavigationSpan = jest.fn(); diff --git a/packages/react/test/reactrouterv5.test.tsx b/packages/react/test/reactrouterv5.test.tsx index b08f7de702a1..0678f65652e4 100644 --- a/packages/react/test/reactrouterv5.test.tsx +++ b/packages/react/test/reactrouterv5.test.tsx @@ -12,309 +12,9 @@ import { createMemoryHistory } from 'history-4'; import * as React from 'react'; import { Route, Router, Switch, matchPath } from 'react-router-5'; -import { - BrowserClient, - reactRouterV5BrowserTracingIntegration, - reactRouterV5Instrumentation, - withSentryRouting, -} from '../src'; +import { BrowserClient, reactRouterV5BrowserTracingIntegration, withSentryRouting } from '../src'; import type { RouteConfig } from '../src/reactrouter'; -describe('reactRouterV5Instrumentation', () => { - function createInstrumentation(_opts?: { - startTransactionOnPageLoad?: boolean; - startTransactionOnLocationChange?: boolean; - routes?: RouteConfig[]; - }): [jest.Mock, any, { mockUpdateName: jest.Mock; mockFinish: jest.Mock; mockSetAttribute: jest.Mock }] { - const options = { - matchPath: _opts && _opts.routes !== undefined ? matchPath : undefined, - routes: undefined, - startTransactionOnLocationChange: true, - startTransactionOnPageLoad: true, - ..._opts, - }; - const history = createMemoryHistory(); - const mockFinish = jest.fn(); - const mockUpdateName = jest.fn(); - const mockSetAttribute = jest.fn(); - const mockStartTransaction = jest - .fn() - .mockReturnValue({ updateName: mockUpdateName, end: mockFinish, setAttribute: mockSetAttribute }); - // eslint-disable-next-line deprecation/deprecation - reactRouterV5Instrumentation(history, options.routes, options.matchPath)( - mockStartTransaction, - options.startTransactionOnPageLoad, - options.startTransactionOnLocationChange, - ); - return [mockStartTransaction, history, { mockUpdateName, mockFinish, mockSetAttribute }]; - } - - it('starts a pageload transaction when instrumentation is started', () => { - const [mockStartTransaction] = createInstrumentation(); - expect(mockStartTransaction).toHaveBeenCalledTimes(1); - expect(mockStartTransaction).toHaveBeenLastCalledWith({ - name: '/', - attributes: { - [SEMANTIC_ATTRIBUTE_SENTRY_SOURCE]: 'url', - [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.pageload.react.reactrouter_v5', - [SEMANTIC_ATTRIBUTE_SENTRY_OP]: 'pageload', - }, - }); - }); - - it('does not start pageload transaction if option is false', () => { - const [mockStartTransaction] = createInstrumentation({ startTransactionOnPageLoad: false }); - expect(mockStartTransaction).toHaveBeenCalledTimes(0); - }); - - it('starts a navigation transaction', () => { - const [mockStartTransaction, history] = createInstrumentation(); - render( - - -
Features
} /> -
About
} /> -
Home
} /> -
-
, - ); - - act(() => { - history.push('/about'); - }); - expect(mockStartTransaction).toHaveBeenCalledTimes(2); - expect(mockStartTransaction).toHaveBeenLastCalledWith({ - name: '/about', - attributes: { - [SEMANTIC_ATTRIBUTE_SENTRY_SOURCE]: 'url', - [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.navigation.react.reactrouter_v5', - [SEMANTIC_ATTRIBUTE_SENTRY_OP]: 'navigation', - }, - }); - - act(() => { - history.push('/features'); - }); - expect(mockStartTransaction).toHaveBeenCalledTimes(3); - expect(mockStartTransaction).toHaveBeenLastCalledWith({ - name: '/features', - attributes: { - [SEMANTIC_ATTRIBUTE_SENTRY_SOURCE]: 'url', - [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.navigation.react.reactrouter_v5', - [SEMANTIC_ATTRIBUTE_SENTRY_OP]: 'navigation', - }, - }); - }); - - it('does not start a transaction if option is false', () => { - const [mockStartTransaction, history] = createInstrumentation({ startTransactionOnLocationChange: false }); - render( - - -
Features
} /> -
About
} /> -
Home
} /> -
-
, - ); - expect(mockStartTransaction).toHaveBeenCalledTimes(1); - }); - - it('only starts a navigation transaction on push', () => { - const [mockStartTransaction, history] = createInstrumentation(); - render( - - -
Features
} /> -
About
} /> -
Home
} /> -
-
, - ); - - act(() => { - history.replace('hello'); - }); - expect(mockStartTransaction).toHaveBeenCalledTimes(1); - }); - - it('finishes a transaction on navigation', () => { - const [mockStartTransaction, history, { mockFinish }] = createInstrumentation(); - render( - - -
Features
} /> -
About
} /> -
Home
} /> -
-
, - ); - expect(mockStartTransaction).toHaveBeenCalledTimes(1); - - act(() => { - history.push('/features'); - }); - expect(mockFinish).toHaveBeenCalledTimes(1); - expect(mockStartTransaction).toHaveBeenCalledTimes(2); - }); - - it('does not normalize transaction name ', () => { - const [mockStartTransaction, history] = createInstrumentation(); - const { getByText } = render( - - -
UserId
} /> -
Users
} /> -
Home
} /> -
-
, - ); - - act(() => { - history.push('/users/123'); - }); - getByText('UserId'); - - expect(mockStartTransaction).toHaveBeenCalledTimes(2); - expect(mockStartTransaction).toHaveBeenLastCalledWith({ - name: '/users/123', - attributes: { - [SEMANTIC_ATTRIBUTE_SENTRY_SOURCE]: 'url', - [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.navigation.react.reactrouter_v5', - [SEMANTIC_ATTRIBUTE_SENTRY_OP]: 'navigation', - }, - }); - }); - - it('normalizes transaction name with custom Route', () => { - const [mockStartTransaction, history, { mockUpdateName, mockSetAttribute }] = createInstrumentation(); - const SentryRoute = withSentryRouting(Route); - const { getByText } = render( - - -
UserId
} /> -
Users
} /> -
Home
} /> -
-
, - ); - - act(() => { - history.push('/users/123'); - }); - getByText('UserId'); - - expect(mockStartTransaction).toHaveBeenCalledTimes(2); - expect(mockStartTransaction).toHaveBeenLastCalledWith({ - name: '/users/123', - attributes: { - [SEMANTIC_ATTRIBUTE_SENTRY_SOURCE]: 'url', - [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.navigation.react.reactrouter_v5', - [SEMANTIC_ATTRIBUTE_SENTRY_OP]: 'navigation', - }, - }); - expect(mockUpdateName).toHaveBeenCalledTimes(2); - expect(mockUpdateName).toHaveBeenLastCalledWith('/users/:userid'); - expect(mockSetAttribute).toHaveBeenCalledWith(SEMANTIC_ATTRIBUTE_SENTRY_SOURCE, 'route'); - }); - - it('normalizes nested transaction names with custom Route', () => { - const [mockStartTransaction, history, { mockUpdateName, mockSetAttribute }] = createInstrumentation(); - const SentryRoute = withSentryRouting(Route); - const { getByText } = render( - - -
Team
} /> -
OrgId
} /> -
Home
} /> -
-
, - ); - - act(() => { - history.push('/organizations/1234/v1/758'); - }); - getByText('Team'); - - expect(mockStartTransaction).toHaveBeenCalledTimes(2); - expect(mockStartTransaction).toHaveBeenLastCalledWith({ - name: '/organizations/1234/v1/758', - attributes: { - [SEMANTIC_ATTRIBUTE_SENTRY_SOURCE]: 'url', - [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.navigation.react.reactrouter_v5', - [SEMANTIC_ATTRIBUTE_SENTRY_OP]: 'navigation', - }, - }); - expect(mockUpdateName).toHaveBeenCalledTimes(2); - expect(mockUpdateName).toHaveBeenLastCalledWith('/organizations/:orgid/v1/:teamid'); - expect(mockSetAttribute).toHaveBeenLastCalledWith(SEMANTIC_ATTRIBUTE_SENTRY_SOURCE, 'route'); - - act(() => { - history.push('/organizations/543'); - }); - getByText('OrgId'); - - expect(mockStartTransaction).toHaveBeenCalledTimes(3); - expect(mockStartTransaction).toHaveBeenLastCalledWith({ - name: '/organizations/543', - attributes: { - [SEMANTIC_ATTRIBUTE_SENTRY_SOURCE]: 'url', - [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.navigation.react.reactrouter_v5', - [SEMANTIC_ATTRIBUTE_SENTRY_OP]: 'navigation', - }, - }); - expect(mockUpdateName).toHaveBeenCalledTimes(3); - expect(mockUpdateName).toHaveBeenLastCalledWith('/organizations/:orgid'); - expect(mockSetAttribute).toHaveBeenLastCalledWith(SEMANTIC_ATTRIBUTE_SENTRY_SOURCE, 'route'); - }); - - it('matches with route object', () => { - const routes: RouteConfig[] = [ - { - path: '/organizations/:orgid/v1/:teamid', - }, - { path: '/organizations/:orgid' }, - { path: '/' }, - ]; - const [mockStartTransaction, history] = createInstrumentation({ routes }); - render( - - -
Team
} /> -
OrgId
} /> -
Home
} /> -
-
, - ); - - act(() => { - history.push('/organizations/1234/v1/758'); - }); - expect(mockStartTransaction).toHaveBeenCalledTimes(2); - expect(mockStartTransaction).toHaveBeenLastCalledWith({ - name: '/organizations/:orgid/v1/:teamid', - attributes: { - [SEMANTIC_ATTRIBUTE_SENTRY_SOURCE]: 'route', - [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.navigation.react.reactrouter_v5', - [SEMANTIC_ATTRIBUTE_SENTRY_OP]: 'navigation', - }, - }); - - act(() => { - history.push('/organizations/1234'); - }); - expect(mockStartTransaction).toHaveBeenCalledTimes(3); - expect(mockStartTransaction).toHaveBeenLastCalledWith({ - name: '/organizations/:orgid', - attributes: { - [SEMANTIC_ATTRIBUTE_SENTRY_SOURCE]: 'route', - [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.navigation.react.reactrouter_v5', - [SEMANTIC_ATTRIBUTE_SENTRY_OP]: 'navigation', - }, - }); - }); -}); - const mockStartBrowserTracingPageLoadSpan = jest.fn(); const mockStartBrowserTracingNavigationSpan = jest.fn(); diff --git a/packages/react/test/reactrouterv6.4.test.tsx b/packages/react/test/reactrouterv6.4.test.tsx index f534d02f97e2..2013524d5185 100644 --- a/packages/react/test/reactrouterv6.4.test.tsx +++ b/packages/react/test/reactrouterv6.4.test.tsx @@ -14,13 +14,12 @@ import { RouterProvider, createMemoryRouter, createRoutesFromChildren, - matchPath, matchRoutes, useLocation, useNavigationType, } from 'react-router-6.4'; -import { BrowserClient, reactRouterV6Instrumentation, wrapCreateBrowserRouter } from '../src'; +import { BrowserClient, wrapCreateBrowserRouter } from '../src'; import { reactRouterV6BrowserTracingIntegration } from '../src/reactrouterv6'; import type { CreateRouterFunction } from '../src/types'; @@ -30,442 +29,6 @@ beforeAll(() => { global.Request = Request; }); -describe('reactRouterV6Instrumentation (v6.4)', () => { - function createInstrumentation(_opts?: { - startTransactionOnPageLoad?: boolean; - startTransactionOnLocationChange?: boolean; - stripBasename?: boolean; - }): [jest.Mock, { mockUpdateName: jest.Mock; mockFinish: jest.Mock; mockSetAttribute: jest.Mock }] { - const options = { - matchPath: _opts ? matchPath : undefined, - startTransactionOnLocationChange: true, - startTransactionOnPageLoad: true, - ..._opts, - }; - const mockFinish = jest.fn(); - const mockUpdateName = jest.fn(); - const mockSetAttribute = jest.fn(); - const mockStartTransaction = jest - .fn() - .mockReturnValue({ updateName: mockUpdateName, end: mockFinish, setAttribute: mockSetAttribute }); - - // eslint-disable-next-line deprecation/deprecation - reactRouterV6Instrumentation( - React.useEffect, - useLocation, - useNavigationType, - createRoutesFromChildren, - matchRoutes, - options.stripBasename, - )(mockStartTransaction, options.startTransactionOnPageLoad, options.startTransactionOnLocationChange); - return [mockStartTransaction, { mockUpdateName, mockFinish, mockSetAttribute }]; - } - - describe('wrapCreateBrowserRouter', () => { - it('starts a pageload transaction', () => { - const [mockStartTransaction] = createInstrumentation(); - const sentryCreateBrowserRouter = wrapCreateBrowserRouter(createMemoryRouter as CreateRouterFunction); - - const router = sentryCreateBrowserRouter( - [ - { - path: '/', - element:
TEST
, - }, - ], - { - initialEntries: ['/'], - }, - ); - - // @ts-expect-error router is fine - render(); - - expect(mockStartTransaction).toHaveBeenCalledTimes(1); - expect(mockStartTransaction).toHaveBeenCalledWith({ - name: '/', - attributes: { - [SEMANTIC_ATTRIBUTE_SENTRY_SOURCE]: 'url', - [SEMANTIC_ATTRIBUTE_SENTRY_OP]: 'pageload', - [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.pageload.react.reactrouter_v6', - }, - }); - }); - - it('starts a navigation transaction', () => { - const [mockStartTransaction] = createInstrumentation(); - const sentryCreateBrowserRouter = wrapCreateBrowserRouter(createMemoryRouter as CreateRouterFunction); - - const router = sentryCreateBrowserRouter( - [ - { - path: '/', - element: , - }, - { - path: 'about', - element:
About
, - }, - ], - { - initialEntries: ['/'], - }, - ); - - // @ts-expect-error router is fine - render(); - - expect(mockStartTransaction).toHaveBeenCalledTimes(2); - expect(mockStartTransaction).toHaveBeenLastCalledWith({ - name: '/about', - attributes: { - [SEMANTIC_ATTRIBUTE_SENTRY_SOURCE]: 'route', - [SEMANTIC_ATTRIBUTE_SENTRY_OP]: 'navigation', - [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.navigation.react.reactrouter_v6', - }, - }); - }); - - it('works with nested routes', () => { - const [mockStartTransaction] = createInstrumentation(); - const sentryCreateBrowserRouter = wrapCreateBrowserRouter(createMemoryRouter as CreateRouterFunction); - - const router = sentryCreateBrowserRouter( - [ - { - path: '/', - element: , - }, - { - path: 'about', - element:
About
, - children: [ - { - path: 'us', - element:
Us
, - }, - ], - }, - ], - { - initialEntries: ['/'], - }, - ); - - // @ts-expect-error router is fine - render(); - - expect(mockStartTransaction).toHaveBeenCalledTimes(2); - expect(mockStartTransaction).toHaveBeenLastCalledWith({ - name: '/about/us', - attributes: { - [SEMANTIC_ATTRIBUTE_SENTRY_SOURCE]: 'route', - [SEMANTIC_ATTRIBUTE_SENTRY_OP]: 'navigation', - [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.navigation.react.reactrouter_v6', - }, - }); - }); - - it('works with parameterized paths', () => { - const [mockStartTransaction] = createInstrumentation(); - const sentryCreateBrowserRouter = wrapCreateBrowserRouter(createMemoryRouter as CreateRouterFunction); - - const router = sentryCreateBrowserRouter( - [ - { - path: '/', - element: , - }, - { - path: 'about', - element:
About
, - children: [ - { - path: ':page', - element:
Page
, - }, - ], - }, - ], - { - initialEntries: ['/'], - }, - ); - - // @ts-expect-error router is fine - render(); - - expect(mockStartTransaction).toHaveBeenCalledTimes(2); - expect(mockStartTransaction).toHaveBeenLastCalledWith({ - name: '/about/:page', - attributes: { - [SEMANTIC_ATTRIBUTE_SENTRY_SOURCE]: 'route', - [SEMANTIC_ATTRIBUTE_SENTRY_OP]: 'navigation', - [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.navigation.react.reactrouter_v6', - }, - }); - }); - - it('works with paths with multiple parameters', () => { - const [mockStartTransaction] = createInstrumentation(); - const sentryCreateBrowserRouter = wrapCreateBrowserRouter(createMemoryRouter as CreateRouterFunction); - - const router = sentryCreateBrowserRouter( - [ - { - path: '/', - element: , - }, - { - path: 'stores', - element:
Stores
, - children: [ - { - path: ':storeId', - element:
Store
, - children: [ - { - path: 'products', - element:
Products
, - children: [ - { - path: ':productId', - element:
Product
, - }, - ], - }, - ], - }, - ], - }, - ], - { - initialEntries: ['/'], - }, - ); - - // @ts-expect-error router is fine - render(); - - expect(mockStartTransaction).toHaveBeenCalledTimes(2); - expect(mockStartTransaction).toHaveBeenLastCalledWith({ - name: '/stores/:storeId/products/:productId', - attributes: { - [SEMANTIC_ATTRIBUTE_SENTRY_SOURCE]: 'route', - [SEMANTIC_ATTRIBUTE_SENTRY_OP]: 'navigation', - [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.navigation.react.reactrouter_v6', - }, - }); - }); - - it('updates pageload transaction to a parameterized route', () => { - const [mockStartTransaction, { mockUpdateName, mockSetAttribute }] = createInstrumentation(); - const sentryCreateBrowserRouter = wrapCreateBrowserRouter(createMemoryRouter as CreateRouterFunction); - - const router = sentryCreateBrowserRouter( - [ - { - path: 'about', - element:
About
, - children: [ - { - path: ':page', - element:
page
, - }, - ], - }, - ], - { - initialEntries: ['/about/us'], - }, - ); - - // @ts-expect-error router is fine - render(); - - expect(mockStartTransaction).toHaveBeenCalledTimes(1); - expect(mockUpdateName).toHaveBeenLastCalledWith('/about/:page'); - expect(mockSetAttribute).toHaveBeenCalledWith(SEMANTIC_ATTRIBUTE_SENTRY_SOURCE, 'route'); - }); - - it('works with `basename` option', () => { - const [mockStartTransaction] = createInstrumentation(); - const sentryCreateBrowserRouter = wrapCreateBrowserRouter(createMemoryRouter as CreateRouterFunction); - - const router = sentryCreateBrowserRouter( - [ - { - path: '/', - element: , - }, - { - path: 'about', - element:
About
, - children: [ - { - path: 'us', - element:
Us
, - }, - ], - }, - ], - { - initialEntries: ['/app'], - basename: '/app', - }, - ); - - // @ts-expect-error router is fine - render(); - - expect(mockStartTransaction).toHaveBeenCalledTimes(2); - expect(mockStartTransaction).toHaveBeenLastCalledWith({ - name: '/app/about/us', - attributes: { - [SEMANTIC_ATTRIBUTE_SENTRY_SOURCE]: 'route', - [SEMANTIC_ATTRIBUTE_SENTRY_OP]: 'navigation', - [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.navigation.react.reactrouter_v6', - }, - }); - }); - - it('works with parameterized paths and `basename`', () => { - const [mockStartTransaction] = createInstrumentation(); - const sentryCreateBrowserRouter = wrapCreateBrowserRouter(createMemoryRouter as CreateRouterFunction); - - const router = sentryCreateBrowserRouter( - [ - { - path: '/', - element: , - }, - { - path: ':orgId', - children: [ - { - path: 'users', - children: [ - { - path: ':userId', - element:
User
, - }, - ], - }, - ], - }, - ], - { - initialEntries: ['/admin'], - basename: '/admin', - }, - ); - - // @ts-expect-error router is fine - render(); - - expect(mockStartTransaction).toHaveBeenCalledTimes(2); - expect(mockStartTransaction).toHaveBeenLastCalledWith({ - name: '/admin/:orgId/users/:userId', - attributes: { - [SEMANTIC_ATTRIBUTE_SENTRY_SOURCE]: 'route', - [SEMANTIC_ATTRIBUTE_SENTRY_OP]: 'navigation', - [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.navigation.react.reactrouter_v6', - }, - }); - }); - - it('strips `basename` from transaction names of parameterized paths', () => { - const [mockStartTransaction] = createInstrumentation({ - stripBasename: true, - }); - const sentryCreateBrowserRouter = wrapCreateBrowserRouter(createMemoryRouter as CreateRouterFunction); - - const router = sentryCreateBrowserRouter( - [ - { - path: '/', - element: , - }, - { - path: ':orgId', - children: [ - { - path: 'users', - children: [ - { - path: ':userId', - element:
User
, - }, - ], - }, - ], - }, - ], - { - initialEntries: ['/admin'], - basename: '/admin', - }, - ); - - // @ts-expect-error router is fine - render(); - - expect(mockStartTransaction).toHaveBeenCalledTimes(2); - expect(mockStartTransaction).toHaveBeenLastCalledWith({ - name: '/:orgId/users/:userId', - attributes: { - [SEMANTIC_ATTRIBUTE_SENTRY_SOURCE]: 'route', - [SEMANTIC_ATTRIBUTE_SENTRY_OP]: 'navigation', - [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.navigation.react.reactrouter_v6', - }, - }); - }); - - it('strips `basename` from transaction names of non-parameterized paths', () => { - const [mockStartTransaction] = createInstrumentation({ - stripBasename: true, - }); - const sentryCreateBrowserRouter = wrapCreateBrowserRouter(createMemoryRouter as CreateRouterFunction); - - const router = sentryCreateBrowserRouter( - [ - { - path: '/', - element: , - }, - { - path: 'about', - element:
About
, - children: [ - { - path: 'us', - element:
Us
, - }, - ], - }, - ], - { - initialEntries: ['/app'], - basename: '/app', - }, - ); - - // @ts-expect-error router is fine - render(); - - expect(mockStartTransaction).toHaveBeenCalledTimes(2); - expect(mockStartTransaction).toHaveBeenLastCalledWith({ - name: '/about/us', - attributes: { - [SEMANTIC_ATTRIBUTE_SENTRY_SOURCE]: 'route', - [SEMANTIC_ATTRIBUTE_SENTRY_OP]: 'navigation', - [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.navigation.react.reactrouter_v6', - }, - }); - }); - }); -}); - const mockStartBrowserTracingPageLoadSpan = jest.fn(); const mockStartBrowserTracingNavigationSpan = jest.fn(); diff --git a/packages/react/test/reactrouterv6.test.tsx b/packages/react/test/reactrouterv6.test.tsx index f2ec3fb3a4b9..65355099476b 100644 --- a/packages/react/test/reactrouterv6.test.tsx +++ b/packages/react/test/reactrouterv6.test.tsx @@ -15,657 +15,19 @@ import { Route, Routes, createRoutesFromChildren, - matchPath, matchRoutes, useLocation, useNavigationType, useRoutes, } from 'react-router-6'; -import { BrowserClient, reactRouterV6Instrumentation } from '../src'; +import { BrowserClient } from '../src'; import { reactRouterV6BrowserTracingIntegration, withSentryReactRouterV6Routing, wrapUseRoutes, } from '../src/reactrouterv6'; -describe('reactRouterV6Instrumentation', () => { - function createInstrumentation(_opts?: { - startTransactionOnPageLoad?: boolean; - startTransactionOnLocationChange?: boolean; - }): [jest.Mock, { mockUpdateName: jest.Mock; mockFinish: jest.Mock; mockSetAttribute: jest.Mock }] { - const options = { - matchPath: _opts ? matchPath : undefined, - startTransactionOnLocationChange: true, - startTransactionOnPageLoad: true, - ..._opts, - }; - const mockFinish = jest.fn(); - const mockUpdateName = jest.fn(); - const mockSetAttribute = jest.fn(); - const mockStartTransaction = jest - .fn() - .mockReturnValue({ updateName: mockUpdateName, end: mockFinish, setAttribute: mockSetAttribute }); - - // eslint-disable-next-line deprecation/deprecation - reactRouterV6Instrumentation( - React.useEffect, - useLocation, - useNavigationType, - createRoutesFromChildren, - matchRoutes, - )(mockStartTransaction, options.startTransactionOnPageLoad, options.startTransactionOnLocationChange); - return [mockStartTransaction, { mockUpdateName, mockFinish, mockSetAttribute }]; - } - - describe('withSentryReactRouterV6Routing', () => { - it('starts a pageload transaction', () => { - const [mockStartTransaction] = createInstrumentation(); - const SentryRoutes = withSentryReactRouterV6Routing(Routes); - - render( - - - Home} /> - - , - ); - - expect(mockStartTransaction).toHaveBeenCalledTimes(1); - expect(mockStartTransaction).toHaveBeenLastCalledWith({ - name: '/', - attributes: { - [SEMANTIC_ATTRIBUTE_SENTRY_SOURCE]: 'url', - [SEMANTIC_ATTRIBUTE_SENTRY_OP]: 'pageload', - [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.pageload.react.reactrouter_v6', - }, - }); - }); - - it('skips pageload transaction with `startTransactionOnPageLoad: false`', () => { - const [mockStartTransaction] = createInstrumentation({ startTransactionOnPageLoad: false }); - const SentryRoutes = withSentryReactRouterV6Routing(Routes); - - render( - - - Home} /> - - , - ); - - expect(mockStartTransaction).toHaveBeenCalledTimes(0); - }); - - it('skips navigation transaction, with `startTransactionOnLocationChange: false`', () => { - const [mockStartTransaction] = createInstrumentation({ startTransactionOnLocationChange: false }); - const SentryRoutes = withSentryReactRouterV6Routing(Routes); - - render( - - - About} /> - } /> - - , - ); - - expect(mockStartTransaction).toHaveBeenCalledTimes(1); - expect(mockStartTransaction).toHaveBeenLastCalledWith({ - name: '/', - attributes: { - [SEMANTIC_ATTRIBUTE_SENTRY_SOURCE]: 'url', - [SEMANTIC_ATTRIBUTE_SENTRY_OP]: 'pageload', - [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.pageload.react.reactrouter_v6', - }, - }); - }); - - it('starts a navigation transaction', () => { - const [mockStartTransaction] = createInstrumentation(); - const SentryRoutes = withSentryReactRouterV6Routing(Routes); - - render( - - - About} /> - } /> - - , - ); - - expect(mockStartTransaction).toHaveBeenCalledTimes(2); - expect(mockStartTransaction).toHaveBeenLastCalledWith({ - name: '/about', - attributes: { - [SEMANTIC_ATTRIBUTE_SENTRY_SOURCE]: 'route', - [SEMANTIC_ATTRIBUTE_SENTRY_OP]: 'navigation', - [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.navigation.react.reactrouter_v6', - }, - }); - }); - - it('works with nested routes', () => { - const [mockStartTransaction] = createInstrumentation(); - const SentryRoutes = withSentryReactRouterV6Routing(Routes); - - render( - - - About}> - us} /> - - } /> - - , - ); - - expect(mockStartTransaction).toHaveBeenCalledTimes(2); - expect(mockStartTransaction).toHaveBeenLastCalledWith({ - name: '/about/us', - attributes: { - [SEMANTIC_ATTRIBUTE_SENTRY_SOURCE]: 'route', - [SEMANTIC_ATTRIBUTE_SENTRY_OP]: 'navigation', - [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.navigation.react.reactrouter_v6', - }, - }); - }); - - it('works with paramaterized paths', () => { - const [mockStartTransaction] = createInstrumentation(); - const SentryRoutes = withSentryReactRouterV6Routing(Routes); - - render( - - - About}> - page} /> - - } /> - - , - ); - - expect(mockStartTransaction).toHaveBeenCalledTimes(2); - expect(mockStartTransaction).toHaveBeenLastCalledWith({ - name: '/about/:page', - attributes: { - [SEMANTIC_ATTRIBUTE_SENTRY_SOURCE]: 'route', - [SEMANTIC_ATTRIBUTE_SENTRY_OP]: 'navigation', - [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.navigation.react.reactrouter_v6', - }, - }); - }); - - it('works with paths with multiple parameters', () => { - const [mockStartTransaction] = createInstrumentation(); - const SentryRoutes = withSentryReactRouterV6Routing(Routes); - - render( - - - Stores}> - Store}> - Product} /> - - - } /> - - , - ); - - expect(mockStartTransaction).toHaveBeenCalledTimes(2); - expect(mockStartTransaction).toHaveBeenLastCalledWith({ - name: '/stores/:storeId/products/:productId', - attributes: { - [SEMANTIC_ATTRIBUTE_SENTRY_SOURCE]: 'route', - [SEMANTIC_ATTRIBUTE_SENTRY_OP]: 'navigation', - [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.navigation.react.reactrouter_v6', - }, - }); - }); - - it('works with nested paths with parameters', () => { - const [mockStartTransaction] = createInstrumentation(); - const SentryRoutes = withSentryReactRouterV6Routing(Routes); - - render( - - - } /> - Account Page} /> - - Project Index} /> - Project Page}> - Project Page Root} /> - Editor}> - View Canvas} /> - Space Canvas} /> - - - - - No Match Page} /> - - , - ); - - expect(mockStartTransaction).toHaveBeenCalledTimes(2); - expect(mockStartTransaction).toHaveBeenLastCalledWith({ - name: '/projects/:projectId/views/:viewId', - attributes: { - [SEMANTIC_ATTRIBUTE_SENTRY_SOURCE]: 'route', - [SEMANTIC_ATTRIBUTE_SENTRY_OP]: 'navigation', - [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.navigation.react.reactrouter_v6', - }, - }); - }); - }); - - describe('wrapUseRoutes', () => { - it('starts a pageload transaction', () => { - const [mockStartTransaction] = createInstrumentation(); - const wrappedUseRoutes = wrapUseRoutes(useRoutes); - - const Routes = () => - wrappedUseRoutes([ - { - path: '/', - element:
Home
, - }, - ]); - - render( - - - , - ); - - expect(mockStartTransaction).toHaveBeenCalledTimes(1); - expect(mockStartTransaction).toHaveBeenLastCalledWith({ - name: '/', - attributes: { - [SEMANTIC_ATTRIBUTE_SENTRY_SOURCE]: 'url', - [SEMANTIC_ATTRIBUTE_SENTRY_OP]: 'pageload', - [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.pageload.react.reactrouter_v6', - }, - }); - }); - - it('skips pageload transaction with `startTransactionOnPageLoad: false`', () => { - const [mockStartTransaction] = createInstrumentation({ startTransactionOnPageLoad: false }); - const wrappedUseRoutes = wrapUseRoutes(useRoutes); - - const Routes = () => - wrappedUseRoutes([ - { - path: '/', - element:
Home
, - }, - ]); - - render( - - - , - ); - - expect(mockStartTransaction).toHaveBeenCalledTimes(0); - }); - - it('skips navigation transaction, with `startTransactionOnLocationChange: false`', () => { - const [mockStartTransaction] = createInstrumentation({ startTransactionOnLocationChange: false }); - const wrappedUseRoutes = wrapUseRoutes(useRoutes); - - const Routes = () => - wrappedUseRoutes([ - { - path: '/', - element: , - }, - { - path: '/about', - element:
About
, - }, - ]); - - render( - - - , - ); - - expect(mockStartTransaction).toHaveBeenCalledTimes(1); - expect(mockStartTransaction).toHaveBeenLastCalledWith({ - name: '/', - attributes: { - [SEMANTIC_ATTRIBUTE_SENTRY_SOURCE]: 'url', - [SEMANTIC_ATTRIBUTE_SENTRY_OP]: 'pageload', - [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.pageload.react.reactrouter_v6', - }, - }); - }); - - it('starts a navigation transaction', () => { - const [mockStartTransaction] = createInstrumentation(); - const wrappedUseRoutes = wrapUseRoutes(useRoutes); - - const Routes = () => - wrappedUseRoutes([ - { - path: '/', - element: , - }, - { - path: '/about', - element:
About
, - }, - ]); - - render( - - - , - ); - - expect(mockStartTransaction).toHaveBeenCalledTimes(2); - expect(mockStartTransaction).toHaveBeenLastCalledWith({ - name: '/about', - attributes: { - [SEMANTIC_ATTRIBUTE_SENTRY_SOURCE]: 'route', - [SEMANTIC_ATTRIBUTE_SENTRY_OP]: 'navigation', - [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.navigation.react.reactrouter_v6', - }, - }); - }); - - it('works with nested routes', () => { - const [mockStartTransaction] = createInstrumentation(); - const wrappedUseRoutes = wrapUseRoutes(useRoutes); - - const Routes = () => - wrappedUseRoutes([ - { - path: '/', - element: , - }, - { - path: '/about', - element:
About
, - children: [ - { - path: '/about/us', - element:
us
, - }, - ], - }, - ]); - - render( - - - , - ); - - expect(mockStartTransaction).toHaveBeenCalledTimes(2); - expect(mockStartTransaction).toHaveBeenLastCalledWith({ - name: '/about/us', - attributes: { - [SEMANTIC_ATTRIBUTE_SENTRY_SOURCE]: 'route', - [SEMANTIC_ATTRIBUTE_SENTRY_OP]: 'navigation', - [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.navigation.react.reactrouter_v6', - }, - }); - }); - - it('works with paramaterized paths', () => { - const [mockStartTransaction] = createInstrumentation(); - const wrappedUseRoutes = wrapUseRoutes(useRoutes); - - const Routes = () => - wrappedUseRoutes([ - { - path: '/', - element: , - }, - { - path: '/about', - element:
About
, - children: [ - { - path: '/about/:page', - element:
page
, - }, - ], - }, - ]); - - render( - - - , - ); - - expect(mockStartTransaction).toHaveBeenCalledTimes(2); - expect(mockStartTransaction).toHaveBeenLastCalledWith({ - name: '/about/:page', - attributes: { - [SEMANTIC_ATTRIBUTE_SENTRY_SOURCE]: 'route', - [SEMANTIC_ATTRIBUTE_SENTRY_OP]: 'navigation', - [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.navigation.react.reactrouter_v6', - }, - }); - }); - - it('works with paths with multiple parameters', () => { - const [mockStartTransaction] = createInstrumentation(); - const wrappedUseRoutes = wrapUseRoutes(useRoutes); - - const Routes = () => - wrappedUseRoutes([ - { - path: '/', - element: , - }, - { - path: '/stores', - element:
Stores
, - children: [ - { - path: '/stores/:storeId', - element:
Store
, - children: [ - { - path: '/stores/:storeId/products/:productId', - element:
Product
, - }, - ], - }, - ], - }, - ]); - - render( - - - , - ); - - expect(mockStartTransaction).toHaveBeenCalledTimes(2); - expect(mockStartTransaction).toHaveBeenLastCalledWith({ - name: '/stores/:storeId/products/:productId', - attributes: { - [SEMANTIC_ATTRIBUTE_SENTRY_SOURCE]: 'route', - [SEMANTIC_ATTRIBUTE_SENTRY_OP]: 'navigation', - [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.navigation.react.reactrouter_v6', - }, - }); - }); - - it('works with nested paths with parameters', () => { - const [mockStartTransaction] = createInstrumentation(); - const wrappedUseRoutes = wrapUseRoutes(useRoutes); - - const Routes = () => - wrappedUseRoutes([ - { - index: true, - element: , - }, - { - path: 'account', - element:
Account Page
, - }, - { - path: 'projects', - children: [ - { - index: true, - element:
Project Index
, - }, - { - path: ':projectId', - element:
Project Page
, - children: [ - { - index: true, - element:
Project Page Root
, - }, - { - element:
Editor
, - children: [ - { - path: 'views/:viewId', - element:
View Canvas
, - }, - { - path: 'spaces/:spaceId', - element:
Space Canvas
, - }, - ], - }, - ], - }, - ], - }, - { - path: '*', - element:
No Match Page
, - }, - ]); - - render( - - - , - ); - - expect(mockStartTransaction).toHaveBeenCalledTimes(2); - expect(mockStartTransaction).toHaveBeenLastCalledWith({ - name: '/projects/:projectId/views/:viewId', - attributes: { - [SEMANTIC_ATTRIBUTE_SENTRY_SOURCE]: 'route', - [SEMANTIC_ATTRIBUTE_SENTRY_OP]: 'navigation', - [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.navigation.react.reactrouter_v6', - }, - }); - }); - - it('does not add double slashes to URLS', () => { - const [mockStartTransaction, { mockUpdateName, mockSetAttribute }] = createInstrumentation(); - const wrappedUseRoutes = wrapUseRoutes(useRoutes); - - const Routes = () => - wrappedUseRoutes([ - { - path: '/', - element: ( -
- -
- ), - children: [ - { - path: 'tests', - children: [ - { index: true, element:
Main Test
}, - { path: ':testId/*', element:
Test Component
}, - ], - }, - { path: '/', element: }, - { path: '*', element: }, - ], - }, - { - path: '/', - element:
, - children: [ - { path: '404', element:
Error
}, - { path: '*', element: }, - ], - }, - ]); - - render( - - - , - ); - - expect(mockStartTransaction).toHaveBeenCalledTimes(1); - // should be /tests not //tests - expect(mockUpdateName).toHaveBeenLastCalledWith('/tests'); - expect(mockSetAttribute).toHaveBeenCalledWith(SEMANTIC_ATTRIBUTE_SENTRY_SOURCE, 'route'); - }); - - it('handles wildcard routes properly', () => { - const [mockStartTransaction, { mockUpdateName, mockSetAttribute }] = createInstrumentation(); - const wrappedUseRoutes = wrapUseRoutes(useRoutes); - - const Routes = () => - wrappedUseRoutes([ - { - path: '/', - element: ( -
- -
- ), - children: [ - { - path: 'tests', - children: [ - { index: true, element:
Main Test
}, - { path: ':testId/*', element:
Test Component
}, - ], - }, - { path: '/', element: }, - { path: '*', element: }, - ], - }, - { - path: '/', - element:
, - children: [ - { path: '404', element:
Error
}, - { path: '*', element: }, - ], - }, - ]); - - render( - - - , - ); - - expect(mockStartTransaction).toHaveBeenCalledTimes(1); - expect(mockUpdateName).toHaveBeenLastCalledWith('/tests/:testId/*'); - expect(mockSetAttribute).toHaveBeenCalledWith(SEMANTIC_ATTRIBUTE_SENTRY_SOURCE, 'route'); - }); - }); -}); - const mockStartBrowserTracingPageLoadSpan = jest.fn(); const mockStartBrowserTracingNavigationSpan = jest.fn();