Skip to content

Commit

Permalink
Merge remote-tracking branch 'origin/feat/enabling-network-automaskin…
Browse files Browse the repository at this point in the history
…g' into feat/enabling-network-automasking
  • Loading branch information
ahmedAlaaInstabug committed Nov 27, 2024
2 parents 936ef03 + 07c88d8 commit 96af2e6
Show file tree
Hide file tree
Showing 23 changed files with 490 additions and 41 deletions.
6 changes: 5 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
# Changelog

## [14.0.0](https://github.com/Instabug/Instabug-React-Native/compare/v13.4.0...14.0.0) (November 11, 2024)
## [14.0.0](https://github.com/Instabug/Instabug-React-Native/compare/v13.4.0...14.0.0) (November 19, 2024)

### Added

- Add support for opting into session syncing ([#1292](https://github.com/Instabug/Instabug-React-Native/pull/1292)).

### Changed

Expand Down
16 changes: 16 additions & 0 deletions android/src/main/java/com/instabug/reactlibrary/ArgsRegistry.java
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
import com.instabug.library.invocation.InstabugInvocationEvent;
import com.instabug.library.invocation.util.InstabugFloatingButtonEdge;
import com.instabug.library.invocation.util.InstabugVideoRecordingButtonPosition;
import com.instabug.library.sessionreplay.model.SessionMetadata;
import com.instabug.library.ui.onboarding.WelcomeMessage;

import java.util.ArrayList;
Expand Down Expand Up @@ -58,6 +59,7 @@ static Map<String, Object> getAll() {
putAll(nonFatalExceptionLevel);
putAll(locales);
putAll(placeholders);
putAll(launchType);
}};
}

Expand Down Expand Up @@ -238,4 +240,18 @@ static Map<String, Object> getAll() {
put("team", Key.CHATS_TEAM_STRING_NAME);
put("insufficientContentMessage", Key.COMMENT_FIELD_INSUFFICIENT_CONTENT);
}};

public static ArgsMap<String> launchType = new ArgsMap<String>() {{
put("cold", SessionMetadata.LaunchType.COLD);
put("warm",SessionMetadata.LaunchType.WARM );
put("unknown","unknown");
}};

// Temporary workaround to be removed in future release
// This is used for mapping native `LaunchType` values into React Native enum values.
public static HashMap<String,String> launchTypeReversed = new HashMap<String,String>() {{
put(SessionMetadata.LaunchType.COLD,"cold");
put(SessionMetadata.LaunchType.WARM,"warm" );
}};

}
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,6 @@ final class Constants {

final static String IBG_ON_NEW_MESSAGE_HANDLER = "IBGonNewMessageHandler";
final static String IBG_ON_NEW_REPLY_RECEIVED_CALLBACK = "IBGOnNewReplyReceivedCallback";
final static String IBG_SESSION_REPLAY_ON_SYNC_CALLBACK_INVOCATION = "IBGSessionReplayOnSyncCallback";

}
Original file line number Diff line number Diff line change
@@ -1,24 +1,45 @@
package com.instabug.reactlibrary;


import androidx.annotation.NonNull;
import androidx.annotation.Nullable;

import com.facebook.react.bridge.Arguments;
import com.facebook.react.bridge.Promise;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.ReactContextBaseJavaModule;
import com.facebook.react.bridge.ReactMethod;
import com.instabug.chat.Replies;
import com.facebook.react.bridge.ReadableArray;
import com.facebook.react.bridge.ReadableMap;
import com.facebook.react.bridge.WritableArray;
import com.facebook.react.bridge.WritableMap;
import com.instabug.library.OnSessionReplayLinkReady;
import com.instabug.library.SessionSyncListener;
import com.instabug.library.sessionreplay.SessionReplay;
import com.instabug.library.sessionreplay.model.SessionMetadata;
import com.instabug.reactlibrary.utils.EventEmitterModule;
import com.instabug.reactlibrary.utils.MainThreadHandler;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CountDownLatch;

import javax.annotation.Nonnull;

