Skip to content

Commit 96af2e6

Browse files
Merge remote-tracking branch 'origin/feat/enabling-network-automasking' into feat/enabling-network-automasking
2 parents 936ef03 + 07c88d8 commit 96af2e6

23 files changed

+490
-41
lines changed

CHANGELOG.md

+5-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
11
# Changelog
22

3-
## [14.0.0](https://github.com/Instabug/Instabug-React-Native/compare/v13.4.0...14.0.0) (November 11, 2024)
3+
## [14.0.0](https://github.com/Instabug/Instabug-React-Native/compare/v13.4.0...14.0.0) (November 19, 2024)
4+
5+
### Added
6+
7+
- Add support for opting into session syncing ([#1292](https://github.com/Instabug/Instabug-React-Native/pull/1292)).
48

59
### Changed
610

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

+16
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
import com.instabug.library.invocation.InstabugInvocationEvent;
1616
import com.instabug.library.invocation.util.InstabugFloatingButtonEdge;
1717
import com.instabug.library.invocation.util.InstabugVideoRecordingButtonPosition;
18+
import com.instabug.library.sessionreplay.model.SessionMetadata;
1819
import com.instabug.library.ui.onboarding.WelcomeMessage;
1920

2021
import java.util.ArrayList;
@@ -58,6 +59,7 @@ static Map<String, Object> getAll() {
5859
putAll(nonFatalExceptionLevel);
5960
putAll(locales);
6061
putAll(placeholders);
62+
putAll(launchType);
6163
}};
6264
}
6365

@@ -238,4 +240,18 @@ static Map<String, Object> getAll() {
238240
put("team", Key.CHATS_TEAM_STRING_NAME);
239241
put("insufficientContentMessage", Key.COMMENT_FIELD_INSUFFICIENT_CONTENT);
240242
}};
243+
244+
public static ArgsMap<String> launchType = new ArgsMap<String>() {{
245+
put("cold", SessionMetadata.LaunchType.COLD);
246+
put("warm",SessionMetadata.LaunchType.WARM );
247+
put("unknown","unknown");
248+
}};
249+
250+
// Temporary workaround to be removed in future release
251+
// This is used for mapping native `LaunchType` values into React Native enum values.
252+
public static HashMap<String,String> launchTypeReversed = new HashMap<String,String>() {{
253+
put(SessionMetadata.LaunchType.COLD,"cold");
254+
put(SessionMetadata.LaunchType.WARM,"warm" );
255+
}};
256+
241257
}

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

+2
Original file line numberDiff line numberDiff line change
@@ -9,4 +9,6 @@ final class Constants {
99

1010
final static String IBG_ON_NEW_MESSAGE_HANDLER = "IBGonNewMessageHandler";
1111
final static String IBG_ON_NEW_REPLY_RECEIVED_CALLBACK = "IBGOnNewReplyReceivedCallback";
12+
final static String IBG_SESSION_REPLAY_ON_SYNC_CALLBACK_INVOCATION = "IBGSessionReplayOnSyncCallback";
13+
1214
}

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

+115-4
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,45 @@
11
package com.instabug.reactlibrary;
22

3+
4+
import androidx.annotation.NonNull;
35
import androidx.annotation.Nullable;
46

7+
import com.facebook.react.bridge.Arguments;
58
import com.facebook.react.bridge.Promise;
69
import com.facebook.react.bridge.ReactApplicationContext;
7-
import com.facebook.react.bridge.ReactContextBaseJavaModule;
810
import com.facebook.react.bridge.ReactMethod;
9-
import com.instabug.chat.Replies;
11+
import com.facebook.react.bridge.ReadableArray;
12+
import com.facebook.react.bridge.ReadableMap;
13+
import com.facebook.react.bridge.WritableArray;
14+
import com.facebook.react.bridge.WritableMap;
1015
import com.instabug.library.OnSessionReplayLinkReady;
16+
import com.instabug.library.SessionSyncListener;
1117
import com.instabug.library.sessionreplay.SessionReplay;
18+
import com.instabug.library.sessionreplay.model.SessionMetadata;
19+
import com.instabug.reactlibrary.utils.EventEmitterModule;
1220
import com.instabug.reactlibrary.utils.MainThreadHandler;
21+
import java.util.ArrayList;
22+
import java.util.List;
23+
import java.util.concurrent.CountDownLatch;
1324

