Skip to content

Commit 748ac40

Browse files
abdelhamid-f-nassera7medev
authored andcommitted
feat: add new session replay apis (#1034)
Jira ID: IBGCRASH-20006 --------- Co-authored-by: Ahmed Mahmoud <[email protected]>
1 parent b8e19eb commit 748ac40

File tree

15 files changed

+454
-0
lines changed

15 files changed

+454
-0
lines changed

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,10 @@
22

33
## [Unreleased](https://github.com/Instabug/Instabug-React-Native/compare/v11.14.0...dev)
44

5+
### Added
6+
7+
- Add support for Session Replay, which includes capturing session details, visual reproduction of sessions as well as support for user steps, network and Instabug logs. ([#1034](https://github.com/Instabug/Instabug-React-Native/pull/1034)).
8+
59
### Changed
610

711
- **BREAKING** Remove deprecated APIs ([#1027](https://github.com/Instabug/Instabug-React-Native/pull/1027)). See migration guide for more details.

android/src/main/java/com/instabug/reactlibrary/RNInstabugReactnativePackage.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ public List<NativeModule> createNativeModules(@NonNull ReactApplicationContext r
2828
modules.add(new RNInstabugFeatureRequestsModule(reactContext));
2929
modules.add(new RNInstabugRepliesModule(reactContext));
3030
modules.add(new RNInstabugAPMModule(reactContext));
31+
modules.add(new RNInstabugSessionReplayModule(reactContext));
3132
return modules;
3233
}
3334

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
package com.instabug.reactlibrary;
2+
3+
import com.facebook.react.bridge.ReactApplicationContext;
4+
import com.facebook.react.bridge.ReactContextBaseJavaModule;
5+
import com.facebook.react.bridge.ReactMethod;
6+
import com.instabug.library.sessionreplay.SessionReplay;
7+
import com.instabug.reactlibrary.utils.MainThreadHandler;
8+
9+
import javax.annotation.Nonnull;
10+
11+
public class RNInstabugSessionReplayModule extends ReactContextBaseJavaModule {
12+
13+
public RNInstabugSessionReplayModule(ReactApplicationContext reactApplicationContext) {
14+
super(reactApplicationContext);
15+
}
16+
17+
@Nonnull
18+
@Override
19+
public String getName() {
20+
return "IBGSessionReplay";
21+
}
22+
23+
@ReactMethod
24+
public void setEnabled(final boolean isEnabled) {
25+
MainThreadHandler.runOnMainThread(new Runnable() {
26+
@Override
27+
public void run() {
28+
try {
29+
SessionReplay.setEnabled(isEnabled);
30+
} catch (Exception e) {
31+
e.printStackTrace();
32+
}
33+
}
34+
});
35+
}
36+
37+
@ReactMethod
38+
public void setNetworkLogsEnabled(final boolean isEnabled) {
39+
MainThreadHandler.runOnMainThread(new Runnable() {
40+
@Override
41+
public void run() {
42+
try {
43+
SessionReplay.setNetworkLogsEnabled(isEnabled);
44+
} catch (Exception e) {
45+
e.printStackTrace();
46+
}
47+
}
48+
});
49+
}
50+
51+
52+
@ReactMethod
53+
public void setInstabugLogsEnabled(final boolean isEnabled) {
54+
MainThreadHandler.runOnMainThread(new Runnable() {
55+
@Override
56+
public void run() {
57+
try {
58+
SessionReplay.setIBGLogsEnabled(isEnabled);
59+
} catch (Exception e) {
60+
e.printStackTrace();
61+
}
62+
}
63+
});
64+
}
65+
66+
@ReactMethod
67+
public void setUserStepsEnabled(final boolean isEnabled) {
68+
MainThreadHandler.runOnMainThread(new Runnable() {
69+
@Override
70+
public void run() {
71+
try {
72+
SessionReplay.setUserStepsEnabled(isEnabled);
73+
} catch (Exception e) {
74+
e.printStackTrace();
75+
}
76+
}
77+
});
78+
}
79+
}
Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
package com.instabug.reactlibrary;
2+
3+
import static org.mockito.Matchers.any;
4+
import static org.mockito.Mockito.mockStatic;
5+
import static org.mockito.Mockito.times;
6+
import static org.mockito.Mockito.verify;
7+
8+
import android.os.Looper;
9+
10+
import com.facebook.react.bridge.Arguments;
11+
import com.facebook.react.bridge.JavaOnlyArray;
12+
import com.facebook.react.bridge.ReadableArray;
13+
import com.facebook.react.bridge.WritableArray;
14+
import com.instabug.featuresrequest.ActionType;
15+
import com.instabug.featuresrequest.FeatureRequests;
16+
import com.instabug.library.Feature;
17+
import com.instabug.library.sessionreplay.SessionReplay;
18+
import com.instabug.reactlibrary.utils.MainThreadHandler;
19+
20+
import org.junit.After;
21+
import org.junit.Before;
22+
import org.junit.Test;
23+
import org.mockito.MockedStatic;
24+
import org.mockito.Mockito;
25+
import org.mockito.invocation.InvocationOnMock;
26+
import org.mockito.stubbing.Answer;
27+
28+
import java.util.concurrent.Executors;
29+
import java.util.concurrent.ScheduledExecutorService;
30+
31+
32+
public class RNInstabugSessionReplayModuleTest {
33+
private RNInstabugSessionReplayModule sessionReplayModule = new RNInstabugSessionReplayModule(null);
34+
35+
private final static ScheduledExecutorService mainThread = Executors.newSingleThreadScheduledExecutor();
36+
37+
// Mock Objects
38+
private MockedStatic<Looper> mockLooper;
39+
private MockedStatic <MainThreadHandler> mockMainThreadHandler;
40+
private MockedStatic <SessionReplay> mockSessionReplay;
41+
42+
@Before
43+
public void mockMainThreadHandler() throws Exception {
44+
// Mock static functions
45+
mockSessionReplay = mockStatic(SessionReplay.class);
46+
mockLooper = mockStatic(Looper.class);
47+
mockMainThreadHandler = mockStatic(MainThreadHandler.class);
48+
49+
// Mock Looper class
50+
Looper mockMainThreadLooper = Mockito.mock(Looper.class);
51+
Mockito.when(Looper.getMainLooper()).thenReturn(mockMainThreadLooper);
52+
53+
// Override runOnMainThread
54+
Answer<Boolean> handlerPostAnswer = new Answer<Boolean>() {
55+
@Override
56+
public Boolean answer(InvocationOnMock invocation) throws Throwable {
57+
invocation.getArgument(0, Runnable.class).run();
58+
return true;
59+
}
60+
};
61+
Mockito.doAnswer(handlerPostAnswer).when(MainThreadHandler.class);
62+
MainThreadHandler.runOnMainThread(any(Runnable.class));
63+
}
64+
@After
65+
public void tearDown() {
66+
// Remove static mocks
67+
mockLooper.close();
68+
mockMainThreadHandler.close();
69+
mockSessionReplay.close();
70+
}
71+
72+
@Test
73+
public void testSetEnabled() {
74+
75+
sessionReplayModule.setEnabled(true);
76+
77+
mockSessionReplay.verify(() -> SessionReplay.setEnabled(true));
78+
mockSessionReplay.verifyNoMoreInteractions();
79+
}
80+
81+
@Test
82+
public void testSetNetworkLogsEnabled() {
83+
84+
sessionReplayModule.setNetworkLogsEnabled(true);
85+
86+
mockSessionReplay.verify(() -> SessionReplay.setNetworkLogsEnabled(true));
87+
mockSessionReplay.verifyNoMoreInteractions();
88+
}
89+
90+
@Test
91+
public void testSetInstabugLogsEnabled() {
92+
93+
sessionReplayModule.setInstabugLogsEnabled(true);
94+
95+
mockSessionReplay.verify(() -> SessionReplay.setIBGLogsEnabled(true));
96+
mockSessionReplay.verifyNoMoreInteractions();
97+
}
98+
99+
@Test
100+
public void testSetUserStepsEnabled() {
101+
102+
sessionReplayModule.setUserStepsEnabled(true);
103+
104+
mockSessionReplay.verify(() -> SessionReplay.setUserStepsEnabled(true));
105+
mockSessionReplay.verifyNoMoreInteractions();
106+
}
107+
108+
109+
}

examples/default/ios/InstabugExample.xcodeproj/project.pbxproj

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
13B07FBC1A68108700A75B9A /* AppDelegate.mm in Sources */ = {isa = PBXBuildFile; fileRef = 13B07FB01A68108700A75B9A /* AppDelegate.mm */; };
1111
13B07FBF1A68108700A75B9A /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 13B07FB51A68108700A75B9A /* Images.xcassets */; };
1212
13B07FC11A68108700A75B9A /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 13B07FB71A68108700A75B9A /* main.m */; };
13+
20E556262AC55766007416B1 /* InstabugSessionReplayTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 20E556252AC55766007416B1 /* InstabugSessionReplayTests.m */; };
1314
81AB9BB82411601600AC10FF /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 81AB9BB72411601600AC10FF /* LaunchScreen.storyboard */; };
1415
CC3DF88E2A1DFC9A003E9914 /* InstabugCrashReportingTests.m in Sources */ = {isa = PBXBuildFile; fileRef = CC3DF8852A1DFC99003E9914 /* InstabugCrashReportingTests.m */; };
1516
CC3DF88F2A1DFC9A003E9914 /* InstabugBugReportingTests.m in Sources */ = {isa = PBXBuildFile; fileRef = CC3DF8862A1DFC99003E9914 /* InstabugBugReportingTests.m */; };
@@ -42,6 +43,7 @@
4243
13B07FB51A68108700A75B9A /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Images.xcassets; path = InstabugExample/Images.xcassets; sourceTree = "<group>"; };
4344
13B07FB61A68108700A75B9A /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = Info.plist; path = InstabugExample/Info.plist; sourceTree = "<group>"; };
4445
13B07FB71A68108700A75B9A /* main.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = main.m; path = InstabugExample/main.m; sourceTree = "<group>"; };
46+
20E556252AC55766007416B1 /* InstabugSessionReplayTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = InstabugSessionReplayTests.m; sourceTree = "<group>"; };
4547
3AF7A6E02D40E0CEEA833CC4 /* libPods-InstabugExample-InstabugTests.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-InstabugExample-InstabugTests.a"; sourceTree = BUILT_PRODUCTS_DIR; };
4648
4FC6A9E6B294FF9929A1423D /* Pods-InstabugExample-InstabugTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-InstabugExample-InstabugTests.debug.xcconfig"; path = "Target Support Files/Pods-InstabugExample-InstabugTests/Pods-InstabugExample-InstabugTests.debug.xcconfig"; sourceTree = "<group>"; };
4749
81AB9BB72411601600AC10FF /* LaunchScreen.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; name = LaunchScreen.storyboard; path = InstabugExample/LaunchScreen.storyboard; sourceTree = "<group>"; };
@@ -91,6 +93,7 @@
9193
CC3DF8852A1DFC99003E9914 /* InstabugCrashReportingTests.m */,
9294
CC3DF8882A1DFC99003E9914 /* InstabugFeatureRequestsTests.m */,
9395
CC3DF88A2A1DFC99003E9914 /* InstabugRepliesTests.m */,
96+
20E556252AC55766007416B1 /* InstabugSessionReplayTests.m */,
9497
CC3DF8872A1DFC99003E9914 /* InstabugSampleTests.m */,
9598
CC3DF88B2A1DFC99003E9914 /* InstabugSurveysTests.m */,
9699
00E356F01AD99517003FC87E /* Supporting Files */,
@@ -442,6 +445,7 @@
442445
CC3DF8902A1DFC9A003E9914 /* InstabugSampleTests.m in Sources */,
443446
CC3DF8912A1DFC9A003E9914 /* InstabugFeatureRequestsTests.m in Sources */,
444447
CC3DF8932A1DFC9A003E9914 /* InstabugSurveysTests.m in Sources */,
448+
20E556262AC55766007416B1 /* InstabugSessionReplayTests.m in Sources */,
445449
CC3DF88F2A1DFC9A003E9914 /* InstabugBugReportingTests.m in Sources */,
446450
CC3DF88E2A1DFC9A003E9914 /* InstabugCrashReportingTests.m in Sources */,
447451
CC3DF8942A1DFC9A003E9914 /* InstabugAPMTests.m in Sources */,
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
#import <XCTest/XCTest.h>
2+
#import "OCMock/OCMock.h"
3+
#import "InstabugSessionReplayBridge.h"
4+
#import <Instabug/IBGTypes.h>
5+
#import "Instabug/Instabug.h"
6+
#import "IBGConstants.h"
7+
8+
@interface InstabugSessionReplayTests : XCTestCase
9+
10+
@property (nonatomic, strong) id mSessionReplay;
11+
@property (nonatomic, strong) InstabugSessionReplayBridge *bridge;
12+
13+
@end
14+
15+
@implementation InstabugSessionReplayTests
16+
17+
18+
- (void)setUp {
19+
self.mSessionReplay = OCMClassMock([IBGSessionReplay class]);
20+
self.bridge = [[InstabugSessionReplayBridge alloc] init];
21+
}
22+
23+
- (void)testSetEnabled {
24+
BOOL enabled = NO;
25+
26+
[self.bridge setEnabled:enabled];
27+
28+
OCMVerify([self.mSessionReplay setEnabled:enabled]);
29+
}
30+
31+
- (void)testSetInstabugLogsEnabled {
32+
BOOL enabled = NO;
33+
34+
[self.bridge setInstabugLogsEnabled:enabled];
35+
36+
OCMVerify([self.mSessionReplay setIBGLogsEnabled:enabled]);
37+
}
38+
39+
- (void)testSetNetworkLogsEnabled {
40+
BOOL enabled = NO;
41+
42+
[self.bridge setNetworkLogsEnabled:enabled];
43+
44+
OCMVerify([self.mSessionReplay setNetworkLogsEnabled:enabled]);
45+
}
46+
47+
- (void)testSetUserStepsEnabled {
48+
BOOL enabled = NO;
49+
50+
[self.bridge setUserStepsEnabled:enabled];
51+
52+
OCMVerify([self.mSessionReplay setUserStepsEnabled:enabled]);
53+
}
54+
55+
56+
@end
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
#import <Foundation/Foundation.h>
2+
#import <React/RCTBridgeModule.h>
3+
#import <React/RCTEventEmitter.h>
4+
#import <Instabug/IBGTypes.h>
5+
6+
@interface InstabugSessionReplayBridge : RCTEventEmitter <RCTBridgeModule>
7+
/*
8+
+------------------------------------------------------------------------+
9+
| Session Replay Module |
10+
+------------------------------------------------------------------------+
11+
*/
12+
13+
- (void)setEnabled:(BOOL)isEnabled;
14+
15+
- (void)setInstabugLogsEnabled:(BOOL)isEnabled;
16+
17+
- (void)setNetworkLogsEnabled:(BOOL)isEnabled;
18+
19+
- (void)setUserStepsEnabled:(BOOL)isEnabled;
20+
21+
22+
@end
23+
24+
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
#import <asl.h>
2+
#import <React/RCTLog.h>
3+
#import <os/log.h>
4+
#import <Instabug/IBGTypes.h>
5+
#import <React/RCTUIManager.h>
6+
#import <Instabug/IBGSessionReplay.h>
7+
#import "InstabugSessionReplayBridge.h"
8+
9+
@implementation InstabugSessionReplayBridge
10+
11+
- (dispatch_queue_t)methodQueue {
12+
return dispatch_get_main_queue();
13+
}
14+
15+
+ (BOOL)requiresMainQueueSetup
16+
{
17+
return NO;
18+
}
19+
20+
- (NSArray<NSString *> *)supportedEvents {
21+
return @[];
22+
}
23+
24+
RCT_EXPORT_MODULE(IBGSessionReplay)
25+
26+
RCT_EXPORT_METHOD(setEnabled:(BOOL)isEnabled) {
27+
IBGSessionReplay.enabled = isEnabled;
28+
}
29+
30+
RCT_EXPORT_METHOD(setNetworkLogsEnabled:(BOOL)isEnabled) {
31+
IBGSessionReplay.networkLogsEnabled = isEnabled;
32+
}
33+
34+
RCT_EXPORT_METHOD(setInstabugLogsEnabled:(BOOL)isEnabled) {
35+
IBGSessionReplay.IBGLogsEnabled = isEnabled;
36+
}
37+
38+
RCT_EXPORT_METHOD(setUserStepsEnabled:(BOOL)isEnabled) {
39+
IBGSessionReplay.userStepsEnabled = isEnabled;
40+
}
41+
42+
@synthesize description;
43+
44+
@synthesize hash;
45+
46+
@synthesize superclass;
47+
48+
@end
49+
50+

src/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import type { NetworkData, NetworkDataObfuscationHandler } from './modules/Netwo
1313
import * as Replies from './modules/Replies';
1414
import type { Survey } from './modules/Surveys';
1515
import * as Surveys from './modules/Surveys';
16+
import * as SessionReplay from './modules/SessionReplay';
1617

1718
export * from './utils/Enums';
1819
export {
@@ -23,6 +24,7 @@ export {
2324
CrashReporting,
2425
FeatureRequests,
2526
NetworkLogger,
27+
SessionReplay,
2628
Replies,
2729
Surveys,
2830
};

0 commit comments

Comments
 (0)