Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,10 @@

- Warn Expo users at Metro startup when prebuilt native projects are missing Sentry configuration ([#5984](https://github.com/getsentry/sentry-react-native/pull/5984))

### Fixes

- Fix iOS UI profiling options being silently ignored since v8.0.0 ([#6012](https://github.com/getsentry/sentry-react-native/pull/6012))

## 8.8.0

### Features
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
#import <OCMock/OCMock.h>
#import <RNSentry/RNSentry.h>
#import <Sentry/PrivateSentrySDKOnly.h>
#import <Sentry/SentryProfilingConditionals.h>
#import <UIKit/UIKit.h>
#import <XCTest/XCTest.h>
@import Sentry;
Expand Down Expand Up @@ -1372,6 +1373,103 @@ - (void)testStartBeforeBreadcrumbsCallbackDoesNotFiltersOutNonDevServerOrDsnRequ
XCTAssertEqual(breadcrumb, result);
}

#if SENTRY_TARGET_PROFILING_SUPPORTED
// Regression test for the v8.0.0 bug where the init path (RNSentryStart) did not
// handle `_experiments.profilingOptions`, silently dropping iOS UI profiling config.
// This pins the full entry point used by `initNativeSdk` in RNSentry.mm.
- (void)testStartWithDictionaryInstallsConfigureProfilingFromExperimentsProfilingOptions
{
NSError *error = nil;

NSDictionary *_Nonnull mockedReactNativeDictionary = @{
@"dsn" : @"https://abcd@efgh.ingest.sentry.io/123456",
@"_experiments" : @ {
@"profilingOptions" : @ {
@"profileSessionSampleRate" : @1.0,
@"lifecycle" : @"trace",
@"startOnAppStart" : @YES,
},
},
};
[RNSentryStart startWithOptions:mockedReactNativeDictionary error:&error];
SentryOptions *actualOptions = PrivateSentrySDKOnly.options;

XCTAssertNotNil(actualOptions, @"Did not create sentry options");
XCTAssertNil(error, @"Should not pass no error");
XCTAssertNotNil(actualOptions.configureProfiling,
@"configureProfiling must be installed after startWithOptions when profilingOptions is "
@"present");

SentryProfileOptions *probe = [[SentryProfileOptions alloc] init];
actualOptions.configureProfiling(probe);
XCTAssertEqual(probe.sessionSampleRate, 1.0f);
XCTAssertEqual(probe.lifecycle, SentryProfileLifecycleTrace);
XCTAssertTrue(probe.profileAppStarts);
}

- (void)testStartCreateOptionsWithDictionaryProfilingOptionsInstallsConfigureProfiling
{
NSError *error = nil;

NSDictionary *_Nonnull mockedReactNativeDictionary = @{
@"dsn" : @"https://abcd@efgh.ingest.sentry.io/123456",
@"_experiments" : @ {
@"profilingOptions" : @ {
@"profileSessionSampleRate" : @1.0,
@"lifecycle" : @"trace",
@"startOnAppStart" : @YES,
},
},
};
SentryOptions *actualOptions =
[RNSentryStart createOptionsWithDictionary:mockedReactNativeDictionary error:&error];

XCTAssertNotNil(actualOptions, @"Did not create sentry options");
XCTAssertNil(error, @"Should not pass no error");
XCTAssertNotNil(actualOptions.configureProfiling,
@"configureProfiling callback should be installed when profilingOptions is present");

SentryProfileOptions *probe = [[SentryProfileOptions alloc] init];
actualOptions.configureProfiling(probe);
XCTAssertEqual(probe.sessionSampleRate, 1.0f);
XCTAssertEqual(probe.lifecycle, SentryProfileLifecycleTrace);
XCTAssertTrue(probe.profileAppStarts);
}

- (void)testStartCreateOptionsWithDictionaryProfilingOptionsMissingDoesNotInstallConfigureProfiling
{
NSError *error = nil;

NSDictionary *_Nonnull mockedReactNativeDictionary = @{
@"dsn" : @"https://abcd@efgh.ingest.sentry.io/123456",
};
SentryOptions *actualOptions =
[RNSentryStart createOptionsWithDictionary:mockedReactNativeDictionary error:&error];

XCTAssertNotNil(actualOptions, @"Did not create sentry options");
XCTAssertNil(error, @"Should not pass no error");
XCTAssertNil(actualOptions.configureProfiling,
@"configureProfiling callback should not be installed without profilingOptions");
}

- (void)testStartCreateOptionsWithDictionaryEmptyExperimentsDoesNotInstallConfigureProfiling
{
NSError *error = nil;

NSDictionary *_Nonnull mockedReactNativeDictionary = @{
@"dsn" : @"https://abcd@efgh.ingest.sentry.io/123456",
@"_experiments" : @ { },
};
SentryOptions *actualOptions =
[RNSentryStart createOptionsWithDictionary:mockedReactNativeDictionary error:&error];

XCTAssertNotNil(actualOptions, @"Did not create sentry options");
XCTAssertNil(error, @"Should not pass no error");
XCTAssertNil(actualOptions.configureProfiling,
@"configureProfiling callback should not be installed when profilingOptions is absent");
}
#endif

- (void)testStartEventFromSentryCocoaReactNativeHasOriginAndEnvironmentTags
{
SentryEvent *testEvent = [[SentryEvent alloc] init];
Expand Down
10 changes: 10 additions & 0 deletions packages/core/ios/RNSentryStart.m
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,16 @@ + (SentryOptions *_Nullable)createOptionsWithDictionary:(NSDictionary *_Nonnull)
}
}

// Configure iOS UI Profiling from _experiments.profilingOptions
NSDictionary *experiments = mutableOptions[@"_experiments"];
if (experiments != nil && [experiments isKindOfClass:[NSDictionary class]]) {
NSDictionary *profilingOptions = experiments[@"profilingOptions"];
if (profilingOptions != nil && [profilingOptions isKindOfClass:[NSDictionary class]]) {
[RNSentryExperimentalOptions configureProfilingWithOptions:profilingOptions
sentryOptions:sentryOptions];
}
}

// Set strict trace continuation options
if ([mutableOptions valueForKey:@"strictTraceContinuation"] != nil) {
sentryOptions.strictTraceContinuation =
Expand Down
Loading