From 77b89938dcbe5a2d958bcfdde28954cf65551dcd Mon Sep 17 00:00:00 2001 From: kholood Date: Wed, 28 Aug 2024 17:23:21 +0300 Subject: [PATCH 01/31] feat(android): add SRSyncCallback --- .../com/instabug/reactlibrary/Constants.java | 2 + .../RNInstabugSessionReplayModule.java | 73 ++++++++++++++++++- .../RNInstabugSessionReplayModuleTest.java | 73 +++++++++++++++---- 3 files changed, 131 insertions(+), 17 deletions(-) diff --git a/android/src/main/java/com/instabug/reactlibrary/Constants.java b/android/src/main/java/com/instabug/reactlibrary/Constants.java index f78d3a732d..fcab683326 100644 --- a/android/src/main/java/com/instabug/reactlibrary/Constants.java +++ b/android/src/main/java/com/instabug/reactlibrary/Constants.java @@ -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"; + } diff --git a/android/src/main/java/com/instabug/reactlibrary/RNInstabugSessionReplayModule.java b/android/src/main/java/com/instabug/reactlibrary/RNInstabugSessionReplayModule.java index 5024c61804..0d836832ca 100644 --- a/android/src/main/java/com/instabug/reactlibrary/RNInstabugSessionReplayModule.java +++ b/android/src/main/java/com/instabug/reactlibrary/RNInstabugSessionReplayModule.java @@ -1,24 +1,40 @@ 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.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.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() { @@ -99,4 +115,55 @@ public void onSessionReplayLinkReady(@Nullable String link) { } + + volatile boolean shouldSync = false; + 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) { + 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()); + + sendEvent(Constants.IBG_SESSION_REPLAY_ON_SYNC_CALLBACK_INVOCATION,params); + + latch = new CountDownLatch(1); + + try { + latch.await(); + } catch (InterruptedException e) { + e.printStackTrace(); + } + + return shouldSync; + } + }); + } + catch(Exception e){ + e.printStackTrace(); + } + + } + }); + } + + @ReactMethod + public void evaluateSync(boolean result) { + shouldSync = result; + + if (latch != null) { + latch.countDown(); + } + } + + + } diff --git a/android/src/test/java/com/instabug/reactlibrary/RNInstabugSessionReplayModuleTest.java b/android/src/test/java/com/instabug/reactlibrary/RNInstabugSessionReplayModuleTest.java index ecf339c72b..cf3928e295 100644 --- a/android/src/test/java/com/instabug/reactlibrary/RNInstabugSessionReplayModuleTest.java +++ b/android/src/test/java/com/instabug/reactlibrary/RNInstabugSessionReplayModuleTest.java @@ -1,28 +1,26 @@ package com.instabug.reactlibrary; +import static junit.framework.TestCase.assertEquals; +import static junit.framework.TestCase.assertTrue; + import static org.mockito.Matchers.any; -import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.mock; 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; @@ -33,6 +31,7 @@ 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; @@ -44,8 +43,8 @@ public class RNInstabugSessionReplayModuleTest { // Mock Objects private MockedStatic mockLooper; - private MockedStatic mockMainThreadHandler; - private MockedStatic mockSessionReplay; + private MockedStatic mockMainThreadHandler; + private MockedStatic mockSessionReplay; @Before public void mockMainThreadHandler() throws Exception { @@ -107,7 +106,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 -> { @@ -136,5 +135,51 @@ public void testSetUserStepsEnabled() { mockSessionReplay.verifyNoMoreInteractions(); } + @Test + + public void testSetSyncCallback() throws Exception { + MockedStatic mockArgument = mockStatic(Arguments.class); + RNInstabugSessionReplayModule SRModule = spy(new RNInstabugSessionReplayModule(mock(ReactApplicationContext.class))); + + CountDownLatch latch =new CountDownLatch(1); + SRModule.latch=latch; + + when(Arguments.createMap()).thenReturn(new JavaOnlyMap()); + + mockSessionReplay.when(() -> SessionReplay.setSyncCallback(any(SessionSyncListener.class))) + .thenAnswer(new Answer() { + @Override + public Void answer(InvocationOnMock invocation) { + ((SessionSyncListener) invocation.getArguments()[0]).onSessionReadyToSync(new SessionMetadata("device","android","1.0",20)); + return null; + } + }); + + Thread thread= new Thread (() ->{ + try { + Thread.sleep(500); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + SRModule.evaluateSync(true); + }); + thread.start(); + + SRModule.setSyncCallback(); + + WritableMap params = Arguments.createMap(); + params.putString("appVersion","1.0"); + params.putString("OS","android"); + params.putString("device","device"); + params.putDouble("sessionDurationInSeconds",20); + + assertEquals(SRModule.shouldSync,true); + assertTrue("Latch should be zero after evaluateSync is called", SRModule.latch.getCount() == 0); + + verify(SRModule).sendEvent(Constants.IBG_SESSION_REPLAY_ON_SYNC_CALLBACK_INVOCATION, params); + mockSessionReplay.verify(() -> SessionReplay.setSyncCallback(any(SessionSyncListener.class))); + + mockArgument.close(); + } } From e6b816f68141f27dc6543fa22fcad131400cd8e6 Mon Sep 17 00:00:00 2001 From: kholood Date: Wed, 28 Aug 2024 17:24:53 +0300 Subject: [PATCH 02/31] feat: implement and test syncCallback CP side --- src/modules/SessionReplay.ts | 37 +++++++++++++++++++++++++++++- src/native/NativeSessionReplay.ts | 9 +++++++- test/mocks/mockSessionReplay.ts | 2 ++ test/modules/SessionReplay.spec.ts | 13 ++++++++++- 4 files changed, 58 insertions(+), 3 deletions(-) diff --git a/src/modules/SessionReplay.ts b/src/modules/SessionReplay.ts index edfef456f3..70a296915c 100644 --- a/src/modules/SessionReplay.ts +++ b/src/modules/SessionReplay.ts @@ -1,4 +1,4 @@ -import { NativeSessionReplay } from '../native/NativeSessionReplay'; +import { NativeSessionReplay, NativeEvents, emitter } from '../native/NativeSessionReplay'; /** * Enables or disables Session Replay for your Instabug integration. @@ -75,3 +75,38 @@ export const setUserStepsEnabled = (isEnabled: boolean) => { export const getSessionReplayLink = async (): Promise => { return NativeSessionReplay.getSessionReplayLink(); }; + +/** + * Set a callback for weather this session should sync + * + * @param handler + + * @example + * ```ts + * SessionReplay.setSyncCallback((payload)=>{ + * if(payload.device == "Xiaomi M2007J3SY" && + * payload.os == "OS Level 33" && + * payload.appVersion == "3.1.4 (4)" || + * payload.sessionDurationInSeconds > 20 ) + * {return true} + * }); + * ``` + */ +export const setSyncCallback = async ( + handler: (payload: { + appVersion: string; + OS: string; + device: string; + sessionDurationInSeconds: number; + }) => boolean, +): Promise => { + emitter.addListener(NativeEvents.SESSION_REPLAY_ON_SYNC_CALLBACK_INVOCATION, (payload) => { + if (typeof handler(payload) != 'boolean') { + console.warn('setSyncCallback expects boolean value'); + } + + NativeSessionReplay.evaluateSync(Boolean(handler(payload))); + }); + + return NativeSessionReplay.setSyncCallback(); +}; diff --git a/src/native/NativeSessionReplay.ts b/src/native/NativeSessionReplay.ts index 9c3090fb19..2d0637e3d1 100644 --- a/src/native/NativeSessionReplay.ts +++ b/src/native/NativeSessionReplay.ts @@ -1,4 +1,4 @@ -import type { NativeModule } from 'react-native'; +import { NativeEventEmitter, type NativeModule } from 'react-native'; import { NativeModules } from './NativePackage'; @@ -8,6 +8,13 @@ export interface SessionReplayNativeModule extends NativeModule { setInstabugLogsEnabled(isEnabled: boolean): void; setUserStepsEnabled(isEnabled: boolean): void; getSessionReplayLink(): Promise; + setSyncCallback(): Promise; + evaluateSync(shouldSync: boolean): void; } export const NativeSessionReplay = NativeModules.IBGSessionReplay; +export enum NativeEvents { + SESSION_REPLAY_ON_SYNC_CALLBACK_INVOCATION = 'IBGSessionReplayOnSyncCallback', +} + +export const emitter = new NativeEventEmitter(NativeSessionReplay); diff --git a/test/mocks/mockSessionReplay.ts b/test/mocks/mockSessionReplay.ts index ea61a8356f..9106a205a0 100644 --- a/test/mocks/mockSessionReplay.ts +++ b/test/mocks/mockSessionReplay.ts @@ -8,6 +8,8 @@ const mockSessionReplay: SessionReplayNativeModule = { setInstabugLogsEnabled: jest.fn(), setUserStepsEnabled: jest.fn(), getSessionReplayLink: jest.fn().mockReturnValue('link'), + setSyncCallback: jest.fn(), + evaluateSync: jest.fn(), }; export default mockSessionReplay; diff --git a/test/modules/SessionReplay.spec.ts b/test/modules/SessionReplay.spec.ts index 052a63891e..b42b7fa646 100644 --- a/test/modules/SessionReplay.spec.ts +++ b/test/modules/SessionReplay.spec.ts @@ -1,5 +1,5 @@ import * as SessionReplay from '../../src/modules/SessionReplay'; -import { NativeSessionReplay } from '../../src/native/NativeSessionReplay'; +import { NativeSessionReplay, emitter, NativeEvents } from '../../src/native/NativeSessionReplay'; describe('Session Replay Module', () => { it('should call the native method setEnabled', () => { @@ -36,4 +36,15 @@ describe('Session Replay Module', () => { expect(NativeSessionReplay.getSessionReplayLink).toBeCalledTimes(1); expect(NativeSessionReplay.getSessionReplayLink).toReturnWith('link'); }); + + it('should call the native method setSyncCallback', () => { + const callback = jest.fn().mockReturnValue(true); + + SessionReplay.setSyncCallback(callback); + emitter.emit(NativeEvents.SESSION_REPLAY_ON_SYNC_CALLBACK_INVOCATION); + + expect(NativeSessionReplay.setSyncCallback).toBeCalledTimes(1); + expect(emitter.listenerCount(NativeEvents.SESSION_REPLAY_ON_SYNC_CALLBACK_INVOCATION)).toBe(1); + expect(NativeSessionReplay.evaluateSync).toBeCalledTimes(1); + }); }); From bad72d60bb5474b8a5eccaa956cbaac954f38d42 Mon Sep 17 00:00:00 2001 From: kholood Date: Wed, 28 Aug 2024 17:25:57 +0300 Subject: [PATCH 03/31] feat(example): use SRSyncCallback in example app --- examples/default/src/App.tsx | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/examples/default/src/App.tsx b/examples/default/src/App.tsx index 33a3c34f94..fa31d67f0a 100644 --- a/examples/default/src/App.tsx +++ b/examples/default/src/App.tsx @@ -8,6 +8,7 @@ import Instabug, { InvocationEvent, LogLevel, ReproStepsMode, + SessionReplay, } from 'instabug-reactnative'; import { NativeBaseProvider } from 'native-base'; @@ -20,8 +21,26 @@ import { QueryClient, QueryClientProvider } from 'react-query'; const queryClient = new QueryClient(); export const App: React.FC = () => { + const shouldSyncSession = (data: { + appVersion: string; + OS: string; + device: string; + sessionDurationInSeconds: number; + }) => { + if (data.sessionDurationInSeconds > 20) { + return true; + } + if (data.OS == 'OS Level 34') { + return true; + } + return false; + }; + const navigationRef = useNavigationContainerRef(); + useEffect(() => { + SessionReplay.setSyncCallback((data) => shouldSyncSession(data)); + Instabug.init({ token: 'deb1910a7342814af4e4c9210c786f35', invocationEvents: [InvocationEvent.floatingButton], From 2b01c9d51992848318f0cc64b084b4a7b89df993 Mon Sep 17 00:00:00 2001 From: kholood Date: Wed, 28 Aug 2024 17:41:35 +0300 Subject: [PATCH 04/31] ci: fix tests --- examples/default/src/App.tsx | 2 +- src/modules/SessionReplay.ts | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/examples/default/src/App.tsx b/examples/default/src/App.tsx index fa31d67f0a..dadd26452e 100644 --- a/examples/default/src/App.tsx +++ b/examples/default/src/App.tsx @@ -30,7 +30,7 @@ export const App: React.FC = () => { if (data.sessionDurationInSeconds > 20) { return true; } - if (data.OS == 'OS Level 34') { + if (data.OS === 'OS Level 34') { return true; } return false; diff --git a/src/modules/SessionReplay.ts b/src/modules/SessionReplay.ts index 70a296915c..b5acd1d18a 100644 --- a/src/modules/SessionReplay.ts +++ b/src/modules/SessionReplay.ts @@ -78,7 +78,7 @@ export const getSessionReplayLink = async (): Promise => { /** * Set a callback for weather this session should sync - * + * * @param handler * @example @@ -101,7 +101,7 @@ export const setSyncCallback = async ( }) => boolean, ): Promise => { emitter.addListener(NativeEvents.SESSION_REPLAY_ON_SYNC_CALLBACK_INVOCATION, (payload) => { - if (typeof handler(payload) != 'boolean') { + if (typeof handler(payload) !== 'boolean') { console.warn('setSyncCallback expects boolean value'); } From 61d6e5217f668b5bacb33d86205a47929562dd06 Mon Sep 17 00:00:00 2001 From: kholood Date: Mon, 2 Sep 2024 17:29:02 +0300 Subject: [PATCH 05/31] fix: export session data type --- src/index.ts | 3 ++- src/modules/SessionReplay.ts | 22 ++++++++++++++-------- 2 files changed, 16 insertions(+), 9 deletions(-) diff --git a/src/index.ts b/src/index.ts index a6c425fcd0..3e0358cc29 100644 --- a/src/index.ts +++ b/src/index.ts @@ -14,6 +14,7 @@ import * as Replies from './modules/Replies'; import type { Survey } from './modules/Surveys'; import * as Surveys from './modules/Surveys'; import * as SessionReplay from './modules/SessionReplay'; +import type { sessionData } from './modules/SessionReplay'; export * from './utils/Enums'; export { @@ -28,6 +29,6 @@ export { Replies, Surveys, }; -export type { InstabugConfig, Survey, NetworkData, NetworkDataObfuscationHandler }; +export type { InstabugConfig, Survey, NetworkData, NetworkDataObfuscationHandler, sessionData }; export default Instabug; diff --git a/src/modules/SessionReplay.ts b/src/modules/SessionReplay.ts index b5acd1d18a..139b2d2113 100644 --- a/src/modules/SessionReplay.ts +++ b/src/modules/SessionReplay.ts @@ -1,5 +1,11 @@ import { NativeSessionReplay, NativeEvents, emitter } from '../native/NativeSessionReplay'; +export interface sessionData { + appVersion: string; + OS: string; + device: string; + sessionDurationInSeconds: number; +} /** * Enables or disables Session Replay for your Instabug integration. * @@ -93,16 +99,16 @@ export const getSessionReplayLink = async (): Promise => { * ``` */ export const setSyncCallback = async ( - handler: (payload: { - appVersion: string; - OS: string; - device: string; - sessionDurationInSeconds: number; - }) => boolean, + handler: (payload: sessionData) => boolean, ): Promise => { emitter.addListener(NativeEvents.SESSION_REPLAY_ON_SYNC_CALLBACK_INVOCATION, (payload) => { - if (typeof handler(payload) !== 'boolean') { - console.warn('setSyncCallback expects boolean value'); + const result = handler(payload); + const shouldSync = Boolean(result); + + if (typeof result !== 'boolean') { + console.warn( + `IBG-RN: The callback passed to SessionReplay.setSyncCallback was expected to return a boolean but returned "${result}". The value has been cast to boolean, proceeding with ${shouldSync}.`, + ); } NativeSessionReplay.evaluateSync(Boolean(handler(payload))); From 849648a635ff48928dbdf6f03054cd77289002f4 Mon Sep 17 00:00:00 2001 From: kholood Date: Mon, 2 Sep 2024 17:29:54 +0300 Subject: [PATCH 06/31] fix(example): use session data type --- examples/default/src/App.tsx | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/examples/default/src/App.tsx b/examples/default/src/App.tsx index dadd26452e..46e05baa42 100644 --- a/examples/default/src/App.tsx +++ b/examples/default/src/App.tsx @@ -10,6 +10,7 @@ import Instabug, { ReproStepsMode, SessionReplay, } from 'instabug-reactnative'; +import type { sessionData } from 'instabug-reactnative'; import { NativeBaseProvider } from 'native-base'; import { RootTabNavigator } from './navigation/RootTab'; @@ -21,12 +22,7 @@ import { QueryClient, QueryClientProvider } from 'react-query'; const queryClient = new QueryClient(); export const App: React.FC = () => { - const shouldSyncSession = (data: { - appVersion: string; - OS: string; - device: string; - sessionDurationInSeconds: number; - }) => { + const shouldSyncSession = (data: sessionData) => { if (data.sessionDurationInSeconds > 20) { return true; } From 02c621c6e898eb24938294c304b4a6b06d8c1c5b Mon Sep 17 00:00:00 2001 From: kholood Date: Mon, 2 Sep 2024 17:32:05 +0300 Subject: [PATCH 07/31] fix(android):remove data modifier --- .../instabug/reactlibrary/RNInstabugSessionReplayModule.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/android/src/main/java/com/instabug/reactlibrary/RNInstabugSessionReplayModule.java b/android/src/main/java/com/instabug/reactlibrary/RNInstabugSessionReplayModule.java index 0d836832ca..59a4768b5f 100644 --- a/android/src/main/java/com/instabug/reactlibrary/RNInstabugSessionReplayModule.java +++ b/android/src/main/java/com/instabug/reactlibrary/RNInstabugSessionReplayModule.java @@ -116,7 +116,7 @@ public void onSessionReplayLinkReady(@Nullable String link) { } - volatile boolean shouldSync = false; + boolean shouldSync = false; CountDownLatch latch; @ReactMethod public void setSyncCallback() { From 0d9c7f1bf2037951547a0de6385021ffc7a060a0 Mon Sep 17 00:00:00 2001 From: kholood Date: Mon, 2 Sep 2024 21:24:29 +0300 Subject: [PATCH 08/31] fix(android): add property modifiers --- .../instabug/reactlibrary/RNInstabugSessionReplayModule.java | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/android/src/main/java/com/instabug/reactlibrary/RNInstabugSessionReplayModule.java b/android/src/main/java/com/instabug/reactlibrary/RNInstabugSessionReplayModule.java index 59a4768b5f..bc24c26f2a 100644 --- a/android/src/main/java/com/instabug/reactlibrary/RNInstabugSessionReplayModule.java +++ b/android/src/main/java/com/instabug/reactlibrary/RNInstabugSessionReplayModule.java @@ -115,9 +115,8 @@ public void onSessionReplayLinkReady(@Nullable String link) { } - - boolean shouldSync = false; - CountDownLatch latch; + private boolean shouldSync = false; + private CountDownLatch latch; @ReactMethod public void setSyncCallback() { MainThreadHandler.runOnMainThread(new Runnable() { From 261aa764c7174ce7c00bcaeeefd421288fb537f0 Mon Sep 17 00:00:00 2001 From: kholood Date: Mon, 2 Sep 2024 21:25:16 +0300 Subject: [PATCH 09/31] fix(android): update test case --- .../RNInstabugSessionReplayModuleTest.java | 31 +++++++++++-------- 1 file changed, 18 insertions(+), 13 deletions(-) diff --git a/android/src/test/java/com/instabug/reactlibrary/RNInstabugSessionReplayModuleTest.java b/android/src/test/java/com/instabug/reactlibrary/RNInstabugSessionReplayModuleTest.java index cf3928e295..270be36282 100644 --- a/android/src/test/java/com/instabug/reactlibrary/RNInstabugSessionReplayModuleTest.java +++ b/android/src/test/java/com/instabug/reactlibrary/RNInstabugSessionReplayModuleTest.java @@ -4,6 +4,7 @@ import static junit.framework.TestCase.assertTrue; import static org.mockito.Matchers.any; +import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.mockStatic; import static org.mockito.Mockito.spy; @@ -34,6 +35,7 @@ import java.util.concurrent.CountDownLatch; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.atomic.AtomicBoolean; public class RNInstabugSessionReplayModuleTest { @@ -141,8 +143,8 @@ public void testSetSyncCallback() throws Exception { MockedStatic mockArgument = mockStatic(Arguments.class); RNInstabugSessionReplayModule SRModule = spy(new RNInstabugSessionReplayModule(mock(ReactApplicationContext.class))); - CountDownLatch latch =new CountDownLatch(1); - SRModule.latch=latch; + AtomicBoolean result = new AtomicBoolean(false); + boolean shouldSync=true; when(Arguments.createMap()).thenReturn(new JavaOnlyMap()); @@ -150,32 +152,35 @@ public void testSetSyncCallback() throws Exception { .thenAnswer(new Answer() { @Override public Void answer(InvocationOnMock invocation) { - ((SessionSyncListener) invocation.getArguments()[0]).onSessionReadyToSync(new SessionMetadata("device","android","1.0",20)); + SessionSyncListener listener = (SessionSyncListener) invocation.getArguments()[0]; + SessionMetadata metadata = new SessionMetadata("device", "android", "1.0", 20); + boolean shouldSync=listener.onSessionReadyToSync(metadata); + result.set(shouldSync); return null; } }); + WritableMap params = Arguments.createMap(); + params.putString("appVersion","1.0"); + params.putString("OS","android"); + params.putString("device","device"); + params.putDouble("sessionDurationInSeconds",20); + Thread thread= new Thread (() ->{ try { Thread.sleep(500); } catch (InterruptedException e) { throw new RuntimeException(e); } - SRModule.evaluateSync(true); + + SRModule.evaluateSync(shouldSync); }); + thread.start(); SRModule.setSyncCallback(); - WritableMap params = Arguments.createMap(); - params.putString("appVersion","1.0"); - params.putString("OS","android"); - params.putString("device","device"); - params.putDouble("sessionDurationInSeconds",20); - - assertEquals(SRModule.shouldSync,true); - assertTrue("Latch should be zero after evaluateSync is called", SRModule.latch.getCount() == 0); - + assertEquals(shouldSync,result.get()); verify(SRModule).sendEvent(Constants.IBG_SESSION_REPLAY_ON_SYNC_CALLBACK_INVOCATION, params); mockSessionReplay.verify(() -> SessionReplay.setSyncCallback(any(SessionSyncListener.class))); From 5056f5840a720cfc534f869efdd93f51c16744f3 Mon Sep 17 00:00:00 2001 From: kholood Date: Thu, 5 Sep 2024 10:48:51 +0300 Subject: [PATCH 10/31] fix: enhance test case --- .../RNInstabugSessionReplayModuleTest.java | 63 +++++++++---------- 1 file changed, 28 insertions(+), 35 deletions(-) diff --git a/android/src/test/java/com/instabug/reactlibrary/RNInstabugSessionReplayModuleTest.java b/android/src/test/java/com/instabug/reactlibrary/RNInstabugSessionReplayModuleTest.java index 270be36282..43c87d8e45 100644 --- a/android/src/test/java/com/instabug/reactlibrary/RNInstabugSessionReplayModuleTest.java +++ b/android/src/test/java/com/instabug/reactlibrary/RNInstabugSessionReplayModuleTest.java @@ -3,9 +3,11 @@ 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.spy; import static org.mockito.Mockito.verify; @@ -27,6 +29,7 @@ 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; @@ -138,53 +141,43 @@ public void testSetUserStepsEnabled() { } @Test - public void testSetSyncCallback() throws Exception { - MockedStatic mockArgument = mockStatic(Arguments.class); - RNInstabugSessionReplayModule SRModule = spy(new RNInstabugSessionReplayModule(mock(ReactApplicationContext.class))); + MockedStatic mockArguments = mockStatic(Arguments.class); + MockedConstruction mockCountDownLatch = mockConstruction(CountDownLatch.class); + RNInstabugSessionReplayModule SRModule = spy(new RNInstabugSessionReplayModule(mock(ReactApplicationContext.class))); - AtomicBoolean result = new AtomicBoolean(false); - boolean shouldSync=true; + final boolean shouldSync = true; + final AtomicBoolean actual = new AtomicBoolean(); - when(Arguments.createMap()).thenReturn(new JavaOnlyMap()); + mockArguments.when(Arguments::createMap).thenReturn(new JavaOnlyMap()); - mockSessionReplay.when(() -> SessionReplay.setSyncCallback(any(SessionSyncListener.class))) - .thenAnswer(new Answer() { - @Override - public Void answer(InvocationOnMock invocation) { + mockSessionReplay.when(() -> SessionReplay.setSyncCallback(any(SessionSyncListener.class))) + .thenAnswer((invocation) -> { SessionSyncListener listener = (SessionSyncListener) invocation.getArguments()[0]; SessionMetadata metadata = new SessionMetadata("device", "android", "1.0", 20); - boolean shouldSync=listener.onSessionReadyToSync(metadata); - result.set(shouldSync); + actual.set(listener.onSessionReadyToSync(metadata)); return null; - } - }); + }); - WritableMap params = Arguments.createMap(); - params.putString("appVersion","1.0"); - params.putString("OS","android"); - params.putString("device","device"); - params.putDouble("sessionDurationInSeconds",20); - - Thread thread= new Thread (() ->{ - try { - Thread.sleep(500); - } catch (InterruptedException e) { - throw new RuntimeException(e); - } + doAnswer((invocation) -> { + SRModule.evaluateSync(shouldSync); + return null; + }).when(SRModule).sendEvent(eq(Constants.IBG_SESSION_REPLAY_ON_SYNC_CALLBACK_INVOCATION), any()); - SRModule.evaluateSync(shouldSync); - }); - - thread.start(); + WritableMap params = Arguments.createMap(); + params.putString("appVersion","1.0"); + params.putString("OS","android"); + params.putString("device","device"); + params.putDouble("sessionDurationInSeconds",20); - SRModule.setSyncCallback(); + SRModule.setSyncCallback(); - assertEquals(shouldSync,result.get()); - verify(SRModule).sendEvent(Constants.IBG_SESSION_REPLAY_ON_SYNC_CALLBACK_INVOCATION, params); - mockSessionReplay.verify(() -> SessionReplay.setSyncCallback(any(SessionSyncListener.class))); + assertEquals(shouldSync, actual.get()); + verify(SRModule).sendEvent(Constants.IBG_SESSION_REPLAY_ON_SYNC_CALLBACK_INVOCATION, params); + mockSessionReplay.verify(() -> SessionReplay.setSyncCallback(any(SessionSyncListener.class))); - mockArgument.close(); + mockArguments.close(); + mockCountDownLatch.close(); } } From 2ff447f78b94c4f63d7ab714f76036b1f10ba1d0 Mon Sep 17 00:00:00 2001 From: kholood Date: Mon, 9 Sep 2024 15:24:46 +0300 Subject: [PATCH 11/31] fix: update session data type --- src/modules/SessionReplay.ts | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/modules/SessionReplay.ts b/src/modules/SessionReplay.ts index 139b2d2113..46bf43ff71 100644 --- a/src/modules/SessionReplay.ts +++ b/src/modules/SessionReplay.ts @@ -1,10 +1,19 @@ import { NativeSessionReplay, NativeEvents, emitter } from '../native/NativeSessionReplay'; +import type { NetworkData } from '../modules/NetworkLogger'; + export interface sessionData { appVersion: string; OS: string; device: string; sessionDurationInSeconds: number; + bugsCount: number; + fatalCrashCount: number; + oomCrashCount: number; + networkLogs: Array; + launchType: string; + hasLinkToAppReview: boolean; + launchDuration: number; } /** * Enables or disables Session Replay for your Instabug integration. From 131a79530a8f9332c5bc3bf98a4a81cb89682748 Mon Sep 17 00:00:00 2001 From: kholood Date: Tue, 10 Sep 2024 16:21:28 +0300 Subject: [PATCH 12/31] fix: add more session metadata to setSyncCallback --- .../RNInstabugSessionReplayModule.java | 31 ++++++++++++++++++- 1 file changed, 30 insertions(+), 1 deletion(-) diff --git a/android/src/main/java/com/instabug/reactlibrary/RNInstabugSessionReplayModule.java b/android/src/main/java/com/instabug/reactlibrary/RNInstabugSessionReplayModule.java index bc24c26f2a..3b0cabd4e2 100644 --- a/android/src/main/java/com/instabug/reactlibrary/RNInstabugSessionReplayModule.java +++ b/android/src/main/java/com/instabug/reactlibrary/RNInstabugSessionReplayModule.java @@ -8,6 +8,7 @@ import com.facebook.react.bridge.Promise; import com.facebook.react.bridge.ReactApplicationContext; import com.facebook.react.bridge.ReactMethod; +import com.facebook.react.bridge.WritableArray; import com.facebook.react.bridge.WritableMap; import com.instabug.library.OnSessionReplayLinkReady; import com.instabug.library.SessionSyncListener; @@ -15,6 +16,8 @@ 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; @@ -95,7 +98,7 @@ public void run() { e.printStackTrace(); } } - }); + }); } @ReactMethod @@ -113,8 +116,25 @@ public void onSessionReplayLinkReady(@Nullable String link) { } }); + } + + public WritableArray getNetworkLogsArray(List networkLogList ){ + List networkLogArrayList = networkLogList; + + WritableArray networkLogs = Arguments.createArray(); + + for (SessionMetadata.NetworkLog log : networkLogArrayList) { + 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 = false; private CountDownLatch latch; @ReactMethod @@ -131,6 +151,15 @@ public boolean onSessionReadyToSync(@NonNull SessionMetadata sessionMetadata) { params.putString("OS",sessionMetadata.getOs()); params.putString("device",sessionMetadata.getDevice()); params.putDouble("sessionDurationInSeconds",(double)sessionMetadata.getSessionDurationInSeconds()); + params.putBoolean("hasLinkToAppReview",sessionMetadata.getLinkedToReview()); + params.putDouble("launchType", sessionMetadata.getLaunchType()); + params.putDouble("launchDuration", sessionMetadata.getLaunchDuration()); + params.putArray("networkLogs",getNetworkLogsArray(sessionMetadata.getNetworkLogs())); + +// TODO:Add rest of sessionMetadata +// params.putDouble("bugsCount", ??); +// params.putDouble("fatalCrashCount",??); +// params.putDouble("oomCrashCount",??); sendEvent(Constants.IBG_SESSION_REPLAY_ON_SYNC_CALLBACK_INVOCATION,params); From 2b53766fb54ba83edd52d47493d4dc0318c5151d Mon Sep 17 00:00:00 2001 From: kholood Date: Tue, 10 Sep 2024 19:19:28 +0300 Subject: [PATCH 13/31] fix: update syncCallback test --- .../reactlibrary/RNInstabugSessionReplayModuleTest.java | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/android/src/test/java/com/instabug/reactlibrary/RNInstabugSessionReplayModuleTest.java b/android/src/test/java/com/instabug/reactlibrary/RNInstabugSessionReplayModuleTest.java index 43c87d8e45..1af5e6da08 100644 --- a/android/src/test/java/com/instabug/reactlibrary/RNInstabugSessionReplayModuleTest.java +++ b/android/src/test/java/com/instabug/reactlibrary/RNInstabugSessionReplayModuleTest.java @@ -154,7 +154,7 @@ public void testSetSyncCallback() throws Exception { mockSessionReplay.when(() -> SessionReplay.setSyncCallback(any(SessionSyncListener.class))) .thenAnswer((invocation) -> { SessionSyncListener listener = (SessionSyncListener) invocation.getArguments()[0]; - SessionMetadata metadata = new SessionMetadata("device", "android", "1.0", 20); + SessionMetadata metadata = mock(SessionMetadata.class); actual.set(listener.onSessionReadyToSync(metadata)); return null; }); @@ -165,10 +165,6 @@ public void testSetSyncCallback() throws Exception { }).when(SRModule).sendEvent(eq(Constants.IBG_SESSION_REPLAY_ON_SYNC_CALLBACK_INVOCATION), any()); WritableMap params = Arguments.createMap(); - params.putString("appVersion","1.0"); - params.putString("OS","android"); - params.putString("device","device"); - params.putDouble("sessionDurationInSeconds",20); SRModule.setSyncCallback(); From 41e6b52a78af496385ddacee9d848e8d1c236eb8 Mon Sep 17 00:00:00 2001 From: kholood Date: Wed, 11 Sep 2024 18:09:49 +0300 Subject: [PATCH 14/31] feat: add launchType to session metadata for setSyncCallback --- android/native.gradle | 2 +- .../instabug/reactlibrary/ArgsRegistry.java | 16 ++++++++++++++++ .../RNInstabugSessionReplayModule.java | 4 ++-- examples/default/src/App.tsx | 4 ++++ src/modules/SessionReplay.ts | 18 +++++++++++------- src/native/NativeConstants.ts | 9 ++++++++- src/utils/Enums.ts | 9 +++++++++ 7 files changed, 51 insertions(+), 11 deletions(-) diff --git a/android/native.gradle b/android/native.gradle index 0e86a4edff..93ff054427 100644 --- a/android/native.gradle +++ b/android/native.gradle @@ -1,5 +1,5 @@ project.ext.instabug = [ - version: '13.3.0' + version: '13.3.0.6212131-SNAPSHOT', ] dependencies { diff --git a/android/src/main/java/com/instabug/reactlibrary/ArgsRegistry.java b/android/src/main/java/com/instabug/reactlibrary/ArgsRegistry.java index 37f730cbe9..39311ceb41 100644 --- a/android/src/main/java/com/instabug/reactlibrary/ArgsRegistry.java +++ b/android/src/main/java/com/instabug/reactlibrary/ArgsRegistry.java @@ -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; @@ -58,6 +59,7 @@ static Map getAll() { putAll(nonFatalExceptionLevel); putAll(locales); putAll(placeholders); + putAll(launchType); }}; } @@ -238,4 +240,18 @@ static Map getAll() { put("team", Key.CHATS_TEAM_STRING_NAME); put("insufficientContentMessage", Key.COMMENT_FIELD_INSUFFICIENT_CONTENT); }}; + + public static ArgsMap launchType = new ArgsMap() {{ + put("cold", SessionMetadata.LaunchType.COLD); + put("hot",SessionMetadata.LaunchType.HOT ); + put("warm",SessionMetadata.LaunchType.WARM ); + }}; + +// Temporary workaround to be removed in future release + public static HashMap launchTypeReversed = new HashMap() {{ + put(SessionMetadata.LaunchType.COLD,"cold"); + put(SessionMetadata.LaunchType.HOT,"hot" ); + put(SessionMetadata.LaunchType.WARM,"warm" ); + }}; + } diff --git a/android/src/main/java/com/instabug/reactlibrary/RNInstabugSessionReplayModule.java b/android/src/main/java/com/instabug/reactlibrary/RNInstabugSessionReplayModule.java index 3b0cabd4e2..0d5411803b 100644 --- a/android/src/main/java/com/instabug/reactlibrary/RNInstabugSessionReplayModule.java +++ b/android/src/main/java/com/instabug/reactlibrary/RNInstabugSessionReplayModule.java @@ -152,10 +152,10 @@ public boolean onSessionReadyToSync(@NonNull SessionMetadata sessionMetadata) { params.putString("device",sessionMetadata.getDevice()); params.putDouble("sessionDurationInSeconds",(double)sessionMetadata.getSessionDurationInSeconds()); params.putBoolean("hasLinkToAppReview",sessionMetadata.getLinkedToReview()); - params.putDouble("launchType", sessionMetadata.getLaunchType()); + params.putString("launchType",ArgsRegistry.launchTypeReversed.get(sessionMetadata.getLaunchType()) ); params.putDouble("launchDuration", sessionMetadata.getLaunchDuration()); params.putArray("networkLogs",getNetworkLogsArray(sessionMetadata.getNetworkLogs())); - + // TODO:Add rest of sessionMetadata // params.putDouble("bugsCount", ??); // params.putDouble("fatalCrashCount",??); diff --git a/examples/default/src/App.tsx b/examples/default/src/App.tsx index 46e05baa42..1052e930a9 100644 --- a/examples/default/src/App.tsx +++ b/examples/default/src/App.tsx @@ -9,6 +9,7 @@ import Instabug, { LogLevel, ReproStepsMode, SessionReplay, + LaunchType, } from 'instabug-reactnative'; import type { sessionData } from 'instabug-reactnative'; import { NativeBaseProvider } from 'native-base'; @@ -23,6 +24,9 @@ const queryClient = new QueryClient(); export const App: React.FC = () => { const shouldSyncSession = (data: sessionData) => { + if (data.launchType === LaunchType.cold) { + return true; + } if (data.sessionDurationInSeconds > 20) { return true; } diff --git a/src/modules/SessionReplay.ts b/src/modules/SessionReplay.ts index 46bf43ff71..cf9d5de256 100644 --- a/src/modules/SessionReplay.ts +++ b/src/modules/SessionReplay.ts @@ -1,19 +1,23 @@ import { NativeSessionReplay, NativeEvents, emitter } from '../native/NativeSessionReplay'; - -import type { NetworkData } from '../modules/NetworkLogger'; +import { LaunchType } from '../utils/Enums'; +export interface networkLog { + url: string; + duration: number; + statusCode: number; +} export interface sessionData { appVersion: string; OS: string; device: string; sessionDurationInSeconds: number; - bugsCount: number; - fatalCrashCount: number; - oomCrashCount: number; - networkLogs: Array; - launchType: string; + networkLogs: Array; + launchType: LaunchType; hasLinkToAppReview: boolean; launchDuration: number; + bugsCount?: number; + fatalCrashCount?: number; + oomCrashCount?: number; } /** * Enables or disables Session Replay for your Instabug integration. diff --git a/src/native/NativeConstants.ts b/src/native/NativeConstants.ts index 5317e963ef..271f179b8d 100644 --- a/src/native/NativeConstants.ts +++ b/src/native/NativeConstants.ts @@ -12,7 +12,8 @@ export type NativeConstants = NativeSdkDebugLogsLevel & NativeReproStepsMode & NativeLocale & NativeNonFatalErrorLevel & - NativeStringKey; + NativeStringKey & + NativeLaunchType; interface NativeSdkDebugLogsLevel { sdkDebugLogsLevelVerbose: any; @@ -188,3 +189,9 @@ interface NativeStringKey { welcomeMessageLiveWelcomeStepContent: any; welcomeMessageLiveWelcomeStepTitle: any; } + +interface NativeLaunchType { + hot: any; + cold: any; + warm: any; +} diff --git a/src/utils/Enums.ts b/src/utils/Enums.ts index 57cb54ca15..39577bac89 100644 --- a/src/utils/Enums.ts +++ b/src/utils/Enums.ts @@ -232,3 +232,12 @@ export enum StringKey { welcomeMessageLiveWelcomeStepContent = constants.welcomeMessageLiveWelcomeStepContent, welcomeMessageLiveWelcomeStepTitle = constants.welcomeMessageLiveWelcomeStepTitle, } + +export enum LaunchType { + hot = constants.hot, + cold = constants.cold, + /** + * android only + */ + warm = constants.warm, +} From c6b9173fd7503e615efabb82e4c8c9f33c677d26 Mon Sep 17 00:00:00 2001 From: kholood Date: Thu, 12 Sep 2024 12:17:42 +0300 Subject: [PATCH 15/31] fix: import type --- src/modules/SessionReplay.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/modules/SessionReplay.ts b/src/modules/SessionReplay.ts index cf9d5de256..bc3fe734ad 100644 --- a/src/modules/SessionReplay.ts +++ b/src/modules/SessionReplay.ts @@ -1,5 +1,5 @@ import { NativeSessionReplay, NativeEvents, emitter } from '../native/NativeSessionReplay'; -import { LaunchType } from '../utils/Enums'; +import type { LaunchType } from '../utils/Enums'; export interface networkLog { url: string; duration: number; From 3b1c37bc95cc287adfe16a908d7de7dad8fc97c7 Mon Sep 17 00:00:00 2001 From: kholood Date: Mon, 16 Sep 2024 10:36:08 +0300 Subject: [PATCH 16/31] fix: assert evaluate sync returns correct value --- test/modules/SessionReplay.spec.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/test/modules/SessionReplay.spec.ts b/test/modules/SessionReplay.spec.ts index b42b7fa646..66e672ec59 100644 --- a/test/modules/SessionReplay.spec.ts +++ b/test/modules/SessionReplay.spec.ts @@ -38,7 +38,8 @@ describe('Session Replay Module', () => { }); it('should call the native method setSyncCallback', () => { - const callback = jest.fn().mockReturnValue(true); + const shouldSync = true; + const callback = jest.fn().mockReturnValue(shouldSync); SessionReplay.setSyncCallback(callback); emitter.emit(NativeEvents.SESSION_REPLAY_ON_SYNC_CALLBACK_INVOCATION); @@ -46,5 +47,6 @@ describe('Session Replay Module', () => { expect(NativeSessionReplay.setSyncCallback).toBeCalledTimes(1); expect(emitter.listenerCount(NativeEvents.SESSION_REPLAY_ON_SYNC_CALLBACK_INVOCATION)).toBe(1); expect(NativeSessionReplay.evaluateSync).toBeCalledTimes(1); + expect(NativeSessionReplay.evaluateSync).toBeCalledWith(shouldSync); }); }); From 446e9e42d573fdcf2e86ff34bf15fb25d34966ab Mon Sep 17 00:00:00 2001 From: kholood Date: Mon, 16 Sep 2024 10:39:33 +0300 Subject: [PATCH 17/31] fix: import type --- src/native/NativeSessionReplay.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/native/NativeSessionReplay.ts b/src/native/NativeSessionReplay.ts index 2d0637e3d1..3139ef44a5 100644 --- a/src/native/NativeSessionReplay.ts +++ b/src/native/NativeSessionReplay.ts @@ -1,4 +1,5 @@ -import { NativeEventEmitter, type NativeModule } from 'react-native'; +import { NativeEventEmitter } from 'react-native'; +import type { NativeModule } from 'react-native'; import { NativeModules } from './NativePackage'; From 8187f60f932340ceda44b17310ddf4002f782bb6 Mon Sep 17 00:00:00 2001 From: kholood Date: Mon, 16 Sep 2024 10:41:58 +0300 Subject: [PATCH 18/31] fix: cleanup --- src/modules/SessionReplay.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/modules/SessionReplay.ts b/src/modules/SessionReplay.ts index bc3fe734ad..7930cc45f2 100644 --- a/src/modules/SessionReplay.ts +++ b/src/modules/SessionReplay.ts @@ -124,7 +124,7 @@ export const setSyncCallback = async ( ); } - NativeSessionReplay.evaluateSync(Boolean(handler(payload))); + NativeSessionReplay.evaluateSync(shouldSync); }); return NativeSessionReplay.setSyncCallback(); From d0e972c990e067d2509b6cd77b5cf48076701365 Mon Sep 17 00:00:00 2001 From: kholood Date: Mon, 16 Sep 2024 10:46:02 +0300 Subject: [PATCH 19/31] chore: update js doc --- src/modules/SessionReplay.ts | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/src/modules/SessionReplay.ts b/src/modules/SessionReplay.ts index 7930cc45f2..5f4429d1b0 100644 --- a/src/modules/SessionReplay.ts +++ b/src/modules/SessionReplay.ts @@ -102,12 +102,11 @@ export const getSessionReplayLink = async (): Promise => { * @example * ```ts - * SessionReplay.setSyncCallback((payload)=>{ - * if(payload.device == "Xiaomi M2007J3SY" && - * payload.os == "OS Level 33" && - * payload.appVersion == "3.1.4 (4)" || - * payload.sessionDurationInSeconds > 20 ) - * {return true} + * SessionReplay.setSyncCallback((metadata) => { + * return metadata.device == "Xiaomi M2007J3SY" && + * metadata.os == "OS Level 33" && + * metadata.appVersion == "3.1.4 (4)" || + * metadata.sessionDurationInSeconds > 20; * }); * ``` */ From c700600964ae1086386172821dea33c588e03bfb Mon Sep 17 00:00:00 2001 From: kholood Date: Mon, 16 Sep 2024 10:47:11 +0300 Subject: [PATCH 20/31] fix: typo --- src/modules/SessionReplay.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/modules/SessionReplay.ts b/src/modules/SessionReplay.ts index 5f4429d1b0..c2a6ab0e85 100644 --- a/src/modules/SessionReplay.ts +++ b/src/modules/SessionReplay.ts @@ -96,7 +96,7 @@ export const getSessionReplayLink = async (): Promise => { }; /** - * Set a callback for weather this session should sync + * Set a callback for whether this session should sync * * @param handler From 38bf28f52c1ad5fd81c24ed2584891fa2565a1b9 Mon Sep 17 00:00:00 2001 From: kholood Date: Mon, 16 Sep 2024 10:51:39 +0300 Subject: [PATCH 21/31] fix: follow interface naming convention --- examples/default/src/App.tsx | 4 ++-- src/index.ts | 4 ++-- src/modules/SessionReplay.ts | 8 ++++---- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/examples/default/src/App.tsx b/examples/default/src/App.tsx index 1052e930a9..abdab11111 100644 --- a/examples/default/src/App.tsx +++ b/examples/default/src/App.tsx @@ -11,7 +11,7 @@ import Instabug, { SessionReplay, LaunchType, } from 'instabug-reactnative'; -import type { sessionData } from 'instabug-reactnative'; +import type { SessionMetadata } from 'instabug-reactnative'; import { NativeBaseProvider } from 'native-base'; import { RootTabNavigator } from './navigation/RootTab'; @@ -23,7 +23,7 @@ import { QueryClient, QueryClientProvider } from 'react-query'; const queryClient = new QueryClient(); export const App: React.FC = () => { - const shouldSyncSession = (data: sessionData) => { + const shouldSyncSession = (data: SessionMetadata) => { if (data.launchType === LaunchType.cold) { return true; } diff --git a/src/index.ts b/src/index.ts index 3e0358cc29..eeef3555e1 100644 --- a/src/index.ts +++ b/src/index.ts @@ -14,7 +14,7 @@ import * as Replies from './modules/Replies'; import type { Survey } from './modules/Surveys'; import * as Surveys from './modules/Surveys'; import * as SessionReplay from './modules/SessionReplay'; -import type { sessionData } from './modules/SessionReplay'; +import type { SessionMetadata } from './modules/SessionReplay'; export * from './utils/Enums'; export { @@ -29,6 +29,6 @@ export { Replies, Surveys, }; -export type { InstabugConfig, Survey, NetworkData, NetworkDataObfuscationHandler, sessionData }; +export type { InstabugConfig, Survey, NetworkData, NetworkDataObfuscationHandler, SessionMetadata }; export default Instabug; diff --git a/src/modules/SessionReplay.ts b/src/modules/SessionReplay.ts index c2a6ab0e85..6d143e2df0 100644 --- a/src/modules/SessionReplay.ts +++ b/src/modules/SessionReplay.ts @@ -1,17 +1,17 @@ import { NativeSessionReplay, NativeEvents, emitter } from '../native/NativeSessionReplay'; import type { LaunchType } from '../utils/Enums'; -export interface networkLog { +export interface NetworkLog { url: string; duration: number; statusCode: number; } -export interface sessionData { +export interface SessionMetadata { appVersion: string; OS: string; device: string; sessionDurationInSeconds: number; - networkLogs: Array; + networkLogs: Array; launchType: LaunchType; hasLinkToAppReview: boolean; launchDuration: number; @@ -111,7 +111,7 @@ export const getSessionReplayLink = async (): Promise => { * ``` */ export const setSyncCallback = async ( - handler: (payload: sessionData) => boolean, + handler: (payload: SessionMetadata) => boolean, ): Promise => { emitter.addListener(NativeEvents.SESSION_REPLAY_ON_SYNC_CALLBACK_INVOCATION, (payload) => { const result = handler(payload); From 8cef372419617f8bd2ae8a36f2c53aa8fc050484 Mon Sep 17 00:00:00 2001 From: kholood Date: Mon, 16 Sep 2024 10:53:53 +0300 Subject: [PATCH 22/31] fix: update type --- src/modules/SessionReplay.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/modules/SessionReplay.ts b/src/modules/SessionReplay.ts index 6d143e2df0..ef08e5db58 100644 --- a/src/modules/SessionReplay.ts +++ b/src/modules/SessionReplay.ts @@ -11,7 +11,7 @@ export interface SessionMetadata { OS: string; device: string; sessionDurationInSeconds: number; - networkLogs: Array; + networkLogs: NetworkLog[]; launchType: LaunchType; hasLinkToAppReview: boolean; launchDuration: number; From 40556ec88db7b527a6793762a72cadc0ad33fa47 Mon Sep 17 00:00:00 2001 From: kholood Date: Mon, 16 Sep 2024 11:18:42 +0300 Subject: [PATCH 23/31] fix: refactor syncCallback --- .../RNInstabugSessionReplayModule.java | 38 ++++++++++--------- 1 file changed, 20 insertions(+), 18 deletions(-) diff --git a/android/src/main/java/com/instabug/reactlibrary/RNInstabugSessionReplayModule.java b/android/src/main/java/com/instabug/reactlibrary/RNInstabugSessionReplayModule.java index 0d5411803b..1dd0131bf0 100644 --- a/android/src/main/java/com/instabug/reactlibrary/RNInstabugSessionReplayModule.java +++ b/android/src/main/java/com/instabug/reactlibrary/RNInstabugSessionReplayModule.java @@ -118,12 +118,28 @@ public void onSessionReplayLinkReady(@Nullable String link) { } + public WritableMap 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.putString("launchType",ArgsRegistry.launchTypeReversed.get(sessionMetadata.getLaunchType()) ); + params.putDouble("launchDuration", sessionMetadata.getLaunchDuration()); + params.putArray("networkLogs",getNetworkLogsArray(sessionMetadata.getNetworkLogs())); + +// TODO:Add rest of sessionMetadata +// params.putDouble("bugsCount", ??); +// params.putDouble("fatalCrashCount",??); +// params.putDouble("oomCrashCount",??); + return params; + } + public WritableArray getNetworkLogsArray(List networkLogList ){ - List networkLogArrayList = networkLogList; - WritableArray networkLogs = Arguments.createArray(); - for (SessionMetadata.NetworkLog log : networkLogArrayList) { + for (SessionMetadata.NetworkLog log : networkLogList) { WritableMap networkLog = Arguments.createMap(); networkLog.putString("url", log.getUrl()); networkLog.putDouble("duration", log.getDuration()); @@ -146,22 +162,8 @@ public void run() { SessionReplay.setSyncCallback(new SessionSyncListener() { @Override public boolean onSessionReadyToSync(@NonNull 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.putString("launchType",ArgsRegistry.launchTypeReversed.get(sessionMetadata.getLaunchType()) ); - params.putDouble("launchDuration", sessionMetadata.getLaunchDuration()); - params.putArray("networkLogs",getNetworkLogsArray(sessionMetadata.getNetworkLogs())); - -// TODO:Add rest of sessionMetadata -// params.putDouble("bugsCount", ??); -// params.putDouble("fatalCrashCount",??); -// params.putDouble("oomCrashCount",??); - sendEvent(Constants.IBG_SESSION_REPLAY_ON_SYNC_CALLBACK_INVOCATION,params); + sendEvent(Constants.IBG_SESSION_REPLAY_ON_SYNC_CALLBACK_INVOCATION,getSessionMetadataMap(sessionMetadata)); latch = new CountDownLatch(1); From bc4f699625875fa53eb5e968c374080eb8b805b4 Mon Sep 17 00:00:00 2001 From: kholood Date: Mon, 16 Sep 2024 11:20:01 +0300 Subject: [PATCH 24/31] fix: default syncing session to true --- .../instabug/reactlibrary/RNInstabugSessionReplayModule.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/android/src/main/java/com/instabug/reactlibrary/RNInstabugSessionReplayModule.java b/android/src/main/java/com/instabug/reactlibrary/RNInstabugSessionReplayModule.java index 1dd0131bf0..b53ca5fb05 100644 --- a/android/src/main/java/com/instabug/reactlibrary/RNInstabugSessionReplayModule.java +++ b/android/src/main/java/com/instabug/reactlibrary/RNInstabugSessionReplayModule.java @@ -151,7 +151,7 @@ public WritableArray getNetworkLogsArray(List networ return networkLogs; } - private boolean shouldSync = false; + private boolean shouldSync = true; private CountDownLatch latch; @ReactMethod public void setSyncCallback() { From 6eec19f39370e8d408ac0ddc0fd73283ebf7a6aa Mon Sep 17 00:00:00 2001 From: kholood Date: Mon, 16 Sep 2024 11:55:58 +0300 Subject: [PATCH 25/31] fix: convert network logs to readable array --- .../instabug/reactlibrary/RNInstabugSessionReplayModule.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/android/src/main/java/com/instabug/reactlibrary/RNInstabugSessionReplayModule.java b/android/src/main/java/com/instabug/reactlibrary/RNInstabugSessionReplayModule.java index b53ca5fb05..8e64afe35a 100644 --- a/android/src/main/java/com/instabug/reactlibrary/RNInstabugSessionReplayModule.java +++ b/android/src/main/java/com/instabug/reactlibrary/RNInstabugSessionReplayModule.java @@ -8,6 +8,7 @@ import com.facebook.react.bridge.Promise; import com.facebook.react.bridge.ReactApplicationContext; import com.facebook.react.bridge.ReactMethod; +import com.facebook.react.bridge.ReadableArray; import com.facebook.react.bridge.WritableArray; import com.facebook.react.bridge.WritableMap; import com.instabug.library.OnSessionReplayLinkReady; @@ -136,7 +137,7 @@ public WritableMap getSessionMetadataMap(SessionMetadata sessionMetadata){ return params; } - public WritableArray getNetworkLogsArray(List networkLogList ){ + public ReadableArray getNetworkLogsArray(List networkLogList ){ WritableArray networkLogs = Arguments.createArray(); for (SessionMetadata.NetworkLog log : networkLogList) { From b3c0a31469f72a26af2ef33d5d8525233cafcf22 Mon Sep 17 00:00:00 2001 From: kholood Date: Mon, 16 Sep 2024 12:04:07 +0300 Subject: [PATCH 26/31] chore: add discriptive comment --- .../src/main/java/com/instabug/reactlibrary/ArgsRegistry.java | 1 + 1 file changed, 1 insertion(+) diff --git a/android/src/main/java/com/instabug/reactlibrary/ArgsRegistry.java b/android/src/main/java/com/instabug/reactlibrary/ArgsRegistry.java index 39311ceb41..f1a4c1fa94 100644 --- a/android/src/main/java/com/instabug/reactlibrary/ArgsRegistry.java +++ b/android/src/main/java/com/instabug/reactlibrary/ArgsRegistry.java @@ -248,6 +248,7 @@ static Map getAll() { }}; // Temporary workaround to be removed in future release +// This is used for mapping native `LaunchType` values into React Native enum values. public static HashMap launchTypeReversed = new HashMap() {{ put(SessionMetadata.LaunchType.COLD,"cold"); put(SessionMetadata.LaunchType.HOT,"hot" ); From e50fbefef821888cceace1c8a75d54c5c988827e Mon Sep 17 00:00:00 2001 From: Ahmed Elrefaey <68241710+a7medev@users.noreply.github.com> Date: Mon, 16 Sep 2024 13:58:32 +0300 Subject: [PATCH 27/31] chore: use readable map for session metadata --- .../instabug/reactlibrary/RNInstabugSessionReplayModule.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/android/src/main/java/com/instabug/reactlibrary/RNInstabugSessionReplayModule.java b/android/src/main/java/com/instabug/reactlibrary/RNInstabugSessionReplayModule.java index 8e64afe35a..3050d6bd71 100644 --- a/android/src/main/java/com/instabug/reactlibrary/RNInstabugSessionReplayModule.java +++ b/android/src/main/java/com/instabug/reactlibrary/RNInstabugSessionReplayModule.java @@ -119,7 +119,7 @@ public void onSessionReplayLinkReady(@Nullable String link) { } - public WritableMap getSessionMetadataMap(SessionMetadata sessionMetadata){ + public ReadableMap getSessionMetadataMap(SessionMetadata sessionMetadata){ WritableMap params = Arguments.createMap(); params.putString("appVersion",sessionMetadata.getAppVersion()); params.putString("OS",sessionMetadata.getOs()); From d08d84c83c4dd3dab4a55be304005e5b17cb865e Mon Sep 17 00:00:00 2001 From: kholood Date: Wed, 18 Sep 2024 12:53:18 +0300 Subject: [PATCH 28/31] fix: setSyncCallback should sync in case of exception --- .../com/instabug/reactlibrary/RNInstabugSessionReplayModule.java | 1 + 1 file changed, 1 insertion(+) diff --git a/android/src/main/java/com/instabug/reactlibrary/RNInstabugSessionReplayModule.java b/android/src/main/java/com/instabug/reactlibrary/RNInstabugSessionReplayModule.java index 3050d6bd71..d4973894a8 100644 --- a/android/src/main/java/com/instabug/reactlibrary/RNInstabugSessionReplayModule.java +++ b/android/src/main/java/com/instabug/reactlibrary/RNInstabugSessionReplayModule.java @@ -172,6 +172,7 @@ public boolean onSessionReadyToSync(@NonNull SessionMetadata sessionMetadata) { latch.await(); } catch (InterruptedException e) { e.printStackTrace(); + return true; } return shouldSync; From 6ee32a89d742b2dfe09c46e4211c8d7e8fde6145 Mon Sep 17 00:00:00 2001 From: kholood Date: Wed, 18 Sep 2024 13:12:49 +0300 Subject: [PATCH 29/31] fix: move SessionMetadata to models --- src/models/SessionMetadata.ts | 57 +++++++++++++++++++++++++++++++++++ src/modules/SessionReplay.ts | 21 +------------ 2 files changed, 58 insertions(+), 20 deletions(-) create mode 100644 src/models/SessionMetadata.ts diff --git a/src/models/SessionMetadata.ts b/src/models/SessionMetadata.ts new file mode 100644 index 0000000000..f95c1cba52 --- /dev/null +++ b/src/models/SessionMetadata.ts @@ -0,0 +1,57 @@ +import type { LaunchType } from '../utils/Enums'; + +/** + * network log item + */ +export interface NetworkLog { + url: string; + duration: number; + statusCode: number; +} + +export interface SessionMetadata { + /** + * app version of the session + */ + appVersion: string; + /** + * operating system of the session + */ + OS: string; + /** + * mobile device model of the session + */ + device: string; + /** + * session duration in seconds + */ + sessionDurationInSeconds: number; + /** + * list of netwrok requests occurred during the session + */ + networkLogs: NetworkLog[]; + /** + * launch type of the session + */ + launchType: LaunchType; + /** + * is an in-app review occurred in the previous session. + */ + hasLinkToAppReview: boolean; + /** + * app launch duration + */ + launchDuration: number; + /** + * number of bugs in the session + */ + bugsCount?: number; + /** + * number of fetal crashes in the session + */ + fatalCrashCount?: number; + /** + * number of out of memory crashes in the session + */ + oomCrashCount?: number; +} diff --git a/src/modules/SessionReplay.ts b/src/modules/SessionReplay.ts index ef08e5db58..a0abcd3d76 100644 --- a/src/modules/SessionReplay.ts +++ b/src/modules/SessionReplay.ts @@ -1,24 +1,5 @@ import { NativeSessionReplay, NativeEvents, emitter } from '../native/NativeSessionReplay'; -import type { LaunchType } from '../utils/Enums'; -export interface NetworkLog { - url: string; - duration: number; - statusCode: number; -} - -export interface SessionMetadata { - appVersion: string; - OS: string; - device: string; - sessionDurationInSeconds: number; - networkLogs: NetworkLog[]; - launchType: LaunchType; - hasLinkToAppReview: boolean; - launchDuration: number; - bugsCount?: number; - fatalCrashCount?: number; - oomCrashCount?: number; -} +import type { SessionMetadata } from '../models/SessionMetadata'; /** * Enables or disables Session Replay for your Instabug integration. * From 6bf9f32e2e9d0dd9442a4653945f0e3fba32ea88 Mon Sep 17 00:00:00 2001 From: kholood Date: Wed, 18 Sep 2024 13:19:07 +0300 Subject: [PATCH 30/31] fix: update SessionMetadata type import --- src/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/index.ts b/src/index.ts index eeef3555e1..6e7de02846 100644 --- a/src/index.ts +++ b/src/index.ts @@ -14,7 +14,7 @@ import * as Replies from './modules/Replies'; import type { Survey } from './modules/Surveys'; import * as Surveys from './modules/Surveys'; import * as SessionReplay from './modules/SessionReplay'; -import type { SessionMetadata } from './modules/SessionReplay'; +import type { SessionMetadata } from './models/SessionMetadata'; export * from './utils/Enums'; export { From df896653a07d5f54392b3c0ff094db1e5bd421be Mon Sep 17 00:00:00 2001 From: kholood Date: Wed, 18 Sep 2024 13:33:01 +0300 Subject: [PATCH 31/31] fix: report bug e2e test --- examples/default/e2e/reportBug.e2e.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/examples/default/e2e/reportBug.e2e.ts b/examples/default/e2e/reportBug.e2e.ts index 08757b7884..e4ba1e2f9e 100644 --- a/examples/default/e2e/reportBug.e2e.ts +++ b/examples/default/e2e/reportBug.e2e.ts @@ -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);