From 9cab16b4cf2602d7dbadc465c56c004fe1248021 Mon Sep 17 00:00:00 2001 From: Antonis Lilis Date: Thu, 31 Oct 2024 17:31:42 +0200 Subject: [PATCH] Enable Spotlight in Android and iOS SDKs (#4211) * Passes spotlight initialisation option to the native SDKs * Adds changelog * Extracts getSentryAndroidOptions method and adds test * Handles spotlight url parameters in cocoa * Handles spotlight url parameters in java * Pass the RN JS defaultSidecarUrl to the platform layers when Spotlight is enabled --------- Co-authored-by: LucasZF --- CHANGELOG.md | 4 + .../io/sentry/react/RNSentryModuleImplTest.kt | 32 +++ .../RNSentryCocoaTesterTests/RNSentryTests.mm | 80 ++++++ .../io/sentry/react/RNSentryModuleImpl.java | 251 +++++++++--------- packages/core/ios/RNSentry.mm | 16 ++ packages/core/src/js/client.ts | 2 + .../core/src/js/integrations/spotlight.ts | 5 +- packages/core/src/js/wrapper.ts | 2 + packages/core/test/wrapper.test.ts | 30 ++- 9 files changed, 296 insertions(+), 126 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3e8022f860..d0308b9daa 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,10 @@ - Skips ignoring require cycle logs for RN 0.70 or newer ([#4214](https://github.com/getsentry/sentry-react-native/pull/4214)) - Enhanced accuracy of time-to-display spans. ([#4189](https://github.com/getsentry/sentry-react-native/pull/4189)) +### Features + +- Enables Spotlight in Android and iOS SDKs ([#4211](https://github.com/getsentry/sentry-react-native/pull/4211)) + ### Dependencies - Bump JavaScript SDK from v8.34.0 to v8.35.0 ([#4196](https://github.com/getsentry/sentry-react-native/pull/4196)) diff --git a/packages/core/RNSentryAndroidTester/app/src/test/java/io/sentry/react/RNSentryModuleImplTest.kt b/packages/core/RNSentryAndroidTester/app/src/test/java/io/sentry/react/RNSentryModuleImplTest.kt index 5a6585ab80..f699f0d530 100644 --- a/packages/core/RNSentryAndroidTester/app/src/test/java/io/sentry/react/RNSentryModuleImplTest.kt +++ b/packages/core/RNSentryAndroidTester/app/src/test/java/io/sentry/react/RNSentryModuleImplTest.kt @@ -3,13 +3,16 @@ package io.sentry.react import android.content.pm.PackageInfo import android.content.pm.PackageManager import com.facebook.react.bridge.Arguments +import com.facebook.react.bridge.JavaOnlyMap import com.facebook.react.bridge.Promise import com.facebook.react.bridge.ReactApplicationContext import com.facebook.react.bridge.WritableMap import io.sentry.ILogger import io.sentry.SentryLevel +import io.sentry.android.core.SentryAndroidOptions import org.junit.After import org.junit.Assert.assertEquals +import org.junit.Assert.assertFalse import org.junit.Before import org.junit.Test import org.junit.runner.RunWith @@ -93,4 +96,33 @@ class RNSentryModuleImplTest { val capturedMap = writableMapCaptor.value assertEquals(false, capturedMap.getBoolean("has_fetched")) } + + @Test + fun `when the spotlight option is enabled, the spotlight SentryAndroidOption is set to true and the default url is used`() { + val options = JavaOnlyMap.of( + "spotlight", true, + "defaultSidecarUrl", "http://localhost:8969/teststream" + ) + val actualOptions = SentryAndroidOptions() + module.getSentryAndroidOptions(actualOptions, options, logger) + assert(actualOptions.isEnableSpotlight) + assertEquals("http://localhost:8969/teststream", actualOptions.spotlightConnectionUrl) + } + + @Test + fun `when the spotlight url is passed, the spotlight is enabled for the given url`() { + val options = JavaOnlyMap.of("spotlight", "http://localhost:8969/teststream") + val actualOptions = SentryAndroidOptions() + module.getSentryAndroidOptions(actualOptions, options, logger) + assert(actualOptions.isEnableSpotlight) + assertEquals("http://localhost:8969/teststream", actualOptions.spotlightConnectionUrl) + } + + @Test + fun `when the spotlight option is disabled, the spotlight SentryAndroidOption is set to false`() { + val options = JavaOnlyMap.of("spotlight", false) + val actualOptions = SentryAndroidOptions() + module.getSentryAndroidOptions(actualOptions, options, logger) + assertFalse(actualOptions.isEnableSpotlight) + } } diff --git a/packages/core/RNSentryCocoaTester/RNSentryCocoaTesterTests/RNSentryTests.mm b/packages/core/RNSentryCocoaTester/RNSentryCocoaTesterTests/RNSentryTests.mm index 615deb45c4..1ef91af466 100644 --- a/packages/core/RNSentryCocoaTester/RNSentryCocoaTesterTests/RNSentryTests.mm +++ b/packages/core/RNSentryCocoaTester/RNSentryCocoaTesterTests/RNSentryTests.mm @@ -122,6 +122,86 @@ - (void)testCreateOptionsWithDictionaryAutoPerformanceTracingDisabled XCTAssertEqual(actualOptions.enableAutoPerformanceTracing, false, @"Did not disable Auto Performance Tracing"); } +- (void)testCreateOptionsWithDictionarySpotlightEnabled +{ + RNSentry * rnSentry = [[RNSentry alloc] init]; + NSError* error = nil; + + NSDictionary *_Nonnull mockedReactNativeDictionary = @{ + @"dsn": @"https://abcd@efgh.ingest.sentry.io/123456", + @"spotlight": @YES, + @"defaultSidecarUrl": @"http://localhost:8969/teststream", + }; + SentryOptions* actualOptions = [rnSentry createOptionsWithDictionary:mockedReactNativeDictionary error:&error]; + XCTAssertNotNil(actualOptions, @"Did not create sentry options"); + XCTAssertNil(error, @"Should not pass no error"); + XCTAssertTrue(actualOptions.enableSpotlight , @"Did not enable spotlight"); + XCTAssertEqual(actualOptions.spotlightUrl , @"http://localhost:8969/teststream"); +} + +- (void)testCreateOptionsWithDictionarySpotlightOne +{ + RNSentry * rnSentry = [[RNSentry alloc] init]; + NSError* error = nil; + + NSDictionary *_Nonnull mockedReactNativeDictionary = @{ + @"dsn": @"https://abcd@efgh.ingest.sentry.io/123456", + @"spotlight": @1, + @"defaultSidecarUrl": @"http://localhost:8969/teststream", + }; + SentryOptions* actualOptions = [rnSentry createOptionsWithDictionary:mockedReactNativeDictionary error:&error]; + XCTAssertNotNil(actualOptions, @"Did not create sentry options"); + XCTAssertNil(error, @"Should not pass no error"); + XCTAssertTrue(actualOptions.enableSpotlight , @"Did not enable spotlight"); + XCTAssertEqual(actualOptions.spotlightUrl , @"http://localhost:8969/teststream"); +} + +- (void)testCreateOptionsWithDictionarySpotlightUrl +{ + RNSentry * rnSentry = [[RNSentry alloc] init]; + NSError* error = nil; + + NSDictionary *_Nonnull mockedReactNativeDictionary = @{ + @"dsn": @"https://abcd@efgh.ingest.sentry.io/123456", + @"spotlight": @"http://localhost:8969/teststream", + }; + SentryOptions* actualOptions = [rnSentry createOptionsWithDictionary:mockedReactNativeDictionary error:&error]; + XCTAssertNotNil(actualOptions, @"Did not create sentry options"); + XCTAssertNil(error, @"Should not pass no error"); + XCTAssertTrue(actualOptions.enableSpotlight , @"Did not enable spotlight"); + XCTAssertEqual(actualOptions.spotlightUrl , @"http://localhost:8969/teststream"); +} + +- (void)testCreateOptionsWithDictionarySpotlightDisabled +{ + RNSentry * rnSentry = [[RNSentry alloc] init]; + NSError* error = nil; + + NSDictionary *_Nonnull mockedReactNativeDictionary = @{ + @"dsn": @"https://abcd@efgh.ingest.sentry.io/123456", + @"spotlight": @NO, + }; + SentryOptions* actualOptions = [rnSentry createOptionsWithDictionary:mockedReactNativeDictionary error:&error]; + XCTAssertNotNil(actualOptions, @"Did not create sentry options"); + XCTAssertNil(error, @"Should not pass no error"); + XCTAssertFalse(actualOptions.enableSpotlight, @"Did not disable spotlight"); +} + +- (void)testCreateOptionsWithDictionarySpotlightZero +{ + RNSentry * rnSentry = [[RNSentry alloc] init]; + NSError* error = nil; + + NSDictionary *_Nonnull mockedReactNativeDictionary = @{ + @"dsn": @"https://abcd@efgh.ingest.sentry.io/123456", + @"spotlight": @0, + }; + SentryOptions* actualOptions = [rnSentry createOptionsWithDictionary:mockedReactNativeDictionary error:&error]; + XCTAssertNotNil(actualOptions, @"Did not create sentry options"); + XCTAssertNil(error, @"Should not pass no error"); + XCTAssertFalse(actualOptions.enableSpotlight, @"Did not disable spotlight"); +} + - (void)testPassesErrorOnWrongDsn { RNSentry * rnSentry = [[RNSentry alloc] init]; diff --git a/packages/core/android/src/main/java/io/sentry/react/RNSentryModuleImpl.java b/packages/core/android/src/main/java/io/sentry/react/RNSentryModuleImpl.java index 7472187dd4..e291b78b2c 100644 --- a/packages/core/android/src/main/java/io/sentry/react/RNSentryModuleImpl.java +++ b/packages/core/android/src/main/java/io/sentry/react/RNSentryModuleImpl.java @@ -20,6 +20,7 @@ import com.facebook.react.bridge.ReactApplicationContext; import com.facebook.react.bridge.ReadableMap; import com.facebook.react.bridge.ReadableMapKeySetIterator; +import com.facebook.react.bridge.ReadableType; import com.facebook.react.bridge.UiThreadUtil; import com.facebook.react.bridge.WritableArray; import com.facebook.react.bridge.WritableMap; @@ -175,135 +176,143 @@ public void initNativeReactNavigationNewFrameTracking(Promise promise) { public void initNativeSdk(final ReadableMap rnOptions, Promise promise) { SentryAndroid.init( this.getReactApplicationContext(), - options -> { - @Nullable SdkVersion sdkVersion = options.getSdkVersion(); - if (sdkVersion == null) { - sdkVersion = new SdkVersion(ANDROID_SDK_NAME, BuildConfig.VERSION_NAME); - } else { - sdkVersion.setName(ANDROID_SDK_NAME); - } - - options.setSentryClientName(sdkVersion.getName() + "/" + sdkVersion.getVersion()); - options.setNativeSdkName(NATIVE_SDK_NAME); - options.setSdkVersion(sdkVersion); + options -> getSentryAndroidOptions(options, rnOptions, logger)); - if (rnOptions.hasKey("debug") && rnOptions.getBoolean("debug")) { - options.setDebug(true); - } - if (rnOptions.hasKey("dsn") && rnOptions.getString("dsn") != null) { - String dsn = rnOptions.getString("dsn"); - logger.log(SentryLevel.INFO, String.format("Starting with DSN: '%s'", dsn)); - options.setDsn(dsn); - } else { - // SentryAndroid needs an empty string fallback for the dsn. - options.setDsn(""); - } - if (rnOptions.hasKey("sampleRate")) { - options.setSampleRate(rnOptions.getDouble("sampleRate")); - } - if (rnOptions.hasKey("sendClientReports")) { - options.setSendClientReports(rnOptions.getBoolean("sendClientReports")); - } - if (rnOptions.hasKey("maxBreadcrumbs")) { - options.setMaxBreadcrumbs(rnOptions.getInt("maxBreadcrumbs")); - } - if (rnOptions.hasKey("maxCacheItems")) { - options.setMaxCacheItems(rnOptions.getInt("maxCacheItems")); - } - if (rnOptions.hasKey("environment") && rnOptions.getString("environment") != null) { - options.setEnvironment(rnOptions.getString("environment")); - } - if (rnOptions.hasKey("release") && rnOptions.getString("release") != null) { - options.setRelease(rnOptions.getString("release")); - } - if (rnOptions.hasKey("dist") && rnOptions.getString("dist") != null) { - options.setDist(rnOptions.getString("dist")); - } - if (rnOptions.hasKey("enableAutoSessionTracking")) { - options.setEnableAutoSessionTracking(rnOptions.getBoolean("enableAutoSessionTracking")); - } - if (rnOptions.hasKey("sessionTrackingIntervalMillis")) { - options.setSessionTrackingIntervalMillis( - rnOptions.getInt("sessionTrackingIntervalMillis")); - } - if (rnOptions.hasKey("shutdownTimeout")) { - options.setShutdownTimeoutMillis(rnOptions.getInt("shutdownTimeout")); - } - if (rnOptions.hasKey("enableNdkScopeSync")) { - options.setEnableScopeSync(rnOptions.getBoolean("enableNdkScopeSync")); - } - if (rnOptions.hasKey("attachStacktrace")) { - options.setAttachStacktrace(rnOptions.getBoolean("attachStacktrace")); - } - if (rnOptions.hasKey("attachThreads")) { - // JS use top level stacktrace and android attaches Threads which hides them so - // by default we hide. - options.setAttachThreads(rnOptions.getBoolean("attachThreads")); - } - if (rnOptions.hasKey("attachScreenshot")) { - options.setAttachScreenshot(rnOptions.getBoolean("attachScreenshot")); - } - if (rnOptions.hasKey("attachViewHierarchy")) { - options.setAttachViewHierarchy(rnOptions.getBoolean("attachViewHierarchy")); - } - if (rnOptions.hasKey("sendDefaultPii")) { - options.setSendDefaultPii(rnOptions.getBoolean("sendDefaultPii")); - } - if (rnOptions.hasKey("maxQueueSize")) { - options.setMaxQueueSize(rnOptions.getInt("maxQueueSize")); - } - if (rnOptions.hasKey("enableNdk")) { - options.setEnableNdk(rnOptions.getBoolean("enableNdk")); - } - if (rnOptions.hasKey("_experiments")) { - options.getExperimental().setSessionReplay(getReplayOptions(rnOptions)); - options - .getReplayController() - .setBreadcrumbConverter(new RNSentryReplayBreadcrumbConverter()); - } - options.setBeforeSend( - (event, hint) -> { - // React native internally throws a JavascriptException - // Since we catch it before that, we don't want to send this one - // because we would send it twice - try { - SentryException ex = event.getExceptions().get(0); - if (null != ex && ex.getType().contains("JavascriptException")) { - return null; - } - } catch (Throwable ignored) { // NOPMD - We don't want to crash in any case - // We do nothing - } + promise.resolve(true); + } - setEventOriginTag(event); - addPackages(event, options.getSdkVersion()); + protected void getSentryAndroidOptions( + @NotNull SentryAndroidOptions options, @NotNull ReadableMap rnOptions, ILogger logger) { + @Nullable SdkVersion sdkVersion = options.getSdkVersion(); + if (sdkVersion == null) { + sdkVersion = new SdkVersion(ANDROID_SDK_NAME, BuildConfig.VERSION_NAME); + } else { + sdkVersion.setName(ANDROID_SDK_NAME); + } - return event; - }); + options.setSentryClientName(sdkVersion.getName() + "/" + sdkVersion.getVersion()); + options.setNativeSdkName(NATIVE_SDK_NAME); + options.setSdkVersion(sdkVersion); - if (rnOptions.hasKey("enableNativeCrashHandling") - && !rnOptions.getBoolean("enableNativeCrashHandling")) { - final List integrations = options.getIntegrations(); - for (final Integration integration : integrations) { - if (integration instanceof UncaughtExceptionHandlerIntegration - || integration instanceof AnrIntegration - || integration instanceof NdkIntegration) { - integrations.remove(integration); - } + if (rnOptions.hasKey("debug") && rnOptions.getBoolean("debug")) { + options.setDebug(true); + } + if (rnOptions.hasKey("dsn") && rnOptions.getString("dsn") != null) { + String dsn = rnOptions.getString("dsn"); + logger.log(SentryLevel.INFO, String.format("Starting with DSN: '%s'", dsn)); + options.setDsn(dsn); + } else { + // SentryAndroid needs an empty string fallback for the dsn. + options.setDsn(""); + } + if (rnOptions.hasKey("sampleRate")) { + options.setSampleRate(rnOptions.getDouble("sampleRate")); + } + if (rnOptions.hasKey("sendClientReports")) { + options.setSendClientReports(rnOptions.getBoolean("sendClientReports")); + } + if (rnOptions.hasKey("maxBreadcrumbs")) { + options.setMaxBreadcrumbs(rnOptions.getInt("maxBreadcrumbs")); + } + if (rnOptions.hasKey("maxCacheItems")) { + options.setMaxCacheItems(rnOptions.getInt("maxCacheItems")); + } + if (rnOptions.hasKey("environment") && rnOptions.getString("environment") != null) { + options.setEnvironment(rnOptions.getString("environment")); + } + if (rnOptions.hasKey("release") && rnOptions.getString("release") != null) { + options.setRelease(rnOptions.getString("release")); + } + if (rnOptions.hasKey("dist") && rnOptions.getString("dist") != null) { + options.setDist(rnOptions.getString("dist")); + } + if (rnOptions.hasKey("enableAutoSessionTracking")) { + options.setEnableAutoSessionTracking(rnOptions.getBoolean("enableAutoSessionTracking")); + } + if (rnOptions.hasKey("sessionTrackingIntervalMillis")) { + options.setSessionTrackingIntervalMillis(rnOptions.getInt("sessionTrackingIntervalMillis")); + } + if (rnOptions.hasKey("shutdownTimeout")) { + options.setShutdownTimeoutMillis(rnOptions.getInt("shutdownTimeout")); + } + if (rnOptions.hasKey("enableNdkScopeSync")) { + options.setEnableScopeSync(rnOptions.getBoolean("enableNdkScopeSync")); + } + if (rnOptions.hasKey("attachStacktrace")) { + options.setAttachStacktrace(rnOptions.getBoolean("attachStacktrace")); + } + if (rnOptions.hasKey("attachThreads")) { + // JS use top level stacktrace and android attaches Threads which hides them so + // by default we hide. + options.setAttachThreads(rnOptions.getBoolean("attachThreads")); + } + if (rnOptions.hasKey("attachScreenshot")) { + options.setAttachScreenshot(rnOptions.getBoolean("attachScreenshot")); + } + if (rnOptions.hasKey("attachViewHierarchy")) { + options.setAttachViewHierarchy(rnOptions.getBoolean("attachViewHierarchy")); + } + if (rnOptions.hasKey("sendDefaultPii")) { + options.setSendDefaultPii(rnOptions.getBoolean("sendDefaultPii")); + } + if (rnOptions.hasKey("maxQueueSize")) { + options.setMaxQueueSize(rnOptions.getInt("maxQueueSize")); + } + if (rnOptions.hasKey("enableNdk")) { + options.setEnableNdk(rnOptions.getBoolean("enableNdk")); + } + if (rnOptions.hasKey("spotlight")) { + if (rnOptions.getType("spotlight") == ReadableType.Boolean) { + options.setEnableSpotlight(rnOptions.getBoolean("spotlight")); + options.setSpotlightConnectionUrl(rnOptions.getString("defaultSidecarUrl")); + } else if (rnOptions.getType("spotlight") == ReadableType.String) { + options.setEnableSpotlight(true); + options.setSpotlightConnectionUrl(rnOptions.getString("spotlight")); + } + } + if (rnOptions.hasKey("_experiments")) { + options.getExperimental().setSessionReplay(getReplayOptions(rnOptions)); + options.getReplayController().setBreadcrumbConverter(new RNSentryReplayBreadcrumbConverter()); + } + options.setBeforeSend( + (event, hint) -> { + // React native internally throws a JavascriptException + // Since we catch it before that, we don't want to send this one + // because we would send it twice + try { + SentryException ex = event.getExceptions().get(0); + if (null != ex && ex.getType().contains("JavascriptException")) { + return null; } + } catch (Throwable ignored) { // NOPMD - We don't want to crash in any case + // We do nothing } - logger.log( - SentryLevel.INFO, - String.format("Native Integrations '%s'", options.getIntegrations())); - - final CurrentActivityHolder currentActivityHolder = CurrentActivityHolder.getInstance(); - final Activity currentActivity = getCurrentActivity(); - if (currentActivity != null) { - currentActivityHolder.setActivity(currentActivity); - } + + setEventOriginTag(event); + addPackages(event, options.getSdkVersion()); + + return event; }); - promise.resolve(true); + if (rnOptions.hasKey("enableNativeCrashHandling") + && !rnOptions.getBoolean("enableNativeCrashHandling")) { + final List integrations = options.getIntegrations(); + for (final Integration integration : integrations) { + if (integration instanceof UncaughtExceptionHandlerIntegration + || integration instanceof AnrIntegration + || integration instanceof NdkIntegration) { + integrations.remove(integration); + } + } + } + logger.log( + SentryLevel.INFO, String.format("Native Integrations '%s'", options.getIntegrations())); + + final CurrentActivityHolder currentActivityHolder = CurrentActivityHolder.getInstance(); + final Activity currentActivity = getCurrentActivity(); + if (currentActivity != null) { + currentActivityHolder.setActivity(currentActivity); + } } private SentryReplayOptions getReplayOptions(@NotNull ReadableMap rnOptions) { diff --git a/packages/core/ios/RNSentry.mm b/packages/core/ios/RNSentry.mm index 3009e46c09..98bb3e5ae8 100644 --- a/packages/core/ios/RNSentry.mm +++ b/packages/core/ios/RNSentry.mm @@ -161,6 +161,22 @@ - (SentryOptions *_Nullable)createOptionsWithDictionary:(NSDictionary *_Nonnull) sentryOptions.integrations = integrations; } } + + // Set spotlight option + if ([mutableOptions valueForKey:@"spotlight"] != nil) { + id spotlightValue = [mutableOptions valueForKey:@"spotlight"]; + if ([spotlightValue isKindOfClass:[NSString class]]) { + NSLog(@"Using Spotlight on address: %@", spotlightValue); + sentryOptions.enableSpotlight = true; + sentryOptions.spotlightUrl = spotlightValue; + } else if ([spotlightValue isKindOfClass:[NSNumber class]]) { + sentryOptions.enableSpotlight = [spotlightValue boolValue]; + id defaultSpotlightUrl = [mutableOptions valueForKey:@"defaultSidecarUrl"]; + if (defaultSpotlightUrl != nil) { + sentryOptions.spotlightUrl = defaultSpotlightUrl; + } + } + } // Enable the App start and Frames tracking measurements if ([mutableOptions valueForKey:@"enableAutoPerformanceTracing"] != nil) { diff --git a/packages/core/src/js/client.ts b/packages/core/src/js/client.ts index 0497df97de..eba33d13d3 100644 --- a/packages/core/src/js/client.ts +++ b/packages/core/src/js/client.ts @@ -15,6 +15,7 @@ import { dateTimestampInSeconds, logger, SentryError } from '@sentry/utils'; import { Alert } from 'react-native'; import { defaultSdkInfo } from './integrations/sdkinfo'; +import { getDefaultSidecarUrl } from './integrations/spotlight'; import type { ReactNativeClientOptions } from './options'; import type { mobileReplayIntegration } from './replay/mobilereplay'; import { MOBILE_REPLAY_INTEGRATION_NAME } from './replay/mobilereplay'; @@ -144,6 +145,7 @@ export class ReactNativeClient extends BaseClient { private _initNativeSdk(): void { NATIVE.initNativeSdk({ ...this._options, + defaultSidecarUrl: getDefaultSidecarUrl(), mobileReplayOptions: this._integrations[MOBILE_REPLAY_INTEGRATION_NAME] && 'options' in this._integrations[MOBILE_REPLAY_INTEGRATION_NAME] diff --git a/packages/core/src/js/integrations/spotlight.ts b/packages/core/src/js/integrations/spotlight.ts index 2116dd0cf8..9707e95026 100644 --- a/packages/core/src/js/integrations/spotlight.ts +++ b/packages/core/src/js/integrations/spotlight.ts @@ -83,7 +83,10 @@ function sendEnvelopesToSidecar(client: Client, sidecarUrl: string): void { }); } -function getDefaultSidecarUrl(): string { +/** + * Gets the default Spotlight sidecar URL. + */ +export function getDefaultSidecarUrl(): string { try { const { url } = ReactNativeLibraries.Devtools?.getDevServer(); return `http://${getHostnameFromString(url)}:8969/stream`; diff --git a/packages/core/src/js/wrapper.ts b/packages/core/src/js/wrapper.ts index 577eaf99e6..f32652a0ce 100644 --- a/packages/core/src/js/wrapper.ts +++ b/packages/core/src/js/wrapper.ts @@ -50,6 +50,8 @@ export interface Screenshot { } export type NativeSdkOptions = Partial & { + defaultSidecarUrl: string | undefined; +} & { mobileReplayOptions: MobileReplayOptions | undefined; }; diff --git a/packages/core/test/wrapper.test.ts b/packages/core/test/wrapper.test.ts index 656c8f2b9a..ff54667211 100644 --- a/packages/core/test/wrapper.test.ts +++ b/packages/core/test/wrapper.test.ts @@ -96,7 +96,12 @@ describe('Tests Native Wrapper', () => { describe('startWithOptions', () => { test('calls native module', async () => { - await NATIVE.initNativeSdk({ dsn: 'test', enableNative: true, mobileReplayOptions: undefined }); + await NATIVE.initNativeSdk({ + dsn: 'test', + enableNative: true, + defaultSidecarUrl: undefined, + mobileReplayOptions: undefined, + }); expect(RNSentry.initNativeSdk).toBeCalled(); }); @@ -104,7 +109,7 @@ describe('Tests Native Wrapper', () => { test('warns if there is no dsn', async () => { logger.warn = jest.fn(); - await NATIVE.initNativeSdk({ enableNative: true, mobileReplayOptions: undefined }); + await NATIVE.initNativeSdk({ enableNative: true, defaultSidecarUrl: undefined, mobileReplayOptions: undefined }); expect(RNSentry.initNativeSdk).not.toBeCalled(); expect(logger.warn).toHaveBeenLastCalledWith( @@ -119,6 +124,7 @@ describe('Tests Native Wrapper', () => { dsn: 'test', enableNative: false, enableNativeNagger: true, + defaultSidecarUrl: undefined, mobileReplayOptions: undefined, }); @@ -133,6 +139,7 @@ describe('Tests Native Wrapper', () => { enableNative: true, autoInitializeNativeSdk: true, beforeSend: jest.fn(), + defaultSidecarUrl: undefined, mobileReplayOptions: undefined, }); @@ -149,6 +156,7 @@ describe('Tests Native Wrapper', () => { enableNative: true, autoInitializeNativeSdk: true, beforeBreadcrumb: jest.fn(), + defaultSidecarUrl: undefined, mobileReplayOptions: undefined, }); @@ -165,6 +173,7 @@ describe('Tests Native Wrapper', () => { enableNative: true, autoInitializeNativeSdk: true, beforeSendTransaction: jest.fn(), + defaultSidecarUrl: undefined, mobileReplayOptions: undefined, }); @@ -181,6 +190,7 @@ describe('Tests Native Wrapper', () => { enableNative: true, autoInitializeNativeSdk: true, integrations: [], + defaultSidecarUrl: undefined, mobileReplayOptions: undefined, }); @@ -199,6 +209,7 @@ describe('Tests Native Wrapper', () => { dsn: 'test', enableNative: true, autoInitializeNativeSdk: false, + defaultSidecarUrl: undefined, mobileReplayOptions: undefined, }); @@ -238,6 +249,7 @@ describe('Tests Native Wrapper', () => { logger.warn = jest.fn(); await NATIVE.initNativeSdk({ + defaultSidecarUrl: undefined, mobileReplayOptions: undefined, dsn: 'test', enableNative: false, @@ -320,7 +332,12 @@ describe('Tests Native Wrapper', () => { }); test('does not call RNSentry at all if enableNative is false', async () => { try { - await NATIVE.initNativeSdk({ dsn: 'test-dsn', enableNative: false, mobileReplayOptions: undefined }); + await NATIVE.initNativeSdk({ + dsn: 'test-dsn', + enableNative: false, + defaultSidecarUrl: undefined, + mobileReplayOptions: undefined, + }); // @ts-expect-error for testing, does not accept an empty class. await NATIVE.sendEnvelope({}); @@ -512,7 +529,12 @@ describe('Tests Native Wrapper', () => { expect(RNSentry.crash).toBeCalled(); }); test('does not call crash if enableNative is false', async () => { - await NATIVE.initNativeSdk({ dsn: 'test-dsn', enableNative: false, mobileReplayOptions: undefined }); + await NATIVE.initNativeSdk({ + dsn: 'test-dsn', + enableNative: false, + defaultSidecarUrl: undefined, + mobileReplayOptions: undefined, + }); NATIVE.nativeCrash(); expect(RNSentry.crash).not.toBeCalled();