Skip to content

Commit

Permalink
feat: add new session replay apis (#1034)
Browse files Browse the repository at this point in the history
Jira ID: IBGCRASH-20006

---------

Co-authored-by: Ahmed Mahmoud <a7med.mahmoud2004@gmail.com>
  • Loading branch information
2 people authored and HeshamMegid committed Oct 3, 2023
1 parent b8e19eb commit 748ac40
Show file tree
Hide file tree
Showing 15 changed files with 454 additions and 0 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@

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

### Added

- 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)).

### Changed

- **BREAKING** Remove deprecated APIs ([#1027](https://github.com/Instabug/Instabug-React-Native/pull/1027)). See migration guide for more details.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ public List<NativeModule> createNativeModules(@NonNull ReactApplicationContext r
modules.add(new RNInstabugFeatureRequestsModule(reactContext));
modules.add(new RNInstabugRepliesModule(reactContext));
modules.add(new RNInstabugAPMModule(reactContext));
modules.add(new RNInstabugSessionReplayModule(reactContext));
return modules;
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
package com.instabug.reactlibrary;

import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.ReactContextBaseJavaModule;
import com.facebook.react.bridge.ReactMethod;
import com.instabug.library.sessionreplay.SessionReplay;
import com.instabug.reactlibrary.utils.MainThreadHandler;

import javax.annotation.Nonnull;

public class RNInstabugSessionReplayModule extends ReactContextBaseJavaModule {

public RNInstabugSessionReplayModule(ReactApplicationContext reactApplicationContext) {
super(reactApplicationContext);
}

@Nonnull
@Override
public String getName() {
return "IBGSessionReplay";
}

@ReactMethod
public void setEnabled(final boolean isEnabled) {
MainThreadHandler.runOnMainThread(new Runnable() {
@Override
public void run() {
try {
SessionReplay.setEnabled(isEnabled);
} catch (Exception e) {
e.printStackTrace();
}
}
});
}

@ReactMethod
public void setNetworkLogsEnabled(final boolean isEnabled) {
MainThreadHandler.runOnMainThread(new Runnable() {
@Override
public void run() {
try {
SessionReplay.setNetworkLogsEnabled(isEnabled);
} catch (Exception e) {
e.printStackTrace();
}
}
});
}


@ReactMethod
public void setInstabugLogsEnabled(final boolean isEnabled) {
MainThreadHandler.runOnMainThread(new Runnable() {
@Override
public void run() {
try {
SessionReplay.setIBGLogsEnabled(isEnabled);
} catch (Exception e) {
e.printStackTrace();
}
}
});
}

@ReactMethod
public void setUserStepsEnabled(final boolean isEnabled) {
MainThreadHandler.runOnMainThread(new Runnable() {
@Override
public void run() {
try {
SessionReplay.setUserStepsEnabled(isEnabled);
} catch (Exception e) {
e.printStackTrace();
}
}
});
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
package com.instabug.reactlibrary;

import static org.mockito.Matchers.any;
import static org.mockito.Mockito.mockStatic;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;

import android.os.Looper;

import com.facebook.react.bridge.Arguments;
import com.facebook.react.bridge.JavaOnlyArray;
import com.facebook.react.bridge.ReadableArray;
import com.facebook.react.bridge.WritableArray;
import com.instabug.featuresrequest.ActionType;
import com.instabug.featuresrequest.FeatureRequests;
import com.instabug.library.Feature;
import com.instabug.library.sessionreplay.SessionReplay;
import com.instabug.reactlibrary.utils.MainThreadHandler;

import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.mockito.MockedStatic;
import org.mockito.Mockito;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;

import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;


public class RNInstabugSessionReplayModuleTest {
private RNInstabugSessionReplayModule sessionReplayModule = new RNInstabugSessionReplayModule(null);

private final static ScheduledExecutorService mainThread = Executors.newSingleThreadScheduledExecutor();

// Mock Objects
private MockedStatic<Looper> mockLooper;
private MockedStatic <MainThreadHandler> mockMainThreadHandler;
private MockedStatic <SessionReplay> mockSessionReplay;

@Before
public void mockMainThreadHandler() throws Exception {
// Mock static functions
mockSessionReplay = mockStatic(SessionReplay.class);
mockLooper = mockStatic(Looper.class);
mockMainThreadHandler = mockStatic(MainThreadHandler.class);

// Mock Looper class
Looper mockMainThreadLooper = Mockito.mock(Looper.class);
Mockito.when(Looper.getMainLooper()).thenReturn(mockMainThreadLooper);

// Override runOnMainThread
Answer<Boolean> handlerPostAnswer = new Answer<Boolean>() {
@Override
public Boolean answer(InvocationOnMock invocation) throws Throwable {
invocation.getArgument(0, Runnable.class).run();
return true;
}
};
Mockito.doAnswer(handlerPostAnswer).when(MainThreadHandler.class);
MainThreadHandler.runOnMainThread(any(Runnable.class));
}
@After
public void tearDown() {
// Remove static mocks
mockLooper.close();
mockMainThreadHandler.close();
mockSessionReplay.close();
}

@Test
public void testSetEnabled() {

sessionReplayModule.setEnabled(true);

mockSessionReplay.verify(() -> SessionReplay.setEnabled(true));
mockSessionReplay.verifyNoMoreInteractions();
}

@Test
public void testSetNetworkLogsEnabled() {

sessionReplayModule.setNetworkLogsEnabled(true);

mockSessionReplay.verify(() -> SessionReplay.setNetworkLogsEnabled(true));
mockSessionReplay.verifyNoMoreInteractions();
}

@Test
public void testSetInstabugLogsEnabled() {

sessionReplayModule.setInstabugLogsEnabled(true);

mockSessionReplay.verify(() -> SessionReplay.setIBGLogsEnabled(true));
mockSessionReplay.verifyNoMoreInteractions();
}

@Test
public void testSetUserStepsEnabled() {

sessionReplayModule.setUserStepsEnabled(true);

mockSessionReplay.verify(() -> SessionReplay.setUserStepsEnabled(true));
mockSessionReplay.verifyNoMoreInteractions();
}


}
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
13B07FBC1A68108700A75B9A /* AppDelegate.mm in Sources */ = {isa = PBXBuildFile; fileRef = 13B07FB01A68108700A75B9A /* AppDelegate.mm */; };
13B07FBF1A68108700A75B9A /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 13B07FB51A68108700A75B9A /* Images.xcassets */; };
13B07FC11A68108700A75B9A /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 13B07FB71A68108700A75B9A /* main.m */; };
20E556262AC55766007416B1 /* InstabugSessionReplayTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 20E556252AC55766007416B1 /* InstabugSessionReplayTests.m */; };
81AB9BB82411601600AC10FF /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 81AB9BB72411601600AC10FF /* LaunchScreen.storyboard */; };
CC3DF88E2A1DFC9A003E9914 /* InstabugCrashReportingTests.m in Sources */ = {isa = PBXBuildFile; fileRef = CC3DF8852A1DFC99003E9914 /* InstabugCrashReportingTests.m */; };
CC3DF88F2A1DFC9A003E9914 /* InstabugBugReportingTests.m in Sources */ = {isa = PBXBuildFile; fileRef = CC3DF8862A1DFC99003E9914 /* InstabugBugReportingTests.m */; };
Expand Down Expand Up @@ -42,6 +43,7 @@
13B07FB51A68108700A75B9A /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Images.xcassets; path = InstabugExample/Images.xcassets; sourceTree = "<group>"; };
13B07FB61A68108700A75B9A /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = Info.plist; path = InstabugExample/Info.plist; sourceTree = "<group>"; };
13B07FB71A68108700A75B9A /* main.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = main.m; path = InstabugExample/main.m; sourceTree = "<group>"; };
20E556252AC55766007416B1 /* InstabugSessionReplayTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = InstabugSessionReplayTests.m; sourceTree = "<group>"; };
3AF7A6E02D40E0CEEA833CC4 /* libPods-InstabugExample-InstabugTests.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-InstabugExample-InstabugTests.a"; sourceTree = BUILT_PRODUCTS_DIR; };
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>"; };
81AB9BB72411601600AC10FF /* LaunchScreen.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; name = LaunchScreen.storyboard; path = InstabugExample/LaunchScreen.storyboard; sourceTree = "<group>"; };
Expand Down Expand Up @@ -91,6 +93,7 @@
CC3DF8852A1DFC99003E9914 /* InstabugCrashReportingTests.m */,
CC3DF8882A1DFC99003E9914 /* InstabugFeatureRequestsTests.m */,
CC3DF88A2A1DFC99003E9914 /* InstabugRepliesTests.m */,
20E556252AC55766007416B1 /* InstabugSessionReplayTests.m */,
CC3DF8872A1DFC99003E9914 /* InstabugSampleTests.m */,
CC3DF88B2A1DFC99003E9914 /* InstabugSurveysTests.m */,
00E356F01AD99517003FC87E /* Supporting Files */,
Expand Down Expand Up @@ -442,6 +445,7 @@
CC3DF8902A1DFC9A003E9914 /* InstabugSampleTests.m in Sources */,
CC3DF8912A1DFC9A003E9914 /* InstabugFeatureRequestsTests.m in Sources */,
CC3DF8932A1DFC9A003E9914 /* InstabugSurveysTests.m in Sources */,
20E556262AC55766007416B1 /* InstabugSessionReplayTests.m in Sources */,
CC3DF88F2A1DFC9A003E9914 /* InstabugBugReportingTests.m in Sources */,
CC3DF88E2A1DFC9A003E9914 /* InstabugCrashReportingTests.m in Sources */,
CC3DF8942A1DFC9A003E9914 /* InstabugAPMTests.m in Sources */,
Expand Down
56 changes: 56 additions & 0 deletions examples/default/ios/InstabugTests/InstabugSessionReplayTests.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
#import <XCTest/XCTest.h>
#import "OCMock/OCMock.h"
#import "InstabugSessionReplayBridge.h"
#import <Instabug/IBGTypes.h>
#import "Instabug/Instabug.h"
#import "IBGConstants.h"

