Skip to content

Commit 61aa154

Browse files
- add proactive bug reporting
1 parent 2b094ca commit 61aa154

File tree

15 files changed

+337
-101
lines changed

15 files changed

+337
-101
lines changed

CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,11 @@
11
# Changelog
22

3+
## [Unreleased](https://github.com/Instabug/Instabug-React-Native/compare/v14.1.0...dev)
4+
5+
### Added
6+
7+
- Add support proactive bug-reporting ([#1354](https://github.com/Instabug/Instabug-React-Native/pull/1354))
8+
39
## [14.1.0](https://github.com/Instabug/Instabug-React-Native/compare/v14.0.0...v14.1.0) (January 2, 2025)
410

511
### Added

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

Lines changed: 49 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
import com.facebook.react.bridge.ReadableArray;
1111
import com.facebook.react.bridge.WritableMap;
1212
import com.instabug.bug.BugReporting;
13+
import com.instabug.bug.ProactiveReportingConfigs;
1314
import com.instabug.bug.invocation.Option;
1415
import com.instabug.library.Feature;
1516
import com.instabug.library.OnSdkDismissCallback;
@@ -49,6 +50,7 @@ public void removeListeners(Integer count) {
4950

5051
/**
5152
* Enable or disable all BugReporting related features.
53+
*
5254
* @param isEnabled boolean indicating enabled or disabled.
5355
*/
5456
@ReactMethod
@@ -116,6 +118,7 @@ public void run() {
116118

117119
/**
118120
* Enables or disables view hierarchy in the dashboard.
121+
*
119122
* @param isEnabled boolean indicating enabled or disabled.
120123
*/
121124
@ReactMethod
@@ -160,10 +163,10 @@ public void run() {
160163
/**
161164
* Sets whether attachments in bug reporting and in-app messaging are enabled or not.
162165
*
163-
* @param screenshot A boolean to enable or disable screenshot attachments.
164-
* @param {boolean} extraScreenShot A boolean to enable or disable extra screenshot attachments.
165-
* @param {boolean} galleryImage A boolean to enable or disable gallery image attachments.
166-
* @param {boolean} screenRecording A boolean to enable or disable screen recording attachments.
166+
* @param screenshot A boolean to enable or disable screenshot attachments.
167+
* @param {boolean} extraScreenShot A boolean to enable or disable extra screenshot attachments.
168+
* @param {boolean} galleryImage A boolean to enable or disable gallery image attachments.
169+
* @param {boolean} screenRecording A boolean to enable or disable screen recording attachments.
167170
*/
168171
@ReactMethod
169172
public void setEnabledAttachmentTypes(final boolean screenshot, final boolean extraScreenshot, final boolean
@@ -235,7 +238,7 @@ public void run() {
235238
* UI changes before the SDK's UI is shown.
236239
*
237240
* @param onInvokeHandler - A callback that gets executed before
238-
* invoking the SDK
241+
* invoking the SDK
239242
*/
240243
@ReactMethod
241244
public void setOnInvokeHandler(final Callback onInvokeHandler) {
@@ -258,7 +261,8 @@ public void onInvoke() {
258261

259262
/**
260263
* Sets the position of the Instabug floating button on the screen.
261-
* @param floatingButtonEdge left or right edge of the screen.
264+
*
265+
* @param floatingButtonEdge left or right edge of the screen.
262266
* @param floatingButtonOffset integer offset from the left or right edge of the screen.
263267
*/
264268
@ReactMethod
@@ -280,7 +284,7 @@ public void run() {
280284
* UI changes after the SDK's UI is dismissed.
281285
*
282286
* @param handler - A callback to get executed after
283-
* dismissing the SDK.
287+
* dismissing the SDK.
284288
*/
285289
@ReactMethod
286290
public void setOnSDKDismissedHandler(final Callback handler) {
@@ -328,6 +332,7 @@ public void run() {
328332

329333
/**
330334
* Sets the enabled report types to be shown in the prompt. Bug or Feedback or both.
335+
*
331336
* @param types
332337
* @see BugReporting.ReportType
333338
*/
@@ -356,8 +361,9 @@ public void run() {
356361

357362
/**
358363
* Shows a bug or feedback report with optional options.
364+
*
359365
* @param reportType Bug or Feedback.
360-
* @param options array of options
366+
* @param options array of options
361367
* @see BugReporting.ReportType
362368
* @see Option
363369
*/
@@ -375,11 +381,12 @@ public void run() {
375381
}
376382

377383
/**
378-
* Adds a disclaimer text within the bug reporting form, which can include hyperlinked text.
379-
* @param text String text.
380-
*/
384+
* Adds a disclaimer text within the bug reporting form, which can include hyperlinked text.
385+
*
386+
* @param text String text.
387+
*/
381388
@ReactMethod
382-
public void setDisclaimerText(final String text){
389+
public void setDisclaimerText(final String text) {
383390
MainThreadHandler.runOnMainThread(new Runnable() {
384391
@Override
385392
public void run() {
@@ -389,12 +396,13 @@ public void run() {
389396
}
390397

391398
/**
392-
* Sets a minimum number of characters as a requirement for the comments field in the different report types.
393-
* @param limit int number of characters.
394-
* @param reportTypes (Optional) Array of reportType. If it's not passed, the limit will apply to all report types.
395-
*/
399+
* Sets a minimum number of characters as a requirement for the comments field in the different report types.
400+
*
401+
* @param limit int number of characters.
402+
* @param reportTypes (Optional) Array of reportType. If it's not passed, the limit will apply to all report types.
403+
*/
396404
@ReactMethod
397-
public void setCommentMinimumCharacterCount(final int limit, final ReadableArray reportTypes){
405+
public void setCommentMinimumCharacterCount(final int limit, final ReadableArray reportTypes) {
398406
MainThreadHandler.runOnMainThread(new Runnable() {
399407
@SuppressLint("WrongConstant")
400408
@Override
@@ -415,4 +423,28 @@ public void run() {
415423
}
416424
});
417425
}
426+
427+
/**
428+
* prompts end users to submit their feedback after our SDK automatically detects a frustrating experience.
429+
*
430+
* @param enabled controls the state of the feature
431+
* @param modalDelayAfterDetection controls the time gap between detecting a frustrating experience
432+
* @param gapBetweenModals controls the time gap between showing 2 proactive reporting dialogs in seconds
433+
*/
434+
@ReactMethod
435+
public void setProactiveReportingConfigurations(final boolean enabled, final int gapBetweenModals, final int modalDelayAfterDetection) {
436+
MainThreadHandler.runOnMainThread(new Runnable() {
437+
@Override
438+
public void run() {
439+
ProactiveReportingConfigs configs = new ProactiveReportingConfigs.Builder()
440+
.setGapBetweenModals(gapBetweenModals) // Time in seconds
441+
.setModalDelayAfterDetection(modalDelayAfterDetection) // Time in seconds
442+
.isEnabled(enabled) //Enable/disable
443+
.build();
444+
BugReporting.setProactiveReportingConfigurations(configs);
445+
446+
447+
}
448+
});
449+
}
418450
}

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

Lines changed: 49 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
import com.facebook.react.bridge.ReactApplicationContext;
99
import com.facebook.react.bridge.WritableMap;
1010
import com.instabug.bug.BugReporting;
11+
import com.instabug.bug.ProactiveReportingConfigs;
1112
import com.instabug.library.Feature;
1213
import com.instabug.library.OnSdkDismissCallback;
1314
import com.instabug.library.extendedbugreport.ExtendedBugReport;
@@ -21,6 +22,7 @@
2122
import org.junit.After;
2223
import org.junit.Before;
2324
import org.junit.Test;
25+
import org.mockito.ArgumentCaptor;
2426
import org.mockito.MockedStatic;
2527
import org.mockito.internal.verification.VerificationModeFactory;
2628
import org.mockito.invocation.InvocationOnMock;
@@ -30,7 +32,10 @@
3032
import java.util.concurrent.Executors;
3133
import java.util.concurrent.ScheduledExecutorService;
3234

35+
import static org.junit.Assert.assertEquals;
36+
import static org.junit.Assert.assertTrue;
3337
import static org.mockito.ArgumentMatchers.any;
38+
import static org.mockito.ArgumentMatchers.argThat;
3439
import static org.mockito.Mockito.doAnswer;
3540
import static org.mockito.Mockito.mock;
3641
import static org.mockito.Mockito.mockStatic;
@@ -45,9 +50,9 @@ public class RNInstabugBugReportingModuleTest {
4550

4651
// Mock Objects
4752
private MockedStatic<Looper> mockLooper;
48-
private MockedStatic <MainThreadHandler> mockMainThreadHandler;
49-
private MockedStatic <BugReporting> mockBugReporting;
50-
private MockedStatic <InstabugUtil> mockInstabugUtil;
53+
private MockedStatic<MainThreadHandler> mockMainThreadHandler;
54+
private MockedStatic<BugReporting> mockBugReporting;
55+
private MockedStatic<InstabugUtil> mockInstabugUtil;
5156

5257
@Before
5358
public void mockMainThreadHandler() throws Exception {
@@ -72,6 +77,7 @@ public Boolean answer(InvocationOnMock invocation) throws Throwable {
7277
doAnswer(handlerPostAnswer).when(MainThreadHandler.class);
7378
MainThreadHandler.runOnMainThread(any(Runnable.class));
7479
}
80+
7581
@After
7682
public void tearDown() {
7783
// Remove static mocks
@@ -91,7 +97,7 @@ public void tearDown() {
9197
// when
9298
bugReportingModule.setShakingThresholdForAndroid(shakingThreshold);
9399
// then
94-
verify(BugReporting.class,VerificationModeFactory.times(1));
100+
verify(BugReporting.class, VerificationModeFactory.times(1));
95101
BugReporting.setShakingThreshold(shakingThreshold);
96102
}
97103

@@ -102,7 +108,7 @@ public void tearDown() {
102108
// when
103109
bugReportingModule.setEnabled(false);
104110
// then
105-
verify(BugReporting.class,VerificationModeFactory.times(1));
111+
verify(BugReporting.class, VerificationModeFactory.times(1));
106112

107113
BugReporting.setState(Feature.State.DISABLED);
108114
}
@@ -114,7 +120,7 @@ public void tearDown() {
114120
// when
115121
bugReportingModule.setEnabled(true);
116122
// then
117-
verify(BugReporting.class,VerificationModeFactory.times(1));
123+
verify(BugReporting.class, VerificationModeFactory.times(1));
118124

119125
BugReporting.setState(Feature.State.ENABLED);
120126
}
@@ -126,7 +132,7 @@ public void tearDown() {
126132
// when
127133
bugReportingModule.setAutoScreenRecordingEnabled(true);
128134
// then
129-
verify(BugReporting.class,VerificationModeFactory.times(1));
135+
verify(BugReporting.class, VerificationModeFactory.times(1));
130136

131137
BugReporting.setAutoScreenRecordingEnabled(true);
132138
}
@@ -138,7 +144,7 @@ public void tearDown() {
138144
// when
139145
bugReportingModule.setViewHierarchyEnabled(true);
140146
// then
141-
verify(BugReporting.class,VerificationModeFactory.times(1));
147+
verify(BugReporting.class, VerificationModeFactory.times(1));
142148

143149
BugReporting.setViewHierarchyState(Feature.State.ENABLED);
144150
}
@@ -150,7 +156,7 @@ public void tearDown() {
150156
// when
151157
bugReportingModule.setViewHierarchyEnabled(false);
152158
// then
153-
verify(BugReporting.class,VerificationModeFactory.times(1));
159+
verify(BugReporting.class, VerificationModeFactory.times(1));
154160

155161
BugReporting.setViewHierarchyState(Feature.State.DISABLED);
156162
}
@@ -162,7 +168,7 @@ public void tearDown() {
162168
// when
163169
bugReportingModule.setEnabledAttachmentTypes(true, true, false, true);
164170
// then
165-
verify(BugReporting.class,VerificationModeFactory.times(1));
171+
verify(BugReporting.class, VerificationModeFactory.times(1));
166172

167173
BugReporting.setAttachmentTypesEnabled(true, true, false, true);
168174
}
@@ -179,7 +185,7 @@ public void tearDown() {
179185

180186
// then
181187
for (ExtendedBugReport.State extendedBugReportMode : args.values()) {
182-
verify(BugReporting.class,VerificationModeFactory.times(1));
188+
verify(BugReporting.class, VerificationModeFactory.times(1));
183189
BugReporting.setExtendedBugReportState(extendedBugReportMode);
184190
}
185191
}
@@ -198,7 +204,7 @@ public void tearDown() {
198204
bugReportingModule.setInvocationEvents(actualArray);
199205

200206
// then
201-
verify(BugReporting.class,VerificationModeFactory.times(1));
207+
verify(BugReporting.class, VerificationModeFactory.times(1));
202208
BugReporting.setInvocationEvents(args.values().toArray(new InstabugInvocationEvent[0]));
203209
}
204210

@@ -215,11 +221,11 @@ public void tearDown() {
215221
bugReportingModule.setOptions(actualArray);
216222

217223
// then
218-
verify(BugReporting.class,VerificationModeFactory.times(1));
224+
verify(BugReporting.class, VerificationModeFactory.times(1));
219225
int option1 = args.get(keysArray[0]);
220226
int option2 = args.get(keysArray[1]);
221227
BugReporting.setOptions(option1);
222-
verify(BugReporting.class,VerificationModeFactory.times(1));
228+
verify(BugReporting.class, VerificationModeFactory.times(1));
223229
BugReporting.setOptions(option2);
224230
}
225231

@@ -255,7 +261,8 @@ public Object answer(InvocationOnMock invocation) {
255261
((OnSdkDismissCallback) invocation.getArguments()[0])
256262
.call(OnSdkDismissCallback.DismissType.CANCEL, OnSdkDismissCallback.ReportType.BUG);
257263
return null;
258-
}});
264+
}
265+
});
259266
bugReportingModule.setOnSDKDismissedHandler(null);
260267

261268
// then
@@ -280,7 +287,7 @@ public Object answer(InvocationOnMock invocation) {
280287
bugReportingModule.setReportTypes(actualArray);
281288

282289
// then
283-
verify(BugReporting.class,VerificationModeFactory.times(1));
290+
verify(BugReporting.class, VerificationModeFactory.times(1));
284291

285292
int type1 = args.get(keysArray[0]);
286293
int type2 = args.get(keysArray[1]);
@@ -295,9 +302,9 @@ public Object answer(InvocationOnMock invocation) {
295302

296303
// when
297304
bugReportingModule.setVideoRecordingFloatingButtonPosition(keysArray[0]);
298-
305+
299306
// then
300-
verify(BugReporting.class,VerificationModeFactory.times(1));
307+
verify(BugReporting.class, VerificationModeFactory.times(1));
301308
InstabugVideoRecordingButtonPosition position = (InstabugVideoRecordingButtonPosition) args.get(keysArray[0]);
302309
BugReporting.setVideoRecordingFloatingButtonPosition(position);
303310
}
@@ -319,13 +326,13 @@ public Object answer(InvocationOnMock invocation) {
319326
// then
320327
int option1 = optionsArgs.get(keysArray[0]);
321328
int option2 = optionsArgs.get(keysArray[1]);
322-
verify(BugReporting.class,VerificationModeFactory.times(1));
329+
verify(BugReporting.class, VerificationModeFactory.times(1));
323330

324331
BugReporting.setOptions(option1);
325-
verify(BugReporting.class,VerificationModeFactory.times(1));
332+
verify(BugReporting.class, VerificationModeFactory.times(1));
326333

327334
BugReporting.setOptions(option2);
328-
verify(BugReporting.class,VerificationModeFactory.times(1));
335+
verify(BugReporting.class, VerificationModeFactory.times(1));
329336

330337
BugReporting.show(reportTypeArgs.get(reportTypeKeys[0]));
331338
}
@@ -359,7 +366,27 @@ public Object answer(InvocationOnMock invocation) {
359366
// then
360367
verify(BugReporting.class, VerificationModeFactory.times(1));
361368
int type1 = args.get(keysArray[0]);
362-
369+
363370
BugReporting.setCommentMinimumCharacterCount(count, type1);
364371
}
372+
373+
@Test
374+
public void testSetProactiveReportingConfigurations() {
375+
// given
376+
boolean enabled = true;
377+
int gapBetweekDialogs = 20;
378+
int modeDelay = 30;
379+
380+
// when
381+
bugReportingModule.setProactiveReportingConfigurations(enabled, gapBetweekDialogs, modeDelay);
382+
383+
// then
384+
mockBugReporting.verify(() -> BugReporting.setProactiveReportingConfigurations(argThat(config ->
385+
config.getModalsGap() == gapBetweekDialogs &&
386+
config.getDetectionGap() == modeDelay &&
387+
config.isEnabled() == enabled
388+
)));
389+
390+
391+
}
365392
}

examples/default/ios/InstabugExample.xcodeproj/xcshareddata/xcschemes/InstabugExample.xcscheme

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -52,9 +52,9 @@
5252
</Testables>
5353
</TestAction>
5454
<LaunchAction
55-
buildConfiguration = "Debug"
56-
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
57-
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
55+
buildConfiguration = "Release"
56+
selectedDebuggerIdentifier = ""
57+
selectedLauncherIdentifier = "Xcode.IDEFoundation.Launcher.PosixSpawn"
5858
launchStyle = "0"
5959
useCustomWorkingDirectory = "NO"
6060
ignoresPersistentStateOnLaunch = "NO"

0 commit comments

Comments
 (0)