Skip to content

Commit faed637

Browse files
committed
Sentry: Feat - Add DEV-only crash test buttons
1 parent 00fb9ef commit faed637

8 files changed

Lines changed: 132 additions & 0 deletions

File tree

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
package com.bitpay.wallet;
2+
3+
import android.os.Handler;
4+
import android.os.Looper;
5+
6+
import com.facebook.react.bridge.ReactApplicationContext;
7+
import com.facebook.react.bridge.ReactContextBaseJavaModule;
8+
import com.facebook.react.bridge.ReactMethod;
9+
10+
public class CrashTestModule extends ReactContextBaseJavaModule {
11+
public CrashTestModule(ReactApplicationContext reactContext) {
12+
super(reactContext);
13+
}
14+
15+
@Override
16+
public String getName() {
17+
return "CrashTest";
18+
}
19+
20+
@ReactMethod
21+
public void crash() {
22+
new Handler(Looper.getMainLooper()).post(new Runnable() {
23+
@Override
24+
public void run() {
25+
throw new RuntimeException("BitPay test: native crash (Sentry)");
26+
}
27+
});
28+
}
29+
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
package com.bitpay.wallet;
2+
3+
import com.facebook.react.ReactPackage;
4+
import com.facebook.react.bridge.NativeModule;
5+
import com.facebook.react.bridge.ReactApplicationContext;
6+
import com.facebook.react.uimanager.ViewManager;
7+
8+
import java.util.ArrayList;
9+
import java.util.Collections;
10+
import java.util.List;
11+
12+
public class CrashTestPackage implements ReactPackage {
13+
14+
@Override
15+
public List<ViewManager> createViewManagers(ReactApplicationContext reactContext) {
16+
return Collections.emptyList();
17+
}
18+
19+
@Override
20+
public List<NativeModule> createNativeModules(ReactApplicationContext reactContext) {
21+
List<NativeModule> modules = new ArrayList<>();
22+
23+
modules.add(new CrashTestModule(reactContext));
24+
25+
return modules;
26+
}
27+
}

android/app/src/main/java/com/bitpay/wallet/MainApplication.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ class MainApplication : Application(), ReactApplication {
2828
add(SilentPushPackage())
2929
add(TimerPackage())
3030
add(InAppMessagePackage())
31+
add(CrashTestPackage())
3132
},
3233
)
3334
}

ios/BitPayApp.xcodeproj/project.pbxproj

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
5BCF1C352740B8B2006D872E /* BootSplash.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 5BCF1C342740B8B2006D872E /* BootSplash.storyboard */; };
2222
5BEA4BF02838402700AB2121 /* RCTPaymentPass.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5BEA4BEF2838402700AB2121 /* RCTPaymentPass.mm */; };
2323
6D615C4929725FD70063DB8E /* RCTTimer.mm in Sources */ = {isa = PBXBuildFile; fileRef = 6D615C4729725FD70063DB8E /* RCTTimer.mm */; };
24+
CA5471E01A2B3C4D5E6F7001 /* RCTCrashTest.mm in Sources */ = {isa = PBXBuildFile; fileRef = CA5471E01A2B3C4D5E6F7002 /* RCTCrashTest.mm */; };
2425
6D7C95E72E9007E200F0D481 /* RCTPushPermissionManager.mm in Sources */ = {isa = PBXBuildFile; fileRef = 6D7C95E62E9007E200F0D481 /* RCTPushPermissionManager.mm */; };
2526
71BABDA4524A43E98A190C87 /* Archivo-Light.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 6D649751C2644FDCA18A5547 /* Archivo-Light.ttf */; };
2627
761780ED2CA45674006654EE /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 761780EC2CA45674006654EE /* AppDelegate.swift */; };
@@ -55,6 +56,8 @@
5556
5D5D40A7E7B5265D652187F9 /* PrivacyInfo.xcprivacy */ = {isa = PBXFileReference; includeInIndex = 1; name = PrivacyInfo.xcprivacy; path = BitPayApp/PrivacyInfo.xcprivacy; sourceTree = "<group>"; };
5657
6D615C4729725FD70063DB8E /* RCTTimer.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTTimer.mm; sourceTree = "<group>"; };
5758
6D615C4829725FD70063DB8E /* RCTTimer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTTimer.h; sourceTree = "<group>"; };
59+
CA5471E01A2B3C4D5E6F7002 /* RCTCrashTest.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTCrashTest.mm; sourceTree = "<group>"; };
60+
CA5471E01A2B3C4D5E6F7003 /* RCTCrashTest.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTCrashTest.h; sourceTree = "<group>"; };
5861
6D649751C2644FDCA18A5547 /* Archivo-Light.ttf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = "Archivo-Light.ttf"; path = "../assets/fonts/Archivo-Light.ttf"; sourceTree = "<group>"; };
5962
6D7C95E62E9007E200F0D481 /* RCTPushPermissionManager.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = RCTPushPermissionManager.mm; sourceTree = "<group>"; };
6063
715670CB346B45A7A9EC2044 /* Archivo-Bold.ttf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = "Archivo-Bold.ttf"; path = "../assets/fonts/Archivo-Bold.ttf"; sourceTree = "<group>"; };
@@ -98,6 +101,8 @@
98101
13B07FB81A68108700A75B9A /* PrivacyInfo.xcprivacy */,
99102
6D615C4829725FD70063DB8E /* RCTTimer.h */,
100103
6D615C4729725FD70063DB8E /* RCTTimer.mm */,
104+
CA5471E01A2B3C4D5E6F7003 /* RCTCrashTest.h */,
105+
CA5471E01A2B3C4D5E6F7002 /* RCTCrashTest.mm */,
101106
5B31AAFC2786381C002765E1 /* RCTDosh.h */,
102107
5B31AAFD2786387B002765E1 /* RCTDosh.mm */,
103108
5B31AB5E2786515A002765E1 /* Dosh.swift */,
@@ -372,6 +377,7 @@
372377
6D7C95E72E9007E200F0D481 /* RCTPushPermissionManager.mm in Sources */,
373378
5BEA4BF02838402700AB2121 /* RCTPaymentPass.mm in Sources */,
374379
6D615C4929725FD70063DB8E /* RCTTimer.mm in Sources */,
380+
CA5471E01A2B3C4D5E6F7001 /* RCTCrashTest.mm in Sources */,
375381
ECCB92782860B25D00F9CF8B /* SilentPushEvent.mm in Sources */,
376382
5B31AB5F2786515A002765E1 /* Dosh.swift in Sources */,
377383
52C9714B9004BCE190EA469D /* BundleHash.swift in Sources */,

