Skip to content

Commit ac22be2

Browse files
authored
feat(react-router): Re-export functions from @sentry/react (#16465)
Re-exports functionality from the `@sentry/react`. However, not everything is exported as the react router exports would be confusing. closes #16459
1 parent 4806b8a commit ac22be2

File tree

3 files changed

+129
-0
lines changed

3 files changed

+129
-0
lines changed

packages/react-router/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@
4242
"@sentry/cli": "^2.45.0",
4343
"@sentry/core": "9.25.1",
4444
"@sentry/node": "9.25.1",
45+
"@sentry/react": "9.25.1",
4546
"@sentry/vite-plugin": "^3.2.4",
4647
"glob": "11.0.1"
4748
},

packages/react-router/src/client/index.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,3 +2,14 @@ export * from '@sentry/browser';
22

33
export { init } from './sdk';
44
export { reactRouterTracingIntegration } from './tracingIntegration';
5+
6+
export {
7+
captureReactException,
8+
reactErrorHandler,
9+
Profiler,
10+
withProfiler,
11+
useProfiler,
12+
ErrorBoundary,
13+
withErrorBoundary,
14+
} from '@sentry/react';
15+
export type { ErrorBoundaryProps, FallbackRender } from '@sentry/react';
Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
import * as SentryReact from '@sentry/react';
2+
import { render } from '@testing-library/react';
3+
import * as React from 'react';
4+
import { afterEach, describe, expect, it, vi } from 'vitest';
5+
import type { ErrorBoundaryProps, FallbackRender } from '../../src/client';
6+
import {
7+
captureReactException,
8+
ErrorBoundary,
9+
Profiler,
10+
reactErrorHandler,
11+
useProfiler,
12+
withErrorBoundary,
13+
withProfiler,
14+
} from '../../src/client';
15+
16+
describe('Re-exports from React SDK', () => {
17+
afterEach(() => {
18+
vi.clearAllMocks();
19+
});
20+
21+
describe('re-export integrity', () => {
22+
it('should have the same reference as original @sentry/react exports', () => {
23+
// Ensure we are re-exporting the exact same functions/components, not copies
24+
expect(captureReactException).toBe(SentryReact.captureReactException);
25+
expect(reactErrorHandler).toBe(SentryReact.reactErrorHandler);
26+
expect(Profiler).toBe(SentryReact.Profiler);
27+
expect(withProfiler).toBe(SentryReact.withProfiler);
28+
expect(useProfiler).toBe(SentryReact.useProfiler);
29+
expect(ErrorBoundary).toBe(SentryReact.ErrorBoundary);
30+
expect(withErrorBoundary).toBe(SentryReact.withErrorBoundary);
31+
});
32+
});
33+
34+
describe('function exports', () => {
35+
it('captureReactException should work when called', () => {
36+
const error = new Error('test error');
37+
const errorInfo = { componentStack: 'component stack' };
38+
39+
const result = captureReactException(error, errorInfo);
40+
expect(typeof result).toBe('string');
41+
expect(result).toMatch(/^[a-f0-9]{32}$/); // assuming event ID is a 32-character hex string
42+
});
43+
});
44+
45+
describe('component exports', () => {
46+
it('ErrorBoundary should render children when no error occurs', () => {
47+
const { getByText } = render(
48+
React.createElement(
49+
ErrorBoundary,
50+
{ fallback: () => React.createElement('div', null, 'Error occurred') },
51+
React.createElement('div', null, 'Child content'),
52+
),
53+
);
54+
55+
expect(getByText('Child content')).toBeDefined();
56+
});
57+
58+
it('Profiler should render children', () => {
59+
const { getByText } = render(
60+
React.createElement(
61+
Profiler,
62+
{ name: 'TestProfiler', updateProps: {} },
63+
React.createElement('div', null, 'Profiled content'),
64+
),
65+
);
66+
67+
expect(getByText('Profiled content')).toBeDefined();
68+
});
69+
});
70+
71+
describe('HOC exports', () => {
72+
it('withErrorBoundary should create a wrapped component', () => {
73+
const TestComponent = () => React.createElement('div', null, 'ErrorBoundary Test Component');
74+
const WrappedComponent = withErrorBoundary(TestComponent, {
75+
fallback: () => React.createElement('div', null, 'Error occurred'),
76+
});
77+
78+
expect(WrappedComponent).toBeDefined();
79+
expect(typeof WrappedComponent).toBe('function');
80+
expect(WrappedComponent.displayName).toBe('errorBoundary(TestComponent)');
81+
82+
const { getByText } = render(React.createElement(WrappedComponent));
83+
expect(getByText('ErrorBoundary Test Component')).toBeDefined();
84+
});
85+
86+
it('withProfiler should create a wrapped component', () => {
87+
const TestComponent = () => React.createElement('div', null, 'Profiler Test Component');
88+
const WrappedComponent = withProfiler(TestComponent, { name: 'TestComponent' });
89+
90+
expect(WrappedComponent).toBeDefined();
91+
expect(typeof WrappedComponent).toBe('function');
92+
expect(WrappedComponent.displayName).toBe('profiler(TestComponent)');
93+
94+
const { getByText } = render(React.createElement(WrappedComponent));
95+
expect(getByText('Profiler Test Component')).toBeDefined();
96+
});
97+
});
98+
99+
describe('type exports', () => {
100+
it('should export ErrorBoundaryProps type', () => {
101+
// This is a compile-time test - if this compiles, the type is exported correctly
102+
const props: ErrorBoundaryProps = {
103+
children: React.createElement('div'),
104+
fallback: () => React.createElement('div', null, 'Error'),
105+
};
106+
expect(props).toBeDefined();
107+
});
108+
109+
it('should export FallbackRender type', () => {
110+
// This is a compile-time test - if this compiles, the type is exported correctly
111+
const fallbackRender: FallbackRender = ({ error }) =>
112+
React.createElement('div', null, `Error: ${error?.toString()}`);
113+
expect(fallbackRender).toBeDefined();
114+
expect(typeof fallbackRender).toBe('function');
115+
});
116+
});
117+
});

0 commit comments

Comments
 (0)