@interface InstabugSessionReplayTests : XCTestCase

@property (nonatomic, strong) id mSessionReplay;
@property (nonatomic, strong) InstabugSessionReplayBridge *bridge;

@end

@implementation InstabugSessionReplayTests


- (void)setUp {
self.mSessionReplay = OCMClassMock([IBGSessionReplay class]);
self.bridge = [[InstabugSessionReplayBridge alloc] init];
}

- (void)testSetEnabled {
BOOL enabled = NO;

[self.bridge setEnabled:enabled];

OCMVerify([self.mSessionReplay setEnabled:enabled]);
}

- (void)testSetInstabugLogsEnabled {
BOOL enabled = NO;

[self.bridge setInstabugLogsEnabled:enabled];

OCMVerify([self.mSessionReplay setIBGLogsEnabled:enabled]);
}

- (void)testSetNetworkLogsEnabled {
BOOL enabled = NO;

[self.bridge setNetworkLogsEnabled:enabled];

OCMVerify([self.mSessionReplay setNetworkLogsEnabled:enabled]);
}

- (void)testSetUserStepsEnabled {
BOOL enabled = NO;

[self.bridge setUserStepsEnabled:enabled];

OCMVerify([self.mSessionReplay setUserStepsEnabled:enabled]);
}


@end
24 changes: 24 additions & 0 deletions ios/RNInstabug/InstabugSessionReplayBridge.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
#import <Foundation/Foundation.h>
#import <React/RCTBridgeModule.h>
#import <React/RCTEventEmitter.h>
#import <Instabug/IBGTypes.h>

