diff --git a/CHANGELOG.md b/CHANGELOG.md index 87c6997572..7004de240b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,11 +9,13 @@ - Add a new string (`StringKey.reproStepsListItemNumberingTitle`) for overriding the repro steps list item (screen) title for consistency between iOS and Android ([#1002](https://github.com/Instabug/Instabug-React-Native/pull/1002)). - Add support for RN version 0.73 by updating the `build.gradle` file with the `namespace` ([#1004](https://github.com/Instabug/Instabug-React-Native/pull/1004)) - Add native-side init API which can be used to catch and report startup crashes on android. ([#1012](https://github.com/Instabug/Instabug-React-Native/pull/1012)) +- Add the new repro steps configuration API `Instabug.setReproStepsConfig` ([#1024](https://github.com/Instabug/Instabug-React-Native/pull/1024)). ### Deprecated - Deprecate the old `StringKey.discardAlertCancel` and `StringKey.discardAlertAction` string keys for overriding the discard alert buttons as they had incosistent behavior between iOS and Android ([#1001](https://github.com/Instabug/Instabug-React-Native/pull/1001)). - Deprecate the old `StringKey.reproStepsListItemNumberingTitle` string key for overriding the repro steps list item (screen) title as it had incosistent behavior between iOS and Android ([#1002](https://github.com/Instabug/Instabug-React-Native/pull/1002)). +- Deprecate `Instabug.setReproStepsMode` in favor of the new `Instabug.setReproStepsConfig` ([#1024](https://github.com/Instabug/Instabug-React-Native/pull/1024)). ## [11.13.0](https://github.com/Instabug/Instabug-React-Native/compare/v11.12.0...v11.13.0) (July 10, 2023) diff --git a/README.md b/README.md index 6099b4247a..ded285e953 100644 --- a/README.md +++ b/README.md @@ -125,7 +125,7 @@ Instabug.reportScreenChange('screenName'); You can disable Repro Steps using the following API: ```javascript -Instabug.setReproStepsMode(Instabug.reproStepsMode.disabled); +Instabug.setReproStepsConfig({ all: ReproStepsMode.disabled }); ``` ## Documentation diff --git a/android/src/main/java/com/instabug/reactlibrary/ArgsRegistry.java b/android/src/main/java/com/instabug/reactlibrary/ArgsRegistry.java index e071ae345c..63e9f1b574 100644 --- a/android/src/main/java/com/instabug/reactlibrary/ArgsRegistry.java +++ b/android/src/main/java/com/instabug/reactlibrary/ArgsRegistry.java @@ -11,6 +11,7 @@ import com.instabug.library.InstabugColorTheme; import com.instabug.library.InstabugCustomTextPlaceHolder.Key; import com.instabug.library.OnSdkDismissCallback.DismissType; +import com.instabug.library.ReproMode; import com.instabug.library.core.plugin.PluginPromptOption; import com.instabug.library.extendedbugreport.ExtendedBugReport; import com.instabug.library.internal.module.InstabugLocale; @@ -43,6 +44,7 @@ public ArrayList getAll(ArrayList keys) { } } + @SuppressWarnings("deprecation") static Map getAll() { return new HashMap() {{ putAll(logLevels); @@ -57,6 +59,7 @@ static Map getAll() { putAll(actionTypes); putAll(extendedBugReportStates); putAll(reproStates); + putAll(reproModes); putAll(sdkLogLevels); putAll(promptOptions); putAll(locales); @@ -140,12 +143,19 @@ static Map getAll() { put("disabled", ExtendedBugReport.State.DISABLED); }}; + @Deprecated() static final ArgsMap reproStates = new ArgsMap() {{ put("reproStepsEnabledWithNoScreenshots", State.ENABLED_WITH_NO_SCREENSHOTS); put("reproStepsEnabled", State.ENABLED); put("reproStepsDisabled", State.DISABLED); }}; + static final ArgsMap reproModes = new ArgsMap() {{ + put("reproStepsEnabledWithNoScreenshots", ReproMode.EnableWithNoScreenshots); + put("reproStepsEnabled", ReproMode.EnableWithScreenshots); + put("reproStepsDisabled", ReproMode.Disable); + }}; + static final ArgsMap sdkLogLevels = new ArgsMap() {{ put("sdkDebugLogsLevelNone", com.instabug.library.LogLevel.NONE); put("sdkDebugLogsLevelError", com.instabug.library.LogLevel.ERROR); diff --git a/android/src/main/java/com/instabug/reactlibrary/RNInstabugReactnativeModule.java b/android/src/main/java/com/instabug/reactlibrary/RNInstabugReactnativeModule.java index 2f8db8e1b2..e12fecf547 100644 --- a/android/src/main/java/com/instabug/reactlibrary/RNInstabugReactnativeModule.java +++ b/android/src/main/java/com/instabug/reactlibrary/RNInstabugReactnativeModule.java @@ -24,7 +24,9 @@ import com.instabug.library.Instabug; import com.instabug.library.InstabugColorTheme; import com.instabug.library.InstabugCustomTextPlaceHolder; +import com.instabug.library.IssueType; import com.instabug.library.LogLevel; +import com.instabug.library.ReproConfigurations; import com.instabug.library.internal.module.InstabugLocale; import com.instabug.library.invocation.InstabugInvocationEvent; import com.instabug.library.logging.InstabugLog; @@ -49,6 +51,8 @@ import java.util.Locale; import java.util.Map; +import javax.annotation.Nullable; + /** * The type Rn instabug reactnative module. @@ -761,12 +765,14 @@ public void run() { }); } - /** + /** * Sets whether user steps tracking is visual, non visual or disabled. * * @param reproStepsMode A string to set user steps tracking to be * enabled, non visual or disabled. */ + @SuppressWarnings("deprecation") + @Deprecated() @ReactMethod public void setReproStepsMode(final String reproStepsMode) { MainThreadHandler.runOnMainThread(new Runnable() { @@ -782,6 +788,28 @@ public void run() { }); } + @ReactMethod + public void setReproStepsConfig(final String bugMode, final String crashMode) { + MainThreadHandler.runOnMainThread(new Runnable() { + @Override + public void run() { + try { + final Integer resolvedBugMode = ArgsRegistry.reproModes.get(bugMode); + final Integer resolvedCrashMode = ArgsRegistry.reproModes.get(crashMode); + + final ReproConfigurations config = new ReproConfigurations.Builder() + .setIssueMode(IssueType.Bug, resolvedBugMode) + .setIssueMode(IssueType.Crash, resolvedCrashMode) + .build(); + + Instabug.setReproConfigurations(config); + } catch (Exception e) { + e.printStackTrace(); + } + } + }); + } + /** * Shows the welcome message in a specific mode. * diff --git a/android/src/test/java/com/instabug/reactlibrary/RNInstabugReactnativeModuleTest.java b/android/src/test/java/com/instabug/reactlibrary/RNInstabugReactnativeModuleTest.java index 2691a2e11c..aaa3d3e953 100644 --- a/android/src/test/java/com/instabug/reactlibrary/RNInstabugReactnativeModuleTest.java +++ b/android/src/test/java/com/instabug/reactlibrary/RNInstabugReactnativeModuleTest.java @@ -17,6 +17,9 @@ import com.instabug.library.Instabug; import com.instabug.library.InstabugColorTheme; import com.instabug.library.InstabugCustomTextPlaceHolder; +import com.instabug.library.IssueType; +import com.instabug.library.ReproConfigurations; +import com.instabug.library.ReproMode; import com.instabug.library.internal.module.InstabugLocale; import com.instabug.library.ui.onboarding.WelcomeMessage; import com.instabug.library.visualusersteps.State; @@ -27,6 +30,7 @@ import org.junit.Before; import org.junit.Test; import org.mockito.Matchers; +import org.mockito.MockedConstruction; import org.mockito.MockedStatic; import org.mockito.Mockito; import org.mockito.internal.verification.VerificationModeFactory; @@ -45,8 +49,10 @@ import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; +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.times; import static org.mockito.Mockito.verify; @@ -283,6 +289,28 @@ public void tearDown() { } } + @Test + public void givenArg$setReproStepsConfig_whenQuery_thenShouldCallNativeApiWithArg() { + String bug = "reproStepsEnabled"; + String crash = "reproStepsDisabled"; + + ReproConfigurations config = mock(ReproConfigurations.class); + MockedConstruction mReproConfigurationsBuilder = mockConstruction(ReproConfigurations.Builder.class, (mock, context) -> { + when(mock.setIssueMode(anyInt(), anyInt())).thenReturn(mock); + when(mock.build()).thenReturn(config); + }); + + rnModule.setReproStepsConfig(bug, crash); + + ReproConfigurations.Builder builder = mReproConfigurationsBuilder.constructed().get(0); + + verify(builder).setIssueMode(IssueType.Bug, ReproMode.EnableWithScreenshots); + verify(builder).setIssueMode(IssueType.Crash, ReproMode.Disable); + verify(builder).build(); + + mockInstabug.verify(() -> Instabug.setReproConfigurations(config)); + } + @Test public void givenArg$showWelcomeMessageWithMode_whenQuery_thenShouldCallNativeApiWithArg() { // given diff --git a/examples/default/ios/InstabugTests/InstabugSampleTests.m b/examples/default/ios/InstabugTests/InstabugSampleTests.m index 0341e7ba30..3ea1eee2f3 100644 --- a/examples/default/ios/InstabugTests/InstabugSampleTests.m +++ b/examples/default/ios/InstabugTests/InstabugSampleTests.m @@ -219,6 +219,17 @@ - (void)testSetReproStepsMode { OCMVerify([mock setReproStepsMode:reproStepsMode]); } +- (void)testSetReproStepsConfig { + id mock = OCMClassMock([Instabug class]); + IBGUserStepsMode bugMode = IBGUserStepsModeDisable; + IBGUserStepsMode crashMode = IBGUserStepsModeEnable; + + [self.instabugBridge setReproStepsConfig:bugMode :crashMode]; + + OCMVerify([mock setReproStepsFor:IBGIssueTypeBug withMode:bugMode]); + OCMVerify([mock setReproStepsFor:IBGIssueTypeCrash withMode:crashMode]); +} + - (void)testSetSdkDebugLogsLevel { id mock = OCMClassMock([Instabug class]); IBGSDKDebugLogsLevel sdkDebugLogsLevel = IBGSDKDebugLogsLevelVerbose; diff --git a/ios/RNInstabug/InstabugReactBridge.h b/ios/RNInstabug/InstabugReactBridge.h index 41e4423723..17f5382ad0 100644 --- a/ios/RNInstabug/InstabugReactBridge.h +++ b/ios/RNInstabug/InstabugReactBridge.h @@ -61,6 +61,8 @@ - (void)setReproStepsMode:(IBGUserStepsMode)reproStepsMode; +- (void)setReproStepsConfig:(IBGUserStepsMode)bugMode:(IBGUserStepsMode)crashMode; + - (void)setUserAttribute:(NSString *)key withValue:(NSString *)value; - (void)getUserAttribute:(NSString *)key diff --git a/ios/RNInstabug/InstabugReactBridge.m b/ios/RNInstabug/InstabugReactBridge.m index b79c749296..a279b77f18 100644 --- a/ios/RNInstabug/InstabugReactBridge.m +++ b/ios/RNInstabug/InstabugReactBridge.m @@ -81,6 +81,11 @@ - (dispatch_queue_t)methodQueue { [Instabug setReproStepsMode:reproStepsMode]; } +RCT_EXPORT_METHOD(setReproStepsConfig:(IBGUserStepsMode)bugMode :(IBGUserStepsMode)crashMode) { + [Instabug setReproStepsFor:IBGIssueTypeBug withMode:bugMode]; + [Instabug setReproStepsFor:IBGIssueTypeCrash withMode:crashMode]; +} + RCT_EXPORT_METHOD(setFileAttachment:(NSString *)fileLocation) { NSURL *url = [NSURL URLWithString:fileLocation]; [Instabug addFileAttachmentWithURL:url]; diff --git a/src/models/ReproConfig.ts b/src/models/ReproConfig.ts new file mode 100644 index 0000000000..63bf5fc601 --- /dev/null +++ b/src/models/ReproConfig.ts @@ -0,0 +1,24 @@ +import type { ReproStepsMode } from '../utils/Enums'; + +export interface ReproConfig { + /** + * Repro steps mode for bug reporting. + * + * @default ReproStepsMode.enabled + */ + bug?: ReproStepsMode; + + /** + * Repro steps mode for crash reporting. + * + * @default ReproStepsMode.enabledWithNoScreenshots + */ + crash?: ReproStepsMode; + + /** + * Repro steps mode for both bug and crash reporting. + * + * When this is set, `bug` and `crash` will be ignored. + */ + all?: ReproStepsMode; +} diff --git a/src/modules/Instabug.ts b/src/modules/Instabug.ts index a5ca02ac35..bd938a3b38 100644 --- a/src/modules/Instabug.ts +++ b/src/modules/Instabug.ts @@ -37,6 +37,7 @@ import InstabugUtils, { } from '../utils/InstabugUtils'; import * as NetworkLogger from './NetworkLogger'; import { captureUnhandledRejections } from '../utils/UnhandledRejectionTracking'; +import type { ReproConfig } from '../models/ReproConfig'; let _currentScreen: string | null = null; let _lastScreen: string | null = null; @@ -365,6 +366,8 @@ export const clearLogs = () => { }; /** + * @deprecated Use {@link setReproStepsConfig} instead. + * * Sets whether user steps tracking is visual, non visual or disabled. * User Steps tracking is enabled by default if it's available * in your current plan. @@ -375,6 +378,40 @@ export const setReproStepsMode = (mode: reproStepsMode | ReproStepsMode) => { NativeInstabug.setReproStepsMode(mode); }; +/** + * Sets the repro steps mode for bugs and crashes. + * + * @param config The repro steps config. + * + * @example + * ```js + * Instabug.setReproStepsConfig({ + * bug: ReproStepsMode.enabled, + * crash: ReproStepsMode.disabled, + * }); + * ``` + */ +export const setReproStepsConfig = (config: ReproConfig) => { + let bug = config.bug ?? ReproStepsMode.enabled; + let crash = config.crash ?? ReproStepsMode.enabledWithNoScreenshots; + + if (config.all != null) { + bug = config.all; + crash = config.all; + } + + // There's an issue with crashes repro steps with screenshots in the iOS SDK + // at the moment, so we'll map enabled with screenshots to enabled with no + // screenshots to avoid storing the images on disk if it's not needed until + // this issue is fixed in a future version. + if (Platform.OS === 'ios' && crash === ReproStepsMode.enabled) { + /* istanbul ignore next */ + crash = ReproStepsMode.enabledWithNoScreenshots; + } + + NativeInstabug.setReproStepsConfig(bug, crash); +}; + /** * Sets user attribute to overwrite it's value or create a new one if it doesn't exist. * diff --git a/src/native/NativeInstabug.ts b/src/native/NativeInstabug.ts index 238878f526..23345d0f0f 100644 --- a/src/native/NativeInstabug.ts +++ b/src/native/NativeInstabug.ts @@ -50,7 +50,9 @@ export interface InstabugNativeModule extends NativeModule { setNetworkLoggingEnabled(isEnabled: boolean): void; // Repro Steps APIs // + /** @deprecated */ setReproStepsMode(mode: ReproStepsMode | reproStepsMode): void; + setReproStepsConfig(bugMode: ReproStepsMode, crashMode: ReproStepsMode): void; setTrackUserSteps(isEnabled: boolean): void; reportScreenChange(firstScreen: string): void; addPrivateView(nativeTag: number | null): void; diff --git a/test/mocks/mockInstabug.ts b/test/mocks/mockInstabug.ts index fdf755cba2..b7c1acfd43 100644 --- a/test/mocks/mockInstabug.ts +++ b/test/mocks/mockInstabug.ts @@ -1,11 +1,16 @@ import type { InstabugNativeModule } from '../../src/native/NativeInstabug'; +/** + * A fake implementation of the NativeConstants object using `Proxy` that + * returns the name of the property as its value instead of hardcoding all + * the constants. + */ +const fakeNativeConstants = new Proxy({}, { get: (_, prop) => prop }); + const mockInstabug: InstabugNativeModule = { + getConstants: jest.fn().mockReturnValue(fakeNativeConstants), addListener: jest.fn(), removeListeners: jest.fn(), - getConstants: jest.fn().mockReturnValue({ - reproStepsListItemNumberingTitle: 'reproStepsListItemNumberingTitle', - }), setEnabled: jest.fn(), init: jest.fn(), setUserData: jest.fn(), @@ -29,6 +34,7 @@ const mockInstabug: InstabugNativeModule = { logDebug: jest.fn(), clearLogs: jest.fn(), setReproStepsMode: jest.fn(), + setReproStepsConfig: jest.fn(), setSdkDebugLogsLevel: jest.fn(), setUserAttribute: jest.fn(), getUserAttribute: jest.fn(), diff --git a/test/modules/Instabug.spec.ts b/test/modules/Instabug.spec.ts index a75ab034cf..320460c761 100644 --- a/test/modules/Instabug.spec.ts +++ b/test/modules/Instabug.spec.ts @@ -419,6 +419,47 @@ describe('Instabug Module', () => { expect(NativeInstabug.setReproStepsMode).toBeCalledWith(mode); }); + it('setReproStepsConfig should call the native setReproStepsConfig', () => { + Platform.OS = 'android'; + + const bug = ReproStepsMode.disabled; + const crash = ReproStepsMode.enabled; + const config = { bug, crash }; + + Instabug.setReproStepsConfig(config); + + expect(NativeInstabug.setReproStepsConfig).toBeCalledTimes(1); + expect(NativeInstabug.setReproStepsConfig).toBeCalledWith(bug, crash); + }); + + it('setReproStepsConfig should prioritize `all` over `bug` and `crash`', () => { + Platform.OS = 'android'; + + const bug = ReproStepsMode.disabled; + const crash = ReproStepsMode.enabled; + const all = ReproStepsMode.enabledWithNoScreenshots; + const config = { all, bug, crash }; + + Instabug.setReproStepsConfig(config); + + expect(NativeInstabug.setReproStepsConfig).toBeCalledTimes(1); + expect(NativeInstabug.setReproStepsConfig).toBeCalledWith(all, all); + }); + + it('setReproStepsConfig should use defaults for `bug` and `crash`', () => { + Platform.OS = 'android'; + + const config = {}; + + Instabug.setReproStepsConfig(config); + + expect(NativeInstabug.setReproStepsConfig).toBeCalledTimes(1); + expect(NativeInstabug.setReproStepsConfig).toBeCalledWith( + ReproStepsMode.enabled, + ReproStepsMode.enabledWithNoScreenshots, + ); + }); + it('should call the native method setSdkDebugLogsLevel on iOS', () => { const debugLevel = Instabug.sdkDebugLogsLevel.sdkDebugLogsLevelVerbose;