Skip to content

Commit 9bf30f0

Browse files
Abd ElHamid NasserHeshamMegid
authored andcommitted
refactor: extract shared crash reporting logic (#1010)
Jira ID: MOB-12538 - refactor: extract duplicate crash reporting code - chore: Resolve utils lint issues - test: add sendCrashReport tests in utils - test: refactor tests validating sendCrashReport - chore: exclude local history from .gitignore - test: refactor errorMock to use TypeError
1 parent ebd7a14 commit 9bf30f0

File tree

6 files changed

+92
-70
lines changed

6 files changed

+92
-70
lines changed

.gitignore

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,3 +60,7 @@ android/keystores/debug.keystore
6060

6161
# Bundle artifact
6262
*.jsbundle
63+
/examples/default/ios/main.jsbundle.map
64+
65+
# Vscode local history
66+
.history/

src/modules/CrashReporting.ts

Lines changed: 1 addition & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,5 @@
1-
import { Platform } from 'react-native';
21
import type { ExtendedError } from 'react-native/Libraries/Core/Devtools/parseErrorStack';
32

4-
import type { CrashData } from '../native/NativeCrashReporting';
53
import { NativeCrashReporting } from '../native/NativeCrashReporting';
64
import InstabugUtils from '../utils/InstabugUtils';
75

@@ -30,20 +28,5 @@ export const reportJSException = (error: any) => {
3028
* @param error Error object to be sent to Instabug's servers
3129
*/
3230
export const reportError = (error: ExtendedError) => {
33-
const jsStackTrace = InstabugUtils.getStackTrace(error);
34-
35-
const jsonObject: CrashData = {
36-
message: error.name + ' - ' + error.message,
37-
e_message: error.message,
38-
e_name: error.name,
39-
os: Platform.OS,
40-
platform: 'react_native',
41-
exception: jsStackTrace,
42-
};
43-
44-
if (Platform.OS === 'android') {
45-
NativeCrashReporting.sendHandledJSCrash(JSON.stringify(jsonObject));
46-
} else {
47-
NativeCrashReporting.sendHandledJSCrash(jsonObject);
48-
}
31+
InstabugUtils.sendCrashReport(error, NativeCrashReporting.sendHandledJSCrash);
4932
};

src/utils/InstabugUtils.ts

Lines changed: 36 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -68,23 +68,7 @@ export const captureJsErrors = () => {
6868
const originalErrorHandler = ErrorUtils.getGlobalHandler();
6969

7070
const instabugErrorHandler: ErrorHandlerCallback = (err) => {
71-
const jsStackTrace = getStackTrace(err);
72-
73-
// JSON object to be sent to the native SDK
74-
const jsonObject: CrashData = {
75-
message: err.name + ' - ' + err.message,
76-
e_message: err.message,
77-
e_name: err.name,
78-
os: Platform.OS,
79-
platform: 'react_native',
80-
exception: jsStackTrace,
81-
};
82-
83-
if (Platform.OS === 'android') {
84-
NativeCrashReporting.sendJSCrash(JSON.stringify(jsonObject));
85-
} else {
86-
NativeCrashReporting.sendJSCrash(jsonObject);
87-
}
71+
sendCrashReport(err, NativeCrashReporting.sendJSCrash);
8872
};
8973

9074
ErrorUtils.setGlobalHandler((err, isFatal) => {
@@ -124,6 +108,40 @@ export const invokeDeprecatedCallback = <T>(callback?: (arg: T) => void, arg?: T
124108
}
125109
};
126110

111+
/**
112+
* Sends crash report to Instabug's servers based on @param sendFunction
113+
*
114+
* @param error Error object to be sent to Instabug's servers
115+
* @param remoteSenderCallback Function to send the crash report to Instabug's servers
116+
*
117+
* @example
118+
* `sendCrashReport(error, NativeCrashReporting.sendHandledJSCrash);`
119+
* or
120+
* `sendCrashReport(error, NativeCrashReporting.sendJSCrash);`
121+
*
122+
*/
123+
export function sendCrashReport(
124+
error: ExtendedError,
125+
remoteSenderCallback: (json: CrashData | string) => void,
126+
) {
127+
const jsStackTrace = getStackTrace(error);
128+
129+
const jsonObject: CrashData = {
130+
message: error.name + ' - ' + error.message,
131+
e_message: error.message,
132+
e_name: error.name,
133+
os: Platform.OS,
134+
platform: 'react_native',
135+
exception: jsStackTrace,
136+
};
137+
138+
if (Platform.OS === 'android') {
139+
remoteSenderCallback(JSON.stringify(jsonObject));
140+
} else {
141+
remoteSenderCallback(jsonObject);
142+
}
143+
}
144+
127145
export default {
128146
parseErrorStack,
129147
captureJsErrors,
@@ -132,4 +150,5 @@ export default {
132150
getStackTrace,
133151
stringifyIfNotString,
134152
invokeDeprecatedCallback,
153+
sendCrashReport,
135154
};

test/mocks/mockInstabugUtils.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ jest.mock('../../src/utils/InstabugUtils', () => {
77
captureJsErrors: jest.fn(),
88
getActiveRouteName: jest.fn(),
99
stringifyIfNotString: jest.fn(),
10+
sendCrashReport: jest.fn(),
1011
getStackTrace: jest.fn().mockReturnValue('javascriptStackTrace'),
1112
getFullRoute: jest.fn().mockImplementation(() => 'ScreenName'),
1213
};

test/modules/CrashReporting.spec.ts

Lines changed: 8 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,8 @@
11
import '../mocks/mockInstabugUtils';
22

3-
import { Platform } from 'react-native';
4-
53
import * as CrashReporting from '../../src/modules/CrashReporting';
64
import { NativeCrashReporting } from '../../src/native/NativeCrashReporting';
5+
import InstabugUtils from '../../src/utils/InstabugUtils';
76

87
describe('CrashReporting Module', () => {
98
it('should call the native method setEnabled', () => {
@@ -13,39 +12,14 @@ describe('CrashReporting Module', () => {
1312
expect(NativeCrashReporting.setEnabled).toBeCalledWith(true);
1413
});
1514

16-
it('should call the native method sendHandledJSCrash with JSON object when platform is iOS', () => {
17-
Platform.OS = 'ios';
18-
const error = { name: 'TypeError', message: 'Invalid type' };
15+
it('should call the native method sendCrashReporting with JSON object and sendHandledJsCrash as a callback', () => {
16+
const error = new TypeError('Invalid type');
1917
CrashReporting.reportError(error);
2018

21-
const expected = {
22-
message: 'TypeError - Invalid type',
23-
e_message: 'Invalid type',
24-
e_name: 'TypeError',
25-
os: 'ios',
26-
platform: 'react_native',
27-
exception: 'javascriptStackTrace',
28-
};
29-
30-
expect(NativeCrashReporting.sendHandledJSCrash).toBeCalledTimes(1);
31-
expect(NativeCrashReporting.sendHandledJSCrash).toBeCalledWith(expected);
32-
});
33-
34-
it('should call the native method sendHandledJSCrash with stringified JSON object when platform is Android', () => {
35-
Platform.OS = 'android';
36-
const error = { name: 'TypeError', message: 'Invalid type' };
37-
CrashReporting.reportError(error);
38-
39-
const expected = JSON.stringify({
40-
message: 'TypeError - Invalid type',
41-
e_message: 'Invalid type',
42-
e_name: 'TypeError',
43-
os: 'android',
44-
platform: 'react_native',
45-
exception: 'javascriptStackTrace',
46-
});
47-
48-
expect(NativeCrashReporting.sendHandledJSCrash).toBeCalledTimes(1);
49-
expect(NativeCrashReporting.sendHandledJSCrash).toBeCalledWith(expected);
19+
expect(InstabugUtils.sendCrashReport).toBeCalledTimes(1);
20+
expect(InstabugUtils.sendCrashReport).toBeCalledWith(
21+
error,
22+
NativeCrashReporting.sendHandledJSCrash,
23+
);
5024
});
5125
});

test/utils/InstabugUtils.spec.ts

Lines changed: 42 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import parseErrorStackLib from 'react-native/Libraries/Core/Devtools/parseErrorS
77
import * as Instabug from '../../src/modules/Instabug';
88
import { NativeCrashReporting } from '../../src/native/NativeCrashReporting';
99
import { InvocationEvent } from '../../src/utils/Enums';
10-
import InstabugUtils from '../../src/utils/InstabugUtils';
10+
import InstabugUtils, { getStackTrace, sendCrashReport } from '../../src/utils/InstabugUtils';
1111

1212
describe('Test global error handler', () => {
1313
beforeEach(() => {
@@ -220,4 +220,45 @@ describe('Instabug Utils', () => {
220220
mockDev.mockRestore();
221221
consoleWarnSpy.mockRestore();
222222
});
223+
224+
it('should call remoteSenderCallback with the correct JSON object on Android', () => {
225+
const remoteSenderCallback = NativeCrashReporting.sendHandledJSCrash;
226+
Platform.OS = 'android';
227+
const errorMock = new TypeError('Invalid type');
228+
const jsStackTrace = getStackTrace(errorMock);
229+
230+
sendCrashReport(errorMock, remoteSenderCallback);
231+
232+
const expectedMap = {
233+
message: 'TypeError - Invalid type',
234+
e_message: 'Invalid type',
235+
e_name: 'TypeError',
236+
os: 'android',
237+
platform: 'react_native',
238+
exception: jsStackTrace,
239+
};
240+
const expectedJsonObject = JSON.stringify(expectedMap);
241+
expect(remoteSenderCallback).toHaveBeenCalledTimes(1);
242+
expect(remoteSenderCallback).toHaveBeenCalledWith(expectedJsonObject);
243+
});
244+
245+
it('should call remoteSenderCallback with the correct JSON object on iOS', () => {
246+
const remoteSenderCallback = NativeCrashReporting.sendHandledJSCrash;
247+
Platform.OS = 'ios';
248+
const errorMock = new TypeError('Invalid type');
249+
const jsStackTrace = getStackTrace(errorMock);
250+
251+
sendCrashReport(errorMock, remoteSenderCallback);
252+
253+
const expectedMap = {
254+
message: 'TypeError - Invalid type',
255+
e_message: 'Invalid type',
256+
e_name: 'TypeError',
257+
os: 'ios',
258+
platform: 'react_native',
259+
exception: jsStackTrace,
260+
};
261+
expect(remoteSenderCallback).toHaveBeenCalledTimes(1);
262+
expect(remoteSenderCallback).toHaveBeenCalledWith(expectedMap);
263+
});
223264
});

0 commit comments

Comments
 (0)