ios/RCTCrashTest.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
#import <Foundation/Foundation.h>
2+
#import <React/RCTBridgeModule.h>
3+
4+
@interface RCTCrashTest : NSObject <RCTBridgeModule>
5+
6+
@end

ios/RCTCrashTest.mm

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
#import "RCTCrashTest.h"
2+
3+
@implementation RCTCrashTest
4+
5+
RCT_EXPORT_MODULE(CrashTest);
6+
7+
RCT_EXPORT_METHOD(crash)
8+
{
9+
dispatch_async(dispatch_get_main_queue(), ^{
10+
[NSException raise:@"BitPayTestCrash"
11+
format:@"BitPay test: native crash (Sentry)"];
12+
});
13+
}
14+
15+
@end

src/lib/crash-test/index.ts

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
import {NativeModules, Platform} from 'react-native';
2+
3+
/**
4+
* DEV-only helpers to validate crash reporting (Sentry).
5+
*/
6+
7+
interface CrashTestNativeModule {
8+
crash(): void;
9+
}
10+
11+
const CrashTest = NativeModules.CrashTest as CrashTestNativeModule | undefined;
12+
13+
export const triggerNativeCrash = (): void => {
14+
if (!CrashTest?.crash) {
15+
throw new Error(
16+
`CrashTest native module unavailable on ${Platform.OS}. ` +
17+
'Rebuild the native app (the JS bundle alone is not enough).',
18+
);
19+
}
20+
CrashTest.crash();
21+
};
22+
23+
export const triggerJsCrash = (): void => {
24+
setTimeout(() => {
25+
throw new Error('BitPay test: JS crash (Sentry)');
26+
}, 0);
27+
};

src/navigation/tabs/settings/components/About.tsx

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import AngleRight from '../../../../../assets/img/angle-right.svg';
2020
import {GIT_COMMIT_HASH} from '@env';
2121
import {Analytics} from '../../../../store/analytics/analytics.effects';
2222
import {useAppDispatch} from '../../../../utils/hooks';
23+
import {triggerJsCrash, triggerNativeCrash} from '../../../../lib/crash-test';
2324

2425
interface LinkSetting {
2526
key: string;
@@ -124,6 +125,26 @@ const About = () => {
124125
</View>
125126
);
126127
})}
128+
129+
{__DEV__ ? (
130+
<>
131+
<Hr />
132+
<Setting>
133+
<SettingTitle>{t('Crash Test (DEV)')}</SettingTitle>
134+
</Setting>
135+
<View style={{paddingHorizontal: 15, paddingBottom: 15}}>
136+
<Button
137+
buttonStyle="danger"
138+
style={{marginBottom: 10}}
139+
onPress={() => triggerNativeCrash()}>
140+
{t('Trigger Native Crash')}
141+
</Button>
142+
<Button buttonStyle="danger" onPress={() => triggerJsCrash()}>
143+
{t('Trigger JS Crash')}
144+
</Button>
145+
</View>
146+
</>
147+
) : null}
127148
</SettingsComponent>
128149
);
129150
};

0 commit comments

Comments
 (0)