From c04e1a73235e06bfc1848277be0c1a7a9703d90b Mon Sep 17 00:00:00 2001 From: ahmed alaa <154802748+ahmedAlaaInstabug@users.noreply.github.com> Date: Wed, 5 Jun 2024 00:48:55 +0300 Subject: [PATCH 1/6] chore(ios): bump sdk to v13.1.0 (#1227) * chore(ios): bump ios sdk v13.1.0 * chore(ios): bump ios sdk v13.1.0 * chore(ios): bump ios sdk v13.1.0 --- CHANGELOG.md | 6 ++++++ examples/default/ios/Podfile.lock | 8 ++++---- ios/native.rb | 2 +- 3 files changed, 11 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2287f6614..8ed7f597f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,11 @@ # Changelog +## [Unreleased](https://github.com/Instabug/Instabug-React-Native/compare/v13.0.4...dev) + +### Changed + +- Bump Instabug iOS SDK to v13.1.0 ([#1227](https://github.com/Instabug/Instabug-React-Native/pull/1227)). [See release notes](https://github.com/Instabug/Instabug-iOS/releases/tag/13.1.0). + ## [13.0.5](https://github.com/Instabug/Instabug-React-Native/compare/v13.0.4...v13.0.5) (May 18, 2024) ### Changed diff --git a/examples/default/ios/Podfile.lock b/examples/default/ios/Podfile.lock index f89a967e5..2b73844bd 100644 --- a/examples/default/ios/Podfile.lock +++ b/examples/default/ios/Podfile.lock @@ -97,7 +97,7 @@ PODS: - hermes-engine (0.72.3): - hermes-engine/Pre-built (= 0.72.3) - hermes-engine/Pre-built (0.72.3) - - Instabug (13.0.5) + - Instabug (13.1.0) - instabug-reactnative-ndk (0.1.0): - RCT-Folly (= 2021.07.22.00) - React-Core @@ -532,7 +532,7 @@ PODS: - RCT-Folly (= 2021.07.22.00) - React-Core - RNInstabug (13.0.5): - - Instabug (= 13.0.5) + - Instabug (= 13.1.0) - React-Core - RNReanimated (3.5.4): - DoubleConversion @@ -798,7 +798,7 @@ SPEC CHECKSUMS: Google-Maps-iOS-Utils: f77eab4c4326d7e6a277f8e23a0232402731913a GoogleMaps: 032f676450ba0779bd8ce16840690915f84e57ac hermes-engine: 10fbd3f62405c41ea07e71973ea61e1878d07322 - Instabug: 54e81af4202faf8ed1e26e47f8816d6c42245d1e + Instabug: 3d55eff7ea55adf22df404908a2b954b8b585c29 instabug-reactnative-ndk: 960119a69380cf4cbe47ccd007c453f757927d17 libevent: 4049cae6c81cdb3654a443be001fb9bdceff7913 OCMock: 300b1b1b9155cb6378660b981c2557448830bdc6 @@ -841,7 +841,7 @@ SPEC CHECKSUMS: React-utils: bcb57da67eec2711f8b353f6e3d33bd8e4b2efa3 ReactCommon: 3ccb8fb14e6b3277e38c73b0ff5e4a1b8db017a9 RNGestureHandler: 6e46dde1f87e5f018a54fe5d40cd0e0b942b49ee - RNInstabug: 5284a7dc148edd3184a6510e9bae0f9f5ef8b254 + RNInstabug: cdc98a25e420c131e539d3a74eae00e56d1433e1 RNReanimated: ab2e96c6d5591c3dfbb38a464f54c8d17fb34a87 RNScreens: b21dc57dfa2b710c30ec600786a3fc223b1b92e7 RNSVG: 80584470ff1ffc7994923ea135a3e5ad825546b9 diff --git a/ios/native.rb b/ios/native.rb index 3e4049bac..dc0b3bf14 100644 --- a/ios/native.rb +++ b/ios/native.rb @@ -1,4 +1,4 @@ -$instabug = { :version => '13.0.5' } +$instabug = { :version => '13.1.0' } def use_instabug! (spec = nil) version = $instabug[:version] From d27317eff99136794199960d79ef0da21166b8f4 Mon Sep 17 00:00:00 2001 From: ahmed alaa <154802748+ahmedAlaaInstabug@users.noreply.github.com> Date: Wed, 5 Jun 2024 01:08:33 +0300 Subject: [PATCH 2/6] chore(android): bump sdk to v13.1.1 (#1228) * chore(android): bump android sdk v13.1.1 --- CHANGELOG.md | 1 + android/native.gradle | 2 +- .../instabug/reactlibrary/utils/ReportUtil.java | 14 -------------- 3 files changed, 2 insertions(+), 15 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8ed7f597f..d5fdd2e9c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,7 @@ ### Changed - Bump Instabug iOS SDK to v13.1.0 ([#1227](https://github.com/Instabug/Instabug-React-Native/pull/1227)). [See release notes](https://github.com/Instabug/Instabug-iOS/releases/tag/13.1.0). +- Bump Instabug android SDK to v13.1.1 ([#1228](https://github.com/Instabug/Instabug-React-Native/pull/1228)). [See release notes](https://github.com/Instabug/android/releases/tag/v13.1.0). ## [13.0.5](https://github.com/Instabug/Instabug-React-Native/compare/v13.0.4...v13.0.5) (May 18, 2024) diff --git a/android/native.gradle b/android/native.gradle index 93cb37d50..4d5d47ed9 100644 --- a/android/native.gradle +++ b/android/native.gradle @@ -1,5 +1,5 @@ project.ext.instabug = [ - version: '13.0.3' + version: '13.1.1' ] dependencies { diff --git a/android/src/main/java/com/instabug/reactlibrary/utils/ReportUtil.java b/android/src/main/java/com/instabug/reactlibrary/utils/ReportUtil.java index a44da1882..d33387e7d 100644 --- a/android/src/main/java/com/instabug/reactlibrary/utils/ReportUtil.java +++ b/android/src/main/java/com/instabug/reactlibrary/utils/ReportUtil.java @@ -64,18 +64,4 @@ public static Report createReport(ReadableArray tags, ReadableArray consoleLogs, return report; } - public static WritableArray parseConsoleLogs(ArrayList consoleLogs) { - WritableArray writableArray = new WritableNativeArray(); - - for(int i = 0; i < consoleLogs.size(); i++) { - try { - writableArray.pushString(consoleLogs.get(i).toJson()); - } catch (Exception e) { - e.printStackTrace(); - } - - } - - return writableArray; - } } From b0ab65ad7c93c68b26e16d67dc37083f9fe72d92 Mon Sep 17 00:00:00 2001 From: ahmed alaa <154802748+ahmedAlaaInstabug@users.noreply.github.com> Date: Wed, 5 Jun 2024 01:43:01 +0300 Subject: [PATCH 3/6] feat: enhance non-fatals support (#1194) * add non fatal api --------- Co-authored-by: Ahmed Mahmoud <68241710+a7medev@users.noreply.github.com> --- CHANGELOG.md | 6 +- .../instabug/reactlibrary/ArgsRegistry.java | 9 ++ .../RNInstabugCrashReportingModule.java | 87 ++++++++---- .../RNInstabugCrashReportingModuleTest.java | 27 ++++ .../reactlibrary/util/GlobalMocks.java | 10 ++ .../reactlibrary/util/MockReflected.java | 11 ++ .../InstabugExample.xcodeproj/project.pbxproj | 10 ++ .../InstabugCrashReportingTests.m | 24 ++++ .../InstabugTests/Util/IBGCrashReporting+CP.h | 13 ++ .../src/screens/CrashReportingScreen.tsx | 124 +++++++++++++++++- ios/RNInstabug/ArgsRegistry.h | 3 + ios/RNInstabug/ArgsRegistry.m | 12 ++ ios/RNInstabug/InstabugBugReportingBridge.m | 1 - ios/RNInstabug/InstabugCrashReportingBridge.h | 11 +- ios/RNInstabug/InstabugCrashReportingBridge.m | 18 +-- ios/RNInstabug/RCTConvert+InstabugEnums.m | 7 + ios/RNInstabug/Util/IBGCrashReporting+CP.h | 13 ++ ios/native.rb | 1 - src/models/NonFatalOptions.ts | 16 +++ src/modules/CrashReporting.ts | 18 ++- src/native/NativeConstants.ts | 9 +- src/native/NativeCrashReporting.ts | 9 +- src/utils/Enums.ts | 10 ++ src/utils/InstabugUtils.ts | 25 ++-- src/utils/UnhandledRejectionTracking.ts | 5 +- test/modules/CrashReporting.spec.ts | 23 ++-- test/utils/InstabugUtils.spec.ts | 25 +++- 27 files changed, 450 insertions(+), 77 deletions(-) create mode 100644 examples/default/ios/InstabugTests/Util/IBGCrashReporting+CP.h create mode 100644 ios/RNInstabug/Util/IBGCrashReporting+CP.h create mode 100644 src/models/NonFatalOptions.ts diff --git a/CHANGELOG.md b/CHANGELOG.md index d5fdd2e9c..84601c9aa 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,10 @@ - Bump Instabug iOS SDK to v13.1.0 ([#1227](https://github.com/Instabug/Instabug-React-Native/pull/1227)). [See release notes](https://github.com/Instabug/Instabug-iOS/releases/tag/13.1.0). - Bump Instabug android SDK to v13.1.1 ([#1228](https://github.com/Instabug/Instabug-React-Native/pull/1228)). [See release notes](https://github.com/Instabug/android/releases/tag/v13.1.0). +### Added + +- Add support for passing a grouping fingerprint, error level, and user attributes to the `CrashReporting.reportError` non-fatals API ([#1194](https://github.com/Instabug/Instabug-React-Native/pull/1194)). + ## [13.0.5](https://github.com/Instabug/Instabug-React-Native/compare/v13.0.4...v13.0.5) (May 18, 2024) ### Changed @@ -33,7 +37,7 @@ - Bump Instabug iOS SDK to v13.0.0 ([#1189](https://github.com/Instabug/Instabug-React-Native/pull/1189)). [See release notes](https://github.com/instabug/instabug-ios/releases/tag/13.0.0). - Bump Instabug Android SDK to v13.0.0 ([#1188](https://github.com/Instabug/Instabug-React-Native/pull/1188)). [See release notes](https://github.com/Instabug/android/releases/tag/v13.0.0). -## [12.9.0](https://github.com/Instabug/Instabug-React-Native/compare/v12.8.0...v12.9.0) (April 2, 2024) +## [12.9.0](https://github.com/Instabug/Instabug-React-Native/compare/v12.8.0...dev)(April 2, 2024) ### Added diff --git a/android/src/main/java/com/instabug/reactlibrary/ArgsRegistry.java b/android/src/main/java/com/instabug/reactlibrary/ArgsRegistry.java index 522bd0dd0..37f730cbe 100644 --- a/android/src/main/java/com/instabug/reactlibrary/ArgsRegistry.java +++ b/android/src/main/java/com/instabug/reactlibrary/ArgsRegistry.java @@ -4,6 +4,7 @@ import com.instabug.bug.BugReporting; import com.instabug.bug.invocation.Option; +import com.instabug.crash.models.IBGNonFatalException; import com.instabug.featuresrequest.ActionType; import com.instabug.library.InstabugColorTheme; import com.instabug.library.InstabugCustomTextPlaceHolder.Key; @@ -54,11 +55,19 @@ static Map getAll() { putAll(extendedBugReportStates); putAll(reproModes); putAll(sdkLogLevels); + putAll(nonFatalExceptionLevel); putAll(locales); putAll(placeholders); }}; } + public static ArgsMap nonFatalExceptionLevel = new ArgsMap() {{ + put("nonFatalErrorLevelCritical", IBGNonFatalException.Level.CRITICAL); + put("nonFatalErrorLevelError", IBGNonFatalException.Level.ERROR); + put("nonFatalErrorLevelWarning", IBGNonFatalException.Level.WARNING); + put("nonFatalErrorLevelInfo", IBGNonFatalException.Level.INFO); + }}; + static ArgsMap invocationEvents = new ArgsMap() {{ put("invocationEventNone", InstabugInvocationEvent.NONE); put("invocationEventShake", InstabugInvocationEvent.SHAKE); diff --git a/android/src/main/java/com/instabug/reactlibrary/RNInstabugCrashReportingModule.java b/android/src/main/java/com/instabug/reactlibrary/RNInstabugCrashReportingModule.java index 4bd84c0bd..080e6cf07 100644 --- a/android/src/main/java/com/instabug/reactlibrary/RNInstabugCrashReportingModule.java +++ b/android/src/main/java/com/instabug/reactlibrary/RNInstabugCrashReportingModule.java @@ -2,11 +2,15 @@ import static com.instabug.reactlibrary.utils.InstabugUtil.getMethod; +import androidx.annotation.NonNull; + 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.facebook.react.bridge.ReadableMap; import com.instabug.crash.CrashReporting; +import com.instabug.crash.models.IBGNonFatalException; import com.instabug.library.Feature; import com.instabug.reactlibrary.utils.MainThreadHandler; @@ -14,6 +18,8 @@ import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; +import java.util.HashMap; +import java.util.Map; import javax.annotation.Nonnull; import javax.annotation.Nullable; @@ -57,8 +63,8 @@ public void run() { * Send unhandled JS error object * * @param exceptionObject Exception object to be sent to Instabug's servers - * @param promise This makes sure that the RN side crashes the app only after the Android SDK - * finishes processing/handling the crash. + * @param promise This makes sure that the RN side crashes the app only after the Android SDK + * finishes processing/handling the crash. */ @ReactMethod public void sendJSCrash(final String exceptionObject, final Promise promise) { @@ -79,41 +85,64 @@ public void run() { * Send handled JS error object * * @param exceptionObject Exception object to be sent to Instabug's servers + * @param userAttributes (Optional) extra user attributes attached to the crash + * @param fingerprint (Optional) key used to customize how crashes are grouped together + * @param level different severity levels for errors */ @ReactMethod - public void sendHandledJSCrash(final String exceptionObject) { + public void sendHandledJSCrash(final String exceptionObject, @Nullable ReadableMap userAttributes, @Nullable String fingerprint, @Nullable String level) { try { JSONObject jsonObject = new JSONObject(exceptionObject); - sendJSCrashByReflection(jsonObject, true, null); - } catch (Exception e) { + MainThreadHandler.runOnMainThread(new Runnable() { + @Override + public void run() { + try { + Method method = getMethod(Class.forName("com.instabug.crash.CrashReporting"), "reportException", JSONObject.class, boolean.class, + Map.class, JSONObject.class, IBGNonFatalException.Level.class); + if (method != null) { + IBGNonFatalException.Level nonFatalExceptionLevel = ArgsRegistry.nonFatalExceptionLevel.getOrDefault(level, IBGNonFatalException.Level.ERROR); + Map userAttributesMap = userAttributes == null ? null : userAttributes.toHashMap(); + JSONObject fingerprintObj = fingerprint == null ? null : CrashReporting.getFingerprintObject(fingerprint); + + method.invoke(null, jsonObject, true, userAttributesMap, fingerprintObj, nonFatalExceptionLevel); + + RNInstabugReactnativeModule.clearCurrentReport(); + } + } catch (ClassNotFoundException | IllegalAccessException | + InvocationTargetException e) { + e.printStackTrace(); + } + } + }); + } catch (Throwable e) { e.printStackTrace(); } } - private void sendJSCrashByReflection(final JSONObject exceptionObject, final boolean isHandled, @Nullable final Runnable onComplete) { - MainThreadHandler.runOnMainThread(new Runnable() { - @Override - public void run() { - try { - Method method = getMethod(Class.forName("com.instabug.crash.CrashReporting"), "reportException", JSONObject.class, boolean.class); - if (method != null) { - method.invoke(null, exceptionObject, isHandled); - RNInstabugReactnativeModule.clearCurrentReport(); - } - } catch (ClassNotFoundException e) { - e.printStackTrace(); - } catch (IllegalAccessException e) { - e.printStackTrace(); - } catch (InvocationTargetException e) { - e.printStackTrace(); - } finally { - if (onComplete != null) { - onComplete.run(); - } - } - } - }); - } + private void sendJSCrashByReflection(final JSONObject exceptionObject, final boolean isHandled, @Nullable final Runnable onComplete) { + MainThreadHandler.runOnMainThread(new Runnable() { + @Override + public void run() { + try { + Method method = getMethod(Class.forName("com.instabug.crash.CrashReporting"), "reportException", JSONObject.class, boolean.class); + if (method != null) { + method.invoke(null, exceptionObject, isHandled); + RNInstabugReactnativeModule.clearCurrentReport(); + } + } catch (ClassNotFoundException e) { + e.printStackTrace(); + } catch (IllegalAccessException e) { + e.printStackTrace(); + } catch (InvocationTargetException e) { + e.printStackTrace(); + } finally { + if (onComplete != null) { + onComplete.run(); + } + } + } + }); + } /** * Enables and disables capturing native C++ NDK crash reporting. diff --git a/android/src/test/java/com/instabug/reactlibrary/RNInstabugCrashReportingModuleTest.java b/android/src/test/java/com/instabug/reactlibrary/RNInstabugCrashReportingModuleTest.java index fd2a9b58e..b5d97985b 100644 --- a/android/src/test/java/com/instabug/reactlibrary/RNInstabugCrashReportingModuleTest.java +++ b/android/src/test/java/com/instabug/reactlibrary/RNInstabugCrashReportingModuleTest.java @@ -1,16 +1,25 @@ package com.instabug.reactlibrary; +import static com.instabug.crash.CrashReporting.getFingerprintObject; +import static com.instabug.reactlibrary.util.GlobalMocks.reflected; +import static org.mockito.AdditionalMatchers.cmpEq; import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.ArgumentMatchers.same; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.mockStatic; import android.os.Looper; import com.instabug.crash.CrashReporting; +import com.instabug.crash.models.IBGNonFatalException; import com.instabug.library.Feature; import com.instabug.reactlibrary.util.GlobalMocks; +import com.instabug.reactlibrary.util.MockReflected; import com.instabug.reactlibrary.utils.MainThreadHandler; +import org.json.JSONException; +import org.json.JSONObject; import org.junit.After; import org.junit.Before; import org.junit.Test; @@ -19,6 +28,9 @@ import org.mockito.invocation.InvocationOnMock; import org.mockito.stubbing.Answer; +import java.util.HashMap; +import java.util.Map; + public class RNInstabugCrashReportingModuleTest { private final RNInstabugCrashReportingModule rnModule = new RNInstabugCrashReportingModule(null); @@ -38,6 +50,7 @@ public void mockMainThreadHandler() throws Exception { // Mock Looper class Looper mockMainThreadLooper = mock(Looper.class); Mockito.when(Looper.getMainLooper()).thenReturn(mockMainThreadLooper); + GlobalMocks.setUp(); // Override runOnMainThread @@ -58,6 +71,8 @@ public void tearDown() { mockLooper.close(); mockMainThreadHandler.close(); mockCrashReporting.close(); + GlobalMocks.close(); + } /********Crashes*********/ @@ -80,6 +95,18 @@ public void testSetNDKCrashesEnabledGivenFalse() { mockCrashReporting.verify(() -> CrashReporting.setNDKCrashesState(Feature.State.DISABLED)); } + @Test + public void testSendNonFatalError() { + String jsonCrash = "{}"; + boolean isHandled = true; + String fingerPrint = "test"; + String level = ArgsRegistry.nonFatalExceptionLevel.keySet().iterator().next(); + JSONObject expectedFingerprint = getFingerprintObject(fingerPrint); + IBGNonFatalException.Level expectedLevel = ArgsRegistry.nonFatalExceptionLevel.get(level); + rnModule.sendHandledJSCrash(jsonCrash, null, fingerPrint, level); + reflected.verify(() -> MockReflected.reportException(any(JSONObject.class), eq(isHandled), eq(null), eq(expectedFingerprint), eq(expectedLevel))); + } + @Test public void givenString$sendHandledJSCrash_whenQuery_thenShouldCallNativeApiWithArgs() throws Exception { // JSONObject json = mock(JSONObject.class); 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 e4810c405..5b88d6dbc 100644 --- a/android/src/test/java/com/instabug/reactlibrary/util/GlobalMocks.java +++ b/android/src/test/java/com/instabug/reactlibrary/util/GlobalMocks.java @@ -4,8 +4,10 @@ import android.util.Log; +import com.instabug.crash.models.IBGNonFatalException; import com.instabug.reactlibrary.utils.InstabugUtil; +import org.json.JSONObject; import org.mockito.MockedStatic; import java.lang.reflect.Method; @@ -37,6 +39,14 @@ public static void setUp() throws NoSuchMethodException { reflection .when(() -> InstabugUtil.getMethod(Class.forName("com.instabug.library.util.InstabugDeprecationLogger"), "setBaseUrl", String.class)) .thenReturn(mSetBaseUrl); + + // reportException mock + Method mCrashReportException = MockReflected.class.getDeclaredMethod("reportException", JSONObject.class, boolean.class, java.util.Map.class, JSONObject.class, IBGNonFatalException.Level.class); + mCrashReportException.setAccessible(true); + reflection + .when(() -> InstabugUtil.getMethod(Class.forName("com.instabug.crash.CrashReporting"), "reportException", JSONObject.class, + boolean.class, java.util.Map.class, JSONObject.class, IBGNonFatalException.Level.class)) + .thenReturn(mCrashReportException); } 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 bfe886b96..ba1c0386d 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,11 @@ package com.instabug.reactlibrary.util; +import com.instabug.crash.models.IBGNonFatalException; + +import org.json.JSONObject; + +import java.util.Map; + /** * Includes fake implementations of methods called by reflection. * Used to verify whether or not a private methods was called. @@ -16,4 +22,9 @@ public static void setCurrentPlatform(int platform) {} * Instabug.util.InstabugDeprecationLogger.setBaseUrl */ public static void setBaseUrl(String baseUrl) {} + /** + * CrashReporting.reportException + */ + public static void reportException(JSONObject exception, boolean isHandled, Map userAttributes, JSONObject fingerPrint, IBGNonFatalException.Level level) {} + } diff --git a/examples/default/ios/InstabugExample.xcodeproj/project.pbxproj b/examples/default/ios/InstabugExample.xcodeproj/project.pbxproj index 25a646edd..f0ea7053f 100644 --- a/examples/default/ios/InstabugExample.xcodeproj/project.pbxproj +++ b/examples/default/ios/InstabugExample.xcodeproj/project.pbxproj @@ -52,6 +52,7 @@ 9A3D962AB03F97E25566779F /* Pods-InstabugExample.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-InstabugExample.debug.xcconfig"; path = "Target Support Files/Pods-InstabugExample/Pods-InstabugExample.debug.xcconfig"; sourceTree = ""; }; BAED0D0441A708AE2390E153 /* libPods-InstabugExample.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-InstabugExample.a"; sourceTree = BUILT_PRODUCTS_DIR; }; BD54B44E2DF85672BB2D4DEE /* Pods-InstabugExample.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-InstabugExample.release.xcconfig"; path = "Target Support Files/Pods-InstabugExample/Pods-InstabugExample.release.xcconfig"; sourceTree = ""; }; + BE3328762BDACE030078249A /* IBGCrashReporting+CP.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "IBGCrashReporting+CP.h"; sourceTree = ""; }; C3C8C24386310A3120006604 /* CrashReportingExampleModule.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CrashReportingExampleModule.m; sourceTree = ""; }; C3C8C784EADC037C5A752B94 /* CrashReportingExampleModule.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CrashReportingExampleModule.h; sourceTree = ""; }; CC3DF8852A1DFC99003E9914 /* InstabugCrashReportingTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = InstabugCrashReportingTests.m; sourceTree = ""; }; @@ -91,6 +92,7 @@ 00E356EF1AD99517003FC87E /* InstabugTests */ = { isa = PBXGroup; children = ( + BE3328752BDACE030078249A /* Util */, CC3DF8892A1DFC99003E9914 /* IBGConstants.h */, CC3DF88D2A1DFC9A003E9914 /* IBGConstants.m */, CC3DF88C2A1DFC99003E9914 /* InstabugAPMTests.m */, @@ -181,6 +183,14 @@ path = Pods; sourceTree = ""; }; + BE3328752BDACE030078249A /* Util */ = { + isa = PBXGroup; + children = ( + BE3328762BDACE030078249A /* IBGCrashReporting+CP.h */, + ); + path = Util; + sourceTree = ""; + }; C3C8C1DDCEA91410F27A3683 /* native */ = { isa = PBXGroup; children = ( diff --git a/examples/default/ios/InstabugTests/InstabugCrashReportingTests.m b/examples/default/ios/InstabugTests/InstabugCrashReportingTests.m index 83f682cd3..0d5ac7348 100644 --- a/examples/default/ios/InstabugTests/InstabugCrashReportingTests.m +++ b/examples/default/ios/InstabugTests/InstabugCrashReportingTests.m @@ -1,15 +1,21 @@ #import #import "Instabug/Instabug.h" #import "InstabugCrashReportingBridge.h" +#import "OCMock/OCMock.h" +#import "Util/IBGCrashReporting+CP.h" @interface InstabugCrashReportingTests : XCTestCase @property (nonatomic, retain) InstabugCrashReportingBridge *bridge; +@property (nonatomic, strong) id mCrashReporting; + @end @implementation InstabugCrashReportingTests - (void)setUp { self.bridge = [[InstabugCrashReportingBridge alloc] init]; + self.mCrashReporting = OCMClassMock([IBGCrashReporting class]); + } - (void)testSetEnabled { @@ -20,4 +26,22 @@ - (void)testSetEnabled { XCTAssertFalse(IBGCrashReporting.enabled); } +- (void)testSendNonFatalErrorJsonCrash { + NSDictionary *jsonCrash = @{}; + NSString *fingerPrint = @"fingerprint"; + RCTPromiseResolveBlock resolve = ^(id result) {}; + RCTPromiseRejectBlock reject = ^(NSString *code, NSString *message, NSError *error) {}; + NSDictionary *userAttributes = @{ @"key" : @"value", }; + IBGNonFatalLevel ibgNonFatalLevel = IBGNonFatalLevelInfo; + + + [self.bridge sendHandledJSCrash:jsonCrash userAttributes:userAttributes fingerprint:fingerPrint nonFatalExceptionLevel:ibgNonFatalLevel resolver:resolve rejecter:reject]; + + OCMVerify([self.mCrashReporting cp_reportNonFatalCrashWithStackTrace:jsonCrash + level:IBGNonFatalLevelInfo + groupingString:fingerPrint + userAttributes:userAttributes + ]); +} + @end diff --git a/examples/default/ios/InstabugTests/Util/IBGCrashReporting+CP.h b/examples/default/ios/InstabugTests/Util/IBGCrashReporting+CP.h new file mode 100644 index 000000000..4229dbcea --- /dev/null +++ b/examples/default/ios/InstabugTests/Util/IBGCrashReporting+CP.h @@ -0,0 +1,13 @@ +#import + + +@interface IBGCrashReporting (CP) + ++ (void)cp_reportFatalCrashWithStackTrace:(NSDictionary*)stackTrace; + ++ (void)cp_reportNonFatalCrashWithStackTrace:(NSDictionary*)stackTrace + level:(IBGNonFatalLevel)level + groupingString:(NSString *)groupingString + userAttributes:(NSDictionary *)userAttributes; +@end + diff --git a/examples/default/src/screens/CrashReportingScreen.tsx b/examples/default/src/screens/CrashReportingScreen.tsx index 24eae5113..c5a53567f 100644 --- a/examples/default/src/screens/CrashReportingScreen.tsx +++ b/examples/default/src/screens/CrashReportingScreen.tsx @@ -1,13 +1,29 @@ -import React from 'react'; -import { Alert, Platform, ScrollView, Text, View } from 'react-native'; +import React, { useState } from 'react'; +import { Alert, Platform, ScrollView, StyleSheet, Text, View } from 'react-native'; -import { CrashReporting } from 'instabug-reactnative'; +import { CrashReporting, NonFatalErrorLevel } from 'instabug-reactnative'; import { ListTile } from '../components/ListTile'; import { Screen } from '../components/Screen'; import { Section } from '../components/Section'; import { PlatformListTile } from '../components/PlatformListTile'; import { NativeExampleCrashReporting } from '../native/NativeCrashReporting'; +import { VerticalListTile } from '../components/VerticalListTile'; +import { Button, VStack } from 'native-base'; +import { InputField } from '../components/InputField'; +import { Select } from '../components/Select'; + +const styles = StyleSheet.create({ + inputWrapper: { + padding: 4, + flex: 1, + }, + + formContainer: { + flexDirection: 'row', + alignItems: 'stretch', + }, +}); export const CrashReportingScreen: React.FC = () => { function throwHandledException(error: Error) { @@ -19,7 +35,7 @@ export const CrashReportingScreen: React.FC = () => { throw error; } catch (err) { if (err instanceof Error) { - CrashReporting.reportError(err).then(() => + CrashReporting.reportError(err, { level: NonFatalErrorLevel.critical }).then(() => Alert.alert(`Crash report for ${error.name} is Sent!`), ); } @@ -46,6 +62,41 @@ export const CrashReportingScreen: React.FC = () => { } } + const [userAttributeKey, setUserAttributeKey] = useState(''); + const [userAttributeValue, setUserAttributeValue] = useState(''); + const [crashNameValue, setCrashNameValue] = useState(''); + const [crashFingerprint, setCrashFingerprint] = useState(''); + const [crashLevelValue, setCrashLevelValue] = useState( + NonFatalErrorLevel.error, + ); + + function sendCrash() { + try { + const error = new Error(crashNameValue); + + throw error; + } catch (err) { + if (err instanceof Error) { + const attrMap: { [k: string]: string } = {}; + attrMap[userAttributeKey] = userAttributeValue; + + const userAttributes: Record = {}; + if (userAttributeKey && userAttributeValue) { + userAttributes[userAttributeKey] = userAttributeValue; + } + const fingerprint = crashFingerprint.length === 0 ? undefined : crashFingerprint; + + CrashReporting.reportError(err, { + userAttributes: userAttributes, + fingerprint: fingerprint, + level: crashLevelValue, + }).then(() => { + Alert.alert(`Crash report for ${crashNameValue} is Sent!`); + }); + } + } + } + return ( @@ -74,6 +125,71 @@ export const CrashReportingScreen: React.FC = () => { title="Throw Handled Native Exception" onPress={() => NativeExampleCrashReporting.sendNativeNonFatal()} /> + + + + setCrashNameValue(key)} + value={crashNameValue} + /> + + + + setUserAttributeKey(key)} + value={userAttributeKey} + /> + + + setUserAttributeValue(value)} + value={userAttributeValue} + /> + + + +