Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add session sync callback #1292

Open
wants to merge 18 commits into
base: dev
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion RNInstabug.podspec
Original file line number Diff line number Diff line change
Expand Up @@ -16,5 +16,5 @@ Pod::Spec.new do |s|
s.source_files = "ios/**/*.{h,m,mm}"

s.dependency 'React-Core'
use_instabug!(s)
s.dependency 'Instabug'
end
2 changes: 1 addition & 1 deletion android/native.gradle
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
project.ext.instabug = [
version: '13.4.1'
version: '13.3.0.6212626-SNAPSHOT',
]

dependencies {
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