Skip to content

Commit 8a28af8

Browse files
feat(native): Set current screen on native scope (#3927)
1 parent 5290b95 commit 8a28af8

File tree

11 files changed

+271
-89
lines changed

11 files changed

+271
-89
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
### Features
66

77
- Improve touch event component info if annotated with [`@sentry/babel-plugin-component-annotate`](https://www.npmjs.com/package/@sentry/babel-plugin-component-annotate) ([#3899](https://github.com/getsentry/sentry-react-native/pull/3899))
8+
- Set `currentScreen` on native scope ([#3927](https://github.com/getsentry/sentry-react-native/pull/3927))
89

910
### Fixes
1011

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
package io.sentry.rnsentryandroidtester
2+
3+
import com.facebook.react.bridge.JavaOnlyMap
4+
import io.sentry.react.RNSentryBreadcrumb
5+
import junit.framework.TestCase.assertEquals
6+
import org.junit.Test
7+
import org.junit.runner.RunWith
8+
import org.junit.runners.JUnit4
9+
10+
@RunWith(JUnit4::class)
11+
class RNSentryBreadcrumbTest {
12+
13+
@Test
14+
fun nullForMissingCategory() {
15+
val map = JavaOnlyMap.of()
16+
val actual = RNSentryBreadcrumb.getCurrentScreenFrom(map)
17+
assertEquals(null, actual)
18+
}
19+
20+
21+
@Test
22+
fun nullForNonNavigationCategory() {
23+
val map = JavaOnlyMap.of(
24+
"category", "unknown"
25+
)
26+
val actual = RNSentryBreadcrumb.getCurrentScreenFrom(map)
27+
assertEquals(null, actual)
28+
}
29+
30+
31+
@Test
32+
fun nullForMissingData() {
33+
val map = JavaOnlyMap.of(
34+
"category", "navigation"
35+
)
36+
val actual = RNSentryBreadcrumb.getCurrentScreenFrom(map)
37+
assertEquals(null, actual)
38+
}
39+
40+
41+
@Test
42+
fun nullForNonStringDataToKey() {
43+
val map = JavaOnlyMap.of(
44+
"category", "unknown",
45+
"data", mapOf(
46+
"to" to 123,
47+
),
48+
)
49+
val actual = RNSentryBreadcrumb.getCurrentScreenFrom(map)
50+
assertEquals(null, actual)
51+
}
52+
53+
@Test
54+
fun screenNameForValidNavigationBreadcrumb() {
55+
val map = JavaOnlyMap.of(
56+
"category", "navigation",
57+
"data", JavaOnlyMap.of(
58+
"to", "newScreen",
59+
),
60+
)
61+
val actual = RNSentryBreadcrumb.getCurrentScreenFrom(map)
62+
assert(actual is String)
63+
assertEquals("newScreen", actual)
64+
}
65+
66+
}

RNSentryCocoaTester/RNSentryCocoaTester.xcodeproj/project.pbxproj

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
objects = {
88

99
/* Begin PBXBuildFile section */
10-
330F308C2C0F3840002A0D4E /* RNSentryBreadcrumbTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 330F308B2C0F3840002A0D4E /* RNSentryBreadcrumbTests.m */; };
10+
3360843D2C340C76008CC412 /* RNSentryBreadcrumbTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3360843C2C340C76008CC412 /* RNSentryBreadcrumbTests.swift */; };
1111
33958C692BFCF12600AD1FB6 /* RNSentryOnDrawReporterTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 33958C682BFCF12600AD1FB6 /* RNSentryOnDrawReporterTests.m */; };
1212
33AFDFED2B8D14B300AAB120 /* RNSentryFramesTrackerListenerTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 33AFDFEC2B8D14B300AAB120 /* RNSentryFramesTrackerListenerTests.m */; };
1313
33AFDFF12B8D15E500AAB120 /* RNSentryDependencyContainerTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 33AFDFF02B8D15E500AAB120 /* RNSentryDependencyContainerTests.m */; };
@@ -17,8 +17,9 @@
1717