1425
import javax.annotation.Nonnull;
1526

16-
public class RNInstabugSessionReplayModule extends ReactContextBaseJavaModule {
27+
public class RNInstabugSessionReplayModule extends EventEmitterModule {
1728

1829
public RNInstabugSessionReplayModule(ReactApplicationContext reactApplicationContext) {
1930
super(reactApplicationContext);
2031
}
2132

33+
@ReactMethod
34+
public void addListener(String event) {
35+
super.addListener(event);
36+
}
37+
38+
@ReactMethod
39+
public void removeListeners(Integer count) {
40+
super.removeListeners(count);
41+
}
42+
2243
@Nonnull
2344
@Override
2445
public String getName() {
@@ -79,7 +100,7 @@ public void run() {
79100
e.printStackTrace();
80101
}
81102
}
82-
});
103+
});
83104
}
84105

85106
@ReactMethod
@@ -97,6 +118,96 @@ public void onSessionReplayLinkReady(@Nullable String link) {
97118
}
98119
});
99120

121+
}
122+
123+
public ReadableMap getSessionMetadataMap(SessionMetadata sessionMetadata){
124+
WritableMap params = Arguments.createMap();
125+
params.putString("appVersion",sessionMetadata.getAppVersion());
126+
params.putString("OS",sessionMetadata.getOs());
127+
params.putString("device",sessionMetadata.getDevice());
128+
params.putDouble("sessionDurationInSeconds",(double)sessionMetadata.getSessionDurationInSeconds());
129+
params.putBoolean("hasLinkToAppReview",sessionMetadata.getLinkedToReview());
130+
params.putArray("networkLogs",getNetworkLogsArray(sessionMetadata.getNetworkLogs()));
131+
132+
String launchType = sessionMetadata.getLaunchType();
133+
Long launchDuration = sessionMetadata.getLaunchDuration();
100134

135+
if (launchType != null) {
136+
params.putString("launchType",ArgsRegistry.launchTypeReversed.get(sessionMetadata.getLaunchType()) );
137+
} else {
138+
params.putString("launchType",ArgsRegistry.launchType.get("unknown"));
139+
}
140+
141+
if (launchDuration != null) {
142+
params.putDouble("launchDuration", (double)launchDuration);
143+
} else {
144+
params.putDouble("launchDuration", 0.0);
145+
}
146+
147+
return params;
148+
}
149+
150+
public ReadableArray getNetworkLogsArray(List<SessionMetadata.NetworkLog> networkLogList ) {
151+
WritableArray networkLogs = Arguments.createArray();
152+
153+
if (networkLogList != null) {
154+
for (SessionMetadata.NetworkLog log : networkLogList) {
155+
WritableMap networkLog = Arguments.createMap();
156+
networkLog.putString("url", log.getUrl());
157+
networkLog.putDouble("duration", log.getDuration());
158+
networkLog.putInt("statusCode", log.getStatusCode());
159+
160+
networkLogs.pushMap(networkLog);
161+
}
162+
}
163+
164+
return networkLogs;
101165
}
166+
167+
private boolean shouldSync = true;
168+
private CountDownLatch latch;
169+
@ReactMethod
170+
public void setSyncCallback() {
171+
MainThreadHandler.runOnMainThread(new Runnable() {
172+
@Override
173+
public void run() {
174+
try {
175+
SessionReplay.setSyncCallback(new SessionSyncListener() {
176+
@Override
177+
public boolean onSessionReadyToSync(@NonNull SessionMetadata sessionMetadata) {
178+
179+
sendEvent(Constants.IBG_SESSION_REPLAY_ON_SYNC_CALLBACK_INVOCATION,getSessionMetadataMap(sessionMetadata));
180+
181+
latch = new CountDownLatch(1);
182+
183+
try {
184+
latch.await();
185+
} catch (InterruptedException e) {
186+
e.printStackTrace();
187+
return true;
188+
}
189+
190+
return shouldSync;
191+
}
192+
});
193+
}
194+
catch(Exception e){
195+
e.printStackTrace();
196+
}
197+
198+
}
199+
});
200+
}
201+
202+
@ReactMethod
203+
public void evaluateSync(boolean result) {
204+
shouldSync = result;
205+
206+
if (latch != null) {
207+
latch.countDown();
208+
}
209+
}
210+
211+
212+
102213
}