public class RNInstabugSessionReplayModule extends ReactContextBaseJavaModule {
public class RNInstabugSessionReplayModule extends EventEmitterModule {

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

@ReactMethod
public void addListener(String event) {
super.addListener(event);
}

@ReactMethod
public void removeListeners(Integer count) {
super.removeListeners(count);
}

@Nonnull
@Override
public String getName() {
Expand Down Expand Up @@ -79,7 +100,7 @@ public void run() {
e.printStackTrace();
}
}
});
});
}

@ReactMethod
Expand All @@ -97,6 +118,96 @@ public void onSessionReplayLinkReady(@Nullable String link) {
}
});

}

public ReadableMap getSessionMetadataMap(SessionMetadata sessionMetadata){
WritableMap params = Arguments.createMap();
params.putString("appVersion",sessionMetadata.getAppVersion());
params.putString("OS",sessionMetadata.getOs());
params.putString("device",sessionMetadata.getDevice());
params.putDouble("sessionDurationInSeconds",(double)sessionMetadata.getSessionDurationInSeconds());
params.putBoolean("hasLinkToAppReview",sessionMetadata.getLinkedToReview());
params.putArray("networkLogs",getNetworkLogsArray(sessionMetadata.getNetworkLogs()));

String launchType = sessionMetadata.getLaunchType();
Long launchDuration = sessionMetadata.getLaunchDuration();

if (launchType != null) {
params.putString("launchType",ArgsRegistry.launchTypeReversed.get(sessionMetadata.getLaunchType()) );
} else {
params.putString("launchType",ArgsRegistry.launchType.get("unknown"));
}

if (launchDuration != null) {
params.putDouble("launchDuration", (double)launchDuration);
} else {
params.putDouble("launchDuration", 0.0);
}

return params;
}

public ReadableArray getNetworkLogsArray(List<SessionMetadata.NetworkLog> networkLogList ) {
WritableArray networkLogs = Arguments.createArray();

if (networkLogList != null) {
for (SessionMetadata.NetworkLog log : networkLogList) {
WritableMap networkLog = Arguments.createMap();
networkLog.putString("url", log.getUrl());
networkLog.putDouble("duration", log.getDuration());
networkLog.putInt("statusCode", log.getStatusCode());

networkLogs.pushMap(networkLog);
}
}

return networkLogs;
}

private boolean shouldSync = true;
private CountDownLatch latch;
@ReactMethod
public void setSyncCallback() {
MainThreadHandler.runOnMainThread(new Runnable() {
@Override
public void run() {
try {
SessionReplay.setSyncCallback(new SessionSyncListener() {
@Override
public boolean onSessionReadyToSync(@NonNull SessionMetadata sessionMetadata) {

sendEvent(Constants.IBG_SESSION_REPLAY_ON_SYNC_CALLBACK_INVOCATION,getSessionMetadataMap(sessionMetadata));

latch = new CountDownLatch(1);

try {
latch.await();
} catch (InterruptedException e) {
e.printStackTrace();
return true;
}

return shouldSync;
}
});
}
catch(Exception e){
e.printStackTrace();
}

}
});
}

@ReactMethod
public void evaluateSync(boolean result) {
shouldSync = result;

if (latch != null) {
latch.countDown();
}
}



}
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.ReactContextBaseJavaModule;
import com.facebook.react.bridge.ReadableMap;
import com.facebook.react.bridge.WritableMap;
import com.facebook.react.modules.core.DeviceEventManagerModule;

Expand All @@ -16,7 +17,7 @@ public EventEmitterModule(ReactApplicationContext context) {
}

