Skip to content

Commit

Permalink
fix(replay): Add tests for touch events (#3924)
Browse files Browse the repository at this point in the history
  • Loading branch information
krystofwoldrich authored Jul 2, 2024
1 parent ac72abc commit 5f3a5c8
Show file tree
Hide file tree
Showing 9 changed files with 324 additions and 107 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
package io.sentry.rnsentryandroidtester

import io.sentry.react.RNSentryReplayBreadcrumbConverter
import org.junit.Assert.assertEquals
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.JUnit4

@RunWith(JUnit4::class)
class RNSentryReplayBreadcrumbConverterTest {

@Test
fun doesNotConvertNullPath() {
val actual = RNSentryReplayBreadcrumbConverter.getTouchPathMessage(null)
assertEquals(null, actual)
}

@Test
fun doesNotConvertPathContainingNull() {
val actual = RNSentryReplayBreadcrumbConverter.getTouchPathMessage(arrayListOf(arrayOfNulls<Any>(1)))
assertEquals(null, actual)
}

@Test
fun doesNotConvertPathWithValuesMissingNameAndLevel() {
val actual = RNSentryReplayBreadcrumbConverter.getTouchPathMessage(arrayListOf(mapOf(
"element" to "element4",
"file" to "file4")))
assertEquals(null, actual)
}

@Test
fun doesConvertValidPathExample1() {
val actual = RNSentryReplayBreadcrumbConverter.getTouchPathMessage(listOf(
mapOf("label" to "label0"),
mapOf("name" to "name1"),
mapOf("name" to "item2", "label" to "label2"),
mapOf("name" to "item3", "label" to "label3", "element" to "element3"),
mapOf("name" to "item4", "label" to "label4", "file" to "file4"),
mapOf("name" to "item5", "label" to "label5", "element" to "element5", "file" to "file5")))
assertEquals("label3(element3) > label2 > name1 > label0", actual)
}

@Test
fun doesConvertValidPathExample2() {
val actual = RNSentryReplayBreadcrumbConverter.getTouchPathMessage(listOf(
mapOf("name" to "item2", "label" to "label2"),
mapOf("name" to "item3", "label" to "label3", "element" to "element3"),
mapOf("name" to "item4", "label" to "label4", "file" to "file4"),
mapOf("name" to "item5", "label" to "label5", "element" to "element5", "file" to "file5"),
mapOf("label" to "label6"),
mapOf("name" to "name7")))
assertEquals("label5(element5, file5) > label4(file4) > label3(element3) > label2", actual)
}
}
17 changes: 17 additions & 0 deletions RNSentryCocoaTester/RNSentryCocoaTester.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@

/* Begin PBXBuildFile section */
330F308C2C0F3840002A0D4E /* RNSentryBreadcrumbTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 330F308B2C0F3840002A0D4E /* RNSentryBreadcrumbTests.m */; };
336084392C32E382008CC412 /* RNSentryReplayBreadcrumbConverterTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 336084382C32E382008CC412 /* RNSentryReplayBreadcrumbConverterTests.swift */; };
33958C692BFCF12600AD1FB6 /* RNSentryOnDrawReporterTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 33958C682BFCF12600AD1FB6 /* RNSentryOnDrawReporterTests.m */; };
33AFDFED2B8D14B300AAB120 /* RNSentryFramesTrackerListenerTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 33AFDFEC2B8D14B300AAB120 /* RNSentryFramesTrackerListenerTests.m */; };
33AFDFF12B8D15E500AAB120 /* RNSentryDependencyContainerTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 33AFDFF02B8D15E500AAB120 /* RNSentryDependencyContainerTests.m */; };
Expand All @@ -19,6 +20,9 @@
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>"; };
330F308B2C0F3840002A0D4E /* RNSentryBreadcrumbTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = RNSentryBreadcrumbTests.m; sourceTree = "<group>"; };
330F308D2C0F385A002A0D4E /* RNSentryBreadcrumb.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = RNSentryBreadcrumb.h; path = ../ios/RNSentryBreadcrumb.h; sourceTree = "<group>"; };
336084372C32E382008CC412 /* RNSentryCocoaTesterTests-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "RNSentryCocoaTesterTests-Bridging-Header.h"; sourceTree = "<group>"; };
336084382C32E382008CC412 /* RNSentryReplayBreadcrumbConverterTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RNSentryReplayBreadcrumbConverterTests.swift; sourceTree = "<group>"; };
3360843A2C32E3A8008CC412 /* RNSentryReplayBreadcrumbConverter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = RNSentryReplayBreadcrumbConverter.h; path = ../ios/RNSentryReplayBreadcrumbConverter.h; sourceTree = "<group>"; };
3360898D29524164007C7730 /* RNSentryCocoaTesterTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RNSentryCocoaTesterTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
338739072A7D7D2800950DDD /* RNSentryTests.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = RNSentryTests.h; sourceTree = "<group>"; };
33958C672BFCEF5A00AD1FB6 /* RNSentryOnDrawReporter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = RNSentryOnDrawReporter.h; path = ../ios/RNSentryOnDrawReporter.h; sourceTree = "<group>"; };
Expand Down Expand Up @@ -76,6 +80,7 @@
3360899029524164007C7730 /* RNSentryCocoaTesterTests */ = {
isa = PBXGroup;
children = (
336084382C32E382008CC412 /* RNSentryReplayBreadcrumbConverterTests.swift */,
33F58ACF2977037D008F60EA /* RNSentryTests.mm */,
338739072A7D7D2800950DDD /* RNSentryTests.h */,
33AFDFEC2B8D14B300AAB120 /* RNSentryFramesTrackerListenerTests.m */,
Expand All @@ -84,13 +89,15 @@
33AFDFF22B8D15F600AAB120 /* RNSentryDependencyContainerTests.h */,
33958C682BFCF12600AD1FB6 /* RNSentryOnDrawReporterTests.m */,
330F308B2C0F3840002A0D4E /* RNSentryBreadcrumbTests.m */,
336084372C32E382008CC412 /* RNSentryCocoaTesterTests-Bridging-Header.h */,
);
path = RNSentryCocoaTesterTests;
sourceTree = "<group>";
};
33AFE0122B8F319000AAB120 /* RNSentry */ = {
isa = PBXGroup;
children = (
3360843A2C32E3A8008CC412 /* RNSentryReplayBreadcrumbConverter.h */,
330F308D2C0F385A002A0D4E /* RNSentryBreadcrumb.h */,
33958C672BFCEF5A00AD1FB6 /* RNSentryOnDrawReporter.h */,
33AFE0132B8F31AF00AAB120 /* RNSentryDependencyContainer.h */,
Expand Down Expand Up @@ -134,10 +141,12 @@
isa = PBXProject;
attributes = {
BuildIndependentTargetsInParallel = 1;
LastSwiftUpdateCheck = 1540;
LastUpgradeCheck = 1420;
TargetAttributes = {
3360898C29524164007C7730 = {
CreatedOnToolsVersion = 14.2;
LastSwiftMigration = 1540;
};
};
};
Expand Down Expand Up @@ -207,6 +216,7 @@
buildActionMask = 2147483647;
files = (
33AFDFF12B8D15E500AAB120 /* RNSentryDependencyContainerTests.m in Sources */,
336084392C32E382008CC412 /* RNSentryReplayBreadcrumbConverterTests.swift in Sources */,
33F58AD02977037D008F60EA /* RNSentryTests.mm in Sources */,
33958C692BFCF12600AD1FB6 /* RNSentryOnDrawReporterTests.m in Sources */,
330F308C2C0F3840002A0D4E /* RNSentryBreadcrumbTests.m in Sources */,
Expand Down Expand Up @@ -333,6 +343,7 @@
isa = XCBuildConfiguration;
baseConfigurationReference = E2321E7CFA55AB617247098E /* Pods-RNSentryCocoaTesterTests.debug.xcconfig */;
buildSettings = {
CLANG_ENABLE_MODULES = YES;
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 1;
GENERATE_INFOPLIST_FILE = YES;
Expand Down Expand Up @@ -387,6 +398,9 @@
SUPPORTS_MACCATALYST = NO;
SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO;
SWIFT_EMIT_LOC_STRINGS = NO;
SWIFT_OBJC_BRIDGING_HEADER = "RNSentryCocoaTesterTests/RNSentryCocoaTesterTests-Bridging-Header.h";
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = 1;
};
name = Debug;
Expand All @@ -395,6 +409,7 @@
isa = XCBuildConfiguration;
baseConfigurationReference = 1482D5685A340AB93348A43D /* Pods-RNSentryCocoaTesterTests.release.xcconfig */;
buildSettings = {
CLANG_ENABLE_MODULES = YES;
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 1;
GENERATE_INFOPLIST_FILE = YES;
Expand Down Expand Up @@ -449,6 +464,8 @@
SUPPORTS_MACCATALYST = NO;
SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO;
SWIFT_EMIT_LOC_STRINGS = NO;
SWIFT_OBJC_BRIDGING_HEADER = "RNSentryCocoaTesterTests/RNSentryCocoaTesterTests-Bridging-Header.h";
SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = 1;
};
name = Release;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
//
// Use this file to import your target's public headers that you would like to expose to Swift.
//

#import "RNSentryReplayBreadcrumbConverter.h"
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import XCTest

final class RNSentryReplayBreadcrumbConverterTests: XCTestCase {

func testTouchMessageReturnsNilOnEmptyArray() throws {
let actual = RNSentryReplayBreadcrumbConverter.getTouchPathMessage(from: [])
XCTAssertEqual(actual, nil);
}

func testTouchMessageReturnsNilOnNilArray() throws {
let actual = RNSentryReplayBreadcrumbConverter.getTouchPathMessage(from: nil as [Any]?)
XCTAssertEqual(actual, nil);
}

func testTouchMessageReturnsNilOnMissingNameAndLevel() throws {
let testPath: [Any?] = [["element": "element4", "file": "file4"]]
let actual = RNSentryReplayBreadcrumbConverter.getTouchPathMessage(from: testPath as [Any])
XCTAssertEqual(actual, nil);
}

func testTouchMessageReturnsMessageOnValidPathExample1() throws {
let testPath: [Any?] = [
["label": "label0"],
["name": "name1"],
["name": "item2", "label": "label2"],
["name": "item3", "label": "label3", "element": "element3"],
["name": "item4", "label": "label4", "file": "file4"],
["name": "item5", "label": "label5", "element": "element5", "file": "file5"],
]
let actual = RNSentryReplayBreadcrumbConverter.getTouchPathMessage(from: testPath as [Any])
XCTAssertEqual(actual, "label3(element3) > label2 > name1 > label0");
}

func testTouchMessageReturnsMessageOnValidPathExample2() throws {
let testPath: [Any?] = [
["name": "item2", "label": "label2"],
["name": "item3", "label": "label3", "element": "element3"],
["name": "item4", "label": "label4", "file": "file4"],
["name": "item5", "label": "label5", "element": "element5", "file": "file5"],
["label": "label6"],
["name": "name7"],
]
let actual = RNSentryReplayBreadcrumbConverter.getTouchPathMessage(from: testPath as [Any])
XCTAssertEqual(actual, "label5(element5, file5) > label4(file4) > label3(element3) > label2");
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,13 @@
import io.sentry.rrweb.RRWebBreadcrumbEvent;
import io.sentry.rrweb.RRWebSpanEvent;

import java.util.ArrayList;
import java.util.HashMap;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.annotations.TestOnly;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

public final class RNSentryReplayBreadcrumbConverter extends DefaultReplayBreadcrumbConverter {
public RNSentryReplayBreadcrumbConverter() {
Expand Down Expand Up @@ -59,31 +59,9 @@ public RNSentryReplayBreadcrumbConverter() {
final RRWebBreadcrumbEvent rrWebBreadcrumb = new RRWebBreadcrumbEvent();

rrWebBreadcrumb.setCategory("ui.tap");
ArrayList path = (ArrayList) breadcrumb.getData("path");
if (path != null) {
StringBuilder message = new StringBuilder();
for (int i = Math.min(3, path.size()); i >= 0; i--) {
HashMap item = (HashMap) path.get(i);
message.append(item.get("name"));
if (item.containsKey("element") || item.containsKey("file")) {
message.append('(');
if (item.containsKey("element")) {
message.append(item.get("element"));
if (item.containsKey("file")) {
message.append(", ");
message.append(item.get("file"));
}
} else if (item.containsKey("file")) {
message.append(item.get("file"));
}
message.append(')');
}
if (i > 0) {
message.append(" > ");
}
}
rrWebBreadcrumb.setMessage(message.toString());
}

rrWebBreadcrumb.setMessage(RNSentryReplayBreadcrumbConverter
.getTouchPathMessage(breadcrumb.getData("path")));

rrWebBreadcrumb.setLevel(breadcrumb.getLevel());
rrWebBreadcrumb.setData(breadcrumb.getData());
Expand All @@ -93,6 +71,66 @@ public RNSentryReplayBreadcrumbConverter() {
return rrWebBreadcrumb;
}

@TestOnly
public static @Nullable String getTouchPathMessage(final @Nullable Object maybePath) {
if (!(maybePath instanceof List)) {
return null;
}

final @NotNull List path = (List) maybePath;
if (path.size() == 0) {
return null;
}

final @NotNull StringBuilder message = new StringBuilder();
for (int i = Math.min(3, path.size() - 1); i >= 0; i--) {
final @Nullable Object maybeItem = path.get(i);
if (!(maybeItem instanceof Map)) {
return null;
}

final @NotNull Map item = (Map) maybeItem;
final @Nullable Object maybeName = item.get("name");
final @Nullable Object maybeLabel = item.get("label");
boolean hasName = maybeName instanceof String;
boolean hasLabel = maybeLabel instanceof String;
if (!hasName && !hasLabel) {
return null; // This again should never be allowed in JS, but to be safe we check it here
}
if (hasLabel) {
message.append(maybeLabel);
} else { // hasName is true
message.append(maybeName);
}

final @Nullable Object maybeElement = item.get("element");
final @Nullable Object maybeFile = item.get("file");
boolean hasElement = maybeElement instanceof String;
boolean hasFile = maybeFile instanceof String;
if (hasElement && hasFile) {
message.append('(')
.append(maybeElement)
.append(", ")
.append(maybeFile)
.append(')');
} else if (hasElement) {
message.append('(')
.append(maybeElement)
.append(')');
} else if (hasFile) {
message.append('(')
.append(maybeFile)
.append(')');
}

if (i > 0) {
message.append(" > ");
}
}

return message.toString();
}

@TestOnly
public @Nullable RRWebEvent convertNetworkBreadcrumb(final @NotNull Breadcrumb breadcrumb) {
final Double startTimestamp = breadcrumb.getData("start_timestamp") instanceof Number
Expand Down
2 changes: 2 additions & 0 deletions ios/RNSentryReplayBreadcrumbConverter.h
Original file line number Diff line number Diff line change
Expand Up @@ -8,5 +8,7 @@

- (instancetype _Nonnull)init;

+ (NSString* _Nullable) getTouchPathMessageFrom:(NSArray* _Nullable) path;

@end
#endif
Loading

0 comments on commit 5f3a5c8

Please sign in to comment.