diff --git a/packages/core/src/js/options.ts b/packages/core/src/js/options.ts index 14cc743fc2..6ecc232ff0 100644 --- a/packages/core/src/js/options.ts +++ b/packages/core/src/js/options.ts @@ -294,7 +294,7 @@ export interface ReactNativeClientOptions export interface ReactNativeWrapperOptions { /** Props for the root React profiler */ - profilerProps?: ProfilerProps; + profilerProps?: Omit; /** Props for the root touch event boundary */ touchEventBoundaryProps?: TouchEventBoundaryProps; diff --git a/packages/core/src/js/sdk.tsx b/packages/core/src/js/sdk.tsx index c50f1c35f1..fefa97d0c1 100644 --- a/packages/core/src/js/sdk.tsx +++ b/packages/core/src/js/sdk.tsx @@ -156,7 +156,7 @@ export function wrap

>( options?: ReactNativeWrapperOptions ): React.ComponentType

{ const profilerProps = { - ...(options?.profilerProps ?? {}), + ...(options?.profilerProps), name: RootComponent.displayName ?? 'Root', updateProps: {} }; diff --git a/packages/core/src/js/tracing/reactnativeprofiler.tsx b/packages/core/src/js/tracing/reactnativeprofiler.tsx index 49ad27df88..72e2483074 100644 --- a/packages/core/src/js/tracing/reactnativeprofiler.tsx +++ b/packages/core/src/js/tracing/reactnativeprofiler.tsx @@ -12,13 +12,15 @@ const ReactNativeProfilerGlobalState = { }, }; +type ProfilerConstructorProps = ConstructorParameters[0]; + /** * Custom profiler for the React Native app root. */ export class ReactNativeProfiler extends Profiler { public readonly name: string = 'ReactNativeProfiler'; - public constructor(props: ConstructorParameters[0]) { + public constructor(props: ProfilerConstructorProps) { _setRootComponentCreationTimestampMs(timestampInSeconds() * 1000); super(props); } diff --git a/packages/core/test/wrap.mocked.test.tsx b/packages/core/test/wrap.mocked.test.tsx new file mode 100644 index 0000000000..dab86de46d --- /dev/null +++ b/packages/core/test/wrap.mocked.test.tsx @@ -0,0 +1,111 @@ +// We can't test wrap with mock and non mocked components, otherwise it will break the RN testing library. +import { render } from '@testing-library/react-native'; +import * as React from 'react'; +import type { ReactNativeWrapperOptions } from 'src/js/options'; + +jest.doMock('../src/js/touchevents', () => { + return { + TouchEventBoundary: ({ children }: { children: React.ReactNode }) => ( + // eslint-disable-next-line react/no-unknown-property +

{children}
+ ), + } +}); + +jest.doMock('../src/js/tracing', () => { + return { + ReactNativeProfiler: jest.fn(({ children }: { children: React.ReactNode }) => ( + // eslint-disable-next-line react/no-unknown-property +
{children}
+ )), + } +}); + +jest.doMock('../src/js/feedback/FeedbackWidgetManager', () => { + return { + FeedbackWidgetProvider: ({ children }: { children: React.ReactNode }) => ( + // eslint-disable-next-line react/no-unknown-property +
{children}
+ ), + }; +}); + + +import { wrap } from '../src/js/sdk'; +import { ReactNativeProfiler } from '../src/js/tracing'; + +describe('Sentry.wrap', () => { + + const DummyComponent: React.FC<{ value?: string }> = ({ value }) =>
{value}
; + + it('should not enforce any keys on the wrapped component', () => { + const Mock: React.FC<{ test: 23 }> = () => <>; + const ActualWrapped = wrap(Mock); + + expect(typeof ActualWrapped.defaultProps).toBe(typeof Mock.defaultProps); + }); + + it('wraps components with Sentry wrappers', () => { + const Wrapped = wrap(DummyComponent); + const renderResult = render(); + + expect(renderResult.toJSON()).toMatchInlineSnapshot(` +
+
+
+
+ wrapped +
+
+
+
+`); + }); + + describe('ReactNativeProfiler', () => { + it('uses given options when set', () => { + const options: ReactNativeWrapperOptions = { + profilerProps: { disabled: false, includeRender: true, includeUpdates: true }, + }; + const Wrapped = wrap(DummyComponent, options); + render(); + + expect(ReactNativeProfiler).toHaveBeenCalledWith( + expect.objectContaining({ + name: 'Root', + disabled: false, + includeRender: true, + includeUpdates: true + }), + expect.anything(), + ); + + expect(ReactNativeProfiler).not.toHaveBeenCalledWith( + expect.objectContaining({ + updateProps: expect.anything(), + }) + ); + }); + + it('ignore updateProps when set', () => { + const { wrap } = jest.requireActual('../src/js/sdk'); + + const Wrapped = wrap(DummyComponent, { updateProps: ['prop'] }); + render(); + + expect(ReactNativeProfiler).toHaveBeenCalledWith( + expect.objectContaining({ + name: 'Root', + updateProps: {}, + }), + expect.anything(), + ); + }); + }); + }); diff --git a/packages/core/test/wrap.test.tsx b/packages/core/test/wrap.test.tsx index f949b36d38..0bac2041c2 100644 --- a/packages/core/test/wrap.test.tsx +++ b/packages/core/test/wrap.test.tsx @@ -1,3 +1,4 @@ +// We can't test wrap with mock and non mocked components, otherwise it will break the RN testing library. import { logger, setCurrentClient } from '@sentry/core'; import { render } from '@testing-library/react-native'; import * as React from 'react'; @@ -7,30 +8,23 @@ import * as AppRegistry from '../src/js/integrations/appRegistry'; import { wrap } from '../src/js/sdk'; import { getDefaultTestClientOptions, TestClient } from './mocks/client'; -describe('Sentry.wrap', () => { - it('should not enforce any keys on the wrapped component', () => { - const Mock: React.FC<{ test: 23 }> = () => <>; - const ActualWrapped = wrap(Mock); +describe('ReactNativeProfiler', () => { + it('should wrap the component and init with a warning when getAppRegistryIntegration returns undefined', () => { + logger.warn = jest.fn(); + const getAppRegistryIntegration = jest.spyOn(AppRegistry, 'getAppRegistryIntegration').mockReturnValueOnce(undefined); + const Mock: React.FC = () => Test; + const client = new TestClient( + getDefaultTestClientOptions(), + ); + setCurrentClient(client); - expect(typeof ActualWrapped.defaultProps).toBe(typeof Mock.defaultProps); - }); - - it('should wrap the component and init with a warning when getAppRegistryIntegration returns undefined', () => { - logger.warn = jest.fn(); - const getAppRegistryIntegration = jest.spyOn(AppRegistry, 'getAppRegistryIntegration').mockReturnValueOnce(undefined); - const Mock: React.FC = () => Test; - const client = new TestClient( - getDefaultTestClientOptions(), - ); - setCurrentClient(client); - - client.init(); - const ActualWrapped = wrap(Mock); + client.init(); + const ActualWrapped = wrap(Mock); - const { getByText } = render(); + const { getByText } = render(); - expect(getAppRegistryIntegration).toHaveBeenCalled(); - expect(logger.warn).toHaveBeenCalledWith('AppRegistryIntegration.onRunApplication not found or invalid.'); - expect(getByText('Test')).toBeTruthy(); + expect(getAppRegistryIntegration).toHaveBeenCalled(); + expect(logger.warn).toHaveBeenCalledWith('AppRegistryIntegration.onRunApplication not found or invalid.'); + expect(getByText('Test')).toBeTruthy(); + }); }); -});