android/src/main/java/com/instabug/reactlibrary/utils/EventEmitterModule.java

+2-1
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55

66
import com.facebook.react.bridge.ReactApplicationContext;
77
import com.facebook.react.bridge.ReactContextBaseJavaModule;
8+
import com.facebook.react.bridge.ReadableMap;
89
import com.facebook.react.bridge.WritableMap;
910
import com.facebook.react.modules.core.DeviceEventManagerModule;
1011

@@ -16,7 +17,7 @@ public EventEmitterModule(ReactApplicationContext context) {
1617
}
1718

1819
@VisibleForTesting
19-
public void sendEvent(String event, @Nullable WritableMap params) {
20+
public void sendEvent(String event, @Nullable ReadableMap params) {
2021
if (listenerCount > 0) {
2122
getReactApplicationContext()
2223
.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class)

android/src/test/java/com/instabug/reactlibrary/RNInstabugSessionReplayModuleTest.java

+52-13
Original file line numberDiff line numberDiff line change
@@ -1,40 +1,44 @@
11
package com.instabug.reactlibrary;
22

3+
import static junit.framework.TestCase.assertEquals;
4+
import static junit.framework.TestCase.assertTrue;
5+
6+
import static org.mockito.ArgumentMatchers.eq;
37
import static org.mockito.Matchers.any;
48
import static org.mockito.Mockito.doAnswer;
59
import static org.mockito.Mockito.mock;
10+
import static org.mockito.Mockito.mockConstruction;
611
import static org.mockito.Mockito.mockStatic;
7-
import static org.mockito.Mockito.timeout;
8-
import static org.mockito.Mockito.times;
12+
import static org.mockito.Mockito.spy;
913
import static org.mockito.Mockito.verify;
1014
import static org.mockito.Mockito.when;
1115

12-
import android.os.Handler;
1316
import android.os.Looper;
1417

1518
import com.facebook.react.bridge.Arguments;
16-
import com.facebook.react.bridge.JavaOnlyArray;
19+
import com.facebook.react.bridge.JavaOnlyMap;
1720
import com.facebook.react.bridge.Promise;
18-
import com.facebook.react.bridge.ReadableArray;
19-
import com.facebook.react.bridge.WritableArray;
20-
import com.instabug.chat.Replies;
21-
import com.instabug.featuresrequest.ActionType;
22-
import com.instabug.featuresrequest.FeatureRequests;
23-
import com.instabug.library.Feature;
21+
import com.facebook.react.bridge.ReactApplicationContext;
22+
import com.facebook.react.bridge.WritableMap;
2423
import com.instabug.library.OnSessionReplayLinkReady;
24+
import com.instabug.library.SessionSyncListener;
2525
import com.instabug.library.sessionreplay.SessionReplay;
26+
import com.instabug.library.sessionreplay.model.SessionMetadata;
2627
import com.instabug.reactlibrary.utils.MainThreadHandler;
2728

2829
import org.junit.After;
2930
import org.junit.Before;
3031
import org.junit.Test;
32+
import org.mockito.MockedConstruction;
3133
import org.mockito.MockedStatic;
3234
import org.mockito.Mockito;
3335
import org.mockito.invocation.InvocationOnMock;
3436
import org.mockito.stubbing.Answer;
3537

38+
import java.util.concurrent.CountDownLatch;
3639
import java.util.concurrent.Executors;
3740
import java.util.concurrent.ScheduledExecutorService;
41+
import java.util.concurrent.atomic.AtomicBoolean;
3842

3943

4044
public class RNInstabugSessionReplayModuleTest {
@@ -44,8 +48,8 @@ public class RNInstabugSessionReplayModuleTest {
4448

4549
// Mock Objects
4650
private MockedStatic<Looper> mockLooper;
47-
private MockedStatic <MainThreadHandler> mockMainThreadHandler;
48-
private MockedStatic <SessionReplay> mockSessionReplay;
51+
private MockedStatic<MainThreadHandler> mockMainThreadHandler;
52+
private MockedStatic<SessionReplay> mockSessionReplay;
4953

5054
@Before
5155
public void mockMainThreadHandler() throws Exception {
@@ -107,7 +111,7 @@ public void testSetInstabugLogsEnabled() {
107111
@Test
108112
public void testGetSessionReplayLink() {
109113
Promise promise = mock(Promise.class);
110-
String link="instabug link";
114+
String link = "instabug link";
111115

112116
mockSessionReplay.when(() -> SessionReplay.getSessionReplayLink(any())).thenAnswer(
113117
invocation -> {
@@ -136,5 +140,40 @@ public void testSetUserStepsEnabled() {
136140
mockSessionReplay.verifyNoMoreInteractions();
137141
}
138142

143+
@Test
144+
public void testSetSyncCallback() throws Exception {
145+
MockedStatic<Arguments> mockArguments = mockStatic(Arguments.class);
146+
MockedConstruction<CountDownLatch> mockCountDownLatch = mockConstruction(CountDownLatch.class);
147+
RNInstabugSessionReplayModule SRModule = spy(new RNInstabugSessionReplayModule(mock(ReactApplicationContext.class)));
148+
149+
final boolean shouldSync = true;
150+
final AtomicBoolean actual = new AtomicBoolean();
151+
152+
mockArguments.when(Arguments::createMap).thenReturn(new JavaOnlyMap());
153+
154+
mockSessionReplay.when(() -> SessionReplay.setSyncCallback(any(SessionSyncListener.class)))
155+
.thenAnswer((invocation) -> {
156+
SessionSyncListener listener = (SessionSyncListener) invocation.getArguments()[0];
157+
SessionMetadata metadata = mock(SessionMetadata.class);
158+
actual.set(listener.onSessionReadyToSync(metadata));
159+
return null;
160+
});
161+
162+
doAnswer((invocation) -> {
163+
SRModule.evaluateSync(shouldSync);
164+
return null;
165+
}).when(SRModule).sendEvent(eq(Constants.IBG_SESSION_REPLAY_ON_SYNC_CALLBACK_INVOCATION), any());
166+
167+
WritableMap params = Arguments.createMap();
168+
169+
SRModule.setSyncCallback();
170+
171+
assertEquals(shouldSync, actual.get());
172+
verify(SRModule).sendEvent(Constants.IBG_SESSION_REPLAY_ON_SYNC_CALLBACK_INVOCATION, params);
173+
mockSessionReplay.verify(() -> SessionReplay.setSyncCallback(any(SessionSyncListener.class)));
174+
175+
mockArguments.close();
176+
mockCountDownLatch.close();
177+
}
139178

140179
}

examples/default/e2e/reportBug.e2e.ts

+3-1
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,9 @@ it('reports a bug', async () => {
1414
await waitFor(floatingButton).toBeVisible().withTimeout(30000);
1515
await floatingButton.tap();
1616

17-
await getElement('reportBugMenuItem').tap();
17+
const reportBugMenuItemButton = getElement('reportBugMenuItem');
18+
await waitFor(reportBugMenuItemButton).toBeVisible().withTimeout(30000);
19+
await reportBugMenuItemButton.tap();
1820

1921
await getElement('emailField').typeText(mockData.email);
2022
await getElement('commentField').typeText(mockData.bugComment);

0 commit comments

Comments
 (0)