@VisibleForTesting
public void sendEvent(String event, @Nullable WritableMap params) {
public void sendEvent(String event, @Nullable ReadableMap params) {
if (listenerCount > 0) {
getReactApplicationContext()
.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,40 +1,44 @@
package com.instabug.reactlibrary;

import static junit.framework.TestCase.assertEquals;
import static junit.framework.TestCase.assertTrue;

import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Matchers.any;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.mockConstruction;
import static org.mockito.Mockito.mockStatic;
import static org.mockito.Mockito.timeout;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

import android.os.Handler;
import android.os.Looper;

import com.facebook.react.bridge.Arguments;
import com.facebook.react.bridge.JavaOnlyArray;
import com.facebook.react.bridge.JavaOnlyMap;
import com.facebook.react.bridge.Promise;
import com.facebook.react.bridge.ReadableArray;
import com.facebook.react.bridge.WritableArray;
import com.instabug.chat.Replies;
import com.instabug.featuresrequest.ActionType;
import com.instabug.featuresrequest.FeatureRequests;
import com.instabug.library.Feature;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.WritableMap;
import com.instabug.library.OnSessionReplayLinkReady;
import com.instabug.library.SessionSyncListener;
import com.instabug.library.sessionreplay.SessionReplay;
import com.instabug.library.sessionreplay.model.SessionMetadata;
import com.instabug.reactlibrary.utils.MainThreadHandler;

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

import java.util.concurrent.CountDownLatch;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.atomic.AtomicBoolean;


public class RNInstabugSessionReplayModuleTest {
Expand All @@ -44,8 +48,8 @@ public class RNInstabugSessionReplayModuleTest {

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

@Before
public void mockMainThreadHandler() throws Exception {
Expand Down Expand Up @@ -107,7 +111,7 @@ public void testSetInstabugLogsEnabled() {
@Test
public void testGetSessionReplayLink() {
Promise promise = mock(Promise.class);
String link="instabug link";
String link = "instabug link";

mockSessionReplay.when(() -> SessionReplay.getSessionReplayLink(any())).thenAnswer(
invocation -> {
Expand Down Expand Up @@ -136,5 +140,40 @@ public void testSetUserStepsEnabled() {
mockSessionReplay.verifyNoMoreInteractions();
}

@Test
public void testSetSyncCallback() throws Exception {
MockedStatic<Arguments> mockArguments = mockStatic(Arguments.class);
MockedConstruction<CountDownLatch> mockCountDownLatch = mockConstruction(CountDownLatch.class);
RNInstabugSessionReplayModule SRModule = spy(new RNInstabugSessionReplayModule(mock(ReactApplicationContext.class)));

final boolean shouldSync = true;
final AtomicBoolean actual = new AtomicBoolean();

mockArguments.when(Arguments::createMap).thenReturn(new JavaOnlyMap());

mockSessionReplay.when(() -> SessionReplay.setSyncCallback(any(SessionSyncListener.class)))
.thenAnswer((invocation) -> {
SessionSyncListener listener = (SessionSyncListener) invocation.getArguments()[0];
SessionMetadata metadata = mock(SessionMetadata.class);
actual.set(listener.onSessionReadyToSync(metadata));
return null;
});

doAnswer((invocation) -> {
SRModule.evaluateSync(shouldSync);
return null;
}).when(SRModule).sendEvent(eq(Constants.IBG_SESSION_REPLAY_ON_SYNC_CALLBACK_INVOCATION), any());

WritableMap params = Arguments.createMap();

SRModule.setSyncCallback();

assertEquals(shouldSync, actual.get());
verify(SRModule).sendEvent(Constants.IBG_SESSION_REPLAY_ON_SYNC_CALLBACK_INVOCATION, params);
mockSessionReplay.verify(() -> SessionReplay.setSyncCallback(any(SessionSyncListener.class)));

mockArguments.close();
mockCountDownLatch.close();
}

}
4 changes: 3 additions & 1 deletion examples/default/e2e/reportBug.e2e.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,9 @@ it('reports a bug', async () => {
await waitFor(floatingButton).toBeVisible().withTimeout(30000);
await floatingButton.tap();

await getElement('reportBugMenuItem').tap();
const reportBugMenuItemButton = getElement('reportBugMenuItem');
await waitFor(reportBugMenuItemButton).toBeVisible().withTimeout(30000);
await reportBugMenuItemButton.tap();

await getElement('emailField').typeText(mockData.email);
await getElement('commentField').typeText(mockData.bugComment);
Expand Down
Loading

0 comments on commit 96af2e6

Please sign in to comment.