@interface InstabugSessionReplayBridge : RCTEventEmitter <RCTBridgeModule>
/*
+------------------------------------------------------------------------+
| Session Replay Module |
+------------------------------------------------------------------------+
*/

- (void)setEnabled:(BOOL)isEnabled;

- (void)setInstabugLogsEnabled:(BOOL)isEnabled;

- (void)setNetworkLogsEnabled:(BOOL)isEnabled;

- (void)setUserStepsEnabled:(BOOL)isEnabled;


@end


50 changes: 50 additions & 0 deletions ios/RNInstabug/InstabugSessionReplayBridge.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
#import <asl.h>
#import <React/RCTLog.h>
#import <os/log.h>
#import <Instabug/IBGTypes.h>
#import <React/RCTUIManager.h>
#import <Instabug/IBGSessionReplay.h>
#import "InstabugSessionReplayBridge.h"

@implementation InstabugSessionReplayBridge

- (dispatch_queue_t)methodQueue {
return dispatch_get_main_queue();
}

+ (BOOL)requiresMainQueueSetup
{
return NO;
}

- (NSArray<NSString *> *)supportedEvents {
return @[];
}

RCT_EXPORT_MODULE(IBGSessionReplay)

RCT_EXPORT_METHOD(setEnabled:(BOOL)isEnabled) {
IBGSessionReplay.enabled = isEnabled;
}

RCT_EXPORT_METHOD(setNetworkLogsEnabled:(BOOL)isEnabled) {
IBGSessionReplay.networkLogsEnabled = isEnabled;
}

RCT_EXPORT_METHOD(setInstabugLogsEnabled:(BOOL)isEnabled) {
IBGSessionReplay.IBGLogsEnabled = isEnabled;
}

RCT_EXPORT_METHOD(setUserStepsEnabled:(BOOL)isEnabled) {
IBGSessionReplay.userStepsEnabled = isEnabled;
}

@synthesize description;

@synthesize hash;

@synthesize superclass;

@end


2 changes: 2 additions & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import type { NetworkData, NetworkDataObfuscationHandler } from './modules/Netwo
import * as Replies from './modules/Replies';
import type { Survey } from './modules/Surveys';
import * as Surveys from './modules/Surveys';
import * as SessionReplay from './modules/SessionReplay';

export * from './utils/Enums';
export {
Expand All @@ -23,6 +24,7 @@ export {
CrashReporting,
FeatureRequests,
NetworkLogger,
SessionReplay,
Replies,
Surveys,
};
Expand Down
Loading

0 comments on commit 748ac40

Please sign in to comment.