From c7ecf92363dbe23c9abbd4661100f195188377e4 Mon Sep 17 00:00:00 2001 From: Abdelhamid Nasser <38096011+abdelhamid-f-nasser@users.noreply.github.com> Date: Sat, 10 Feb 2024 10:05:48 +0200 Subject: [PATCH] chore: adjust changelog entry for supporting user steps Jira ID: MOB-13966 --- CHANGELOG.md | 2 +- .../com/instabug/reactlibrary/Constants.java | 1 + .../RNInstabugReactnativeModule.java | 36 ++++++++++ .../RNInstabugReactnativeModuleTest.java | 68 ++++++++++++++----- .../reactlibrary/util/GlobalMocks.java | 26 +++++++ .../reactlibrary/util/MockReflected.java | 23 +++++++ .../ios/InstabugTests/InstabugSampleTests.m | 24 ++++++- ios/RNInstabug/InstabugReactBridge.h | 1 + ios/RNInstabug/InstabugReactBridge.m | 18 ++++- ios/RNInstabug/Util/Instabug+CP.h | 1 + src/modules/Instabug.ts | 18 +++++ src/native/NativeInstabug.ts | 4 ++ test/mocks/mockInstabug.ts | 1 + test/modules/Instabug.spec.ts | 18 +++++ 14 files changed, 221 insertions(+), 20 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 720c62fa25..7cf94dff1a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,7 +5,7 @@ ### Added - Support user identification using ID ([#1115](https://github.com/Instabug/Instabug-React-Native/pull/1115)). -- Support button detection and label extraction for repro steps ([#1109](https://github.com/Instabug/Instabug-React-Native/pull/1109)). +- Add support for user steps on Android ([#1109](https://github.com/Instabug/Instabug-React-Native/pull/1109)). ### Changed diff --git a/android/src/main/java/com/instabug/reactlibrary/Constants.java b/android/src/main/java/com/instabug/reactlibrary/Constants.java index f78d3a732d..d33bfe767c 100644 --- a/android/src/main/java/com/instabug/reactlibrary/Constants.java +++ b/android/src/main/java/com/instabug/reactlibrary/Constants.java @@ -3,6 +3,7 @@ final class Constants { final static String IBG_PRE_INVOCATION_HANDLER = "IBGpreInvocationHandler"; final static String IBG_POST_INVOCATION_HANDLER = "IBGpostInvocationHandler"; + final static String IBG_NETWORK_DIAGNOSTICS_HANDLER = "IBGNetworkDiagnosticsHandler"; final static String IBG_ON_SHOW_SURVEY_HANDLER = "IBGWillShowSurvey"; final static String IBG_ON_DISMISS_SURVEY_HANDLER = "IBGDidDismissSurvey"; diff --git a/android/src/main/java/com/instabug/reactlibrary/RNInstabugReactnativeModule.java b/android/src/main/java/com/instabug/reactlibrary/RNInstabugReactnativeModule.java index 514b98f7d2..dac904653c 100644 --- a/android/src/main/java/com/instabug/reactlibrary/RNInstabugReactnativeModule.java +++ b/android/src/main/java/com/instabug/reactlibrary/RNInstabugReactnativeModule.java @@ -8,6 +8,7 @@ import android.view.View; import androidx.annotation.UiThread; +import androidx.annotation.NonNull; import com.facebook.react.bridge.Arguments; import com.facebook.react.bridge.Callback; @@ -33,6 +34,7 @@ import com.instabug.library.logging.InstabugLog; import com.instabug.library.model.NetworkLog; import com.instabug.library.model.Report; +import com.instabug.library.networkDiagnostics.model.NetworkDiagnosticsCallback; import com.instabug.library.ui.onboarding.WelcomeMessage; import com.instabug.reactlibrary.utils.ArrayUtil; import com.instabug.reactlibrary.utils.EventEmitterModule; @@ -44,6 +46,7 @@ import org.json.JSONTokener; import java.io.File; +import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.Arrays; @@ -1042,6 +1045,39 @@ public void run() { }); } + @ReactMethod + public void setOnNetworkDiagnosticsHandler() { + MainThreadHandler.runOnMainThread(new Runnable() { + @Override + public void run() { + try { + Method method = getMethod(Class.forName("com.instabug.library.Instabug"), "setNetworkDiagnosticsCallback", NetworkDiagnosticsCallback.class); + + if (method != null) { + method.invoke(null, new NetworkDiagnosticsCallback() { + @Override + public void onReady(@NonNull String date, int totalRequestCount, int failureCount) { + try { + WritableMap params = Arguments.createMap(); + params.putString("date", date); + params.putInt("totalRequestCount", totalRequestCount); + params.putInt("failureCount", failureCount); + + sendEvent(Constants.IBG_NETWORK_DIAGNOSTICS_HANDLER, params); + } catch (Exception e) { + e.printStackTrace(); + } + } + }); + } + } catch (ClassNotFoundException | IllegalAccessException | + InvocationTargetException e) { + e.printStackTrace(); + } + } + }); + } + /** * Map between the exported JS constant and the arg key in {@link ArgsRegistry}. * The constant name and the arg key should match to be able to resolve the diff --git a/android/src/test/java/com/instabug/reactlibrary/RNInstabugReactnativeModuleTest.java b/android/src/test/java/com/instabug/reactlibrary/RNInstabugReactnativeModuleTest.java index 88174949ae..169b7220da 100644 --- a/android/src/test/java/com/instabug/reactlibrary/RNInstabugReactnativeModuleTest.java +++ b/android/src/test/java/com/instabug/reactlibrary/RNInstabugReactnativeModuleTest.java @@ -9,6 +9,7 @@ import com.facebook.react.bridge.JavaOnlyArray; import com.facebook.react.bridge.JavaOnlyMap; import com.facebook.react.bridge.Promise; +import com.facebook.react.bridge.ReactApplicationContext; import com.facebook.react.bridge.WritableArray; import com.facebook.react.bridge.WritableMap; import com.instabug.library.Feature; @@ -19,7 +20,10 @@ import com.instabug.library.ReproConfigurations; import com.instabug.library.ReproMode; import com.instabug.library.internal.module.InstabugLocale; +import com.instabug.library.networkDiagnostics.model.NetworkDiagnosticsCallback; import com.instabug.library.ui.onboarding.WelcomeMessage; +import com.instabug.reactlibrary.util.GlobalMocks; +import com.instabug.reactlibrary.util.MockReflected; import com.instabug.reactlibrary.utils.MainThreadHandler; import org.junit.After; @@ -37,6 +41,7 @@ import java.lang.reflect.Field; import java.lang.reflect.Method; import java.util.ArrayList; +import java.util.Date; import java.util.HashMap; import java.util.List; import java.util.Locale; @@ -45,18 +50,21 @@ import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; +import static com.instabug.reactlibrary.util.GlobalMocks.reflected; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.Matchers.any; 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.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; public class RNInstabugReactnativeModuleTest { - private RNInstabugReactnativeModule rnModule = new RNInstabugReactnativeModule(null); + private RNInstabugReactnativeModule rnModule; + private ReactApplicationContext mReactContext = mock(ReactApplicationContext.class); private final static ScheduledExecutorService mainThread = Executors.newSingleThreadScheduledExecutor(); @@ -66,7 +74,9 @@ public class RNInstabugReactnativeModuleTest { private MockedStatic mockInstabug; @Before - public void mockMainThreadHandler() throws Exception { + public void setUp() throws Exception { + rnModule = spy(new RNInstabugReactnativeModule(mReactContext)); + // Mock static functions mockInstabug = mockStatic(Instabug.class); mockLooper = mockStatic(Looper.class); @@ -86,6 +96,9 @@ public Boolean answer(InvocationOnMock invocation) throws Throwable { }; Mockito.doAnswer(handlerPostAnswer).when(MainThreadHandler.class); MainThreadHandler.runOnMainThread(any(Runnable.class)); + + // Set up global mocks + GlobalMocks.setUp(); } @After public void tearDown() { @@ -93,6 +106,9 @@ public void tearDown() { mockLooper.close(); mockMainThreadHandler.close(); mockInstabug.close(); + + // Remove global mocks + GlobalMocks.close(); } /********Instabug*********/ @@ -496,28 +512,17 @@ public void testIdentifyUserWithId() { } @Test - public void givenString$reportCurrentViewChange_whenQuery_thenShouldCallNativeApiWithString() throws Exception { - // when + public void testReportCurrentViewChange() { rnModule.reportCurrentViewChange("screen"); - Method privateStringMethod = getMethod(Class.forName("com.instabug.library.Instabug"), "reportCurrentViewChange", String.class); - privateStringMethod.setAccessible(true); - // then - verify(Instabug.class, VerificationModeFactory.times(1)); - privateStringMethod.invoke("reportCurrentViewChange","screen"); + reflected.verify(() -> MockReflected.reportCurrentViewChange("screen"), times(1)); } @Test - public void givenString$reportScreenChange_whenQuery_thenShouldCallNativeApiWithString() throws Exception { - // when + public void testReportScreenChange() { rnModule.reportScreenChange("screen"); - Method privateStringMethod = getMethod(Class.forName("com.instabug.library.Instabug"), "reportScreenChange", Bitmap.class, String.class); - privateStringMethod.setAccessible(true); - - // then - verify(Instabug.class, VerificationModeFactory.times(1)); - privateStringMethod.invoke("reportScreenChange", null,"screen"); + reflected.verify(() -> MockReflected.reportScreenChange(null, "screen"), times(1)); } @Test @@ -567,4 +572,33 @@ public void testIdentifyUserWithId() { verify(Instabug.class,times(1)); Instabug.clearAllExperiments(); } + + @Test + public void testSetOnNetworkDiagnosticsHandler() { + String date = new Date().toString(); + int successOrderCount = 2; + int failureCount = 1; + + MockedStatic mockArgument = mockStatic(Arguments.class); + mockArgument.when(Arguments::createMap).thenReturn(new JavaOnlyMap()); + + reflected + .when(() -> MockReflected.setNetworkDiagnosticsCallback(any(NetworkDiagnosticsCallback.class))) + .thenAnswer((InvocationOnMock invocation) -> { + NetworkDiagnosticsCallback callback = invocation.getArgument(0); + callback.onReady(date, successOrderCount, failureCount); + return null; + }); + + rnModule.setOnNetworkDiagnosticsHandler(); + + WritableMap params = new JavaOnlyMap(); + params.putString("date", date); + params.putInt("totalRequestCount", successOrderCount); + params.putInt("failureCount", failureCount); + + verify(rnModule).sendEvent(Constants.IBG_NETWORK_DIAGNOSTICS_HANDLER, params); + + mockArgument.close(); + } } diff --git a/android/src/test/java/com/instabug/reactlibrary/util/GlobalMocks.java b/android/src/test/java/com/instabug/reactlibrary/util/GlobalMocks.java index e4810c4056..48b4c3fff8 100644 --- a/android/src/test/java/com/instabug/reactlibrary/util/GlobalMocks.java +++ b/android/src/test/java/com/instabug/reactlibrary/util/GlobalMocks.java @@ -1,9 +1,12 @@ package com.instabug.reactlibrary.util; +import static com.instabug.reactlibrary.utils.InstabugUtil.getMethod; import static org.mockito.Mockito.mockStatic; +import android.graphics.Bitmap; import android.util.Log; +import com.instabug.library.networkDiagnostics.model.NetworkDiagnosticsCallback; import com.instabug.reactlibrary.utils.InstabugUtil; import org.mockito.MockedStatic; @@ -37,6 +40,29 @@ public static void setUp() throws NoSuchMethodException { reflection .when(() -> InstabugUtil.getMethod(Class.forName("com.instabug.library.util.InstabugDeprecationLogger"), "setBaseUrl", String.class)) .thenReturn(mSetBaseUrl); + + // setNetworkDiagnosticsCallback mock + Method mSetNetworkDiagnosticsCallback = MockReflected.class.getDeclaredMethod("setNetworkDiagnosticsCallback", NetworkDiagnosticsCallback.class); + mSetNetworkDiagnosticsCallback.setAccessible(true); + reflection + .when(() -> InstabugUtil.getMethod(Class.forName("com.instabug.library.Instabug"), "setNetworkDiagnosticsCallback", NetworkDiagnosticsCallback.class)) + .thenReturn(mSetNetworkDiagnosticsCallback); + + // reportCurrentViewChange mock + Method mReportCurrentViewChange = MockReflected.class.getDeclaredMethod("reportCurrentViewChange", String.class); + mReportCurrentViewChange.setAccessible(true); + + reflection + .when(() -> InstabugUtil.getMethod(Class.forName("com.instabug.library.Instabug"), "reportCurrentViewChange", String.class)) + .thenReturn(mReportCurrentViewChange); + + // reportScreenChange mock + Method mReportScreenChange = MockReflected.class.getDeclaredMethod("reportScreenChange", Bitmap.class, String.class); + mReportScreenChange.setAccessible(true); + + reflection + .when(() -> InstabugUtil.getMethod(Class.forName("com.instabug.library.Instabug"), "reportScreenChange", Bitmap.class, String.class)) + .thenReturn(mReportScreenChange); } public static void close() { diff --git a/android/src/test/java/com/instabug/reactlibrary/util/MockReflected.java b/android/src/test/java/com/instabug/reactlibrary/util/MockReflected.java index bfe886b961..a5a98526dd 100644 --- a/android/src/test/java/com/instabug/reactlibrary/util/MockReflected.java +++ b/android/src/test/java/com/instabug/reactlibrary/util/MockReflected.java @@ -1,5 +1,13 @@ package com.instabug.reactlibrary.util; +import static com.instabug.reactlibrary.utils.InstabugUtil.getMethod; + +import android.graphics.Bitmap; + +import com.instabug.library.networkDiagnostics.model.NetworkDiagnosticsCallback; + +import java.lang.reflect.Method; + /** * Includes fake implementations of methods called by reflection. * Used to verify whether or not a private methods was called. @@ -16,4 +24,19 @@ public static void setCurrentPlatform(int platform) {} * Instabug.util.InstabugDeprecationLogger.setBaseUrl */ public static void setBaseUrl(String baseUrl) {} + + /** + * com.instabug.library.Instabug.setNetworkDiagnosticsCallback + */ + public static void setNetworkDiagnosticsCallback(NetworkDiagnosticsCallback callback) {} + + /** + * com.instabug.library.Instabug.reportCurrentViewChange + */ + public static void reportCurrentViewChange(String currentView) {} + + /** + * com.instabug.library.Instabug.reportScreenChange + */ + public static void reportScreenChange(Bitmap screenshot, String screen) {} } diff --git a/examples/default/ios/InstabugTests/InstabugSampleTests.m b/examples/default/ios/InstabugTests/InstabugSampleTests.m index 1b8183a068..695979ef03 100644 --- a/examples/default/ios/InstabugTests/InstabugSampleTests.m +++ b/examples/default/ios/InstabugTests/InstabugSampleTests.m @@ -13,6 +13,7 @@ #import #import "IBGConstants.h" #import "RNInstabug.h" +#import "Instabug+CP.h" @protocol InstabugCPTestProtocol /** @@ -45,7 +46,7 @@ @implementation InstabugSampleTests - (void)setUp { // Put setup code here. This method is called before the invocation of each test method in the class. - self.instabugBridge = [[InstabugReactBridge alloc] init]; + self.instabugBridge = OCMPartialMock([[InstabugReactBridge alloc] init]); self.mRNInstabug = OCMClassMock([RNInstabug class]); } @@ -420,4 +421,25 @@ - (void)testClearAllExperiments { OCMVerify([mock clearAllExperiments]); } +- (void)testSetOnNetworkDiagnosticsHandler { + id mInstabug = OCMClassMock([Instabug class]); + NSString* date = @"1/2/2024"; + NSInteger totalRequestCount = 10; + NSInteger failureCount = 8; + + NSDictionary *expected = @{ + @"date": date, + @"totalRequestCount": @(totalRequestCount), + @"failureCount": @(failureCount) + }; + + OCMStub([mInstabug setWillSendNetworkDiagnosticsHandler:([OCMArg invokeBlockWithArgs:date, OCMOCK_VALUE(totalRequestCount), OCMOCK_VALUE(failureCount), nil])]); + + OCMStub([self.instabugBridge sendEventWithName:[OCMArg any] body:[OCMArg any]]); + + [self.instabugBridge setOnNetworkDiagnosticsHandler]; + + OCMVerify([self.instabugBridge sendEventWithName:@"IBGNetworkDiagnosticsHandler" body:expected]); +} + @end diff --git a/ios/RNInstabug/InstabugReactBridge.h b/ios/RNInstabug/InstabugReactBridge.h index f3eb9e81a9..47687da67c 100644 --- a/ios/RNInstabug/InstabugReactBridge.h +++ b/ios/RNInstabug/InstabugReactBridge.h @@ -112,5 +112,6 @@ - (void)addExperiments:(NSArray *)experiments; - (void)removeExperiments:(NSArray *)experiments; - (void)clearAllExperiments; +- (void)setOnNetworkDiagnosticsHandler; @end diff --git a/ios/RNInstabug/InstabugReactBridge.m b/ios/RNInstabug/InstabugReactBridge.m index 1ba3658299..0007bedc69 100644 --- a/ios/RNInstabug/InstabugReactBridge.m +++ b/ios/RNInstabug/InstabugReactBridge.m @@ -15,6 +15,7 @@ #import #import #import "RNInstabug.h" +#import "Util/Instabug+CP.h" @interface Instabug (PrivateWillSendAPI) + (void)setWillSendReportHandler_private:(void(^)(IBGReport *report, void(^reportCompletionHandler)(IBGReport *)))willSendReportHandler_private; @@ -23,7 +24,10 @@ + (void)setWillSendReportHandler_private:(void(^)(IBGReport *report, void(^repor @implementation InstabugReactBridge - (NSArray *)supportedEvents { - return @[@"IBGpreSendingHandler"]; + return @[ + @"IBGpreSendingHandler", + @"IBGNetworkDiagnosticsHandler" + ]; } RCT_EXPORT_MODULE(Instabug) @@ -378,6 +382,18 @@ - (dispatch_queue_t)methodQueue { [Instabug clearAllExperiments]; } +RCT_EXPORT_METHOD(setOnNetworkDiagnosticsHandler) { + [Instabug setWillSendNetworkDiagnosticsHandler:^(NSString *date, NSInteger totalRequestCount, NSInteger failureCount) { + NSDictionary *params = @{ + @"date": date, + @"totalRequestCount": @(totalRequestCount), + @"failureCount": @(failureCount) + }; + + [self sendEventWithName:@"IBGNetworkDiagnosticsHandler" body:params]; + }]; +} + - (NSDictionary *)constantsToExport { return ArgsRegistry.getAll; } diff --git a/ios/RNInstabug/Util/Instabug+CP.h b/ios/RNInstabug/Util/Instabug+CP.h index 8666413f0c..333142e48e 100644 --- a/ios/RNInstabug/Util/Instabug+CP.h +++ b/ios/RNInstabug/Util/Instabug+CP.h @@ -6,6 +6,7 @@ NS_ASSUME_NONNULL_BEGIN @interface Instabug (CP) + (void)setCurrentPlatform:(IBGPlatform)platform; ++ (void)setWillSendNetworkDiagnosticsHandler:(void (^_Nullable)(NSString*, NSInteger, NSInteger))willSendNetworkDiagnosticsHandler; @end diff --git a/src/modules/Instabug.ts b/src/modules/Instabug.ts index 781371c183..869ce9b1a4 100644 --- a/src/modules/Instabug.ts +++ b/src/modules/Instabug.ts @@ -549,6 +549,24 @@ export const clearAllExperiments = () => { NativeInstabug.clearAllExperiments(); }; +export type NetworkDiagnosticsHandler = ( + date: String, + totalRequestCount: number, + failureCount: number, +) => void; + +export const onNetworkDiagnosticsHandler = (handler?: NetworkDiagnosticsHandler) => { + emitter.addListener(NativeEvents.NETWORK_DIAGNOSTICS_HANDLER, (data) => { + const { date, totalRequestCount, failureCount } = data; + + if (handler) { + handler(date, totalRequestCount, failureCount); + } + }); + + NativeInstabug.setOnNetworkDiagnosticsHandler(); +}; + export const componentDidAppearListener = (event: ComponentDidAppearEvent) => { if (_isFirstScreen) { _lastScreen = event.componentName; diff --git a/src/native/NativeInstabug.ts b/src/native/NativeInstabug.ts index f1adba7e9f..915bf6629c 100644 --- a/src/native/NativeInstabug.ts +++ b/src/native/NativeInstabug.ts @@ -104,12 +104,16 @@ export interface InstabugNativeModule extends NativeModule { logInfoToReport(log: string): void; addFileAttachmentWithURLToReport(url: string, filename?: string): void; addFileAttachmentWithDataToReport(data: string, filename?: string): void; + + // Callbacks // + setOnNetworkDiagnosticsHandler(): void; } export const NativeInstabug = NativeModules.Instabug; export enum NativeEvents { PRESENDING_HANDLER = 'IBGpreSendingHandler', + NETWORK_DIAGNOSTICS_HANDLER = 'IBGNetworkDiagnosticsHandler', } export const emitter = new NativeEventEmitter(NativeInstabug); diff --git a/test/mocks/mockInstabug.ts b/test/mocks/mockInstabug.ts index 6353cf64c7..0385fe61df 100644 --- a/test/mocks/mockInstabug.ts +++ b/test/mocks/mockInstabug.ts @@ -63,6 +63,7 @@ const mockInstabug: InstabugNativeModule = { addFileAttachmentWithURLToReport: jest.fn(), addFileAttachmentWithDataToReport: jest.fn(), setNetworkLoggingEnabled: jest.fn(), + setOnNetworkDiagnosticsHandler: jest.fn(), }; export default mockInstabug; diff --git a/test/modules/Instabug.spec.ts b/test/modules/Instabug.spec.ts index a78e141a14..7b199f8afc 100644 --- a/test/modules/Instabug.spec.ts +++ b/test/modules/Instabug.spec.ts @@ -760,4 +760,22 @@ describe('Instabug Module', () => { Instabug.clearAllExperiments(); expect(NativeInstabug.clearAllExperiments).toBeCalledTimes(1); }); + + it('onNetworkDiagnosticsHandler should be called with appropriate arguments', () => { + const callback = jest.fn(); + const data = { + date: 'date', + totalRequestCount: 1, + failureCount: 1, + }; + + Instabug.onNetworkDiagnosticsHandler(callback); + + emitter.emit(NativeEvents.NETWORK_DIAGNOSTICS_HANDLER, data); + + expect(NativeInstabug.setOnNetworkDiagnosticsHandler).toBeCalledTimes(1); + expect(emitter.listenerCount(NativeEvents.NETWORK_DIAGNOSTICS_HANDLER)).toBe(1); + expect(callback).toBeCalledTimes(1); + expect(callback).toBeCalledWith(data.date, data.totalRequestCount, data.failureCount); + }); });