Skip to content

Commit d56c148

Browse files
Merge branch 'main' into v6
2 parents a615e7e + 7259f12 commit d56c148

File tree

9 files changed

+180
-13
lines changed

9 files changed

+180
-13
lines changed

CHANGELOG.md

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,19 @@
11
# Changelog
22

3+
## Unreleased
4+
5+
### Fixes
6+
7+
- Handles error with string cause ([#4163](https://github.com/getsentry/sentry-react-native/pull/4163))
8+
- Use `appLaunchedInForeground` to determine invalid app start data on Android ([#4146](https://github.com/getsentry/sentry-react-native/pull/4146))
9+
10+
- Bump Cocoa SDK from v8.36.0 to v8.37.0 ([#4156](https://github.com/getsentry/sentry-react-native/pull/4156))
11+
- [changelog](https://github.com/getsentry/sentry-cocoa/blob/main/CHANGELOG.md#8370)
12+
- [diff](https://github.com/getsentry/sentry-cocoa/compare/8.36.0...8.37.0)
13+
- Bump Android SDK from v7.14.0 to v7.15.0 ([#4161](https://github.com/getsentry/sentry-react-native/pull/4161))
14+
- [changelog](https://github.com/getsentry/sentry-java/blob/main/CHANGELOG.md#7150)
15+
- [diff](https://github.com/getsentry/sentry-java/compare/7.14.0...7.15.0)
16+
317
## 6.0.0-rc.1
418

519
### Fixes
@@ -11,7 +25,7 @@
1125
- Bump CLI from v2.36.6 to v2.37.0 ([#4153](https://github.com/getsentry/sentry-react-native/pull/4153))
1226
- [changelog](https://github.com/getsentry/sentry-cli/blob/master/CHANGELOG.md#2370)
1327
- [diff](https://github.com/getsentry/sentry-cli/compare/2.36.6...2.37.0)
14-
- Bump JavaScript SDK from v8.30.0 to v8.33.1` ([#4154](https://github.com/getsentry/sentry-react-native/pull/4154))
28+
- Bump JavaScript SDK from v8.30.0 to v8.33.1 ([#4154](https://github.com/getsentry/sentry-react-native/pull/4154))
1529
- [changelog](https://github.com/getsentry/sentry-javascript/blob/master/CHANGELOG.md#8331)
1630
- [diff](https://github.com/getsentry/sentry-javascript/compare/v8.30.0...8.33.1)
1731

README.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,14 @@ _Bad software is everywhere, and we're tired of it. Sentry is on a mission to he
1919
[![Discord Chat](https://img.shields.io/discord/621778831602221064?logo=discord&logoColor=ffffff&color=7389D8)](https://discord.gg/PXa5Apfe7K)
2020
[![Runs with Expo](https://img.shields.io/badge/Runs%20with%20Expo-4630EB.svg?style=flat-square&logo=EXPO&labelColor=f3f3f3&logoColor=000)](https://expo.dev/)
2121

22+
## Releases
23+
24+
This repo uses the following ways to release SDK updates:
25+
26+
- `Pre-release`: We create pre-releases (alpha, beta, RC,…) for larger and potentially more impactful changes, such as new features or major versions.
27+
- `Latest`: We continuously release major/minor/hotfix versions from the `main` branch. These releases go through all our internal quality gates and are very safe to use and intended to be the default for most teams.
28+
- `Stable`: We promote releases from `Latest` when they have been used in the field for some time and in scale, considering time since release, adoption, and other quality and stability metrics. These releases will be indicated on [the releases page](https://github.com/getsentry/sentry-react-native/releases/) with the `Stable` suffix.
29+
2230
## Requirements
2331

2432
- `react-native >= 0.65.0`
Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
package io.sentry.react
2+
3+
import android.content.pm.PackageInfo
4+
import android.content.pm.PackageManager
5+
import com.facebook.react.bridge.Arguments
6+
import com.facebook.react.bridge.Promise
7+
import com.facebook.react.bridge.ReactApplicationContext
8+
import com.facebook.react.bridge.WritableMap
9+
import io.sentry.ILogger
10+
import io.sentry.SentryLevel
11+
import org.junit.After
12+
import org.junit.Assert.assertEquals
13+
import org.junit.Before
14+
import org.junit.Test
15+
import org.junit.runner.RunWith
16+
import org.junit.runners.JUnit4
17+
import org.mockito.ArgumentCaptor
18+
import org.mockito.Captor
19+
import org.mockito.Mockito.*
20+
import org.mockito.MockitoAnnotations
21+
import org.mockito.MockedStatic
22+
import org.mockito.Mockito.mockStatic
23+
import org.mockito.kotlin.whenever
24+
25+
@RunWith(JUnit4::class)
26+
class RNSentryModuleImplTest {
27+
28+
private lateinit var module: RNSentryModuleImpl
29+
private lateinit var promise: Promise
30+
private lateinit var logger: ILogger
31+
private var argumentsMock: MockedStatic<Arguments>? = null
32+
33+
@Captor
34+
private lateinit var writableMapCaptor: ArgumentCaptor<WritableMap>
35+
36+
@Before
37+
fun setUp() {
38+
MockitoAnnotations.openMocks(this)
39+
val reactContext = mock(ReactApplicationContext::class.java)
40+
promise = mock(Promise::class.java)
41+
logger = mock(ILogger::class.java)
42+
val packageManager = mock(PackageManager::class.java)
43+
val packageInfo = mock(PackageInfo::class.java)
44+
45+
whenever(reactContext.packageManager).thenReturn(packageManager)
46+
whenever(packageManager.getPackageInfo(anyString(), anyInt())).thenReturn(packageInfo)
47+
48+
module = RNSentryModuleImpl(reactContext)
49+
50+
// Mock the Arguments class
51+
argumentsMock = mockStatic(Arguments::class.java)
52+
val writableMap = mock(WritableMap::class.java)
53+
whenever(Arguments.createMap()).thenReturn(writableMap)
54+
}
55+
56+
@After
57+
fun tearDown() {
58+
argumentsMock?.close()
59+
}
60+
61+
@Test
62+
fun `fetchNativeAppStart resolves promise with null when app is not launched in the foreground`() {
63+
// Mock the app start measurement
64+
val appStartMeasurement = mapOf<String, Any>()
65+
66+
// Call the method
67+
module.fetchNativeAppStart(promise, appStartMeasurement, logger, false)
68+
69+
// Verify a warning log is emitted
70+
verify(logger, org.mockito.kotlin.times(1)).log(
71+
SentryLevel.WARNING,
72+
"Invalid app start data: app not launched in foreground."
73+
)
74+
75+
// Verify the promise is resolved with null
76+
verify(promise).resolve(null)
77+
}
78+
79+
@Test
80+
fun `fetchNativeAppStart resolves promise with app start data when app is launched in the foreground`() {
81+
// Mock the app start measurement
82+
val appStartMeasurement = mapOf<String, Any>()
83+
84+
// Call the method
85+
module.fetchNativeAppStart(promise, appStartMeasurement, logger, true)
86+
87+
// Verify no logs are emitted
88+
verify(logger, org.mockito.kotlin.times(0)).log(any(), any())
89+
90+
// Verify the promise is resolved with the expected data
91+
verify(promise).resolve(any(WritableMap::class.java))
92+
verify(promise).resolve(writableMapCaptor.capture())
93+
val capturedMap = writableMapCaptor.value
94+
assertEquals(false, capturedMap.getBoolean("has_fetched"))
95+
}
96+
}

packages/core/RNSentry.podspec

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ Pod::Spec.new do |s|
3737

3838
s.compiler_flags = other_cflags
3939

40-
s.dependency 'Sentry/HybridSDK', '8.36.0'
40+
s.dependency 'Sentry/HybridSDK', '8.37.0'
4141

4242
if defined? install_modules_dependencies
4343
# Default React Native dependencies for 0.71 and above (new and legacy architecture)

packages/core/android/build.gradle

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,5 +54,5 @@ android {
5454

5555
dependencies {
5656
implementation 'com.facebook.react:react-native:+'
57-
api 'io.sentry:sentry-android:7.14.0'
57+
api 'io.sentry:sentry-android:7.15.0'
5858
}

packages/core/android/src/main/java/io/sentry/react/RNSentryModuleImpl.java

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -304,7 +304,7 @@ public void initNativeSdk(final ReadableMap rnOptions, Promise promise) {
304304
}
305305

306306
private SentryReplayOptions getReplayOptions(@NotNull ReadableMap rnOptions) {
307-
@NotNull final SentryReplayOptions androidReplayOptions = new SentryReplayOptions();
307+
@NotNull final SentryReplayOptions androidReplayOptions = new SentryReplayOptions(false);
308308

309309
@Nullable final ReadableMap rnExperimentsOptions = rnOptions.getMap("_experiments");
310310
if (rnExperimentsOptions == null) {
@@ -317,7 +317,7 @@ private SentryReplayOptions getReplayOptions(@NotNull ReadableMap rnOptions) {
317317

318318
androidReplayOptions.setSessionSampleRate(rnExperimentsOptions.hasKey("replaysSessionSampleRate")
319319
? rnExperimentsOptions.getDouble("replaysSessionSampleRate") : null);
320-
androidReplayOptions.setErrorSampleRate(rnExperimentsOptions.hasKey("replaysOnErrorSampleRate")
320+
androidReplayOptions.setOnErrorSampleRate(rnExperimentsOptions.hasKey("replaysOnErrorSampleRate")
321321
? rnExperimentsOptions.getDouble("replaysOnErrorSampleRate") : null);
322322

323323
if (!rnOptions.hasKey("mobileReplayOptions")) {
@@ -328,12 +328,12 @@ private SentryReplayOptions getReplayOptions(@NotNull ReadableMap rnOptions) {
328328
return androidReplayOptions;
329329
}
330330

331-
androidReplayOptions.setRedactAllText(!rnMobileReplayOptions.hasKey("maskAllText") || rnMobileReplayOptions.getBoolean("maskAllText"));
332-
androidReplayOptions.setRedactAllImages(!rnMobileReplayOptions.hasKey("maskAllImages") || rnMobileReplayOptions.getBoolean("maskAllImages"));
331+
androidReplayOptions.setMaskAllText(!rnMobileReplayOptions.hasKey("maskAllText") || rnMobileReplayOptions.getBoolean("maskAllText"));
332+
androidReplayOptions.setMaskAllImages(!rnMobileReplayOptions.hasKey("maskAllImages") || rnMobileReplayOptions.getBoolean("maskAllImages"));
333333

334334
final boolean redactVectors = !rnMobileReplayOptions.hasKey("maskAllVectors") || rnMobileReplayOptions.getBoolean("maskAllVectors");
335335
if (redactVectors) {
336-
androidReplayOptions.addClassToRedact("com.horcrux.svg.SvgView"); // react-native-svg
336+
androidReplayOptions.addMaskViewClass("com.horcrux.svg.SvgView"); // react-native-svg
337337
}
338338

339339
return androidReplayOptions;
@@ -380,9 +380,17 @@ public void fetchNativeRelease(Promise promise) {
380380
}
381381

382382
public void fetchNativeAppStart(Promise promise) {
383-
final Map<String, Object> measurement = InternalSentrySdk.getAppStartMeasurement();
383+
fetchNativeAppStart(promise, InternalSentrySdk.getAppStartMeasurement(), logger, AppStartMetrics.getInstance().isAppLaunchedInForeground());
384+
}
385+
386+
protected void fetchNativeAppStart(Promise promise, final Map<String, Object> appStartMeasurement, ILogger logger, boolean isAppLaunchedInForeground) {
387+
if (!isAppLaunchedInForeground) {
388+
logger.log(SentryLevel.WARNING, "Invalid app start data: app not launched in foreground.");
389+
promise.resolve(null);
390+
return;
391+
}
384392

385-
WritableMap mutableMeasurement = (WritableMap) RNSentryMapConverter.convertToWritable(measurement);
393+
WritableMap mutableMeasurement = (WritableMap) RNSentryMapConverter.convertToWritable(appStartMeasurement);
386394
mutableMeasurement.putBoolean("has_fetched", hasFetchedAppStart);
387395

388396
// This is always set to true, as we would only allow an app start fetch to only

packages/core/src/js/integrations/nativelinkederrors.ts

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import type {
1010
StackFrame,
1111
StackParser,
1212
} from '@sentry/types';
13-
import { isInstanceOf, isPlainObject } from '@sentry/utils';
13+
import { isInstanceOf, isPlainObject, isString } from '@sentry/utils';
1414

1515
import type { NativeStackFrames } from '../NativeRNSentry';
1616
import { NATIVE } from '../wrapper';
@@ -87,7 +87,11 @@ function walkErrorTree(
8787

8888
let exception: Exception;
8989
let exceptionDebugImages: DebugImage[] | undefined;
90-
if ('stackElements' in linkedError) {
90+
if (isString(linkedError)) {
91+
exception = {
92+
value: linkedError,
93+
};
94+
} else if ('stackElements' in linkedError) {
9195
// isJavaException
9296
exception = exceptionFromJavaStackElements(linkedError);
9397
} else if ('stackReturnAddresses' in linkedError) {

packages/core/test/integrations/nativelinkederrors.test.ts

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -337,6 +337,43 @@ describe('NativeLinkedErrors', () => {
337337
}),
338338
);
339339
});
340+
341+
it('handles events with a string cause', async () => {
342+
const actualEvent = await executeIntegrationFor(
343+
{
344+
exception: {
345+
values: [
346+
{
347+
type: 'Error',
348+
value: 'Captured exception',
349+
},
350+
],
351+
},
352+
},
353+
{
354+
originalException: createNewError({
355+
message: 'Error with string cause',
356+
cause: 'string cause',
357+
}),
358+
},
359+
);
360+
361+
expect(actualEvent).toEqual(
362+
expect.objectContaining(<Partial<Event>>{
363+
exception: {
364+
values: [
365+
{
366+
type: 'Error',
367+
value: 'Captured exception',
368+
},
369+
{
370+
value: 'string cause',
371+
},
372+
],
373+
},
374+
}),
375+
);
376+
});
340377
});
341378

342379
function executeIntegrationFor(mockedEvent: Event, mockedHint: EventHint): Event | null {

performance-tests/metrics-android.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ apps:
88

99
startupTimeTest:
1010
runs: 50
11-
diffMin: 0
11+
diffMin: -20
1212
diffMax: 150
1313

1414
binarySizeTest:

0 commit comments

Comments
 (0)