1818
/* Begin PBXFileReference section */
1919
1482D5685A340AB93348A43D /* Pods-RNSentryCocoaTesterTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RNSentryCocoaTesterTests.release.xcconfig"; path = "Target Support Files/Pods-RNSentryCocoaTesterTests/Pods-RNSentryCocoaTesterTests.release.xcconfig"; sourceTree = "<group>"; };
20-
330F308B2C0F3840002A0D4E /* RNSentryBreadcrumbTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = RNSentryBreadcrumbTests.m; sourceTree = "<group>"; };
2120
330F308D2C0F385A002A0D4E /* RNSentryBreadcrumb.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = RNSentryBreadcrumb.h; path = ../ios/RNSentryBreadcrumb.h; sourceTree = "<group>"; };
21+
3360843B2C340C75008CC412 /* RNSentryCocoaTesterTests-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "RNSentryCocoaTesterTests-Bridging-Header.h"; sourceTree = "<group>"; };
22+
3360843C2C340C76008CC412 /* RNSentryBreadcrumbTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RNSentryBreadcrumbTests.swift; sourceTree = "<group>"; };
2223
3360898D29524164007C7730 /* RNSentryCocoaTesterTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RNSentryCocoaTesterTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
2324
338739072A7D7D2800950DDD /* RNSentryTests.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = RNSentryTests.h; sourceTree = "<group>"; };
2425
33958C672BFCEF5A00AD1FB6 /* RNSentryOnDrawReporter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = RNSentryOnDrawReporter.h; path = ../ios/RNSentryOnDrawReporter.h; sourceTree = "<group>"; };
@@ -83,7 +84,8 @@
8384
33AFDFF02B8D15E500AAB120 /* RNSentryDependencyContainerTests.m */,
8485
33AFDFF22B8D15F600AAB120 /* RNSentryDependencyContainerTests.h */,
8586
33958C682BFCF12600AD1FB6 /* RNSentryOnDrawReporterTests.m */,
86-
330F308B2C0F3840002A0D4E /* RNSentryBreadcrumbTests.m */,
87+
3360843C2C340C76008CC412 /* RNSentryBreadcrumbTests.swift */,
88+
3360843B2C340C75008CC412 /* RNSentryCocoaTesterTests-Bridging-Header.h */,
8789
);
8890
path = RNSentryCocoaTesterTests;
8991
sourceTree = "<group>";
@@ -138,6 +140,7 @@
138140
TargetAttributes = {
139141
3360898C29524164007C7730 = {
140142
CreatedOnToolsVersion = 14.2;
143+
LastSwiftMigration = 1540;
141144
};
142145
};
143146
};
@@ -209,7 +212,7 @@
209212
33AFDFF12B8D15E500AAB120 /* RNSentryDependencyContainerTests.m in Sources */,
210213
33F58AD02977037D008F60EA /* RNSentryTests.mm in Sources */,
211214
33958C692BFCF12600AD1FB6 /* RNSentryOnDrawReporterTests.m in Sources */,
212-
330F308C2C0F3840002A0D4E /* RNSentryBreadcrumbTests.m in Sources */,
215+
3360843D2C340C76008CC412 /* RNSentryBreadcrumbTests.swift in Sources */,
213216
33AFDFED2B8D14B300AAB120 /* RNSentryFramesTrackerListenerTests.m in Sources */,
214217
);
215218
runOnlyForDeploymentPostprocessing = 0;
@@ -333,6 +336,7 @@
333336
isa = XCBuildConfiguration;
334337
baseConfigurationReference = E2321E7CFA55AB617247098E /* Pods-RNSentryCocoaTesterTests.debug.xcconfig */;
335338
buildSettings = {
339+
CLANG_ENABLE_MODULES = YES;
336340
CODE_SIGN_STYLE = Automatic;
337341
CURRENT_PROJECT_VERSION = 1;
338342
GENERATE_INFOPLIST_FILE = YES;
@@ -387,6 +391,9 @@
387391
SUPPORTS_MACCATALYST = NO;
388392
SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO;
389393
SWIFT_EMIT_LOC_STRINGS = NO;
394+
SWIFT_OBJC_BRIDGING_HEADER = "RNSentryCocoaTesterTests/RNSentryCocoaTesterTests-Bridging-Header.h";
395+
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
396+
SWIFT_VERSION = 5.0;
390397
TARGETED_DEVICE_FAMILY = 1;
391398
};
392399
name = Debug;
@@ -395,6 +402,7 @@
395402
isa = XCBuildConfiguration;
396403
baseConfigurationReference = 1482D5685A340AB93348A43D /* Pods-RNSentryCocoaTesterTests.release.xcconfig */;
397404
buildSettings = {
405+
CLANG_ENABLE_MODULES = YES;
398406
CODE_SIGN_STYLE = Automatic;
399407
CURRENT_PROJECT_VERSION = 1;
400408
GENERATE_INFOPLIST_FILE = YES;
@@ -449,6 +457,8 @@
449457
SUPPORTS_MACCATALYST = NO;
450458
SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO;
451459
SWIFT_EMIT_LOC_STRINGS = NO;
460+
SWIFT_OBJC_BRIDGING_HEADER = "RNSentryCocoaTesterTests/RNSentryCocoaTesterTests-Bridging-Header.h";
461+
SWIFT_VERSION = 5.0;
452462
TARGETED_DEVICE_FAMILY = 1;
453463
};
454464
name = Release;

RNSentryCocoaTester/RNSentryCocoaTesterTests/RNSentryBreadcrumbTests.m

Lines changed: 0 additions & 40 deletions
This file was deleted.
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
import XCTest
2+
import Sentry
3+
4+
class RNSentryBreadcrumbTests: XCTestCase {
5+
6+
func testGeneratesSentryBreadcrumbFromNSDictionary() {
7+
let actualCrumb = RNSentryBreadcrumb.from([
8+
"level": "error",
9+
"category": "testCategory",
10+
"type": "testType",
11+
"message": "testMessage",
12+
"data": [
13+
"test": "data"
14+
]
15+
])
16+
17+
XCTAssertEqual(actualCrumb!.level, SentryLevel.error)
18+
XCTAssertEqual(actualCrumb!.category, "testCategory")
19+
XCTAssertEqual(actualCrumb!.type, "testType")
20+
XCTAssertEqual(actualCrumb!.message, "testMessage")
21+
XCTAssertEqual((actualCrumb!.data)!["test"] as! String, "data")
22+
}
23+
24+
func testUsesInfoAsDefaultSentryLevel() {
25+
let actualCrumb = RNSentryBreadcrumb.from([
26+
"message": "testMessage"
27+
])
28+
29+
XCTAssertEqual(actualCrumb!.level, SentryLevel.info)
30+
}
31+
32+
func testNullForMissingCategory() {
33+
let map: [String: Any] = [:]
34+
let actual = RNSentryBreadcrumb.getCurrentScreen(from: map)
35+
XCTAssertNil(actual)
36+
}
37+
38+
func testNullForNonNavigationCategory() {
39+
let map: [String: Any] = ["category": "unknown"]
40+
let actual = RNSentryBreadcrumb.getCurrentScreen(from: map)
41+
XCTAssertNil(actual)
42+
}
43+
44+
func testNullForMissingData() {
45+
let map: [String: Any] = ["category": "navigation"]
46+
let actual = RNSentryBreadcrumb.getCurrentScreen(from: map)
47+
XCTAssertNil(actual)
48+
}
49+
50+
func testNullForNonStringDataToKey() {
51+
let map: [String: Any] = [
52+
"category": "unknown",
53+
"data": ["to": 123]
54+
]
55+
let actual = RNSentryBreadcrumb.getCurrentScreen(from: map)
56+
XCTAssertNil(actual)
57+
}
58+
59+
func testScreenNameForValidNavigationBreadcrumb() {
60+
let map: [String: Any] = [
61+
"category": "navigation",
62+
"data": ["to": "newScreen"]
63+
]
64+
let actual = RNSentryBreadcrumb.getCurrentScreen(from: map)
65+
XCTAssertEqual(actual, "newScreen")
66+
}
67+
68+
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
#import "RNSentryBreadcrumb.h"
Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
package io.sentry.react;
2+
3+
import com.facebook.react.bridge.ReadableMap;
4+
5+
import org.jetbrains.annotations.NotNull;
6+
import org.jetbrains.annotations.Nullable;
7+
8+
import java.util.Map;
9+
10+
import io.sentry.Breadcrumb;
11+
import io.sentry.SentryLevel;
12+
13+
public class RNSentryBreadcrumb {
14+
15+
@Nullable
16+
public static String getCurrentScreenFrom(ReadableMap from) {
17+
final @Nullable String maybeCategory = from.hasKey("category") ? from.getString("category") : null;
18+
if (maybeCategory == null || !maybeCategory.equals("navigation")) {
19+
return null;
20+
}
21+
22+
final @Nullable ReadableMap maybeData = from.hasKey("data") ? from.getMap("data") : null;
23+
if (maybeData == null) {
24+
return null;
25+
}
26+
27+
try {
28+
// getString might throw if cast to string fails (data.to is not enforced by TS to be a string)
29+
return maybeData.hasKey("to") ? maybeData.getString("to") : null;
30+
} catch (Throwable exception) {
31+
return null;
32+
}
33+
}
34+
35+
@NotNull
36+
public static Breadcrumb fromMap(ReadableMap from) {
37+
final @NotNull Breadcrumb breadcrumb = new Breadcrumb();
38+
39+
if (from.hasKey("message")) {
40+
breadcrumb.setMessage(from.getString("message"));
41+
}
42+
43+
if (from.hasKey("type")) {
44+
breadcrumb.setType(from.getString("type"));
45+
}
46+
47+
if (from.hasKey("category")) {
48+
breadcrumb.setCategory(from.getString("category"));
49+
}
50+
51+
if (from.hasKey("level")) {
52+
switch (from.getString("level")) {
53+
case "fatal":
54+
breadcrumb.setLevel(SentryLevel.FATAL);
55+
break;
56+
case "warning":
57+
breadcrumb.setLevel(SentryLevel.WARNING);
58+
break;
59+
case "debug":
60+
breadcrumb.setLevel(SentryLevel.DEBUG);
61+
break;
62+
case "error":
63+
breadcrumb.setLevel(SentryLevel.ERROR);
64+
break;
65+
case "info":
66+
default:
67+
breadcrumb.setLevel(SentryLevel.INFO);
68+
break;
69+
}
70+
}
71+
72+
73+
if (from.hasKey("data")) {
74+
final ReadableMap data = from.getMap("data");
75+
for (final Map.Entry<String, Object> entry : data.toHashMap().entrySet()) {
76+
final Object value = entry.getValue();
77+
// data is ConcurrentHashMap and can't have null values
78+
if (value != null) {
79+
breadcrumb.setData(entry.getKey(), entry.getValue());
80+
}
81+
}
82+
}
83+
84+
return breadcrumb;
85+
}
86+
87+
}

0 commit comments

Comments
 (0)