├── .circleci └── config.yml ├── .eslintrc.js ├── .github ├── issue_template.md ├── pull_request_template.md └── stale.yml ├── .gitignore ├── .npmignore ├── .prettierrc.js ├── CHANGELOG.md ├── Gemfile ├── Gemfile.lock ├── LICENSE ├── README.md ├── RNInstabug.podspec ├── android ├── build.gradle ├── gradle.properties ├── jacoco.gradle ├── native.gradle ├── proguard-rules.txt ├── sourcemaps.gradle └── src │ ├── main │ ├── AndroidManifest.xml │ └── java │ │ └── com │ │ └── instabug │ │ └── reactlibrary │ │ ├── ArgsRegistry.java │ │ ├── Constants.java │ │ ├── RNInstabug.java │ │ ├── RNInstabugAPMModule.java │ │ ├── RNInstabugBugReportingModule.java │ │ ├── RNInstabugCrashReportingModule.java │ │ ├── RNInstabugFeatureRequestsModule.java │ │ ├── RNInstabugReactnativeModule.java │ │ ├── RNInstabugReactnativePackage.java │ │ ├── RNInstabugRepliesModule.java │ │ ├── RNInstabugSessionReplayModule.java │ │ ├── RNInstabugSurveysModule.java │ │ └── utils │ │ ├── ArrayUtil.java │ │ ├── EventEmitterModule.java │ │ ├── InstabugUtil.java │ │ ├── MainThreadHandler.java │ │ ├── MapUtil.java │ │ ├── RNTouchedViewExtractor.java │ │ └── ReportUtil.java │ └── test │ └── java │ └── com │ └── instabug │ └── reactlibrary │ ├── RNInstabugAPMModuleTest.java │ ├── RNInstabugBugReportingModuleTest.java │ ├── RNInstabugCrashReportingModuleTest.java │ ├── RNInstabugFeatureRequestsModuleTest.java │ ├── RNInstabugReactnativeModuleTest.java │ ├── RNInstabugRepliesModuleTest.java │ ├── RNInstabugSessionReplayModuleTest.java │ ├── RNInstabugSurveysModuleTest.java │ ├── RNInstabugTest.java │ └── util │ ├── GlobalMocks.java │ └── MockReflected.java ├── babel.config.js ├── cli ├── commands │ ├── UploadSoFiles.ts │ └── UploadSourcemaps.ts ├── index.ts └── upload │ ├── index.ts │ ├── uploadSoFiles.ts │ └── uploadSourcemaps.ts ├── dangerfile.ts ├── examples └── default │ ├── .detoxrc.js │ ├── .env │ ├── .watchmanconfig │ ├── android │ ├── app │ │ ├── build.gradle │ │ ├── debug.keystore │ │ ├── proguard-rules.pro │ │ └── src │ │ │ ├── androidTest │ │ │ └── java │ │ │ │ └── com │ │ │ │ └── instabug │ │ │ │ └── react │ │ │ │ └── example │ │ │ │ └── DetoxTest.java │ │ │ ├── debug │ │ │ └── AndroidManifest.xml │ │ │ └── main │ │ │ ├── AndroidManifest.xml │ │ │ ├── cpp │ │ │ ├── CMakeLists.txt │ │ │ ├── crasher.c │ │ │ ├── crasher_2.c │ │ │ ├── crasher_2.h │ │ │ ├── crasher_3.c │ │ │ ├── crasher_3.h │ │ │ ├── crasher_4.cpp │ │ │ ├── crasher_4.h │ │ │ └── native-lib.cpp │ │ │ ├── java │ │ │ └── com │ │ │ │ └── instabug │ │ │ │ └── react │ │ │ │ └── example │ │ │ │ ├── MainActivity.kt │ │ │ │ ├── MainApplication.kt │ │ │ │ ├── RNInstabugExampleCrashReportingModule.java │ │ │ │ ├── RNInstabugExampleReactnativePackage.java │ │ │ │ └── nativeLibs │ │ │ │ └── CppNativeLib.java │ │ │ └── res │ │ │ ├── drawable │ │ │ └── rn_edit_text_material.xml │ │ │ ├── mipmap-hdpi │ │ │ ├── ic_launcher.png │ │ │ └── ic_launcher_round.png │ │ │ ├── mipmap-mdpi │ │ │ ├── ic_launcher.png │ │ │ └── ic_launcher_round.png │ │ │ ├── mipmap-xhdpi │ │ │ ├── ic_launcher.png │ │ │ └── ic_launcher_round.png │ │ │ ├── mipmap-xxhdpi │ │ │ ├── ic_launcher.png │ │ │ └── ic_launcher_round.png │ │ │ ├── mipmap-xxxhdpi │ │ │ ├── ic_launcher.png │ │ │ └── ic_launcher_round.png │ │ │ ├── raw │ │ │ └── instabug_config.json │ │ │ ├── values │ │ │ ├── strings.xml │ │ │ └── styles.xml │ │ │ └── xml │ │ │ └── network_security_config.xml │ ├── build.gradle │ ├── gradle.properties │ ├── gradle │ │ └── wrapper │ │ │ ├── gradle-wrapper.jar │ │ │ └── gradle-wrapper.properties │ ├── gradlew │ ├── gradlew.bat │ └── settings.gradle │ ├── app.json │ ├── babel.config.js │ ├── e2e │ ├── config.json │ ├── environment.js │ ├── reportBug.e2e.ts │ └── utils │ │ ├── elements.ts │ │ └── mockData.ts │ ├── index.js │ ├── ios │ ├── .xcode.env │ ├── InstabugExample.xcodeproj │ │ ├── project.pbxproj │ │ └── xcshareddata │ │ │ └── xcschemes │ │ │ └── InstabugExample.xcscheme │ ├── InstabugExample.xcworkspace │ │ ├── contents.xcworkspacedata │ │ └── xcshareddata │ │ │ └── IDEWorkspaceChecks.plist │ ├── InstabugExample │ │ ├── AppDelegate.h │ │ ├── AppDelegate.mm │ │ ├── Images.xcassets │ │ │ ├── AppIcon.appiconset │ │ │ │ └── Contents.json │ │ │ └── Contents.json │ │ ├── Info.plist │ │ ├── Instabug.plist │ │ ├── LaunchScreen.storyboard │ │ ├── PrivacyInfo.xcprivacy │ │ └── main.m │ ├── InstabugTests │ │ ├── IBGConstants.h │ │ ├── IBGConstants.m │ │ ├── Info.plist │ │ ├── InstabugAPMTests.m │ │ ├── InstabugBugReportingTests.m │ │ ├── InstabugCrashReportingTests.m │ │ ├── InstabugFeatureRequestsTests.m │ │ ├── InstabugRepliesTests.m │ │ ├── InstabugSampleTests.m │ │ ├── InstabugSessionReplayTests.m │ │ ├── InstabugSurveysTests.m │ │ ├── RNInstabugTests.m │ │ └── Util │ │ │ └── IBGCrashReporting+CP.h │ ├── Podfile │ ├── Podfile.lock │ └── native │ │ ├── CrashReportingExampleModule.h │ │ └── CrashReportingExampleModule.m │ ├── jest.config.js │ ├── metro.config.js │ ├── package.json │ ├── patches │ ├── react-native-background-timer+2.4.1.patch │ └── react-native-vector-icons+10.2.0.patch │ ├── src │ ├── App.tsx │ ├── components │ │ ├── ClipboardTextInput.tsx │ │ ├── CustomButton.tsx │ │ ├── CustomGap.tsx │ │ ├── CustomImage.tsx │ │ ├── CustomText.tsx │ │ ├── InputField.tsx │ │ ├── ListTile.tsx │ │ ├── NestedView.tsx │ │ ├── PasteFromClipboardButton.tsx │ │ ├── PlatformListTile.tsx │ │ ├── Screen.tsx │ │ ├── Section.tsx │ │ ├── Select.tsx │ │ └── VerticalListTile.tsx │ ├── images │ │ └── logo.png │ ├── native │ │ ├── NativeCrashReporting.ts │ │ └── NativePackage.ts │ ├── navigation │ │ ├── HomeStack.tsx │ │ └── RootTab.tsx │ ├── screens │ │ ├── BugReportingScreen.tsx │ │ ├── CrashReportingScreen.tsx │ │ ├── FeatureRequestsScreen.tsx │ │ ├── HomeScreen.tsx │ │ ├── LegacyModeScreen.tsx │ │ ├── RepliesScreen.tsx │ │ ├── SessionReplayScreen.tsx │ │ ├── SettingsScreen.tsx │ │ ├── SurveysScreen.tsx │ │ ├── apm │ │ │ ├── APMScreen.tsx │ │ │ ├── FlowsScreen.tsx │ │ │ ├── HttpScreen.tsx │ │ │ ├── NetworkScreen.tsx │ │ │ ├── TracesScreen.tsx │ │ │ └── webViews │ │ │ │ ├── FullWebViewsScreen.tsx │ │ │ │ ├── PartialWebViewsScreen.tsx │ │ │ │ └── WebViewsScreen.tsx │ │ └── user-steps │ │ │ ├── BackAndForthScreen.tsx │ │ │ ├── BasicComponentsScreen.tsx │ │ │ ├── ComplexViewsScreen.tsx │ │ │ ├── FlatListScreen.tsx │ │ │ ├── GesturesScreen.tsx │ │ │ ├── GoogleMapsScreen.tsx │ │ │ ├── LargeImageListScreen.tsx │ │ │ ├── ScrollViewScreen.tsx │ │ │ ├── SectionListScreen.tsx │ │ │ └── UserStepsScreen.tsx │ ├── theme │ │ ├── nativeBaseTheme.ts │ │ └── navigationTheme.ts │ └── utils │ │ ├── createList.ts │ │ ├── showNotification.ts │ │ └── useDelayedRefresh.ts │ ├── tsconfig.json │ └── yarn.lock ├── ios ├── RNInstabug.xcodeproj │ └── project.pbxproj ├── RNInstabug │ ├── ArgsRegistry.h │ ├── ArgsRegistry.m │ ├── InstabugAPMBridge.h │ ├── InstabugAPMBridge.m │ ├── InstabugBugReportingBridge.h │ ├── InstabugBugReportingBridge.m │ ├── InstabugCrashReportingBridge.h │ ├── InstabugCrashReportingBridge.m │ ├── InstabugFeatureRequestsBridge.h │ ├── InstabugFeatureRequestsBridge.m │ ├── InstabugReactBridge.h │ ├── InstabugReactBridge.m │ ├── InstabugRepliesBridge.h │ ├── InstabugRepliesBridge.m │ ├── InstabugSessionReplayBridge.h │ ├── InstabugSessionReplayBridge.m │ ├── InstabugSurveysBridge.h │ ├── InstabugSurveysBridge.m │ ├── RCTConvert+InstabugEnums.h │ ├── RCTConvert+InstabugEnums.m │ ├── RNInstabug.h │ ├── RNInstabug.m │ └── Util │ │ ├── IBGAPM+PrivateAPIs.h │ │ ├── IBGCrashReporting+CP.h │ │ ├── IBGNetworkLogger+CP.h │ │ └── Instabug+CP.h ├── native.rb └── sourcemaps.sh ├── jest.config.js ├── package.json ├── react-native.config.js ├── rollup.config.js ├── scripts ├── customize-ios-endpoints.sh ├── dream-11-delete-unused-features.sh ├── find-token.sh ├── notify-github.sh ├── replace.js ├── snapshot-comment.md └── snapshot-version.sh ├── src ├── index.ts ├── models │ ├── FeatureFlag.ts │ ├── InstabugConfig.ts │ ├── NonFatalOptions.ts │ ├── Report.ts │ ├── ReproConfig.ts │ ├── SessionMetadata.ts │ ├── Trace.ts │ └── W3cExternalTraceAttributes.ts ├── modules │ ├── APM.ts │ ├── BugReporting.ts │ ├── CrashReporting.ts │ ├── FeatureRequests.ts │ ├── Instabug.ts │ ├── NetworkLogger.ts │ ├── Replies.ts │ ├── SessionReplay.ts │ └── Surveys.ts ├── native │ ├── NativeAPM.ts │ ├── NativeBugReporting.ts │ ├── NativeConstants.ts │ ├── NativeCrashReporting.ts │ ├── NativeFeatureRequests.ts │ ├── NativeInstabug.ts │ ├── NativePackage.ts │ ├── NativeReplies.ts │ ├── NativeSessionReplay.ts │ └── NativeSurveys.ts ├── promise.d.ts └── utils │ ├── Enums.ts │ ├── FeatureFlags.ts │ ├── InstabugConstants.ts │ ├── InstabugUtils.ts │ ├── UnhandledRejectionTracking.ts │ ├── XhrNetworkInterceptor.ts │ ├── config.ts │ └── logger.ts ├── test ├── mocks │ ├── fakeNetworkRequest.ts │ ├── mockAPM.ts │ ├── mockBugReporting.ts │ ├── mockCrashReporting.ts │ ├── mockDevMode.ts │ ├── mockFeatureRequests.ts │ ├── mockHermesInternal.ts │ ├── mockInstabug.ts │ ├── mockInstabugUtils.ts │ ├── mockNativeModules.ts │ ├── mockNetworkLogger.ts │ ├── mockParseErrorStackLib.ts │ ├── mockPromiseRejectionTracking.ts │ ├── mockReplies.ts │ ├── mockSessionReplay.ts │ ├── mockSurveys.ts │ └── mockXhrNetworkInterceptor.ts ├── models │ ├── Report.spec.ts │ └── Trace.spec.ts ├── modules │ ├── APM.spec.ts │ ├── BugReporting.spec.ts │ ├── CrashReporting.spec.ts │ ├── FeatureRequests.spec.ts │ ├── Instabug.spec.ts │ ├── NetworkLogger.spec.ts │ ├── Replies.spec.ts │ ├── SessionReplay.spec.ts │ └── Surveys.spec.ts ├── setup.ts ├── utils │ ├── InstabugUtils.spec.ts │ ├── UnhandledRejectionTracking.spec.ts │ └── XhrNetworkInterceptor.spec.ts └── xhr2.d.ts ├── tsconfig.cli.json ├── tsconfig.json ├── tsconfig.test.json ├── tsconfig.upload.json ├── upload └── package.json └── yarn.lock /.eslintrc.js: -------------------------------------------------------------------------------- 1 | /** @type {import('eslint').ESLint.ConfigData} */ 2 | module.exports = { 3 | extends: '@react-native-community', 4 | plugins: ['prettier', 'jest', 'jsdoc'], 5 | overrides: [ 6 | { 7 | // Jest Overrides 8 | files: ['test/**'], 9 | env: { 10 | node: true, 11 | browser: true, 12 | 'jest/globals': true, 13 | }, 14 | }, 15 | { 16 | // Node Scripts Overrides 17 | files: ['scripts/**'], 18 | env: { 19 | node: true, 20 | }, 21 | }, 22 | { 23 | // Detox Overrides 24 | files: ['examples/default/e2e/**.js'], 25 | env: { 26 | 'jest/globals': true, 27 | }, 28 | globals: { 29 | device: false, 30 | waitFor: false, 31 | element: false, 32 | by: false, 33 | }, 34 | }, 35 | ], 36 | rules: { 37 | 'jsdoc/no-undefined-types': 'warn', 38 | 'prettier/prettier': 'error', 39 | 'prefer-const': 'error', 40 | }, 41 | }; 42 | -------------------------------------------------------------------------------- /.github/issue_template.md: -------------------------------------------------------------------------------- 1 | 8 | 9 | ### Steps to Reproduce the Problem 10 | 11 | ### Expected Behavior 12 | 13 | ### Actual Behavior 14 | 15 | ### Instabug integration code 16 | 17 | 18 | 19 | ### SDK Version 20 | 21 | 22 | 23 | ### React Native, iOS and Android Versions 24 | 25 | ### Device Model 26 | 27 | 28 | 29 | ### [Optional] Project That Reproduces the Issue 30 | 31 | 32 | -------------------------------------------------------------------------------- /.github/pull_request_template.md: -------------------------------------------------------------------------------- 1 | ## Description of the change 2 | 3 | > Description goes here 4 | 5 | ## Type of change 6 | 7 | - [ ] Bug fix (non-breaking change that fixes an issue) 8 | - [ ] New feature (non-breaking change that adds functionality) 9 | - [ ] Breaking change (fix or feature that would cause existing functionality to not work as expected) 10 | 11 | ## Related issues 12 | 13 | > Issue links go here 14 | 15 | ## Checklists 16 | 17 | ### Development 18 | 19 | - [ ] Lint rules pass locally 20 | - [ ] The code changed/added as part of this pull request has been covered with tests 21 | 22 | ### Code review 23 | 24 | - [ ] This pull request has a descriptive title and information useful to a reviewer 25 | - [ ] Issue from task tracker has a link to this pull request 26 | -------------------------------------------------------------------------------- /.github/stale.yml: -------------------------------------------------------------------------------- 1 | # Configuration for probot-stale - https://github.com/probot/stale 2 | 3 | # Number of days of inactivity before an Issue or Pull Request becomes stale 4 | daysUntilStale: 7 5 | 6 | # Number of days of inactivity before an Issue or Pull Request with the Pending Feedback label is closed. 7 | # Set to false to disable. If disabled, issues still need to be closed manually, but will remain marked as stale. 8 | daysUntilClose: 7 9 | 10 | # Only issues or pull requests with all of these labels are checked if stale. Defaults to `[]` (disabled) 11 | onlyLabels: [Pending Feedback] 12 | 13 | # Issues or Pull Requests with these labels will never be considered stale. Set to `[]` to disable 14 | exemptLabels: 15 | - WIP 16 | - Planned 17 | 18 | # Label to use when marking as stale 19 | staleLabel: Pending Feedback 20 | 21 | # Comment to post when marking as stale. Set to `false` to disable 22 | markComment: > 23 | This issue has been automatically marked as pending feedback because we need additional information 24 | to be able to investigate it further. It will be closed in 7 days if it remains inactive. Thank you 25 | for your contributions. 26 | 27 | # Comment to post when closing a stale Issue. 28 | closeComment: > 29 | This issue has been automatically closed since we haven't heard back from 30 | you. Please feel free to re-open the issue if you have more information 31 | to add. 32 | 33 | # Limit to only `issues` or `pulls` 34 | only: issues 35 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Distribution 2 | dist/ 3 | bin/ 4 | upload/* 5 | !upload/package.json 6 | 7 | # NodeJS 8 | node_modules/ 9 | npm-debug.log 10 | yarn-debug.log 11 | yarn-error.log 12 | coverage/ 13 | .jest/cache/ 14 | 15 | # OSX 16 | .DS_Store 17 | 18 | # VSCode 19 | .vscode/ 20 | jsconfig.json 21 | 22 | # Xcode 23 | build/ 24 | *.pbxuser 25 | !default.pbxuser 26 | *.mode1v3 27 | !default.mode1v3 28 | *.mode2v3 29 | !default.mode2v3 30 | *.perspectivev3 31 | !default.perspectivev3 32 | xcuserdata 33 | *.xccheckout 34 | *.moved-aside 35 | DerivedData 36 | *.hmap 37 | *.ipa 38 | *.xcuserstate 39 | project.xcworkspace 40 | 41 | # Cocoapods 42 | Pods/ 43 | 44 | # Android/IJ 45 | .classpath 46 | .cxx 47 | .gradle 48 | .idea 49 | .project 50 | .settings 51 | local.properties 52 | android.iml 53 | 54 | # BUCK 55 | buck-out/ 56 | \.buckd/ 57 | android/app/libs 58 | android/keystores/debug.keystore 59 | 60 | # Expo 61 | .expo/* 62 | 63 | # Bundle artifact 64 | *.jsbundle 65 | /examples/default/ios/main.jsbundle.map 66 | 67 | # Vscode local history 68 | .history/ 69 | 70 | # .idea run configurations 71 | /.run/* 72 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | ## Extra ignores 2 | 3 | # Misc 4 | .github/ 5 | .circleci/ 6 | examples/ 7 | 8 | # Config files 9 | .eslintrc.js 10 | .prettierrc.js 11 | Dangerfile 12 | 13 | # CLI Config files 14 | rollup.config.js 15 | tsconfig.cli.json 16 | 17 | # Tests 18 | android/src/test/ 19 | test/ 20 | jest.config.js 21 | 22 | ## Same as .gitignore 23 | 24 | # Must be Included in published npm package 25 | # # Distribution 26 | # dist/ 27 | # bin/ 28 | 29 | # NodeJS 30 | node_modules/ 31 | npm-debug.log 32 | yarn-debug.log 33 | yarn-error.log 34 | coverage/ 35 | .jest/cache/ 36 | 37 | # OSX 38 | .DS_Store 39 | 40 | # VSCode 41 | .vscode/ 42 | jsconfig.json 43 | 44 | # Xcode 45 | build/ 46 | *.pbxuser 47 | !default.pbxuser 48 | *.mode1v3 49 | !default.mode1v3 50 | *.mode2v3 51 | !default.mode2v3 52 | *.perspectivev3 53 | !default.perspectivev3 54 | xcuserdata 55 | *.xccheckout 56 | *.moved-aside 57 | DerivedData 58 | *.hmap 59 | *.ipa 60 | *.xcuserstate 61 | project.xcworkspace 62 | 63 | # Cocoapods 64 | Pods/ 65 | 66 | # Android/IJ 67 | .classpath 68 | .cxx 69 | .gradle 70 | .idea 71 | .project 72 | .settings 73 | local.properties 74 | android.iml 75 | 76 | # BUCK 77 | buck-out/ 78 | \.buckd/ 79 | android/app/libs 80 | android/keystores/debug.keystore 81 | 82 | # Expo 83 | .expo/* 84 | -------------------------------------------------------------------------------- /.prettierrc.js: -------------------------------------------------------------------------------- 1 | /** @type {import('prettier').Config} */ 2 | module.exports = { 3 | semi: true, 4 | singleQuote: true, 5 | bracketSameLine: true, 6 | useTabs: false, 7 | tabWidth: 2, 8 | trailingComma: 'all', 9 | endOfLine: 'auto', 10 | printWidth: 100, 11 | importOrder: ['mocks', '^react(-native)?(/.*)?$', '', '^[./]'], 12 | importOrderSeparation: true, 13 | importOrderSortSpecifiers: true, 14 | }; 15 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source "https://rubygems.org" -------------------------------------------------------------------------------- /Gemfile.lock: -------------------------------------------------------------------------------- 1 | GEM 2 | remote: https://rubygems.org/ 3 | specs: 4 | 5 | PLATFORMS 6 | ruby 7 | 8 | DEPENDENCIES 9 | 10 | BUNDLED WITH 11 | 2.4.17 12 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 Instabug 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /RNInstabug.podspec: -------------------------------------------------------------------------------- 1 | require 'json' 2 | require_relative './ios/native' 3 | 4 | package = JSON.parse(File.read('package.json')) 5 | 6 | Pod::Spec.new do |s| 7 | s.name = 'RNInstabug' 8 | s.version = package["version"] 9 | s.summary = package["description"] 10 | s.author = package["author"] 11 | s.license = package["license"] 12 | s.homepage = package["homepage"] 13 | s.source = { :git => "https://github.com/Instabug/Instabug-React-Native.git", :tag => 'v' + package["version"] } 14 | 15 | s.platform = :ios, "9.0" 16 | s.source_files = "ios/**/*.{h,m,mm}" 17 | 18 | s.dependency 'React-Core' 19 | use_instabug!(s) 20 | 21 | end 22 | -------------------------------------------------------------------------------- /android/gradle.properties: -------------------------------------------------------------------------------- 1 | InstabugReactNative_compileSdkVersion=30 2 | InstabugReactNative_buildToolsVersion=30.0.2 3 | InstabugReactNative_targetSdkVersion=31 4 | InstabugReactNative_minSdkVersion=16 5 | -------------------------------------------------------------------------------- /android/jacoco.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'jacoco' 2 | 3 | jacoco { 4 | toolVersion '0.8.8' 5 | } 6 | 7 | task jacocoTestReport(type: JacocoReport) { 8 | group = 'Reporting' 9 | description = 'Generate Jacoco coverage reports for the debug build.' 10 | 11 | dependsOn 'testDebugUnitTest' 12 | 13 | reports { 14 | html.required = true 15 | xml.required = true 16 | } 17 | 18 | def excludes = [ 19 | '**/R.class', 20 | '**/R$*.class', 21 | '**/BuildConfig.*', 22 | '**/Manifest*.*', 23 | '**/*Test*.*', 24 | 'android/**/*.*', 25 | 'androidx/**/*.*', 26 | '**/*$ViewInjector*.*', 27 | '**/*Dagger*.*', 28 | '**/*MembersInjector*.*', 29 | '**/*_Factory.*', 30 | '**/*_Provide*Factory*.*', 31 | '**/*_ViewBinding*.*', 32 | '**/AutoValue_*.*', 33 | '**/R2.class', 34 | '**/R2$*.class', 35 | '**/*Directions$*', 36 | '**/*Directions.*', 37 | '**/*Binding.*' 38 | ] 39 | 40 | def jClasses = "${project.buildDir}/intermediates/javac/debug/compileDebugJavaWithJavac/classes" 41 | def javaClasses = fileTree(dir: jClasses, excludes: excludes) 42 | def sourceDirs = ["${project.projectDir}/src/main/java"] 43 | classDirectories.from = files(javaClasses) 44 | sourceDirectories.from = files(sourceDirs) 45 | executionData.from = files(["${project.buildDir}/jacoco/testDebugUnitTest.exec"]) 46 | } 47 | 48 | tasks.whenTaskAdded { task -> 49 | if (task.name == 'testDebugUnitTest') { 50 | task.finalizedBy jacocoTestReport 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /android/native.gradle: -------------------------------------------------------------------------------- 1 | project.ext.instabug = [ 2 | version: '14.3.0' 3 | ] 4 | 5 | dependencies { 6 | api "com.instabug.library:instabug:${project.ext.instabug.version}" 7 | } 8 | -------------------------------------------------------------------------------- /android/proguard-rules.txt: -------------------------------------------------------------------------------- 1 | -keep class com.instabug.** {*;} -------------------------------------------------------------------------------- /android/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /android/src/main/java/com/instabug/reactlibrary/Constants.java: -------------------------------------------------------------------------------- 1 | package com.instabug.reactlibrary; 2 | 3 | final class Constants { 4 | final static String IBG_PRE_INVOCATION_HANDLER = "IBGpreInvocationHandler"; 5 | final static String IBG_POST_INVOCATION_HANDLER = "IBGpostInvocationHandler"; 6 | 7 | final static String IBG_ON_SHOW_SURVEY_HANDLER = "IBGWillShowSurvey"; 8 | final static String IBG_ON_DISMISS_SURVEY_HANDLER = "IBGDidDismissSurvey"; 9 | 10 | final static String IBG_ON_NEW_MESSAGE_HANDLER = "IBGonNewMessageHandler"; 11 | final static String IBG_ON_NEW_REPLY_RECEIVED_CALLBACK = "IBGOnNewReplyReceivedCallback"; 12 | 13 | final static String IBG_ON_NEW_W3C_FLAGS_UPDATE_RECEIVED_CALLBACK = "IBGOnNewW3CFlagsUpdateReceivedCallback"; 14 | 15 | final static String IBG_SESSION_REPLAY_ON_SYNC_CALLBACK_INVOCATION = "IBGSessionReplayOnSyncCallback"; 16 | 17 | } 18 | -------------------------------------------------------------------------------- /android/src/main/java/com/instabug/reactlibrary/RNInstabugReactnativePackage.java: -------------------------------------------------------------------------------- 1 | package com.instabug.reactlibrary; 2 | 3 | import androidx.annotation.NonNull; 4 | 5 | import com.facebook.react.ReactPackage; 6 | import com.facebook.react.bridge.NativeModule; 7 | import com.facebook.react.bridge.ReactApplicationContext; 8 | import com.facebook.react.uimanager.ViewManager; 9 | 10 | import java.util.ArrayList; 11 | import java.util.Collections; 12 | import java.util.List; 13 | 14 | public class RNInstabugReactnativePackage implements ReactPackage { 15 | 16 | private static final String TAG = RNInstabugReactnativePackage.class.getSimpleName(); 17 | 18 | public RNInstabugReactnativePackage() {} 19 | 20 | @NonNull 21 | @Override 22 | public List createNativeModules(@NonNull ReactApplicationContext reactContext) { 23 | List modules = new ArrayList<>(); 24 | modules.add(new RNInstabugReactnativeModule(reactContext)); 25 | modules.add(new RNInstabugBugReportingModule(reactContext)); 26 | modules.add(new RNInstabugCrashReportingModule(reactContext)); 27 | modules.add(new RNInstabugSurveysModule(reactContext)); 28 | modules.add(new RNInstabugFeatureRequestsModule(reactContext)); 29 | modules.add(new RNInstabugRepliesModule(reactContext)); 30 | modules.add(new RNInstabugAPMModule(reactContext)); 31 | modules.add(new RNInstabugSessionReplayModule(reactContext)); 32 | return modules; 33 | } 34 | 35 | @NonNull 36 | @Override 37 | public List createViewManagers(@NonNull ReactApplicationContext reactContext) { 38 | return Collections.emptyList(); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /android/src/main/java/com/instabug/reactlibrary/utils/EventEmitterModule.java: -------------------------------------------------------------------------------- 1 | package com.instabug.reactlibrary.utils; 2 | 3 | import androidx.annotation.Nullable; 4 | import androidx.annotation.VisibleForTesting; 5 | 6 | import com.facebook.react.bridge.ReactApplicationContext; 7 | import com.facebook.react.bridge.ReactContextBaseJavaModule; 8 | import com.facebook.react.bridge.ReadableMap; 9 | import com.facebook.react.bridge.WritableMap; 10 | import com.facebook.react.modules.core.DeviceEventManagerModule; 11 | 12 | public abstract class EventEmitterModule extends ReactContextBaseJavaModule { 13 | private int listenerCount = 0; 14 | 15 | public EventEmitterModule(ReactApplicationContext context) { 16 | super(context); 17 | } 18 | 19 | @VisibleForTesting 20 | public void sendEvent(String event, @Nullable ReadableMap params) { 21 | if (listenerCount > 0) { 22 | getReactApplicationContext() 23 | .getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class) 24 | .emit(event, params); 25 | } 26 | } 27 | 28 | protected void addListener(String ignoredEvent) { 29 | listenerCount++; 30 | } 31 | 32 | protected void removeListeners(Integer count) { 33 | listenerCount -= count; 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /android/src/main/java/com/instabug/reactlibrary/utils/InstabugUtil.java: -------------------------------------------------------------------------------- 1 | package com.instabug.reactlibrary.utils; 2 | 3 | import com.instabug.survey.Survey; 4 | 5 | import org.json.JSONArray; 6 | import org.json.JSONException; 7 | import org.json.JSONObject; 8 | 9 | import java.lang.reflect.Method; 10 | import java.util.List; 11 | 12 | public class InstabugUtil { 13 | public static Method getMethod(Class clazz, String methodName, Class... parameterType) { 14 | final Method[] methods = clazz.getDeclaredMethods(); 15 | 16 | for (Method method : methods) { 17 | if (method.getName().equals(methodName) && method.getParameterTypes().length == 18 | parameterType.length) { 19 | if (parameterType.length == 0) { 20 | method.setAccessible(true); 21 | return method; 22 | } 23 | for (int i = 0; i < parameterType.length; i++) { 24 | if (method.getParameterTypes()[i] == parameterType[i]) { 25 | if (i == method.getParameterTypes().length - 1) { 26 | method.setAccessible(true); 27 | return method; 28 | } 29 | } else { 30 | break; 31 | } 32 | } 33 | } 34 | } 35 | return null; 36 | } 37 | 38 | /** 39 | * Convenience method to convert from a list of Surveys to a JSON array 40 | * 41 | * @param list 42 | * List of Surveys to be converted to JSON array 43 | */ 44 | public static JSONArray surveyObjectToJson(List list) { 45 | JSONArray jsonArray = new JSONArray(); 46 | try{ 47 | for (Survey obj : list) { 48 | JSONObject object = new JSONObject(); 49 | object.put("title", obj.getTitle()); 50 | jsonArray.put(object); 51 | } 52 | } catch (JSONException e) { 53 | e.printStackTrace(); 54 | } 55 | return jsonArray; 56 | } 57 | 58 | } -------------------------------------------------------------------------------- /android/src/main/java/com/instabug/reactlibrary/utils/MainThreadHandler.java: -------------------------------------------------------------------------------- 1 | package com.instabug.reactlibrary.utils; 2 | 3 | import android.os.Handler; 4 | import android.os.Looper; 5 | 6 | public class MainThreadHandler { 7 | /** 8 | * Runs a block of code on the Main Thread 9 | */ 10 | public static void runOnMainThread(Runnable runnable) { 11 | new Handler(Looper.getMainLooper()).post(runnable); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /android/src/main/java/com/instabug/reactlibrary/utils/ReportUtil.java: -------------------------------------------------------------------------------- 1 | package com.instabug.reactlibrary.utils; 2 | 3 | import android.net.Uri; 4 | 5 | import com.facebook.react.bridge.ReadableArray; 6 | import com.facebook.react.bridge.ReadableMap; 7 | import com.facebook.react.bridge.ReadableMapKeySetIterator; 8 | import com.facebook.react.bridge.ReadableType; 9 | import com.facebook.react.bridge.WritableArray; 10 | import com.facebook.react.bridge.WritableNativeArray; 11 | import com.instabug.library.model.Report; 12 | import com.instabug.library.model.a; 13 | 14 | import org.json.JSONException; 15 | 16 | import java.util.ArrayList; 17 | 18 | 19 | /** 20 | * Created by salmaali on 8/29/18. 21 | */ 22 | 23 | public class ReportUtil { 24 | 25 | public static Report createReport(ReadableArray tags, ReadableArray consoleLogs, String userData, ReadableMap userAttributes, ReadableMap fileAttachments) { 26 | Report report = new Report(); 27 | // map tags 28 | report.addTag(ArrayUtil.parseReadableArrayOfStrings(tags).toArray(new String[0])); 29 | 30 | // map consoleLogs 31 | for (int i = 0; i < consoleLogs.size(); i++) { 32 | ReadableType type = consoleLogs.getType(i); 33 | if (type == ReadableType.String) { 34 | report.appendToConsoleLogs(consoleLogs.getString(i)); 35 | } 36 | } 37 | 38 | // map userData 39 | report.setUserData(userData); 40 | 41 | // map userAttributes 42 | ReadableMapKeySetIterator userAttrIterator = userAttributes.keySetIterator(); 43 | while (userAttrIterator.hasNextKey()) { 44 | String key = userAttrIterator.nextKey(); 45 | ReadableType type = userAttributes.getType(key); 46 | if (type == ReadableType.String) { 47 | report.setUserAttribute(key, userAttributes.getString(key)); 48 | } 49 | 50 | } 51 | 52 | // map fileAttachments 53 | ReadableMapKeySetIterator fileAttachmentsIterator = userAttributes.keySetIterator(); 54 | while (fileAttachmentsIterator.hasNextKey()) { 55 | String key = fileAttachmentsIterator.nextKey(); 56 | ReadableType type = fileAttachments.getType(key); 57 | if (type == ReadableType.String) { 58 | Uri uri = Uri.parse(key); 59 | report.addFileAttachment(uri, fileAttachments.getString(key)); 60 | } 61 | 62 | } 63 | 64 | return report; 65 | } 66 | 67 | } 68 | -------------------------------------------------------------------------------- /android/src/test/java/com/instabug/reactlibrary/util/GlobalMocks.java: -------------------------------------------------------------------------------- 1 | package com.instabug.reactlibrary.util; 2 | 3 | import static org.mockito.Mockito.mockStatic; 4 | 5 | import android.util.Log; 6 | 7 | import com.instabug.crash.models.IBGNonFatalException; 8 | import com.instabug.reactlibrary.utils.InstabugUtil; 9 | 10 | import org.json.JSONObject; 11 | import org.mockito.MockedStatic; 12 | 13 | import java.lang.reflect.Method; 14 | 15 | public class GlobalMocks { 16 | public static MockedStatic log; 17 | private static MockedStatic reflection; 18 | public static MockedStatic reflected; 19 | 20 | public static void setUp() throws NoSuchMethodException { 21 | // Log mock 22 | log = mockStatic(Log.class); 23 | 24 | // Reflection mock 25 | reflection = mockStatic(InstabugUtil.class); 26 | reflected = mockStatic(MockReflected.class); 27 | 28 | Method mSetCurrentPlatform = MockReflected.class.getDeclaredMethod("setCurrentPlatform", int.class); 29 | mSetCurrentPlatform.setAccessible(true); 30 | 31 | // setCurrentPlatform mock 32 | reflection 33 | .when(() -> InstabugUtil.getMethod(Class.forName("com.instabug.library.Instabug"), "setCurrentPlatform", int.class)) 34 | .thenReturn(mSetCurrentPlatform); 35 | 36 | // setBaseUrl mock 37 | Method mSetBaseUrl = MockReflected.class.getDeclaredMethod("setBaseUrl", String.class); 38 | mSetBaseUrl.setAccessible(true); 39 | reflection 40 | .when(() -> InstabugUtil.getMethod(Class.forName("com.instabug.library.util.InstabugDeprecationLogger"), "setBaseUrl", String.class)) 41 | .thenReturn(mSetBaseUrl); 42 | 43 | // reportException mock 44 | Method mCrashReportException = MockReflected.class.getDeclaredMethod("reportException", JSONObject.class, boolean.class, java.util.Map.class, JSONObject.class, IBGNonFatalException.Level.class); 45 | mCrashReportException.setAccessible(true); 46 | reflection 47 | .when(() -> InstabugUtil.getMethod(Class.forName("com.instabug.crash.CrashReporting"), "reportException", JSONObject.class, 48 | boolean.class, java.util.Map.class, JSONObject.class, IBGNonFatalException.Level.class)) 49 | .thenReturn(mCrashReportException); 50 | } 51 | 52 | public static void close() { 53 | log.close(); 54 | reflection.close(); 55 | reflected.close(); 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /android/src/test/java/com/instabug/reactlibrary/util/MockReflected.java: -------------------------------------------------------------------------------- 1 | package com.instabug.reactlibrary.util; 2 | 3 | import com.instabug.crash.models.IBGNonFatalException; 4 | 5 | import org.json.JSONObject; 6 | 7 | import java.util.Map; 8 | 9 | /** 10 | * Includes fake implementations of methods called by reflection. 11 | * Used to verify whether or not a private methods was called. 12 | */ 13 | @SuppressWarnings("unused") 14 | public class MockReflected { 15 | 16 | /** 17 | * Instabug.setCurrentPlatform 18 | */ 19 | public static void setCurrentPlatform(int platform) {} 20 | 21 | /** 22 | * Instabug.util.InstabugDeprecationLogger.setBaseUrl 23 | */ 24 | public static void setBaseUrl(String baseUrl) {} 25 | /** 26 | * CrashReporting.reportException 27 | */ 28 | public static void reportException(JSONObject exception, boolean isHandled, Map userAttributes, JSONObject fingerPrint, IBGNonFatalException.Level level) {} 29 | 30 | } 31 | -------------------------------------------------------------------------------- /babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | presets: ['module:metro-react-native-babel-preset'], 3 | }; 4 | -------------------------------------------------------------------------------- /cli/commands/UploadSoFiles.ts: -------------------------------------------------------------------------------- 1 | import { Command, Option } from 'commander'; 2 | import { uploadSoFiles, UploadSoFilesOptions } from '../upload/uploadSoFiles'; 3 | 4 | /** 5 | * This script uploads .so files to the specified endpoint used in NDK crash reporting. 6 | * Usage: node upload-so-files.js --arch --file --api_key --token --name 7 | */ 8 | 9 | export const UploadSoFilesCommand = new Command(); 10 | 11 | UploadSoFilesCommand.name('upload-so-files') 12 | .addOption( 13 | new Option('-arch, --arch ', 'arch') 14 | .choices(['x86', 'x86_64', 'arm64-v8a', 'armeabi-v7a']) 15 | .makeOptionMandatory(), 16 | ) 17 | .addOption( 18 | new Option( 19 | '-f, --file ', 20 | 'The path of the symbol files in Zip format', 21 | ).makeOptionMandatory(), 22 | ) 23 | .addOption(new Option('--api_key ', 'Your App key').makeOptionMandatory()) 24 | .addOption( 25 | new Option('-t, --token ', 'Your App Token') 26 | .env('INSTABUG_APP_TOKEN') 27 | .makeOptionMandatory(), 28 | ) 29 | .addOption( 30 | new Option('-n, --name ', 'The app version name') 31 | .env('INSTABUG_APP_VERSION_NAME') 32 | .makeOptionMandatory(), 33 | ) 34 | .action(function (this: Command) { 35 | const options = this.opts(); 36 | uploadSoFiles(options); 37 | }) 38 | .showHelpAfterError(); 39 | -------------------------------------------------------------------------------- /cli/commands/UploadSourcemaps.ts: -------------------------------------------------------------------------------- 1 | import { Command, Option } from 'commander'; 2 | import { uploadSourcemaps, UploadSourcemapsOptions } from '../upload/uploadSourcemaps'; 3 | 4 | export const uploadSourcemapsCommand = new Command(); 5 | 6 | uploadSourcemapsCommand 7 | .name('upload-sourcemaps') 8 | .addOption( 9 | new Option('-p, --platform ', 'Platform') 10 | .choices(['ios', 'android']) 11 | .makeOptionMandatory(), 12 | ) 13 | .addOption( 14 | new Option('-f, --file ', 'The path of the source map file').makeOptionMandatory(), 15 | ) 16 | .addOption( 17 | new Option('-t, --token ', 'Your App Token') 18 | .env('INSTABUG_APP_TOKEN') 19 | .makeOptionMandatory(), 20 | ) 21 | .addOption( 22 | new Option('-n, --name ', 'The app version name') 23 | .env('INSTABUG_APP_VERSION_NAME') 24 | .makeOptionMandatory(), 25 | ) 26 | .addOption( 27 | new Option('-c, --code ', 'The app version code') 28 | .env('INSTABUG_APP_VERSION_CODE') 29 | .makeOptionMandatory(), 30 | ) 31 | .addOption( 32 | new Option('-l, --label ', "The CodePush label if it's a CodePush release").env( 33 | 'INSTABUG_APP_VERSION_LABEL', 34 | ), 35 | ) 36 | .action(function (this: Command) { 37 | const options = this.opts(); 38 | uploadSourcemaps(options); 39 | }) 40 | .showHelpAfterError(); 41 | -------------------------------------------------------------------------------- /cli/index.ts: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | import { Command } from 'commander'; 3 | 4 | import { uploadSourcemapsCommand } from './commands/UploadSourcemaps'; 5 | import { UploadSoFilesCommand } from './commands/UploadSoFiles'; 6 | 7 | const program = new Command(); 8 | 9 | program 10 | .name('instabug') 11 | .version('1.0.0-beta1') 12 | .description('A CLI for uploading source maps to Instabug dashboard.') 13 | .usage('[command]') 14 | .addCommand(uploadSourcemapsCommand) 15 | .addCommand(UploadSoFilesCommand); 16 | 17 | program.parse(process.argv); 18 | -------------------------------------------------------------------------------- /cli/upload/index.ts: -------------------------------------------------------------------------------- 1 | export * from './uploadSourcemaps'; 2 | export * from './uploadSoFiles'; 3 | -------------------------------------------------------------------------------- /cli/upload/uploadSourcemaps.ts: -------------------------------------------------------------------------------- 1 | import axios from 'axios'; 2 | import FormData from 'form-data'; 3 | import fs from 'fs'; 4 | 5 | export interface UploadSourcemapsOptions { 6 | platform: 'android' | 'ios'; 7 | file: string; 8 | token: string; 9 | name: string; 10 | code: string; 11 | label?: string; 12 | 13 | /** 14 | * Disables logging to the console and prevents process exit on error. 15 | * 16 | * @default false 17 | * */ 18 | silent?: boolean; 19 | } 20 | 21 | /** 22 | * Uploads JavaScript sourcemaps to Instabug. 23 | * 24 | * @param opts Options for the sourcemaps upload process. 25 | * @returns A promise that resolves to a boolean indicating whether the upload was successful. 26 | */ 27 | export const uploadSourcemaps = async (opts: UploadSourcemapsOptions): Promise => { 28 | const fileName = `${opts.platform}-sourcemap.json`; 29 | const fileBlob = fs.readFileSync(opts.file); 30 | 31 | const version = { 32 | code: opts.code, 33 | name: opts.name, 34 | codepush: opts.label, 35 | }; 36 | 37 | const form = new FormData(); 38 | form.append('app_version', JSON.stringify(version)); 39 | form.append('symbols_file', fileBlob, fileName); 40 | form.append('application_token', opts.token); 41 | form.append('platform', 'react_native'); 42 | form.append('os', opts.platform); 43 | 44 | if (!opts.silent) { 45 | console.log('Uploading Source map file...'); 46 | } 47 | 48 | try { 49 | const response = await axios.post('https://api.instabug.com/api/sdk/v3/symbols_files', form, { 50 | headers: form.getHeaders(), 51 | }); 52 | 53 | const appVersion = version.codepush 54 | ? `${version.name} (${version.code})+codepush:${version.codepush}` 55 | : `${version.name} (${version.code})`; 56 | 57 | if (!opts.silent) { 58 | console.log(`Successfully uploaded Source maps for version: ${appVersion}`); 59 | console.log(response.data); 60 | } 61 | 62 | return true; 63 | } catch (err) { 64 | if (!opts.silent) { 65 | console.error( 66 | 'Failed to upload source maps:', 67 | axios.isAxiosError(err) ? err.response?.data : err, 68 | ); 69 | } 70 | 71 | return false; 72 | } 73 | }; 74 | -------------------------------------------------------------------------------- /dangerfile.ts: -------------------------------------------------------------------------------- 1 | import collectCoverage, { ReportType } from '@instabug/danger-plugin-coverage'; 2 | import { danger, fail, schedule, warn } from 'danger'; 3 | 4 | const hasSourceChanges = danger.git.modified_files.some((file) => file.startsWith('src/')); 5 | const declaredTrivial = 6 | !hasSourceChanges || danger.github.issue.labels.some((label) => label.name === 'trivial'); 7 | 8 | // Make sure PR has a description. 9 | async function hasDescription() { 10 | const linesOfCode = (await danger.git.linesOfCode()) ?? 0; 11 | const hasNoDiscription = danger.github.pr.body.includes('> Description goes here'); 12 | if (hasNoDiscription && linesOfCode > 10) { 13 | fail('Please provide a summary of the changes in the pull request description.'); 14 | } 15 | 16 | if (!danger.git.modified_files.includes('CHANGELOG.md') && !declaredTrivial) { 17 | warn( 18 | 'You have not included a CHANGELOG entry! \nYou can find it at [CHANGELOG.md](https://github.com/Instabug/Instabug-React-Native/blob/master/CHANGELOG.md).', 19 | ); 20 | } 21 | } 22 | 23 | schedule(hasDescription()); 24 | 25 | collectCoverage([ 26 | { 27 | label: 'JavaScript', 28 | type: ReportType.LCOV, 29 | filePath: 'coverage/lcov.info', 30 | threshold: 90, 31 | }, 32 | { 33 | label: 'Android', 34 | type: ReportType.JACOCO, 35 | filePath: 'coverage/jacocoTestReport.xml', 36 | threshold: 40, 37 | }, 38 | { 39 | label: 'iOS', 40 | type: ReportType.XCODE, 41 | filePath: 'coverage/xcode.json', 42 | threshold: 30, 43 | }, 44 | ]); 45 | -------------------------------------------------------------------------------- /examples/default/.env: -------------------------------------------------------------------------------- 1 | GOOGLE_MAPS_API_KEY = API_KEY 2 | -------------------------------------------------------------------------------- /examples/default/.watchmanconfig: -------------------------------------------------------------------------------- 1 | {} 2 | -------------------------------------------------------------------------------- /examples/default/android/app/debug.keystore: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Instabug/Instabug-React-Native/7ff76ead15088db09079ef0a54e57a2a6d6ef9f8/examples/default/android/app/debug.keystore -------------------------------------------------------------------------------- /examples/default/android/app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # By default, the flags in this file are appended to flags specified 3 | # in /usr/local/Cellar/android-sdk/24.3.3/tools/proguard/proguard-android.txt 4 | # You can edit the include path and order by changing the proguardFiles 5 | # directive in build.gradle. 6 | # 7 | # For more details, see 8 | # http://developer.android.com/guide/developing/tools/proguard.html 9 | 10 | # Add any project specific keep options here: 11 | -------------------------------------------------------------------------------- /examples/default/android/app/src/androidTest/java/com/instabug/react/example/DetoxTest.java: -------------------------------------------------------------------------------- 1 | package com.instabug.react.example; 2 | 3 | import com.wix.detox.Detox; 4 | import com.wix.detox.config.DetoxConfig; 5 | 6 | import org.junit.Rule; 7 | import org.junit.Test; 8 | import org.junit.runner.RunWith; 9 | 10 | import androidx.test.ext.junit.runners.AndroidJUnit4; 11 | import androidx.test.filters.LargeTest; 12 | import androidx.test.rule.ActivityTestRule; 13 | 14 | @RunWith(AndroidJUnit4.class) 15 | @LargeTest 16 | public class DetoxTest { 17 | 18 | @Rule 19 | public ActivityTestRule mActivityRule = new ActivityTestRule<>(MainActivity.class, false, false); 20 | 21 | @Test 22 | public void runDetoxTests() { 23 | DetoxConfig detoxConfig = new DetoxConfig(); 24 | detoxConfig.idlePolicyConfig.masterTimeoutSec = 90; 25 | detoxConfig.idlePolicyConfig.idleResourceTimeoutSec = 60; 26 | detoxConfig.rnContextLoadTimeoutSec = (com.instabug.react.example.BuildConfig.DEBUG ? 180 : 60); 27 | 28 | Detox.runTests(mActivityRule, detoxConfig); 29 | } 30 | } -------------------------------------------------------------------------------- /examples/default/android/app/src/debug/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 11 | 12 | -------------------------------------------------------------------------------- /examples/default/android/app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 14 | 21 | 22 | 23 | 24 | 25 | 26 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /examples/default/android/app/src/main/cpp/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | 2 | # For more information about using CMake with Android Studio, read the 3 | # documentation: https://d.android.com/studio/projects/add-native-code.html. 4 | # For more examples on how to use CMake, see https://github.com/android/ndk-samples. 5 | 6 | # Sets the minimum CMake version required for this project. 7 | cmake_minimum_required(VERSION 3.22.1) 8 | 9 | # Declares the project name. The project name can be accessed via ${ PROJECT_NAME}, 10 | # Since this is the top level CMakeLists.txt, the project name is also accessible 11 | # with ${CMAKE_PROJECT_NAME} (both CMake variables are in-sync within the top level 12 | # build script scope). 13 | project("native-lib") 14 | 15 | # Creates and names a library, sets it as either STATIC 16 | # or SHARED, and provides the relative paths to its source code. 17 | # You can define multiple libraries, and CMake builds them for you. 18 | # Gradle automatically packages shared libraries with your APK. 19 | # 20 | # In this top level CMakeLists.txt, ${CMAKE_PROJECT_NAME} is used to define 21 | # the target library name; in the sub-module's CMakeLists.txt, ${PROJECT_NAME} 22 | # is preferred for the same purpose. 23 | # 24 | # In order to load a library into your app from Java/Kotlin, you must call 25 | # System.loadLibrary() and pass the name of the library defined here; 26 | # for GameActivity/NativeActivity derived applications, the same library name must be 27 | # used in the AndroidManifest.xml file. 28 | add_library(${CMAKE_PROJECT_NAME} SHARED 29 | # List C/C++ source files with relative paths to this CMakeLists.txt. 30 | native-lib.cpp 31 | crasher.c 32 | crasher_2.c 33 | crasher_3.c 34 | crasher_4.cpp 35 | ) 36 | find_library( # Sets the name of the path variable. 37 | log-lib 38 | 39 | # Specifies the name of the NDK library that 40 | # you want CMake to locate. 41 | log) 42 | 43 | 44 | # Specifies libraries CMake should link to your target library. You 45 | # can link libraries from various origins, such as libraries defined in this 46 | # build script, prebuilt third-party libraries, or Android system libraries. 47 | target_link_libraries(${CMAKE_PROJECT_NAME} 48 | # List libraries link to the target library 49 | android 50 | log 51 | ${log-lib} 52 | ) 53 | -------------------------------------------------------------------------------- /examples/default/android/app/src/main/cpp/crasher.c: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include "crasher_2.h" 7 | 8 | /************* SIGSEGV *******************************/ 9 | JNIEXPORT void JNICALL 10 | Java_com_instabug_react_example_nativeLibs_CppNativeLib_causeSIGSEGVCrash(JNIEnv *env, jobject thiz) { 11 | causeSIGSEGVCrashF1(); 12 | } 13 | 14 | /*****************************************************/ 15 | 16 | /************* SIGABRT *******************************/ 17 | void JNICALL 18 | Java_com_instabug_react_example_nativeLibs_CppNativeLib_causeSIGABRTCrash(JNIEnv *env, jobject thiz) { 19 | causeSIGABRTCrashF1(); 20 | } 21 | /****************************************************/ 22 | 23 | /************* SIGFPE *******************************/ 24 | void JNICALL 25 | Java_com_instabug_react_example_nativeLibs_CppNativeLib_causeSIGFPECrash(JNIEnv *env, jobject thiz) { 26 | causeSIGFPECrashF1(); 27 | } 28 | /***************************************************/ 29 | 30 | /************* SIGILL *******************************/ 31 | 32 | void JNICALL 33 | Java_com_instabug_react_example_nativeLibs_CppNativeLib_causeSIGILLCrash(JNIEnv *env, jobject thiz) { 34 | causeSIGILLCrashF1(); 35 | } 36 | /***************************************************/ 37 | 38 | /************* SIGBUS *******************************/ 39 | void JNICALL 40 | Java_com_instabug_react_example_nativeLibs_CppNativeLib_causeSIGBUSCrash(JNIEnv *env, jobject thiz) { 41 | causeSIGBUSCrashF1(); 42 | } 43 | /***************************************************/ 44 | 45 | /************* SIGTRAP *******************************/ 46 | void JNICALL 47 | Java_com_instabug_react_example_nativeLibs_CppNativeLib_causeSIGTRAPCrash(JNIEnv *env, jobject thiz) { 48 | causeSIGTRAPCrashF1(); 49 | } 50 | /***************************************************/ 51 | -------------------------------------------------------------------------------- /examples/default/android/app/src/main/cpp/crasher_2.c: -------------------------------------------------------------------------------- 1 | 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include "crasher_3.h" 8 | 9 | /************* SIGSEGV *******************************/ 10 | void causeSIGSEGVCrashF1() { 11 | causeSIGSEGVCrashF2(); 12 | } 13 | /*****************************************************/ 14 | 15 | /************* SIGABRT *******************************/ 16 | void causeSIGABRTCrashF1() { 17 | causeSIGABRTCrashF2(); 18 | } 19 | /****************************************************/ 20 | 21 | /************* SIGFPE *******************************/ 22 | 23 | 24 | void causeSIGFPECrashF1() { 25 | causeSIGFPECrashF2(); 26 | } 27 | /***************************************************/ 28 | 29 | /************* SIGILL *******************************/ 30 | 31 | void causeSIGILLCrashF1() { 32 | causeSIGILLCrashF2(); 33 | } 34 | /***************************************************/ 35 | 36 | /************* SIGBUS *******************************/ 37 | 38 | void causeSIGBUSCrashF1() { 39 | causeSIGBUSCrashF2(); 40 | } 41 | /***************************************************/ 42 | 43 | /************* SIGTRAP *******************************/ 44 | void causeSIGTRAPCrashF1() { 45 | causeSIGTRAPCrashF2(); 46 | } 47 | /***************************************************/ 48 | -------------------------------------------------------------------------------- /examples/default/android/app/src/main/cpp/crasher_2.h: -------------------------------------------------------------------------------- 1 | 2 | 3 | /************* SIGSEGV *******************************/ 4 | void causeSIGSEGVCrashF3(); 5 | 6 | void causeSIGSEGVCrashF2(); 7 | 8 | void causeSIGSEGVCrashF1(); 9 | 10 | /*****************************************************/ 11 | 12 | /************* SIGABRT *******************************/ 13 | void causeSIGABRTCrashF3(); 14 | 15 | void causeSIGABRTCrashF2(); 16 | 17 | void causeSIGABRTCrashF1(); 18 | 19 | /****************************************************/ 20 | 21 | /************* SIGFPE *******************************/ 22 | 23 | int causeSIGFPECrashF3(); 24 | 25 | void causeSIGFPECrashF2(); 26 | 27 | void causeSIGFPECrashF1(); 28 | 29 | /***************************************************/ 30 | 31 | /************* SIGILL *******************************/ 32 | 33 | void causeSIGILLCrashF3(); 34 | 35 | void causeSIGILLCrashF2(); 36 | 37 | void causeSIGILLCrashF1(); 38 | 39 | /***************************************************/ 40 | 41 | /************* SIGBUS *******************************/ 42 | 43 | void causeSIGBUSCrashF3(); 44 | 45 | void causeSIGBUSCrashF2(); 46 | 47 | void causeSIGBUSCrashF1(); 48 | 49 | /***************************************************/ 50 | 51 | /************* SIGTRAP *******************************/ 52 | 53 | void causeSIGTRAPCrashF3(); 54 | 55 | void causeSIGTRAPCrashF2(); 56 | 57 | void causeSIGTRAPCrashF1(); 58 | /***************************************************/ 59 | -------------------------------------------------------------------------------- /examples/default/android/app/src/main/cpp/crasher_3.c: -------------------------------------------------------------------------------- 1 | 2 | #pragma GCC optimize ("O0") 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include "crasher_4.h" 8 | 9 | /************* SIGSEGV *******************************/ 10 | void causeSIGSEGVCrashF2() { 11 | causeSIGSEGVCrashF3(NULL); 12 | } 13 | /*****************************************************/ 14 | 15 | /************* SIGABRT *******************************/ 16 | void causeSIGABRTCrashF2() { 17 | causeSIGABRTCrashF3(); 18 | } 19 | /****************************************************/ 20 | 21 | /************* SIGFPE *******************************/ 22 | void causeSIGFPECrashF2() { 23 | unsigned int *bad_pointer = (unsigned int *)(0xdeadbeef); 24 | *bad_pointer=0xfeedface; 25 | } 26 | /***************************************************/ 27 | 28 | /************* SIGILL *******************************/ 29 | void causeSIGILLCrashF2() { 30 | causeSIGILLCrashF3(); 31 | } 32 | /***************************************************/ 33 | 34 | /************* SIGBUS *******************************/ 35 | void causeSIGBUSCrashF2() { 36 | causeSIGBUSCrashF3(); 37 | } 38 | /***************************************************/ 39 | 40 | /************* SIGTRAP *******************************/ 41 | void causeSIGTRAPCrashF2() { 42 | causeSIGTRAPCrashF3(); 43 | } 44 | /***************************************************/ 45 | -------------------------------------------------------------------------------- /examples/default/android/app/src/main/cpp/crasher_3.h: -------------------------------------------------------------------------------- 1 | 2 | /************* SIGSEGV *******************************/ 3 | void causeSIGSEGVCrashF2(); 4 | /*****************************************************/ 5 | 6 | /************* SIGABRT *******************************/ 7 | void causeSIGABRTCrashF2(); 8 | /****************************************************/ 9 | 10 | /************* SIGFPE *******************************/ 11 | void causeSIGFPECrashF2(); 12 | /***************************************************/ 13 | 14 | /************* SIGILL *******************************/ 15 | void causeSIGILLCrashF2(); 16 | /***************************************************/ 17 | 18 | /************* SIGBUS *******************************/ 19 | void causeSIGBUSCrashF2(); 20 | /***************************************************/ 21 | 22 | /************* SIGTRAP *******************************/ 23 | void causeSIGTRAPCrashF2(); 24 | /***************************************************/ 25 | -------------------------------------------------------------------------------- /examples/default/android/app/src/main/cpp/crasher_4.cpp: -------------------------------------------------------------------------------- 1 | 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | extern "C" { 10 | /************* SIGSEGV *******************************/ 11 | void causeSIGSEGVCrashF3(volatile int *i) { 12 | //SIGSEGV 13 | volatile int j = 34 / *i; 14 | } 15 | /*****************************************************/ 16 | 17 | /************* SIGABRT *******************************/ 18 | void causeSIGABRTCrashF3() { 19 | //SIGABRT 20 | throw std::invalid_argument("received invalid value"); 21 | } 22 | /****************************************************/ 23 | 24 | /************* SIGFPE *******************************/ 25 | void causeSIGFPECrashF3() { 26 | //SIGFPE 27 | raise(SIGFPE); 28 | pthread_kill(getpid(), SIGFPE); 29 | } 30 | /***************************************************/ 31 | 32 | /************* SIGILL *******************************/ 33 | 34 | int causeSIGILLCrashF3() { 35 | //SIGILL 36 | raise(SIGILL); 37 | pthread_kill(getpid(), SIGILL); 38 | } 39 | /***************************************************/ 40 | 41 | /************* SIGBUS *******************************/ 42 | 43 | void causeSIGBUSCrashF3() { 44 | //SIGBUS 45 | raise(SIGBUS); 46 | pthread_kill(getpid(), SIGBUS); 47 | } 48 | /***************************************************/ 49 | 50 | /************* SIGTRAP *******************************/ 51 | 52 | void causeSIGTRAPCrashF3() { 53 | //SIGBUS 54 | __builtin_trap(); 55 | } 56 | /***************************************************/ 57 | } 58 | -------------------------------------------------------------------------------- /examples/default/android/app/src/main/cpp/crasher_4.h: -------------------------------------------------------------------------------- 1 | 2 | /************* SIGSEGV *******************************/ 3 | void causeSIGSEGVCrashF3(int* i); 4 | /*****************************************************/ 5 | 6 | /************* SIGABRT *******************************/ 7 | void causeSIGABRTCrashF3(); 8 | /****************************************************/ 9 | 10 | /************* SIGFPE *******************************/ 11 | void causeSIGFPECrashF3(); 12 | /***************************************************/ 13 | 14 | /************* SIGILL *******************************/ 15 | int causeSIGILLCrashF3(); 16 | /***************************************************/ 17 | 18 | /************* SIGBUS *******************************/ 19 | void causeSIGBUSCrashF3(); 20 | /***************************************************/ 21 | 22 | /************* SIGTRAP *******************************/ 23 | void causeSIGTRAPCrashF3(); 24 | /***************************************************/ 25 | -------------------------------------------------------------------------------- /examples/default/android/app/src/main/cpp/native-lib.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | 6 | 7 | 8 | /* 9 | * Throws invalid argument exception 10 | */ 11 | extern "C" 12 | JNIEXPORT void JNICALL 13 | Java_com_instabug_react_example_nativeLibs_CppNativeLib_crashNDK(JNIEnv *env, 14 | jobject object) { 15 | __android_log_print(ANDROID_LOG_DEBUG, "NativeC++", "%s", "received invalid value"); 16 | 17 | // in Android SDK it's equivalent to causeSIGABRTCrash() 18 | throw std::invalid_argument("received invalid value"); 19 | } -------------------------------------------------------------------------------- /examples/default/android/app/src/main/java/com/instabug/react/example/MainActivity.kt: -------------------------------------------------------------------------------- 1 | package com.instabug.react.example 2 | 3 | import com.facebook.react.ReactActivity 4 | import com.facebook.react.ReactActivityDelegate 5 | import com.facebook.react.defaults.DefaultNewArchitectureEntryPoint.fabricEnabled 6 | import com.facebook.react.defaults.DefaultReactActivityDelegate 7 | 8 | class MainActivity : ReactActivity() { 9 | 10 | /** 11 | * Returns the name of the main component registered from JavaScript. This is used to schedule 12 | * rendering of the component. 13 | */ 14 | override fun getMainComponentName(): String = "InstabugExample" 15 | 16 | /** 17 | * Returns the instance of the [ReactActivityDelegate]. We use [DefaultReactActivityDelegate] 18 | * which allows you to enable New Architecture with a single boolean flags [fabricEnabled] 19 | */ 20 | override fun createReactActivityDelegate(): ReactActivityDelegate = 21 | DefaultReactActivityDelegate(this, mainComponentName, fabricEnabled) 22 | } 23 | -------------------------------------------------------------------------------- /examples/default/android/app/src/main/java/com/instabug/react/example/MainApplication.kt: -------------------------------------------------------------------------------- 1 | package com.instabug.react.example 2 | 3 | import android.app.Application 4 | import com.facebook.react.PackageList 5 | import com.facebook.react.ReactApplication 6 | import com.facebook.react.ReactHost 7 | import com.facebook.react.ReactNativeHost 8 | import com.facebook.react.ReactPackage 9 | import com.facebook.react.defaults.DefaultNewArchitectureEntryPoint.load 10 | import com.facebook.react.defaults.DefaultReactHost.getDefaultReactHost 11 | import com.facebook.react.defaults.DefaultReactNativeHost 12 | import com.facebook.soloader.SoLoader 13 | 14 | class MainApplication : Application(), ReactApplication { 15 | 16 | override val reactNativeHost: ReactNativeHost = 17 | object : DefaultReactNativeHost(this) { 18 | override fun getPackages(): List = 19 | PackageList(this).packages.apply { 20 | // Packages that cannot be autolinked yet can be added manually here, for example: 21 | add(RNInstabugExampleReactnativePackage()) 22 | } 23 | 24 | override fun getJSMainModuleName(): String = "index" 25 | 26 | override fun getUseDeveloperSupport(): Boolean = BuildConfig.DEBUG 27 | 28 | override val isNewArchEnabled: Boolean = BuildConfig.IS_NEW_ARCHITECTURE_ENABLED 29 | override val isHermesEnabled: Boolean = BuildConfig.IS_HERMES_ENABLED 30 | } 31 | 32 | override val reactHost: ReactHost 33 | get() = getDefaultReactHost(applicationContext, reactNativeHost) 34 | 35 | override fun onCreate() { 36 | super.onCreate() 37 | SoLoader.init(this, false) 38 | if (BuildConfig.IS_NEW_ARCHITECTURE_ENABLED) { 39 | // If you opted-in for the New Architecture, we load the native entry point for this app. 40 | load() 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /examples/default/android/app/src/main/java/com/instabug/react/example/RNInstabugExampleReactnativePackage.java: -------------------------------------------------------------------------------- 1 | package com.instabug.react.example; 2 | 3 | import androidx.annotation.NonNull; 4 | 5 | import com.facebook.react.ReactPackage; 6 | import com.facebook.react.bridge.NativeModule; 7 | import com.facebook.react.bridge.ReactApplicationContext; 8 | import com.facebook.react.uimanager.ViewManager; 9 | 10 | import java.util.ArrayList; 11 | import java.util.Collections; 12 | import java.util.List; 13 | 14 | public class RNInstabugExampleReactnativePackage implements ReactPackage { 15 | 16 | private static final String TAG = RNInstabugExampleReactnativePackage.class.getSimpleName(); 17 | 18 | public RNInstabugExampleReactnativePackage() {} 19 | 20 | @NonNull 21 | @Override 22 | public List createNativeModules(@NonNull ReactApplicationContext reactContext) { 23 | List modules = new ArrayList<>(); 24 | modules.add(new RNInstabugExampleCrashReportingModule(reactContext)); 25 | return modules; 26 | } 27 | 28 | @NonNull 29 | @Override 30 | public List createViewManagers(@NonNull ReactApplicationContext reactContext) { 31 | return Collections.emptyList(); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /examples/default/android/app/src/main/java/com/instabug/react/example/nativeLibs/CppNativeLib.java: -------------------------------------------------------------------------------- 1 | package com.instabug.react.example.nativeLibs; 2 | 3 | /** 4 | * C++ Native library bridge. 5 | */ 6 | public class CppNativeLib { 7 | 8 | static { 9 | System.loadLibrary("native-lib"); 10 | } 11 | 12 | /** 13 | * Crashes the app with an invalid argument exception in the C++ native library. 14 | */ 15 | public static native void crashNDK(); 16 | public static native void causeSIGSEGVCrash(); 17 | public static native void causeSIGABRTCrash(); 18 | public static native void causeSIGFPECrash(); 19 | public static native void causeSIGILLCrash(); 20 | public static native void causeSIGBUSCrash(); 21 | public static native void causeSIGTRAPCrash(); 22 | 23 | 24 | } 25 | -------------------------------------------------------------------------------- /examples/default/android/app/src/main/res/drawable/rn_edit_text_material.xml: -------------------------------------------------------------------------------- 1 | 2 | 16 | 21 | 22 | 23 | 32 | 33 | 34 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /examples/default/android/app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Instabug/Instabug-React-Native/7ff76ead15088db09079ef0a54e57a2a6d6ef9f8/examples/default/android/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /examples/default/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Instabug/Instabug-React-Native/7ff76ead15088db09079ef0a54e57a2a6d6ef9f8/examples/default/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /examples/default/android/app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Instabug/Instabug-React-Native/7ff76ead15088db09079ef0a54e57a2a6d6ef9f8/examples/default/android/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /examples/default/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Instabug/Instabug-React-Native/7ff76ead15088db09079ef0a54e57a2a6d6ef9f8/examples/default/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /examples/default/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Instabug/Instabug-React-Native/7ff76ead15088db09079ef0a54e57a2a6d6ef9f8/examples/default/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /examples/default/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Instabug/Instabug-React-Native/7ff76ead15088db09079ef0a54e57a2a6d6ef9f8/examples/default/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /examples/default/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Instabug/Instabug-React-Native/7ff76ead15088db09079ef0a54e57a2a6d6ef9f8/examples/default/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /examples/default/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Instabug/Instabug-React-Native/7ff76ead15088db09079ef0a54e57a2a6d6ef9f8/examples/default/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /examples/default/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Instabug/Instabug-React-Native/7ff76ead15088db09079ef0a54e57a2a6d6ef9f8/examples/default/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /examples/default/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Instabug/Instabug-React-Native/7ff76ead15088db09079ef0a54e57a2a6d6ef9f8/examples/default/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /examples/default/android/app/src/main/res/raw/instabug_config.json: -------------------------------------------------------------------------------- 1 | { 2 | "instabug-domain": "api.instabug.com", 3 | "apm-domain": "api-apm.instabug.com" 4 | } 5 | -------------------------------------------------------------------------------- /examples/default/android/app/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | Instabug - React Native 3 | 4 | 5 | -------------------------------------------------------------------------------- /examples/default/android/app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /examples/default/android/app/src/main/res/xml/network_security_config.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 10.0.2.2 5 | localhost 6 | api.instabug.com 7 | api-apm.instabug.com 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /examples/default/android/build.gradle: -------------------------------------------------------------------------------- 1 | // Top-level build file where you can add configuration options common to all sub-projects/modules. 2 | 3 | buildscript { 4 | ext { 5 | buildToolsVersion = "34.0.0" 6 | minSdkVersion = 23 7 | compileSdkVersion = 34 8 | targetSdkVersion = 34 9 | ndkVersion = "26.1.10909125" 10 | kotlinVersion = "1.9.24" 11 | } 12 | repositories { 13 | google() 14 | mavenCentral() 15 | } 16 | dependencies { 17 | classpath("com.android.tools.build:gradle:8.1.0") 18 | classpath("com.facebook.react:react-native-gradle-plugin") 19 | classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlinVersion") 20 | } 21 | } 22 | 23 | allprojects { 24 | repositories { 25 | maven { 26 | url("$rootDir/../node_modules/detox/Detox-android") 27 | } 28 | 29 | maven { 30 | credentials { 31 | username System.getenv("DREAM11_MAVEN_USERNAME") 32 | password System.getenv("DREAM11_MAVEN_PASSWORD") 33 | } 34 | url "https://mvn.instabug.com/nexus/repository/dream-11" 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /examples/default/android/gradle.properties: -------------------------------------------------------------------------------- 1 | # Project-wide Gradle settings. 2 | 3 | # IDE (e.g. Android Studio) users: 4 | # Gradle settings configured through the IDE *will override* 5 | # any settings specified in this file. 6 | 7 | # For more details on how to configure your build environment visit 8 | # http://www.gradle.org/docs/current/userguide/build_environment.html 9 | 10 | # Specifies the JVM arguments used for the daemon process. 11 | # The setting is particularly useful for tweaking memory settings. 12 | # Default value: -Xmx512m -XX:MaxMetaspaceSize=256m 13 | org.gradle.jvmargs=-Xmx2048m -XX:MaxMetaspaceSize=512m 14 | 15 | # When configured, Gradle will run in incubating parallel mode. 16 | # This option should only be used with decoupled projects. More details, visit 17 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects 18 | # org.gradle.parallel=true 19 | 20 | # AndroidX package structure to make it clearer which packages are bundled with the 21 | # Android operating system, and which are packaged with your app's APK 22 | # https://developer.android.com/topic/libraries/support-library/androidx-rn 23 | android.useAndroidX=true 24 | 25 | # Use this property to specify which architecture you want to build. 26 | # You can also override it from the CLI using 27 | # ./gradlew -PreactNativeArchitectures=x86_64 28 | reactNativeArchitectures=armeabi-v7a,arm64-v8a,x86,x86_64 29 | 30 | # Use this property to enable support to the new architecture. 31 | # This will allow you to use TurboModules and the Fabric render in 32 | # your application. You should enable this flag either if you want 33 | # to write custom TurboModules/Fabric components OR use libraries that 34 | # are providing them. 35 | newArchEnabled=false 36 | 37 | # Use this property to enable or disable the Hermes JS engine. 38 | # If set to false, you will be using JSC instead. 39 | hermesEnabled=true 40 | 41 | -------------------------------------------------------------------------------- /examples/default/android/gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Instabug/Instabug-React-Native/7ff76ead15088db09079ef0a54e57a2a6d6ef9f8/examples/default/android/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /examples/default/android/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.8-all.zip 3 | networkTimeout=10000 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | -------------------------------------------------------------------------------- /examples/default/android/settings.gradle: -------------------------------------------------------------------------------- 1 | pluginManagement { includeBuild("../node_modules/@react-native/gradle-plugin") } 2 | plugins { id("com.facebook.react.settings") } 3 | extensions.configure(com.facebook.react.ReactSettingsExtension){ ex -> ex.autolinkLibrariesFromCommand() } 4 | rootProject.name = 'InstabugExample' 5 | include ':app' 6 | includeBuild('../node_modules/@react-native/gradle-plugin') 7 | -------------------------------------------------------------------------------- /examples/default/app.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "InstabugExample", 3 | "displayName": "Instabug - React Native" 4 | } 5 | -------------------------------------------------------------------------------- /examples/default/babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | presets: ['module:@react-native/babel-preset'], 3 | plugins: ['react-native-reanimated/plugin'], 4 | }; 5 | -------------------------------------------------------------------------------- /examples/default/e2e/config.json: -------------------------------------------------------------------------------- 1 | { 2 | "preset": "ts-jest", 3 | "testEnvironment": "./environment", 4 | "testTimeout": 120000, 5 | "reporters": ["detox/runners/jest/reporter"], 6 | "verbose": true, 7 | "rootDir": ".", 8 | "testMatch": ["/**/*.e2e.ts"], 9 | "maxWorkers": 1, 10 | "globalSetup": "detox/runners/jest/globalSetup", 11 | "globalTeardown": "detox/runners/jest/globalTeardown" 12 | } 13 | -------------------------------------------------------------------------------- /examples/default/e2e/environment.js: -------------------------------------------------------------------------------- 1 | const { DetoxCircusEnvironment } = require('detox/runners/jest'); 2 | 3 | class CustomDetoxEnvironment extends DetoxCircusEnvironment { 4 | constructor(config, context) { 5 | super(config, context); 6 | 7 | // Can be safely removed, if you are content with the default value (=300000ms) 8 | this.initTimeout = 300000; 9 | } 10 | } 11 | 12 | module.exports = CustomDetoxEnvironment; 13 | -------------------------------------------------------------------------------- /examples/default/e2e/reportBug.e2e.ts: -------------------------------------------------------------------------------- 1 | import { expect, waitFor } from 'detox'; 2 | 3 | import { getElement } from './utils/elements'; 4 | import mockData from './utils/mockData'; 5 | 6 | beforeEach(async () => { 7 | await device.launchApp(); 8 | await device.reloadReactNative(); 9 | await device.setURLBlacklist(['https://api.instabug.com']); 10 | }); 11 | 12 | it('reports a bug', async () => { 13 | const floatingButton = getElement('floatingButton'); 14 | await waitFor(floatingButton).toBeVisible().withTimeout(30000); 15 | await floatingButton.tap(); 16 | 17 | const reportBugMenuItemButton = getElement('reportBugMenuItem'); 18 | await waitFor(reportBugMenuItemButton).toBeVisible().withTimeout(30000); 19 | await reportBugMenuItemButton.tap(); 20 | 21 | await getElement('emailField').typeText(mockData.email); 22 | await getElement('commentField').typeText(mockData.bugComment); 23 | await getElement('sendBugButton').tap(); 24 | 25 | await expect(getElement('thanksMessage')).toBeVisible(); 26 | }); 27 | -------------------------------------------------------------------------------- /examples/default/e2e/utils/elements.ts: -------------------------------------------------------------------------------- 1 | import { by, device, element } from 'detox'; 2 | 3 | const elements = { 4 | floatingButton: { 5 | ios: () => element(by.id('IBGFloatingButtonAccessibilityIdentifier')), 6 | android: () => element(by.type('android.widget.ImageButton')), 7 | }, 8 | reportBugMenuItem: () => element(by.text('Report a bug')), 9 | emailField: { 10 | ios: () => element(by.id('IBGBugInputViewEmailFieldAccessibilityIdentifier')), 11 | android: () => element(by.type('android.widget.EditText')).atIndex(0), 12 | }, 13 | commentField: { 14 | ios: () => element(by.id('IBGBugInputViewCommentFieldAccessibilityIdentifier')), 15 | android: () => element(by.type('android.widget.EditText')).atIndex(1), 16 | }, 17 | sendBugButton: { 18 | ios: () => element(by.id('IBGBugVCNextButtonAccessibilityIdentifier')), 19 | android: () => element(by.type('androidx.appcompat.widget.ActionMenuView')), 20 | }, 21 | thanksMessage: { 22 | ios: () => element(by.text('Thank you')), 23 | android: () => element(by.text('Thank you!')), 24 | }, 25 | }; 26 | 27 | /** 28 | * Get element based on current platform. 29 | */ 30 | export function getElement(name: keyof typeof elements) { 31 | if (!elements.hasOwnProperty(name)) { 32 | throw new Error(`Element with name ${name} does not exist.`); 33 | } 34 | 35 | const el = elements[name]; 36 | 37 | if (typeof el === 'function') { 38 | return el(); 39 | } 40 | 41 | if (device.getPlatform() === 'ios') { 42 | return el.ios(); 43 | } 44 | 45 | return el.android(); 46 | } 47 | -------------------------------------------------------------------------------- /examples/default/e2e/utils/mockData.ts: -------------------------------------------------------------------------------- 1 | export default { 2 | email: 'rn_e2e@instabug.com', 3 | bugComment: 'This is a test bug', 4 | }; 5 | -------------------------------------------------------------------------------- /examples/default/index.js: -------------------------------------------------------------------------------- 1 | import 'react-native-gesture-handler'; 2 | import { AppRegistry } from 'react-native'; 3 | 4 | import { name as appName } from './app.json'; 5 | import { App } from './src/App'; 6 | 7 | AppRegistry.registerComponent(appName, () => App); 8 | -------------------------------------------------------------------------------- /examples/default/ios/.xcode.env: -------------------------------------------------------------------------------- 1 | export NODE_BINARY=$(command -v node) 2 | -------------------------------------------------------------------------------- /examples/default/ios/InstabugExample.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /examples/default/ios/InstabugExample.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /examples/default/ios/InstabugExample/AppDelegate.h: -------------------------------------------------------------------------------- 1 | #import 2 | #import 3 | 4 | @interface AppDelegate : RCTAppDelegate 5 | 6 | @end 7 | -------------------------------------------------------------------------------- /examples/default/ios/InstabugExample/AppDelegate.mm: -------------------------------------------------------------------------------- 1 | #import "AppDelegate.h" 2 | #import 3 | 4 | #import 5 | #import "RNCConfig.h" 6 | 7 | @implementation AppDelegate 8 | 9 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions 10 | { 11 | self.moduleName = @"InstabugExample"; 12 | // You can add your custom initial props in the dictionary below. 13 | // They will be passed down to the ViewController used by React Native. 14 | self.initialProps = @{}; 15 | NSString *googleApiKey = [RNCConfig envFor:@"GOOGLE_MAPS_API_KEY"]; 16 | 17 | [GMSServices provideAPIKey:googleApiKey]; // use the api key obtained from Google Console 18 | 19 | return [super application:application didFinishLaunchingWithOptions:launchOptions]; 20 | } 21 | 22 | - (NSURL *)sourceURLForBridge:(RCTBridge *)bridge 23 | { 24 | return [self bundleURL]; 25 | } 26 | 27 | - (NSURL *)bundleURL 28 | { 29 | #if DEBUG 30 | return [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:@"index"]; 31 | #else 32 | return [[NSBundle mainBundle] URLForResource:@"main" withExtension:@"jsbundle"]; 33 | #endif 34 | } 35 | 36 | /// This method controls whether the `concurrentRoot`feature of React18 is turned on or off. 37 | /// 38 | /// @see: https://reactjs.org/blog/2022/03/29/react-v18.html 39 | /// @note: This requires to be rendering on Fabric (i.e. on the New Architecture). 40 | /// @return: `true` if the `concurrentRoot` feature is enabled. Otherwise, it returns `false`. 41 | - (BOOL)concurrentRootEnabled 42 | { 43 | return true; 44 | } 45 | 46 | @end 47 | -------------------------------------------------------------------------------- /examples/default/ios/InstabugExample/Images.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images": [ 3 | { 4 | "idiom": "iphone", 5 | "scale": "2x", 6 | "size": "20x20" 7 | }, 8 | { 9 | "idiom": "iphone", 10 | "scale": "3x", 11 | "size": "20x20" 12 | }, 13 | { 14 | "idiom": "iphone", 15 | "scale": "2x", 16 | "size": "29x29" 17 | }, 18 | { 19 | "idiom": "iphone", 20 | "scale": "3x", 21 | "size": "29x29" 22 | }, 23 | { 24 | "idiom": "iphone", 25 | "scale": "2x", 26 | "size": "40x40" 27 | }, 28 | { 29 | "idiom": "iphone", 30 | "scale": "3x", 31 | "size": "40x40" 32 | }, 33 | { 34 | "idiom": "iphone", 35 | "scale": "2x", 36 | "size": "60x60" 37 | }, 38 | { 39 | "idiom": "iphone", 40 | "scale": "3x", 41 | "size": "60x60" 42 | }, 43 | { 44 | "idiom": "ios-marketing", 45 | "scale": "1x", 46 | "size": "1024x1024" 47 | } 48 | ], 49 | "info": { 50 | "author": "xcode", 51 | "version": 1 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /examples/default/ios/InstabugExample/Images.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info": { 3 | "version": 1, 4 | "author": "xcode" 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /examples/default/ios/InstabugExample/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleDisplayName 8 | Instabug - React Native 9 | CFBundleExecutable 10 | $(EXECUTABLE_NAME) 11 | CFBundleIdentifier 12 | $(PRODUCT_BUNDLE_IDENTIFIER) 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | $(PRODUCT_NAME) 17 | CFBundlePackageType 18 | APPL 19 | CFBundleShortVersionString 20 | 1.0 21 | CFBundleSignature 22 | ???? 23 | CFBundleVersion 24 | 1 25 | LSRequiresIPhoneOS 26 | 27 | NSAppTransportSecurity 28 | 29 | 30 | NSAllowsArbitraryLoads 31 | 32 | NSAllowsLocalNetworking 33 | 34 | 35 | NSLocationWhenInUseUsageDescription 36 | 37 | NSMicrophoneUsageDescription 38 | Instabug needs access to your microphone so you can attach voice notes. 39 | NSPhotoLibraryUsageDescription 40 | Instabug needs access to your photo library so you can attach images. 41 | UIAppFonts 42 | 43 | Ionicons.ttf 44 | 45 | UILaunchStoryboardName 46 | LaunchScreen 47 | UIRequiredDeviceCapabilities 48 | 49 | arm64 50 | 51 | UISupportedInterfaceOrientations 52 | 53 | UIInterfaceOrientationPortrait 54 | UIInterfaceOrientationLandscapeLeft 55 | UIInterfaceOrientationLandscapeRight 56 | 57 | UIViewControllerBasedStatusBarAppearance 58 | 59 | 60 | 61 | -------------------------------------------------------------------------------- /examples/default/ios/InstabugExample/Instabug.plist: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Instabug/Instabug-React-Native/7ff76ead15088db09079ef0a54e57a2a6d6ef9f8/examples/default/ios/InstabugExample/Instabug.plist -------------------------------------------------------------------------------- /examples/default/ios/InstabugExample/PrivacyInfo.xcprivacy: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | NSPrivacyAccessedAPITypes 6 | 7 | 8 | NSPrivacyAccessedAPIType 9 | NSPrivacyAccessedAPICategoryFileTimestamp 10 | NSPrivacyAccessedAPITypeReasons 11 | 12 | C617.1 13 | 14 | 15 | 16 | NSPrivacyAccessedAPIType 17 | NSPrivacyAccessedAPICategoryUserDefaults 18 | NSPrivacyAccessedAPITypeReasons 19 | 20 | CA92.1 21 | 22 | 23 | 24 | NSPrivacyAccessedAPIType 25 | NSPrivacyAccessedAPICategorySystemBootTime 26 | NSPrivacyAccessedAPITypeReasons 27 | 28 | 35F9.1 29 | 30 | 31 | 32 | NSPrivacyCollectedDataTypes 33 | 34 | NSPrivacyTracking 35 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /examples/default/ios/InstabugExample/main.m: -------------------------------------------------------------------------------- 1 | #import 2 | 3 | #import "AppDelegate.h" 4 | 5 | int main(int argc, char * argv[]) { 6 | @autoreleasepool { 7 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /examples/default/ios/InstabugTests/IBGConstants.h: -------------------------------------------------------------------------------- 1 | // 2 | // IBGConstants.h 3 | // InstabugSample 4 | // 5 | // Created by Salma Ali on 7/30/19. 6 | // Copyright © 2019 Facebook. All rights reserved. 7 | // 8 | 9 | #ifndef IBGConstants_h 10 | #define IBGConstants_h 11 | 12 | extern NSTimeInterval const EXPECTATION_TIMEOUT; 13 | 14 | #endif /* IBGConstants_h */ 15 | -------------------------------------------------------------------------------- /examples/default/ios/InstabugTests/IBGConstants.m: -------------------------------------------------------------------------------- 1 | // 2 | // IBGConstants.m 3 | // InstabugSampleTests 4 | // 5 | // Created by Salma Ali on 7/30/19. 6 | // Copyright © 2019 Facebook. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | NSTimeInterval const EXPECTATION_TIMEOUT = 10; 12 | -------------------------------------------------------------------------------- /examples/default/ios/InstabugTests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | 24 | 25 | -------------------------------------------------------------------------------- /examples/default/ios/InstabugTests/InstabugCrashReportingTests.m: -------------------------------------------------------------------------------- 1 | #import 2 | #import "Instabug/Instabug.h" 3 | #import "InstabugCrashReportingBridge.h" 4 | #import "OCMock/OCMock.h" 5 | #import "Util/IBGCrashReporting+CP.h" 6 | 7 | @interface InstabugCrashReportingTests : XCTestCase 8 | @property (nonatomic, retain) InstabugCrashReportingBridge *bridge; 9 | @property (nonatomic, strong) id mCrashReporting; 10 | 11 | @end 12 | 13 | @implementation InstabugCrashReportingTests 14 | 15 | - (void)setUp { 16 | self.bridge = [[InstabugCrashReportingBridge alloc] init]; 17 | self.mCrashReporting = OCMClassMock([IBGCrashReporting class]); 18 | 19 | } 20 | 21 | - (void)testSetEnabled { 22 | 23 | [self.bridge setEnabled:NO]; 24 | XCTAssertFalse(IBGCrashReporting.enabled); 25 | 26 | [self.bridge setEnabled:YES]; 27 | XCTAssertTrue(IBGCrashReporting.enabled); 28 | 29 | } 30 | 31 | - (void)testSendJSCrash { 32 | NSDictionary *stackTrace = @{}; 33 | 34 | XCTestExpectation *expectation = [self expectationWithDescription:@"Expected resolve to be called."]; 35 | 36 | RCTPromiseResolveBlock resolve = ^(id result) { 37 | [expectation fulfill]; 38 | }; 39 | RCTPromiseRejectBlock reject = ^(NSString *code, NSString *message, NSError *error) {}; 40 | 41 | [self.bridge sendJSCrash:stackTrace resolver:resolve rejecter:reject]; 42 | 43 | [self waitForExpectations:@[expectation] timeout:1]; 44 | OCMVerify([self.mCrashReporting cp_reportFatalCrashWithStackTrace:stackTrace]); 45 | } 46 | 47 | - (void)testSendNonFatalErrorJsonCrash { 48 | NSDictionary *jsonCrash = @{}; 49 | NSString *fingerPrint = @"fingerprint"; 50 | RCTPromiseResolveBlock resolve = ^(id result) {}; 51 | RCTPromiseRejectBlock reject = ^(NSString *code, NSString *message, NSError *error) {}; 52 | NSDictionary *userAttributes = @{ @"key" : @"value", }; 53 | IBGNonFatalLevel ibgNonFatalLevel = IBGNonFatalLevelInfo; 54 | 55 | 56 | [self.bridge sendHandledJSCrash:jsonCrash userAttributes:userAttributes fingerprint:fingerPrint nonFatalExceptionLevel:ibgNonFatalLevel resolver:resolve rejecter:reject]; 57 | 58 | OCMVerify([self.mCrashReporting cp_reportNonFatalCrashWithStackTrace:jsonCrash 59 | level:IBGNonFatalLevelInfo 60 | groupingString:fingerPrint 61 | userAttributes:userAttributes 62 | ]); 63 | } 64 | 65 | @end 66 | -------------------------------------------------------------------------------- /examples/default/ios/InstabugTests/InstabugFeatureRequestsTests.m: -------------------------------------------------------------------------------- 1 | // 2 | // InstabugFeatureRequestsTests.m 3 | // InstabugSampleTests 4 | // 5 | // Created by Salma Ali on 7/31/19. 6 | // Copyright © 2019 Facebook. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "OCMock/OCMock.h" 11 | #import "InstabugFeatureRequestsBridge.h" 12 | #import 13 | #import "Instabug/Instabug.h" 14 | #import "IBGConstants.h" 15 | 16 | @interface InstabugFeatureRequestsTests : XCTestCase 17 | @property (nonatomic, retain) InstabugFeatureRequestsBridge *instabugBridge; 18 | @end 19 | 20 | @implementation InstabugFeatureRequestsTests 21 | 22 | - (void)setUp { 23 | // Put setup code here. This method is called before the invocation of each test method in the class. 24 | self.instabugBridge = [[InstabugFeatureRequestsBridge alloc] init]; 25 | } 26 | 27 | /* 28 | +------------------------------------------------------------------------+ 29 | | Feature Requets Module | 30 | +------------------------------------------------------------------------+ 31 | */ 32 | 33 | - (void) testgivenArgs$setEmailFieldRequiredForFeatureRequests_whenQuery_thenShouldCallNativeApi { 34 | id mock = OCMClassMock([IBGFeatureRequests class]); 35 | BOOL required = true; 36 | NSArray *actionTypesArray = [NSArray arrayWithObjects: @(IBGActionReportBug), nil]; 37 | IBGAction actionTypes = 0; 38 | for (NSNumber *boxedValue in actionTypesArray) { 39 | actionTypes |= [boxedValue intValue]; 40 | } 41 | OCMStub([mock setEmailFieldRequired:required forAction:actionTypes]); 42 | [self.instabugBridge setEmailFieldRequiredForFeatureRequests:required forAction:actionTypesArray]; 43 | OCMVerify([mock setEmailFieldRequired:required forAction:actionTypes]); 44 | } 45 | 46 | - (void) testgive$show_whenQuery_thenShouldCallNativeApi { 47 | id mock = OCMClassMock([IBGFeatureRequests class]); 48 | OCMStub([mock show]); 49 | [self.instabugBridge show]; 50 | XCTestExpectation *expectation = [self expectationWithDescription:@"Test ME PLX"]; 51 | 52 | [[NSRunLoop mainRunLoop] performBlock:^{ 53 | OCMVerify([mock show]); 54 | [expectation fulfill]; 55 | }]; 56 | 57 | [self waitForExpectationsWithTimeout:EXPECTATION_TIMEOUT handler:nil]; 58 | } 59 | 60 | - (void) testgivenBoolean$setEnabled_whenQuery_thenShouldCallNativeApi { 61 | BOOL enabled = false; 62 | [self.instabugBridge setEnabled:enabled]; 63 | XCTAssertFalse(IBGFeatureRequests.enabled); 64 | } 65 | 66 | 67 | @end 68 | 69 | -------------------------------------------------------------------------------- /examples/default/ios/InstabugTests/Util/IBGCrashReporting+CP.h: -------------------------------------------------------------------------------- 1 | #import 2 | 3 | 4 | @interface IBGCrashReporting (CP) 5 | 6 | + (void)cp_reportFatalCrashWithStackTrace:(NSDictionary*)stackTrace; 7 | 8 | + (void)cp_reportNonFatalCrashWithStackTrace:(NSDictionary*)stackTrace 9 | level:(IBGNonFatalLevel)level 10 | groupingString:(NSString *)groupingString 11 | userAttributes:(NSDictionary *)userAttributes; 12 | @end 13 | 14 | -------------------------------------------------------------------------------- /examples/default/ios/Podfile: -------------------------------------------------------------------------------- 1 | require_relative '../node_modules/react-native/scripts/react_native_pods' 2 | 3 | require_relative '../node_modules/@react-native-community/cli-platform-ios/native_modules' 4 | 5 | platform :ios, '13.4' 6 | prepare_react_native_project! 7 | 8 | linkage = ENV['USE_FRAMEWORKS'] 9 | if linkage != nil 10 | Pod::UI.puts "Configuring Pod with #{linkage}ally linked Frameworks".green 11 | use_frameworks! :linkage => linkage.to_sym 12 | end 13 | 14 | target 'InstabugExample' do 15 | config = use_native_modules! 16 | rn_maps_path = '../node_modules/react-native-maps' 17 | pod 'react-native-google-maps', :path => rn_maps_path 18 | # Flags change depending on the env values. 19 | flags = get_default_flags() 20 | 21 | use_react_native!( 22 | :path => config[:reactNativePath], 23 | # Hermes is now enabled by default. Disable by setting this flag to false. 24 | # Upcoming versions of React Native may rely on get_default_flags(), but 25 | # we make it explicit here to aid in the React Native upgrade process. 26 | :hermes_enabled => flags[:hermes_enabled], 27 | :fabric_enabled => flags[:fabric_enabled], 28 | # An absolute path to your application root. 29 | :app_path => "#{Pod::Config.instance.installation_root}/.." 30 | ) 31 | 32 | target 'InstabugTests' do 33 | inherit! :complete 34 | pod 'OCMock' 35 | end 36 | 37 | post_install do |installer| 38 | react_native_post_install( 39 | installer, 40 | # Set `mac_catalyst_enabled` to `true` in order to apply patches 41 | # necessary for Mac Catalyst builds 42 | :mac_catalyst_enabled => false 43 | ) 44 | end 45 | end 46 | -------------------------------------------------------------------------------- /examples/default/ios/native/CrashReportingExampleModule.h: -------------------------------------------------------------------------------- 1 | #import 2 | #import 3 | #import 4 | 5 | @interface CrashReportingExampleModule : RCTEventEmitter 6 | 7 | - (void)sendNativeNonFatal; 8 | - (void)sendNativeFatalCrash; 9 | - (void)sendFatalHang; 10 | - (void)sendOOM; 11 | 12 | @end 13 | -------------------------------------------------------------------------------- /examples/default/ios/native/CrashReportingExampleModule.m: -------------------------------------------------------------------------------- 1 | #import "CrashReportingExampleModule.h" 2 | #import 3 | #import 4 | 5 | @interface CrashReportingExampleModule() 6 | @property (nonatomic, strong) NSMutableArray *oomBelly; 7 | @property (nonatomic, strong) dispatch_queue_t serialQueue; 8 | @end 9 | 10 | @implementation CrashReportingExampleModule 11 | 12 | - (instancetype)init { 13 | self = [super init]; 14 | if (self) { 15 | self.serialQueue = dispatch_queue_create("QUEUE>SERIAL", DISPATCH_QUEUE_SERIAL); 16 | } 17 | return self; 18 | } 19 | 20 | - (dispatch_queue_t)methodQueue { 21 | return dispatch_get_main_queue(); 22 | } 23 | 24 | + (BOOL)requiresMainQueueSetup 25 | { 26 | return NO; 27 | } 28 | 29 | 30 | - (void)oomCrash { 31 | dispatch_async(self.serialQueue, ^{ 32 | self.oomBelly = [NSMutableArray array]; 33 | [UIApplication.sharedApplication beginBackgroundTaskWithName:@"OOM Crash" expirationHandler:nil]; 34 | while (true) { 35 | unsigned long dinnerLength = 1024 * 1024 * 10; 36 | char *dinner = malloc(sizeof(char) * dinnerLength); 37 | for (int i=0; i < dinnerLength; i++) 38 | { 39 | //write to each byte ensure that the memory pages are actually allocated 40 | dinner[i] = '0'; 41 | } 42 | NSData *plate = [NSData dataWithBytesNoCopy:dinner length:dinnerLength freeWhenDone:YES]; 43 | [self.oomBelly addObject:plate]; 44 | } 45 | }); 46 | } 47 | 48 | RCT_EXPORT_MODULE(CrashReportingExampleModule) 49 | 50 | 51 | RCT_EXPORT_METHOD(sendNativeNonFatal) { 52 | IBGNonFatalException *nonFatalException = [IBGCrashReporting exception:[NSException exceptionWithName:@"native Handled NS Exception" reason:@"Test iOS Handled Crash" userInfo:@{@"Key": @"Value"}]]; 53 | 54 | [nonFatalException report]; 55 | } 56 | 57 | RCT_EXPORT_METHOD(sendNativeFatalCrash) { 58 | NSException *exception = [NSException exceptionWithName:@"native Unhandled NS Exception" reason:@"Test iOS Unhandled Crash" userInfo:nil]; 59 | @throw exception; 60 | } 61 | RCT_EXPORT_METHOD(sendFatalHang) { 62 | [NSThread sleepForTimeInterval:3.0f]; 63 | } 64 | 65 | RCT_EXPORT_METHOD(sendOOM) { 66 | [self oomCrash]; 67 | } 68 | 69 | @synthesize description; 70 | 71 | @synthesize hash; 72 | 73 | @synthesize superclass; 74 | 75 | @end 76 | 77 | 78 | -------------------------------------------------------------------------------- /examples/default/jest.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | preset: 'react-native', 3 | moduleNameMapper: { 4 | '^react-native$': require.resolve('react-native'), 5 | }, 6 | moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'json', 'node'], 7 | transformIgnorePatterns: [ 8 | 'node_modules/(?!(react-native' + 9 | '|@react-native' + 10 | '|react-navigation-tabs' + 11 | '|react-native-splash-screen' + 12 | '|react-native-screens' + 13 | '|react-native-reanimated' + 14 | '|instabug-reactnative' + 15 | ')/)', 16 | ], 17 | }; 18 | -------------------------------------------------------------------------------- /examples/default/metro.config.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | const escape = require('escape-string-regexp'); 3 | const { mergeConfig, getDefaultConfig } = require('@react-native/metro-config'); 4 | const exclusionList = require('metro-config/src/defaults/exclusionList'); 5 | 6 | const root = path.resolve(__dirname, '../..'); 7 | const pkg = require(path.join(root, 'package.json')); 8 | const peerDependencies = Object.keys(pkg.peerDependencies); 9 | const modules = [ 10 | ...peerDependencies, 11 | '@babel/runtime', 12 | 13 | // We need to exclude the `promise` package in the root node_modules directory 14 | // to be able to track unhandled Promise rejections on the correct example app 15 | // Promise object. 16 | 'promise', 17 | ]; 18 | 19 | const config = { 20 | watchFolders: [root], 21 | transformer: { 22 | getTransformOptions: async () => ({ 23 | transform: { 24 | experimentalImportSupport: false, 25 | inlineRequires: true, 26 | }, 27 | }), 28 | }, 29 | resolver: { 30 | blacklistRE: exclusionList( 31 | modules.map((m) => new RegExp(`^${escape(path.join(root, 'node_modules', m))}\\/.*$`)), 32 | ), 33 | extraNodeModules: modules.reduce((acc, name) => { 34 | acc[name] = path.join(__dirname, 'node_modules', name); 35 | return acc; 36 | }, {}), 37 | }, 38 | }; 39 | 40 | module.exports = mergeConfig(getDefaultConfig(__dirname), config); 41 | -------------------------------------------------------------------------------- /examples/default/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "instabug-reactnative-example", 3 | "version": "0.0.1", 4 | "private": true, 5 | "scripts": { 6 | "android": "react-native run-android", 7 | "ios": "react-native run-ios", 8 | "start": "react-native start", 9 | "lint": "eslint .", 10 | "postinstall": "patch-package" 11 | }, 12 | "dependencies": { 13 | "@react-native-clipboard/clipboard": "^1.14.3", 14 | "@react-native-community/slider": "^4.5.5", 15 | "@react-navigation/bottom-tabs": "^6.5.7", 16 | "@react-navigation/native": "^6.1.6", 17 | "@react-navigation/native-stack": "^6.9.12", 18 | "axios": "^1.7.4", 19 | "graphql": "^16.8.1", 20 | "graphql-request": "^6.1.0", 21 | "instabug-reactnative": "link:../..", 22 | "instabug-reactnative-ndk": "github:https://github.com/Instabug/Instabug-React-Native-NDK", 23 | "native-base": "^3.4.28", 24 | "react": "18.3.1", 25 | "react-native": "0.75.4", 26 | "react-native-background-timer": "^2.4.1", 27 | "react-native-config": "^1.5.3", 28 | "react-native-gesture-handler": "^2.13.4", 29 | "react-native-maps": "1.10.3", 30 | "react-native-reanimated": "^3.16.1", 31 | "react-native-safe-area-context": "^4.12.0", 32 | "react-native-screens": "^3.35.0", 33 | "react-native-svg": "^15.8.0", 34 | "react-native-vector-icons": "^10.2.0", 35 | "react-native-webview": "^13.13.2", 36 | "react-query": "^3.39.3" 37 | }, 38 | "devDependencies": { 39 | "@babel/core": "^7.20.0", 40 | "@babel/preset-env": "^7.20.0", 41 | "@babel/runtime": "^7.20.0", 42 | "@react-native/metro-config": "^0.75.4", 43 | "@types/jest": "^29.2.1", 44 | "@types/react": "^18.2.6", 45 | "@types/react-native-background-timer": "^2.0.2", 46 | "@types/react-native-vector-icons": "^6.4.13", 47 | "@types/react-test-renderer": "^18.0.0", 48 | "babel-jest": "^29.2.1", 49 | "detox": "^20.9.0", 50 | "jest": "^29.2.1", 51 | "metro-react-native-babel-preset": "0.77.0", 52 | "patch-package": "^8.0.0", 53 | "react-test-renderer": "18.3.1", 54 | "ts-jest": "^29.1.0", 55 | "typescript": "5.1.6" 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /examples/default/patches/react-native-background-timer+2.4.1.patch: -------------------------------------------------------------------------------- 1 | diff --git a/node_modules/react-native-background-timer/android/build.gradle b/node_modules/react-native-background-timer/android/build.gradle 2 | index 85cda09..eb6cbeb 100644 3 | --- a/node_modules/react-native-background-timer/android/build.gradle 4 | +++ b/node_modules/react-native-background-timer/android/build.gradle 5 | @@ -5,6 +5,7 @@ def safeExtGet(prop, fallback) { 6 | } 7 | 8 | android { 9 | + namespace "com.ocetnik.timer" 10 | compileSdkVersion safeExtGet('compileSdkVersion', 28) 11 | 12 | defaultConfig { 13 | -------------------------------------------------------------------------------- /examples/default/patches/react-native-vector-icons+10.2.0.patch: -------------------------------------------------------------------------------- 1 | diff --git a/node_modules/react-native-vector-icons/android/build.gradle b/node_modules/react-native-vector-icons/android/build.gradle 2 | index 3e615e9..92be701 100644 3 | --- a/node_modules/react-native-vector-icons/android/build.gradle 4 | +++ b/node_modules/react-native-vector-icons/android/build.gradle 5 | @@ -24,6 +24,9 @@ android { 6 | namespace = "com.oblador.vectoricons" 7 | compileSdkVersion safeExtGet('compileSdkVersion', 31) 8 | 9 | + buildFeatures { 10 | + buildConfig true 11 | + } 12 | defaultConfig { 13 | minSdkVersion safeExtGet('minSdkVersion', 21) 14 | targetSdkVersion safeExtGet('targetSdkVersion', 31) 15 | -------------------------------------------------------------------------------- /examples/default/src/App.tsx: -------------------------------------------------------------------------------- 1 | import React, { useEffect } from 'react'; 2 | import { StyleSheet } from 'react-native'; 3 | 4 | import { GestureHandlerRootView } from 'react-native-gesture-handler'; 5 | import { NavigationContainer, useNavigationContainerRef } from '@react-navigation/native'; 6 | import Instabug, { 7 | CrashReporting, 8 | InvocationEvent, 9 | LogLevel, 10 | ReproStepsMode, 11 | SessionReplay, 12 | LaunchType, 13 | } from 'instabug-reactnative'; 14 | import type { SessionMetadata } from 'instabug-reactnative'; 15 | import { NativeBaseProvider } from 'native-base'; 16 | 17 | import { RootTabNavigator } from './navigation/RootTab'; 18 | import { nativeBaseTheme } from './theme/nativeBaseTheme'; 19 | import { navigationTheme } from './theme/navigationTheme'; 20 | 21 | import { QueryClient, QueryClientProvider } from 'react-query'; 22 | 23 | const queryClient = new QueryClient(); 24 | 25 | export const App: React.FC = () => { 26 | const shouldSyncSession = (data: SessionMetadata) => { 27 | if (data.launchType === LaunchType.cold) { 28 | return true; 29 | } 30 | if (data.sessionDurationInSeconds > 20) { 31 | return true; 32 | } 33 | if (data.OS === 'OS Level 34') { 34 | return true; 35 | } 36 | return false; 37 | }; 38 | 39 | const navigationRef = useNavigationContainerRef(); 40 | 41 | useEffect(() => { 42 | SessionReplay.setSyncCallback((data) => shouldSyncSession(data)); 43 | 44 | Instabug.init({ 45 | token: 'deb1910a7342814af4e4c9210c786f35', 46 | invocationEvents: [InvocationEvent.floatingButton], 47 | debugLogsLevel: LogLevel.verbose, 48 | }); 49 | CrashReporting.setNDKCrashesEnabled(true); 50 | 51 | Instabug.setReproStepsConfig({ 52 | all: ReproStepsMode.enabled, 53 | }); 54 | }, []); 55 | 56 | useEffect(() => { 57 | const unregisterListener = Instabug.setNavigationListener(navigationRef); 58 | 59 | return unregisterListener; 60 | }, [navigationRef]); 61 | 62 | return ( 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | ); 73 | }; 74 | 75 | const styles = StyleSheet.create({ 76 | root: { 77 | flex: 1, 78 | }, 79 | }); 80 | -------------------------------------------------------------------------------- /examples/default/src/components/ClipboardTextInput.tsx: -------------------------------------------------------------------------------- 1 | import React, { PropsWithChildren } from 'react'; 2 | import { StyleProp, StyleSheet, TextStyle } from 'react-native'; 3 | import PasteFromClipboardButton from './PasteFromClipboardButton'; 4 | import { HStack } from 'native-base'; 5 | import { InputField } from './InputField'; 6 | 7 | interface ClipboardTextInputProps extends PropsWithChildren { 8 | placeholder?: string; 9 | onChangeText?: (text: string) => void; 10 | value?: string; 11 | selectTextOnFocus?: boolean; 12 | style?: StyleProp; 13 | } 14 | 15 | export const ClipboardTextInput: React.FC = ({ 16 | onChangeText, 17 | style, 18 | ...restProps 19 | }) => { 20 | return ( 21 | 22 | 23 | 24 | 25 | ); 26 | 27 | function handleClipboardPress(text: string) { 28 | onChangeText?.call(undefined, text); 29 | } 30 | }; 31 | 32 | export const styles = StyleSheet.create({ 33 | inputField: { 34 | flex: 1, 35 | }, 36 | }); 37 | -------------------------------------------------------------------------------- /examples/default/src/components/CustomButton.tsx: -------------------------------------------------------------------------------- 1 | import React, { PropsWithChildren } from 'react'; 2 | import type { StyleProp, TextStyle } from 'react-native'; 3 | import { Button } from 'native-base'; 4 | import { CustomText } from './CustomText'; 5 | import { StyleSheet } from 'react-native'; 6 | 7 | interface CustomButtonProps extends PropsWithChildren { 8 | title?: string; 9 | onPress?: () => void; 10 | style?: StyleProp; 11 | } 12 | 13 | export const CustomButton: React.FC = ({ 14 | style, 15 | title, 16 | children, 17 | ...restProps 18 | }) => { 19 | return ( 20 | 24 | ); 25 | }; 26 | 27 | export const styles = StyleSheet.create({ 28 | text: { 29 | color: '#fff', 30 | }, 31 | }); 32 | -------------------------------------------------------------------------------- /examples/default/src/components/CustomGap.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { type DimensionValue, View } from 'react-native'; 3 | 4 | // Define the component type with static properties 5 | interface CustomGapComponent extends React.FC<{ height?: DimensionValue; width?: DimensionValue }> { 6 | small: JSX.Element; 7 | smallV: JSX.Element; 8 | smallH: JSX.Element; 9 | large: JSX.Element; 10 | largeV: JSX.Element; 11 | largeH: JSX.Element; 12 | } 13 | 14 | const defaultDimensionValue = 16; 15 | 16 | // Define the CustomGap component 17 | const CustomGap: CustomGapComponent = ({ height, width }) => { 18 | return ( 19 | 22 | ); 23 | }; 24 | 25 | // Assign static properties for predefined gaps 26 | CustomGap.small = ; 27 | CustomGap.large = ; 28 | CustomGap.smallV = ; 29 | CustomGap.largeV = ; 30 | CustomGap.smallH = ; 31 | CustomGap.largeH = ; 32 | 33 | export default CustomGap; 34 | -------------------------------------------------------------------------------- /examples/default/src/components/CustomImage.tsx: -------------------------------------------------------------------------------- 1 | import React, { PropsWithChildren } from 'react'; 2 | import { StyleProp, StyleSheet, TextStyle, Text } from 'react-native'; 3 | 4 | interface CustomTextProps extends PropsWithChildren { 5 | style?: StyleProp; 6 | } 7 | 8 | export const CustomImage: React.FC = ({ style, children }) => { 9 | return {children}; 10 | }; 11 | 12 | export const styles = StyleSheet.create({ 13 | text: { 14 | fontSize: 16, 15 | lineHeight: 24, 16 | }, 17 | }); 18 | -------------------------------------------------------------------------------- /examples/default/src/components/CustomText.tsx: -------------------------------------------------------------------------------- 1 | import React, { PropsWithChildren } from 'react'; 2 | import { StyleProp, StyleSheet, TextStyle, Text } from 'react-native'; 3 | 4 | interface CustomTextProps extends PropsWithChildren { 5 | style?: StyleProp; 6 | } 7 | 8 | export const CustomText: React.FC = ({ style, children }) => { 9 | return {children}; 10 | }; 11 | 12 | export const styles = StyleSheet.create({ 13 | text: { 14 | fontSize: 16, 15 | lineHeight: 24, 16 | }, 17 | }); 18 | -------------------------------------------------------------------------------- /examples/default/src/components/InputField.tsx: -------------------------------------------------------------------------------- 1 | import React, { forwardRef } from 'react'; 2 | 3 | import { 4 | KeyboardTypeOptions, 5 | StyleProp, 6 | StyleSheet, 7 | TextInput, 8 | ViewStyle, 9 | View, 10 | } from 'react-native'; 11 | import { Text } from 'native-base'; 12 | 13 | interface InputFieldProps { 14 | placeholder?: string; 15 | value?: string; 16 | style?: StyleProp; 17 | onChangeText?: (text: string) => void; 18 | keyboardType?: KeyboardTypeOptions; 19 | selectTextOnFocus?: boolean | undefined; 20 | errorText?: string; 21 | maxLength?: number; 22 | accessibilityLabel?: string; 23 | flex?: number; 24 | } 25 | 26 | export const InputField = forwardRef( 27 | ( 28 | { 29 | placeholder, 30 | value, 31 | style, 32 | onChangeText, 33 | accessibilityLabel, 34 | maxLength, 35 | keyboardType, 36 | errorText, 37 | ...restProps 38 | }, 39 | ref, 40 | ) => { 41 | return ( 42 | 43 | 54 | {errorText ? {errorText} : null} 55 | 56 | ); 57 | }, 58 | ); 59 | 60 | const styles = StyleSheet.create({ 61 | textInput: { 62 | backgroundColor: 'white', 63 | borderWidth: 1, 64 | borderColor: '#ccc', 65 | paddingVertical: 10, 66 | paddingHorizontal: 24, 67 | fontSize: 16, 68 | borderRadius: 5, 69 | }, 70 | errorText: { 71 | color: '#ff0000', 72 | }, 73 | }); 74 | -------------------------------------------------------------------------------- /examples/default/src/components/ListTile.tsx: -------------------------------------------------------------------------------- 1 | import React, { PropsWithChildren } from 'react'; 2 | 3 | import { Box, HStack, Pressable, Text } from 'native-base'; 4 | 5 | interface ListTileProps extends PropsWithChildren { 6 | title: string; 7 | onPress?: () => void; 8 | } 9 | 10 | export const ListTile: React.FC = ({ title, onPress, children }) => { 11 | return ( 12 | 21 | 22 | {title} 23 | {children} 24 | 25 | 26 | ); 27 | }; 28 | -------------------------------------------------------------------------------- /examples/default/src/components/NestedView.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import { Text } from 'native-base'; 4 | import { StyleSheet, View } from 'react-native'; 5 | 6 | interface NestedViewProps { 7 | children?: React.ReactNode; 8 | depth: number; 9 | breadth?: number; 10 | } 11 | 12 | export const NestedView: React.FC = ({ depth, breadth = 1, children }) => { 13 | if (!depth) { 14 | return <>{children}; 15 | } 16 | return ( 17 | 18 | {depth} 19 | {new Array(breadth).fill(null).map((_, index) => ( 20 | 21 | ))} 22 | 23 | ); 24 | }; 25 | 26 | const styles = StyleSheet.create({ 27 | container: { 28 | borderWidth: 1, 29 | padding: 1, 30 | }, 31 | }); 32 | -------------------------------------------------------------------------------- /examples/default/src/components/PasteFromClipboardButton.tsx: -------------------------------------------------------------------------------- 1 | import Clipboard from '@react-native-clipboard/clipboard'; 2 | import { StyleProp, TextStyle, TouchableHighlight } from 'react-native'; 3 | import React, { PropsWithChildren } from 'react'; 4 | import Icon from 'react-native-vector-icons/Ionicons'; 5 | 6 | interface PasteFromClipboardButtonProps extends PropsWithChildren { 7 | onPress?: (text: string) => void; 8 | style?: StyleProp; 9 | } 10 | 11 | const PasteFromClipboardButton: React.FC = ({ onPress, style }) => { 12 | const handlePress = async () => { 13 | const text = await Clipboard.getString(); 14 | onPress?.call(undefined, text); // Using call to pass the text to the callback 15 | }; 16 | 17 | return ( 18 | 19 | 20 | 21 | ); 22 | }; 23 | 24 | export default PasteFromClipboardButton; 25 | -------------------------------------------------------------------------------- /examples/default/src/components/PlatformListTile.tsx: -------------------------------------------------------------------------------- 1 | import React, { PropsWithChildren } from 'react'; 2 | 3 | import { Box, HStack, Pressable, Text } from 'native-base'; 4 | import { Platform } from 'react-native'; 5 | 6 | interface PlatformListTileProps extends PropsWithChildren { 7 | title: string; 8 | onPress?: () => void; 9 | platform?: 'ios' | 'android'; 10 | } 11 | 12 | export const PlatformListTile: React.FC = ({ 13 | title, 14 | onPress, 15 | platform, 16 | children, 17 | }) => { 18 | if (Platform.OS === platform || !platform) { 19 | return ( 20 | 29 | 30 | {title} 31 | {children} 32 | 33 | 34 | ); 35 | } 36 | return null; 37 | }; 38 | -------------------------------------------------------------------------------- /examples/default/src/components/Screen.tsx: -------------------------------------------------------------------------------- 1 | import React, { PropsWithChildren } from 'react'; 2 | 3 | import { VStack } from 'native-base'; 4 | 5 | export const Screen: React.FC = ({ children }) => { 6 | return ( 7 | 8 | {children} 9 | 10 | ); 11 | }; 12 | -------------------------------------------------------------------------------- /examples/default/src/components/Section.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Heading, VStack } from 'native-base'; 3 | 4 | interface SectionProps { 5 | title: string; 6 | flex?: number; 7 | children?: React.ReactNode; 8 | } 9 | 10 | export const Section: React.FC = ({ title, flex, children }) => { 11 | return ( 12 | 13 | 14 | {title} 15 | 16 | 17 | {children} 18 | 19 | ); 20 | }; 21 | -------------------------------------------------------------------------------- /examples/default/src/components/Select.tsx: -------------------------------------------------------------------------------- 1 | import React, { useState } from 'react'; 2 | 3 | import { CheckIcon, Select as NativeBaseSelect } from 'native-base'; 4 | 5 | interface SelectItem { 6 | label: string; 7 | value: T; 8 | isInitial?: boolean; 9 | } 10 | 11 | interface SelectProps { 12 | label: string; 13 | items: SelectItem[]; 14 | onValueChange: (value: T) => void; 15 | } 16 | 17 | export function Select({ label, items, onValueChange }: SelectProps) { 18 | const initialItem = items.find((i) => i.isInitial) ?? items[0]; 19 | const [selectedItem, setSelectedItem] = useState(initialItem); 20 | 21 | return ( 22 | { 28 | const item = items.find((i) => i.label === value)!; 29 | setSelectedItem(item); 30 | onValueChange(item.value); 31 | }} 32 | _selectedItem={{ 33 | bg: 'coolGray.200', 34 | rounded: '10', 35 | endIcon: , 36 | }}> 37 | {items.map((item) => ( 38 | 39 | ))} 40 | 41 | ); 42 | } 43 | -------------------------------------------------------------------------------- /examples/default/src/components/VerticalListTile.tsx: -------------------------------------------------------------------------------- 1 | import React, { PropsWithChildren } from 'react'; 2 | 3 | import { Box, Pressable, Text, VStack } from 'native-base'; 4 | 5 | interface ListTileProps extends PropsWithChildren { 6 | title: string; 7 | onPress?: () => void; 8 | } 9 | 10 | export const VerticalListTile: React.FC = ({ title, onPress, children }) => { 11 | return ( 12 | 21 | 22 | {title} 23 | {children} 24 | 25 | 26 | ); 27 | }; 28 | -------------------------------------------------------------------------------- /examples/default/src/images/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Instabug/Instabug-React-Native/7ff76ead15088db09079ef0a54e57a2a6d6ef9f8/examples/default/src/images/logo.png -------------------------------------------------------------------------------- /examples/default/src/native/NativeCrashReporting.ts: -------------------------------------------------------------------------------- 1 | import type { NativeModule } from 'react-native'; 2 | 3 | import { NativeExampleModules } from './NativePackage'; 4 | 5 | export interface CrashReportingExampleNativeModule extends NativeModule { 6 | sendNativeNonFatal(): Promise; 7 | sendNativeFatalCrash(): Promise; 8 | sendFatalHang(): Promise; 9 | sendANR(): Promise; 10 | sendOOM(): Promise; 11 | 12 | sendNDKCrash(): Promise; 13 | 14 | causeSIGSEGVCrash(): Promise; 15 | 16 | causeSIGABRTCrash(): Promise; 17 | 18 | causeSIGFPECrash(): Promise; 19 | 20 | causeSIGILLCrash(): Promise; 21 | 22 | causeSIGBUSCrash(): Promise; 23 | 24 | causeSIGTRAPCrash(): Promise; 25 | } 26 | 27 | export const NativeExampleCrashReporting = NativeExampleModules.CrashReportingExampleModule; 28 | -------------------------------------------------------------------------------- /examples/default/src/native/NativePackage.ts: -------------------------------------------------------------------------------- 1 | import { NativeModules as ReactNativeModules } from 'react-native'; 2 | 3 | import type { CrashReportingExampleNativeModule } from './NativeCrashReporting'; 4 | 5 | export interface InstabugExampleNativePackage { 6 | CrashReportingExampleModule: CrashReportingExampleNativeModule; 7 | } 8 | 9 | export const NativeExampleModules = ReactNativeModules as InstabugExampleNativePackage; 10 | -------------------------------------------------------------------------------- /examples/default/src/navigation/RootTab.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import { 4 | BottomTabNavigationOptions, 5 | createBottomTabNavigator, 6 | } from '@react-navigation/bottom-tabs'; 7 | import Icon from 'react-native-vector-icons/Ionicons'; 8 | 9 | import { SettingsScreen } from '../screens/SettingsScreen'; 10 | import { HomeStackNavigator } from './HomeStack'; 11 | import { Platform } from 'react-native'; 12 | 13 | export type RootTabParamList = { 14 | HomeStack: undefined; 15 | Settings: undefined; 16 | }; 17 | 18 | const RootTab = createBottomTabNavigator(); 19 | 20 | const createTabBarIcon = (name: string): BottomTabNavigationOptions['tabBarIcon'] => { 21 | return (props) => ; 22 | }; 23 | 24 | export const RootTabNavigator: React.FC = () => { 25 | return ( 26 | 27 | 36 | 43 | 44 | ); 45 | }; 46 | -------------------------------------------------------------------------------- /examples/default/src/screens/FeatureRequestsScreen.tsx: -------------------------------------------------------------------------------- 1 | import React, { useState } from 'react'; 2 | 3 | import { FeatureRequests, ActionType } from 'instabug-reactnative'; 4 | 5 | import { ListTile } from '../components/ListTile'; 6 | import { Text, Switch } from 'react-native'; 7 | 8 | import { Screen } from '../components/Screen'; 9 | import { showNotification } from '../utils/showNotification'; 10 | 11 | export const FeatureRequestsScreen: React.FC = () => { 12 | const [isEnabled, setIsEnabled] = useState(false); 13 | 14 | const toggleSwitch = (value: boolean) => { 15 | setIsEnabled(value); 16 | 17 | FeatureRequests.setEmailFieldRequired(value, ActionType.requestNewFeature); 18 | showNotification('Email status', 'Email field required set to ' + value); 19 | }; 20 | return ( 21 | 22 | Email field Required: 23 | 24 | FeatureRequests.show()} /> 25 | 26 | ); 27 | }; 28 | -------------------------------------------------------------------------------- /examples/default/src/screens/HomeScreen.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import type { NativeStackScreenProps } from '@react-navigation/native-stack'; 4 | 5 | import { ListTile } from '../components/ListTile'; 6 | import { Screen } from '../components/Screen'; 7 | import type { HomeStackParamList } from '../navigation/HomeStack'; 8 | 9 | export const HomeScreen: React.FC> = ({ 10 | navigation, 11 | }) => { 12 | return ( 13 | 14 | navigation.navigate('BugReporting')} /> 15 | navigation.navigate('CrashReporting')} /> 16 | navigation.navigate('FeatureRequests')} /> 17 | navigation.navigate('Replies')} /> 18 | navigation.navigate('Surveys')} /> 19 | navigation.navigate('UserSteps')} /> 20 | navigation.navigate('APM')} /> 21 | navigation.navigate('SessionReplay')} /> 22 | navigation.navigate('LegacyMode')} /> 23 | 24 | ); 25 | }; 26 | -------------------------------------------------------------------------------- /examples/default/src/screens/SessionReplayScreen.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import { SessionReplay } from 'instabug-reactnative'; 4 | import { useToast } from 'native-base'; 5 | 6 | import { ListTile } from '../components/ListTile'; 7 | import { Screen } from '../components/Screen'; 8 | 9 | export const SessionReplayScreen: React.FC = () => { 10 | const toast = useToast(); 11 | return ( 12 | 13 | { 16 | const link = await SessionReplay.getSessionReplayLink(); 17 | if (link === null) { 18 | toast.show({ 19 | description: 'link not found', 20 | }); 21 | } else { 22 | toast.show({ 23 | description: link, 24 | }); 25 | } 26 | }} 27 | /> 28 | 29 | ); 30 | }; 31 | -------------------------------------------------------------------------------- /examples/default/src/screens/apm/APMScreen.tsx: -------------------------------------------------------------------------------- 1 | import type { NativeStackScreenProps } from '@react-navigation/native-stack'; 2 | import type { HomeStackParamList } from '../../navigation/HomeStack'; 3 | import React, { useState } from 'react'; 4 | import { ListTile } from '../../components/ListTile'; 5 | import { Screen } from '../../components/Screen'; 6 | import { StyleSheet, Switch, Text, View } from 'react-native'; 7 | import { APM } from 'instabug-reactnative'; 8 | import { showNotification } from '../../utils/showNotification'; 9 | import CustomGap from '../../components/CustomGap'; 10 | 11 | export const APMScreen: React.FC> = ({ 12 | navigation, 13 | }) => { 14 | const [isEnabled, setIsEnabled] = useState(false); 15 | 16 | const toggleSwitch = (value: boolean) => { 17 | setIsEnabled(value); 18 | APM.setEnabled(value); 19 | showNotification('APM status', 'APM enabled set to ' + value); 20 | }; 21 | const styles = StyleSheet.create({ 22 | switch: { 23 | flexDirection: 'row', 24 | justifyContent: 'space-between', 25 | }, 26 | }); 27 | 28 | return ( 29 | 30 | 31 | Enable APM: 32 | 33 | 34 | {CustomGap.smallV} 35 | APM.endAppLaunch()} /> 36 | navigation.navigate('NetworkTraces')} /> 37 | navigation.navigate('ExecutionTraces')} /> 38 | navigation.navigate('AppFlows')} /> 39 | navigation.navigate('WebViews')} /> 40 | navigation.navigate('ComplexViews')} /> 41 | 42 | ); 43 | }; 44 | -------------------------------------------------------------------------------- /examples/default/src/screens/apm/FlowsScreen.tsx: -------------------------------------------------------------------------------- 1 | import React, { useState } from 'react'; 2 | import { APM } from 'instabug-reactnative'; 3 | import { ScrollView } from 'react-native'; 4 | import { Section } from '../../components/Section'; 5 | import { Screen } from '../../components/Screen'; 6 | import { VStack } from 'native-base'; 7 | import { InputField } from '../../components/InputField'; 8 | import { CustomButton } from '../../components/CustomButton'; 9 | import BackgroundTimer from 'react-native-background-timer'; 10 | 11 | export const FlowsScreen: React.FC = () => { 12 | const [flowName, setFlowName] = useState(''); 13 | const [flowAttributeKey, setFlowAttributeKey] = useState(''); 14 | const [flowAttributeValue, setFlowAttributeValue] = useState(''); 15 | 16 | async function startFlow() { 17 | return APM.startFlow(flowName); 18 | } 19 | 20 | async function startDelayedFlow() { 21 | return BackgroundTimer.setTimeout(() => { 22 | APM.startFlow(flowName); 23 | }, 5000); 24 | } 25 | 26 | function setFlowAttribute() { 27 | return APM.setFlowAttribute(flowName, flowAttributeKey, flowAttributeValue); 28 | } 29 | 30 | function endFlow() { 31 | APM.endFlow(flowName); 32 | } 33 | 34 | return ( 35 | 36 | 37 |
38 | 39 | setFlowName(text)} 42 | value={flowName} 43 | /> 44 | 45 | 46 | setFlowAttributeKey(text)} 49 | value={flowAttributeKey} 50 | /> 51 | setFlowAttributeValue(text)} 54 | value={flowAttributeValue} 55 | /> 56 | 57 | 58 | 59 |
60 |
61 |
62 | ); 63 | }; 64 | -------------------------------------------------------------------------------- /examples/default/src/screens/apm/webViews/FullWebViewsScreen.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Screen } from '../../../components/Screen'; 3 | import { WebView } from 'react-native-webview'; 4 | 5 | export const FullWebViewsScreen: React.FC = () => { 6 | return ( 7 | 8 | 13 | 14 | ); 15 | }; 16 | -------------------------------------------------------------------------------- /examples/default/src/screens/apm/webViews/PartialWebViewsScreen.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Screen } from '../../../components/Screen'; 3 | import { WebView } from 'react-native-webview'; 4 | import { StyleSheet } from 'react-native'; 5 | 6 | export const PartialWebViewsScreen: React.FC = () => { 7 | return ( 8 | 9 | 15 | 20 | 21 | ); 22 | }; 23 | const styles = StyleSheet.create({ 24 | webView: { 25 | marginBottom: 20, 26 | }, 27 | }); 28 | -------------------------------------------------------------------------------- /examples/default/src/screens/apm/webViews/WebViewsScreen.tsx: -------------------------------------------------------------------------------- 1 | import type { NativeStackScreenProps } from '@react-navigation/native-stack'; 2 | import type { HomeStackParamList } from '../../../navigation/HomeStack'; 3 | import React from 'react'; 4 | import { Screen } from '../../../components/Screen'; 5 | import { ListTile } from '../../../components/ListTile'; 6 | 7 | export const WebViewsScreen: React.FC> = ({ 8 | navigation, 9 | }) => { 10 | return ( 11 | 12 | navigation.navigate('FullWebViews')} /> 13 | navigation.navigate('PartialWebViews')} /> 14 | 15 | ); 16 | }; 17 | -------------------------------------------------------------------------------- /examples/default/src/screens/user-steps/ComplexViewsScreen.tsx: -------------------------------------------------------------------------------- 1 | import React, { useRef, useState } from 'react'; 2 | 3 | import { Screen } from '../../components/Screen'; 4 | import { Section } from '../../components/Section'; 5 | import { NestedView } from '../../components/NestedView'; 6 | import { Button } from 'react-native'; 7 | import { ScrollView, VStack } from 'native-base'; 8 | import { InputField } from '../../components/InputField'; 9 | 10 | export const ComplexViewsScreen: React.FC = () => { 11 | const initialDepth = 10; 12 | const initialBreadth = 2; 13 | 14 | const depthRef = useRef(initialDepth); 15 | const breadthRef = useRef(initialBreadth); 16 | 17 | const [depth, setDepth] = useState(initialDepth); 18 | const [breadth, setBreadth] = useState(initialBreadth); 19 | 20 | function handleRender() { 21 | setDepth(depthRef.current); 22 | setBreadth(breadthRef.current); 23 | } 24 | 25 | return ( 26 | 27 | 28 |
29 | 30 | (depthRef.current = +text)} 34 | /> 35 | (breadthRef.current = +text)} 39 | /> 40 |
44 |
45 |
46 | ); 47 | }; 48 | -------------------------------------------------------------------------------- /examples/default/src/screens/user-steps/FlatListScreen.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { FlatList, RefreshControl } from 'react-native'; 3 | import { Skeleton } from 'native-base'; 4 | 5 | import { Screen } from '../../components/Screen'; 6 | import { Section } from '../../components/Section'; 7 | import { createList } from '../../utils/createList'; 8 | import { useDelayedRefresh } from '../../utils/useDelayedRefresh'; 9 | 10 | export const FlatListScreen: React.FC = () => { 11 | const { refreshing, onRefresh } = useDelayedRefresh(); 12 | 13 | return ( 14 | 15 |
16 | item.toString()} 20 | renderItem={() => ( 21 | 22 | )} 23 | /> 24 |
25 | 26 |
27 | } 29 | data={createList(20)} 30 | keyExtractor={(item) => item.toString()} 31 | renderItem={() => ( 32 | 33 | )} 34 | /> 35 |
36 |
37 | ); 38 | }; 39 | -------------------------------------------------------------------------------- /examples/default/src/screens/user-steps/GoogleMapsScreen.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import { Screen } from '../../components/Screen'; 4 | import MapView, { PROVIDER_GOOGLE } from 'react-native-maps'; 5 | import { StyleSheet, View } from 'react-native'; 6 | 7 | /** 8 | * don't forgot to add Google API key to 9 | * examples/default/android/app/src/main/res/values/strings.xml 10 | * examples/default/ios/InstabugExample/AppDelegate.mm 11 | * 12 | * **/ 13 | 14 | const styles = StyleSheet.create({ 15 | container: { 16 | ...StyleSheet.absoluteFillObject, 17 | height: '100%', 18 | width: '100%', 19 | alignItems: 'stretch', 20 | }, 21 | map: { 22 | ...StyleSheet.absoluteFillObject, 23 | height: '100%', 24 | width: '100%', 25 | }, 26 | }); 27 | export const GoogleMapsScreen: React.FC = () => { 28 | return ( 29 | 30 | 31 | 41 | 42 | 43 | ); 44 | }; 45 | -------------------------------------------------------------------------------- /examples/default/src/screens/user-steps/LargeImageListScreen.tsx: -------------------------------------------------------------------------------- 1 | import React, { useState } from 'react'; 2 | import { FlatList, RefreshControl } from 'react-native'; 3 | import { Screen } from '../../components/Screen'; 4 | import { useDelayedRefresh } from '../../utils/useDelayedRefresh'; 5 | import { AspectRatio, Skeleton, Image, Box } from 'native-base'; 6 | 7 | type ImageItemProps = { link: string }; 8 | 9 | const LargeImageListItem: React.FC = ({ link }: ImageItemProps) => { 10 | const [isLoading, setLoading] = useState(true); 11 | return ( 12 | 13 | 14 | 15 | setLoading(true)} 17 | onLoadEnd={() => setLoading(false)} 18 | source={{ 19 | uri: link, 20 | }} 21 | alt="image" 22 | /> 23 | 24 | 25 | ); 26 | }; 27 | 28 | export const LargeImageListScreen: React.FC = () => { 29 | const { refreshing, onRefresh } = useDelayedRefresh(); 30 | 31 | return ( 32 | 33 | 34 | refreshControl={} 35 | data={Array.from( 36 | { length: 100 }, 37 | () => 38 | `https://picsum.photos/${Math.floor(Math.random() * 200) + 300}/${ 39 | Math.floor(Math.random() * 200) + 600 40 | }`, 41 | )} 42 | keyExtractor={(item) => item.toString()} 43 | renderItem={({ item }) => } 44 | /> 45 | 46 | ); 47 | }; 48 | -------------------------------------------------------------------------------- /examples/default/src/screens/user-steps/ScrollViewScreen.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import { Screen } from '../../components/Screen'; 4 | import { RefreshControl, ScrollView } from 'react-native'; 5 | import { VStack, Skeleton, HStack } from 'native-base'; 6 | import { createList } from '../../utils/createList'; 7 | import { Section } from '../../components/Section'; 8 | import { useDelayedRefresh } from '../../utils/useDelayedRefresh'; 9 | 10 | export const ScrollViewScreen: React.FC = () => { 11 | const { refreshing, onRefresh } = useDelayedRefresh(); 12 | 13 | return ( 14 | 15 |
16 | 17 | 18 | {createList(10).map((num) => ( 19 | 20 | ))} 21 | 22 | 23 |
24 | 25 |
26 | }> 28 | 29 | {createList(20).map((num) => ( 30 | 31 | ))} 32 | 33 | 34 |
35 |
36 | ); 37 | }; 38 | -------------------------------------------------------------------------------- /examples/default/src/screens/user-steps/SectionListScreen.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { RefreshControl, SectionList } from 'react-native'; 3 | import { Heading, Skeleton } from 'native-base'; 4 | 5 | import { Screen } from '../../components/Screen'; 6 | import { createList } from '../../utils/createList'; 7 | import { useDelayedRefresh } from '../../utils/useDelayedRefresh'; 8 | 9 | const sections = [ 10 | { 11 | title: 'Section A', 12 | data: createList(10), 13 | }, 14 | { 15 | title: 'Section B', 16 | data: createList(10), 17 | }, 18 | ]; 19 | 20 | export const SectionListScreen: React.FC = () => { 21 | const { refreshing, onRefresh } = useDelayedRefresh(); 22 | 23 | return ( 24 | 25 | } 27 | sections={sections} 28 | keyExtractor={(item) => item.toString()} 29 | renderSectionHeader={(info) => ( 30 | 31 | {info.section.title} 32 | 33 | )} 34 | renderItem={() => ( 35 | 36 | )} 37 | /> 38 | 39 | ); 40 | }; 41 | -------------------------------------------------------------------------------- /examples/default/src/screens/user-steps/UserStepsScreen.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import { ListTile } from '../../components/ListTile'; 4 | import { Screen } from '../../components/Screen'; 5 | import type { HomeStackParamList } from '../../navigation/HomeStack'; 6 | import type { NativeStackScreenProps } from '@react-navigation/native-stack'; 7 | import { navToBackAndForthScreen } from './BackAndForthScreen'; 8 | 9 | export const UserStepsScreen: React.FC> = ({ 10 | navigation, 11 | }) => { 12 | return ( 13 | 14 | navigation.navigate('BasicComponents')} /> 15 | navigation.navigate('ScrollView')} /> 16 | navigation.navigate('FlatList')} /> 17 | navigation.navigate('SectionList')} /> 18 | navigation.navigate('ComplexViews')} /> 19 | navigation.navigate('Gestures')} /> 20 | navigation.navigate('GoogleMapsScreen')} /> 21 | navigation.navigate('LargeImageList')} /> 22 | { 25 | navToBackAndForthScreen(navigation); 26 | }} 27 | /> 28 | 29 | ); 30 | }; 31 | -------------------------------------------------------------------------------- /examples/default/src/theme/nativeBaseTheme.ts: -------------------------------------------------------------------------------- 1 | import { extendTheme } from 'native-base'; 2 | 3 | export const nativeBaseTheme = extendTheme({ 4 | colors: { 5 | primary: { 6 | 50: '#99a9c9', 7 | 100: '#7f93bc', 8 | 200: '#667eaf', 9 | 300: '#4c68a1', 10 | 400: '#325294', 11 | 500: '#193d87', 12 | 600: '#00287a', 13 | 700: '#002061', 14 | 800: '#00143d', 15 | 900: '#00081f', 16 | }, 17 | }, 18 | }); 19 | -------------------------------------------------------------------------------- /examples/default/src/theme/navigationTheme.ts: -------------------------------------------------------------------------------- 1 | import { DefaultTheme } from '@react-navigation/native'; 2 | 3 | import { nativeBaseTheme } from './nativeBaseTheme'; 4 | 5 | export const navigationTheme = { 6 | ...DefaultTheme, 7 | colors: { 8 | ...DefaultTheme.colors, 9 | primary: nativeBaseTheme.colors.primary[600], 10 | }, 11 | }; 12 | -------------------------------------------------------------------------------- /examples/default/src/utils/createList.ts: -------------------------------------------------------------------------------- 1 | export function createList(length: number) { 2 | return Array.from({ length }, (_, i) => i + 1); 3 | } 4 | -------------------------------------------------------------------------------- /examples/default/src/utils/showNotification.ts: -------------------------------------------------------------------------------- 1 | import { ToastAndroid, Platform, Alert } from 'react-native'; 2 | 3 | export function showNotification(title: string, message: string): void { 4 | if (Platform.OS === 'android') { 5 | ToastAndroid.show(message, ToastAndroid.SHORT); 6 | } else { 7 | Alert.alert(title, message); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /examples/default/src/utils/useDelayedRefresh.ts: -------------------------------------------------------------------------------- 1 | import { useState } from 'react'; 2 | 3 | export function useDelayedRefresh() { 4 | const [refreshing, setIsRefreshing] = useState(false); 5 | 6 | const onRefresh = () => { 7 | if (refreshing) { 8 | return; 9 | } 10 | 11 | setIsRefreshing(true); 12 | setTimeout(() => { 13 | setIsRefreshing(false); 14 | }, 1000); 15 | }; 16 | 17 | return { refreshing, onRefresh }; 18 | } 19 | -------------------------------------------------------------------------------- /examples/default/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.json", 3 | "include": ["src/**/*", "e2e/**/*"], 4 | "compilerOptions": { 5 | "paths": { 6 | "instabug-reactnative": ["../../src/index.ts"] 7 | } 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /ios/RNInstabug/ArgsRegistry.h: -------------------------------------------------------------------------------- 1 | #import 2 | #import 3 | #import 4 | 5 | typedef NSDictionary ArgsDictionary; 6 | 7 | @interface ArgsRegistry : NSObject 8 | 9 | + (NSMutableDictionary *) getAll; 10 | 11 | + (ArgsDictionary *) sdkLogLevels; 12 | + (ArgsDictionary *) invocationEvents; 13 | + (ArgsDictionary *) invocationOptions; 14 | + (ArgsDictionary *) colorThemes; 15 | + (ArgsDictionary *) floatingButtonEdges; 16 | + (ArgsDictionary *) recordButtonPositions; 17 | + (ArgsDictionary *) welcomeMessageStates; 18 | + (ArgsDictionary *) reportTypes; 19 | + (ArgsDictionary *) dismissTypes; 20 | + (ArgsDictionary *) actionTypes; 21 | + (ArgsDictionary *) extendedBugReportStates; 22 | + (ArgsDictionary *) reproStates; 23 | + (ArgsDictionary *) locales; 24 | + (ArgsDictionary *)nonFatalExceptionLevel; 25 | + (ArgsDictionary *) launchType; 26 | 27 | + (NSDictionary *) placeholders; 28 | 29 | @end 30 | -------------------------------------------------------------------------------- /ios/RNInstabug/InstabugAPMBridge.h: -------------------------------------------------------------------------------- 1 | 2 | #import 3 | #import 4 | #import 5 | #import 6 | 7 | @interface InstabugAPMBridge : RCTEventEmitter 8 | /* 9 | +------------------------------------------------------------------------+ 10 | | APM Module | 11 | +------------------------------------------------------------------------+ 12 | */ 13 | 14 | - (void)setEnabled:(BOOL)isEnabled; 15 | - (void)setAppLaunchEnabled:(BOOL)isEnabled; 16 | - (void)endAppLaunch; 17 | - (void)setAutoUITraceEnabled:(BOOL)isEnabled; 18 | - (void)startExecutionTrace:(NSString *)name :(NSString *)id 19 | :(RCTPromiseResolveBlock)resolve 20 | :(RCTPromiseRejectBlock)reject DEPRECATED_MSG_ATTRIBUTE("Please use APM.startFlow instead."); 21 | - (void)setExecutionTraceAttribute:(NSString *)id:(NSString *)key 22 | :(NSString *)value DEPRECATED_MSG_ATTRIBUTE("Please use APM.setTraceAttribute instead."); 23 | - (void)endExecutionTrace:(NSString *)id DEPRECATED_MSG_ATTRIBUTE("Please use APM.endFlow instead."); 24 | - (void)startFlow:(NSString *)name; 25 | - (void)endFlow:(NSString *)name; 26 | - (void)setFlowAttribute:(NSString *)name :(NSString *)key :(NSString *_Nullable)value; 27 | - (void)startUITrace:(NSString *)name; 28 | - (void)endUITrace; 29 | 30 | extern NSMutableDictionary *traces; 31 | 32 | @end 33 | -------------------------------------------------------------------------------- /ios/RNInstabug/InstabugBugReportingBridge.h: -------------------------------------------------------------------------------- 1 | // 2 | // InstabugBugReportingBridge.h 3 | // RNInstabug 4 | // 5 | // Created by Salma Ali on 7/30/19. 6 | // Copyright © 2019 instabug. All rights reserved. 7 | // 8 | 9 | #import 10 | #import 11 | #import 12 | #import 13 | #import 14 | 15 | @interface InstabugBugReportingBridge : RCTEventEmitter 16 | /* 17 | +------------------------------------------------------------------------+ 18 | | BugReporting Module | 19 | +------------------------------------------------------------------------+ 20 | */ 21 | 22 | - (void)setEnabled:(BOOL)isEnabled; 23 | 24 | - (void)setInvocationEvents:(NSArray *)invocationEventsArray; 25 | 26 | - (void)setOptions:(NSArray *)optionsArray; 27 | 28 | - (void)setFloatingButtonEdge:(CGRectEdge)floatingButtonEdge withTopOffset:(double)floatingButtonOffsetFromTop; 29 | 30 | - (void)setOnInvokeHandler:(RCTResponseSenderBlock)callBack; 31 | 32 | - (void)setOnSDKDismissedHandler:(RCTResponseSenderBlock)callBack; 33 | 34 | - (void)setShakingThresholdForiPhone:(double)iPhoneShakingThreshold; 35 | 36 | - (void)setShakingThresholdForiPad:(double)iPadShakingThreshold; 37 | 38 | - (void)setExtendedBugReportMode:(IBGExtendedBugReportMode)extendedBugReportMode; 39 | 40 | - (void)setReportTypes:(NSArray *)types; 41 | 42 | - (void)show:(IBGBugReportingReportType)type options:(NSArray *)options; 43 | 44 | - (void)setAutoScreenRecordingEnabled:(BOOL)enabled; 45 | 46 | - (void)setAutoScreenRecordingDuration:(CGFloat)duration; 47 | 48 | - (void)setViewHierarchyEnabled:(BOOL)viewHirearchyEnabled; 49 | 50 | - (void)setDisclaimerText:(NSString *)text; 51 | 52 | - (void)setCommentMinimumCharacterCount:(NSNumber *)limit reportTypes:(NSArray *)reportTypes; 53 | 54 | @end 55 | -------------------------------------------------------------------------------- /ios/RNInstabug/InstabugCrashReportingBridge.h: -------------------------------------------------------------------------------- 1 | #import 2 | #import 3 | #import 4 | #import 5 | #import 6 | #import 7 | #import 8 | #import "ArgsRegistry.h" 9 | 10 | @interface InstabugCrashReportingBridge : RCTEventEmitter 11 | 12 | - (void)setEnabled:(BOOL) isEnabled; 13 | - (void)sendJSCrash:(NSDictionary *_Nonnull )stackTrace resolver:(RCTPromiseResolveBlock _Nullable )resolve 14 | rejecter:(RCTPromiseRejectBlock _Nullable )reject; 15 | - (void)sendHandledJSCrash:(NSDictionary *_Nonnull)stackTrace userAttributes:(nullable NSDictionary *)userAttributes fingerprint:(nullable NSString *)fingerprint nonFatalExceptionLevel:(IBGNonFatalLevel) nonFatalExceptionLevel resolver:(RCTPromiseResolveBlock _Nullable )resolve 16 | rejecter:(RCTPromiseRejectBlock _Nullable )reject; 17 | 18 | @end 19 | -------------------------------------------------------------------------------- /ios/RNInstabug/InstabugCrashReportingBridge.m: -------------------------------------------------------------------------------- 1 | #import "InstabugCrashReportingBridge.h" 2 | #import "Util/IBGCrashReporting+CP.h" 3 | 4 | 5 | @implementation InstabugCrashReportingBridge 6 | 7 | - (dispatch_queue_t)methodQueue { 8 | return dispatch_get_main_queue(); 9 | } 10 | 11 | + (BOOL)requiresMainQueueSetup 12 | { 13 | return NO; 14 | } 15 | 16 | - (NSArray *)supportedEvents { 17 | return @[ 18 | @"IBGSendHandledJSCrash", 19 | @"IBGSendUnhandledJSCrash", 20 | ]; 21 | } 22 | 23 | RCT_EXPORT_MODULE(IBGCrashReporting) 24 | 25 | RCT_EXPORT_METHOD(setEnabled: (BOOL) isEnabled) { 26 | IBGCrashReporting.enabled = isEnabled; 27 | } 28 | 29 | RCT_EXPORT_METHOD(sendJSCrash:(NSDictionary *)stackTrace 30 | resolver:(RCTPromiseResolveBlock)resolve 31 | rejecter:(RCTPromiseRejectBlock)reject) { 32 | 33 | dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0ul); 34 | dispatch_async(queue, ^{ 35 | [IBGCrashReporting cp_reportFatalCrashWithStackTrace:stackTrace]; 36 | resolve([NSNull null]); 37 | }); 38 | } 39 | 40 | RCT_EXPORT_METHOD(sendHandledJSCrash: (NSDictionary *)stackTrace 41 | userAttributes:(nullable NSDictionary *)userAttributes fingerprint:(nullable NSString *)fingerprint nonFatalExceptionLevel:(IBGNonFatalLevel)nonFatalExceptionLevel 42 | resolver:(RCTPromiseResolveBlock)resolve 43 | rejecter:(RCTPromiseRejectBlock)reject) { 44 | 45 | if([fingerprint isKindOfClass:NSNull.class]){ 46 | fingerprint = nil; 47 | } 48 | 49 | if([userAttributes isKindOfClass:NSNull.class]){ 50 | userAttributes = nil; 51 | } 52 | dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0ul); 53 | dispatch_async(queue, ^{ 54 | [IBGCrashReporting cp_reportNonFatalCrashWithStackTrace:stackTrace level:nonFatalExceptionLevel groupingString:fingerprint userAttributes:userAttributes]; 55 | 56 | resolve([NSNull null]); 57 | }); 58 | 59 | } 60 | @synthesize description; 61 | 62 | @synthesize hash; 63 | 64 | @synthesize superclass; 65 | 66 | @end 67 | 68 | 69 | -------------------------------------------------------------------------------- /ios/RNInstabug/InstabugFeatureRequestsBridge.h: -------------------------------------------------------------------------------- 1 | // 2 | // InstabugFeatureRequestsBridge.h 3 | // RNInstabug 4 | // 5 | // Created by Salma Ali on 7/30/19. 6 | // Copyright © 2019 instabug. All rights reserved. 7 | // 8 | 9 | #import 10 | #import 11 | #import 12 | #import 13 | 14 | @interface InstabugFeatureRequestsBridge : RCTEventEmitter 15 | /* 16 | +------------------------------------------------------------------------+ 17 | | Feature Requests Module | 18 | +------------------------------------------------------------------------+ 19 | */ 20 | 21 | - (void)setEmailFieldRequiredForFeatureRequests:(BOOL)isEmailFieldRequired forAction:(NSArray *)actionTypesArray; 22 | 23 | - (void)show; 24 | 25 | - (void)setEnabled:(BOOL) isEnabled; 26 | 27 | 28 | @end 29 | 30 | 31 | -------------------------------------------------------------------------------- /ios/RNInstabug/InstabugFeatureRequestsBridge.m: -------------------------------------------------------------------------------- 1 | // 2 | // InstabugFeatureRequestsBridge.m 3 | // RNInstabug 4 | // 5 | // Created by Salma Ali on 7/30/19. 6 | // Copyright © 2019 instabug. All rights reserved. 7 | // 8 | 9 | #import "InstabugFeatureRequestsBridge.h" 10 | #import 11 | #import 12 | #import 13 | #import 14 | #import 15 | #import 16 | 17 | @implementation InstabugFeatureRequestsBridge 18 | 19 | - (dispatch_queue_t)methodQueue { 20 | return dispatch_get_main_queue(); 21 | } 22 | 23 | + (BOOL)requiresMainQueueSetup 24 | { 25 | return NO; 26 | } 27 | 28 | - (NSArray *)supportedEvents { 29 | return @[]; 30 | } 31 | 32 | RCT_EXPORT_MODULE(IBGFeatureRequests) 33 | 34 | RCT_EXPORT_METHOD(show) { 35 | [[NSRunLoop mainRunLoop] performSelector:@selector(show) target:[IBGFeatureRequests class] argument:nil order:0 modes:@[NSDefaultRunLoopMode]]; 36 | } 37 | 38 | RCT_EXPORT_METHOD(setEmailFieldRequiredForFeatureRequests:(BOOL)isEmailFieldRequired 39 | forAction:(NSArray *)actionTypesArray) { 40 | IBGAction actionTypes = 0; 41 | 42 | for (NSNumber *boxedValue in actionTypesArray) { 43 | actionTypes |= [boxedValue intValue]; 44 | } 45 | 46 | [IBGFeatureRequests setEmailFieldRequired:isEmailFieldRequired forAction:actionTypes]; 47 | } 48 | 49 | RCT_EXPORT_METHOD(setEnabled: (BOOL) isEnabled) { 50 | IBGFeatureRequests.enabled = isEnabled; 51 | } 52 | 53 | @synthesize description; 54 | 55 | @synthesize hash; 56 | 57 | @synthesize superclass; 58 | 59 | @end 60 | 61 | 62 | -------------------------------------------------------------------------------- /ios/RNInstabug/InstabugRepliesBridge.h: -------------------------------------------------------------------------------- 1 | // 2 | // InstabugRepliesBridge.h 3 | // RNInstabug 4 | // 5 | // Created by Salma Ali on 7/30/19. 6 | // Copyright © 2019 instabug. All rights reserved. 7 | // 8 | 9 | 10 | #import 11 | #import 12 | #import 13 | #import 14 | 15 | @interface InstabugRepliesBridge : RCTEventEmitter 16 | /* 17 | +------------------------------------------------------------------------+ 18 | | Replies Module | 19 | +------------------------------------------------------------------------+ 20 | */ 21 | 22 | - (void)setEnabled:(BOOL) isEnabled; 23 | 24 | - (void)hasChats:(RCTPromiseResolveBlock)resolve :(RCTPromiseRejectBlock)reject; 25 | 26 | - (void)show; 27 | 28 | - (void)setOnNewReplyReceivedHandler:(RCTResponseSenderBlock) callback; 29 | 30 | - (void)getUnreadRepliesCount:(RCTPromiseResolveBlock)resolve :(RCTPromiseRejectBlock)reject; 31 | 32 | - (void)setInAppNotificationEnabled:(BOOL)isChatNotificationEnabled; 33 | 34 | - (void)setPushNotificationsEnabled:(BOOL)isPushNotificationEnabled; 35 | 36 | 37 | 38 | @end 39 | 40 | 41 | -------------------------------------------------------------------------------- /ios/RNInstabug/InstabugRepliesBridge.m: -------------------------------------------------------------------------------- 1 | // 2 | // InstabugRepliesBridge.m 3 | // RNInstabug 4 | // 5 | // Created by Salma Ali on 7/30/19. 6 | // Copyright © 2019 instabug. All rights reserved. 7 | // 8 | // 9 | 10 | #import "InstabugRepliesBridge.h" 11 | #import 12 | #import 13 | #import 14 | #import 15 | #import 16 | #import 17 | 18 | @implementation InstabugRepliesBridge 19 | 20 | - (dispatch_queue_t)methodQueue { 21 | return dispatch_get_main_queue(); 22 | } 23 | 24 | + (BOOL)requiresMainQueueSetup 25 | { 26 | return NO; 27 | } 28 | 29 | - (NSArray *)supportedEvents { 30 | return @[@"IBGOnNewReplyReceivedCallback"]; 31 | } 32 | 33 | RCT_EXPORT_MODULE(IBGReplies) 34 | 35 | RCT_EXPORT_METHOD(setEnabled:(BOOL) isEnabled) { 36 | IBGReplies.enabled = isEnabled; 37 | } 38 | 39 | RCT_EXPORT_METHOD(hasChats:(RCTPromiseResolveBlock)resolve :(RCTPromiseRejectBlock)reject) { 40 | BOOL hasChats = IBGReplies.hasChats; 41 | resolve(@(hasChats)); 42 | 43 | } 44 | 45 | RCT_EXPORT_METHOD(show) { 46 | [[NSRunLoop mainRunLoop] performSelector:@selector(show) target:[IBGReplies class] argument:nil order:0 modes:@[NSDefaultRunLoopMode]]; 47 | } 48 | 49 | RCT_EXPORT_METHOD(setOnNewReplyReceivedHandler:(RCTResponseSenderBlock) callback) { 50 | if (callback != nil) { 51 | IBGReplies.didReceiveReplyHandler = ^{ 52 | [self sendEventWithName:@"IBGOnNewReplyReceivedCallback" body:nil]; 53 | }; 54 | } else { 55 | IBGReplies.didReceiveReplyHandler = nil; 56 | } 57 | 58 | } 59 | 60 | RCT_EXPORT_METHOD(getUnreadRepliesCount:(RCTPromiseResolveBlock)resolve :(RCTPromiseRejectBlock)reject) { 61 | resolve(@(IBGReplies.unreadRepliesCount)); 62 | } 63 | 64 | RCT_EXPORT_METHOD(setInAppNotificationEnabled:(BOOL)isChatNotificationEnabled) { 65 | IBGReplies.inAppNotificationsEnabled = isChatNotificationEnabled; 66 | } 67 | 68 | RCT_EXPORT_METHOD(setPushNotificationsEnabled:(BOOL)isPushNotificationEnabled) { 69 | [IBGReplies setPushNotificationsEnabled:isPushNotificationEnabled]; 70 | } 71 | 72 | @synthesize description; 73 | 74 | @synthesize hash; 75 | 76 | @synthesize superclass; 77 | 78 | @end 79 | 80 | 81 | -------------------------------------------------------------------------------- /ios/RNInstabug/InstabugSessionReplayBridge.h: -------------------------------------------------------------------------------- 1 | #import 2 | #import 3 | #import 4 | #import 5 | #import 6 | 7 | @interface InstabugSessionReplayBridge : RCTEventEmitter 8 | /* 9 | +------------------------------------------------------------------------+ 10 | | Session Replay Module | 11 | +------------------------------------------------------------------------+ 12 | */ 13 | 14 | - (void)setEnabled:(BOOL)isEnabled; 15 | 16 | - (void)setInstabugLogsEnabled:(BOOL)isEnabled; 17 | 18 | - (void)setNetworkLogsEnabled:(BOOL)isEnabled; 19 | 20 | - (void)setUserStepsEnabled:(BOOL)isEnabled; 21 | 22 | - (void)getSessionReplayLink:(RCTPromiseResolveBlock)resolve :(RCTPromiseRejectBlock)reject; 23 | 24 | - (void)setSyncCallback; 25 | 26 | - (void)evaluateSync:(BOOL)result; 27 | 28 | @property (atomic, copy) SessionEvaluationCompletion sessionEvaluationCompletion; 29 | 30 | @end 31 | 32 | 33 | -------------------------------------------------------------------------------- /ios/RNInstabug/InstabugSurveysBridge.h: -------------------------------------------------------------------------------- 1 | // 2 | // InstabugSurveysBridge.h 3 | // RNInstabug 4 | // 5 | // Created by Salma Ali on 7/30/19. 6 | // Copyright © 2019 instabug. All rights reserved. 7 | // 8 | 9 | #import 10 | #import 11 | #import 12 | #import 13 | 14 | @interface InstabugSurveysBridge : RCTEventEmitter 15 | /* 16 | +------------------------------------------------------------------------+ 17 | | Surveys Module | 18 | +------------------------------------------------------------------------+ 19 | */ 20 | 21 | - (void)showSurvey:(NSString *)surveyToken; 22 | 23 | - (void)showSurveysIfAvailable; 24 | 25 | - (void)setOnShowHandler:(RCTResponseSenderBlock)callBack; 26 | 27 | - (void)setOnDismissHandler:(RCTResponseSenderBlock)callBack; 28 | 29 | - (void)setAutoShowingEnabled:(BOOL)autoShowingSurveysEnabled; 30 | 31 | - (void)setShouldShowWelcomeScreen:(BOOL)shouldShowWelcomeScreen; 32 | 33 | - (void)hasRespondedToSurvey:(NSString *)surveyToken 34 | :(RCTPromiseResolveBlock)resolve 35 | :(RCTPromiseRejectBlock)reject; 36 | 37 | - (void)getAvailableSurveys:(RCTPromiseResolveBlock)resolve :(RCTPromiseRejectBlock)reject; 38 | 39 | - (void)setEnabled:(BOOL)surveysEnabled; 40 | 41 | - (void)setAppStoreURL:(NSString *)appStoreURL; 42 | 43 | 44 | @end 45 | 46 | 47 | -------------------------------------------------------------------------------- /ios/RNInstabug/RCTConvert+InstabugEnums.h: -------------------------------------------------------------------------------- 1 | // 2 | // RCTConvert+InstabugEnums.h 3 | // instabugDemo 4 | // 5 | // Created by Yousef Hamza on 9/29/16. 6 | // Copyright © 2016 Facebook. All rights reserved. 7 | // 8 | #if __has_include("React/RCTConvert.h") 9 | #import 10 | #else 11 | #import "RCTConvert.h" 12 | #endif 13 | 14 | #import "ArgsRegistry.h" 15 | 16 | @interface RCTConvert (InstabugEnums) 17 | 18 | @end 19 | -------------------------------------------------------------------------------- /ios/RNInstabug/RNInstabug.h: -------------------------------------------------------------------------------- 1 | #ifndef RNInstabug_h 2 | #define RNInstabug_h 3 | 4 | #import 5 | 6 | @interface RNInstabug : NSObject 7 | 8 | + (void)initWithToken:(NSString *)token invocationEvents:(IBGInvocationEvent)invocationEvents debugLogsLevel:(IBGSDKDebugLogsLevel)debugLogsLevel; 9 | 10 | + (void)initWithToken:(NSString *)token invocationEvents:(IBGInvocationEvent)invocationEvents debugLogsLevel:(IBGSDKDebugLogsLevel)debugLogsLevel 11 | useNativeNetworkInterception:(BOOL)useNativeNetworkInterception; 12 | 13 | + (void)initWithToken:(NSString *)token 14 | invocationEvents:(IBGInvocationEvent)invocationEvents 15 | useNativeNetworkInterception:(BOOL)useNativeNetworkInterception; 16 | 17 | + (void)initWithToken:(NSString *)token invocationEvents:(IBGInvocationEvent)invocationEvents; 18 | 19 | /** 20 | @brief Set codePush version before starting the SDK. 21 | 22 | @discussion Sets Code Push version to be used for all reports. 23 | should be called from `-[UIApplicationDelegate application:didFinishLaunchingWithOptions:]` 24 | and before `startWithToken`. 25 | 26 | @param codePushVersion the Code Push version to be used for all reports. 27 | */ 28 | + (void)setCodePushVersion:(NSString *)codePushVersion; 29 | 30 | @end 31 | 32 | #endif /* RNInstabug_h */ 33 | -------------------------------------------------------------------------------- /ios/RNInstabug/Util/IBGAPM+PrivateAPIs.h: -------------------------------------------------------------------------------- 1 | // 2 | // IBGAPM+PrivateAPIs.h 3 | // Pods 4 | // 5 | // Created by Instabug on 02/06/2024. 6 | // 7 | 8 | //#import "IBGAPM.h" 9 | 10 | @interface IBGAPM (PrivateAPIs) 11 | 12 | @property (class, atomic, assign) BOOL networkEnabled; 13 | 14 | 15 | @end 16 | -------------------------------------------------------------------------------- /ios/RNInstabug/Util/IBGCrashReporting+CP.h: -------------------------------------------------------------------------------- 1 | #import 2 | 3 | 4 | @interface IBGCrashReporting (CP) 5 | 6 | + (void)cp_reportFatalCrashWithStackTrace:(NSDictionary*)stackTrace; 7 | 8 | + (void)cp_reportNonFatalCrashWithStackTrace:(NSDictionary*)stackTrace 9 | level:(IBGNonFatalLevel)level 10 | groupingString:(NSString *)groupingString 11 | userAttributes:(NSDictionary *)userAttributes; 12 | @end 13 | 14 | -------------------------------------------------------------------------------- /ios/RNInstabug/Util/Instabug+CP.h: -------------------------------------------------------------------------------- 1 | #import 2 | #import 3 | 4 | NS_ASSUME_NONNULL_BEGIN 5 | 6 | @interface Instabug (CP) 7 | 8 | + (void)setCurrentPlatform:(IBGPlatform)platform; 9 | 10 | @end 11 | 12 | NS_ASSUME_NONNULL_END 13 | -------------------------------------------------------------------------------- /ios/native.rb: -------------------------------------------------------------------------------- 1 | $instabug = { :version => '14.3.0' } 2 | 3 | def use_instabug! (spec = nil) 4 | version = $instabug[:version] 5 | if (!spec) 6 | pod 'Instabug', version 7 | else 8 | spec.dependency 'Instabug', version 9 | end 10 | 11 | $instabug 12 | end 13 | -------------------------------------------------------------------------------- /jest.config.js: -------------------------------------------------------------------------------- 1 | /** @type {import('ts-jest').JestConfigWithTsJest} */ 2 | module.exports = { 3 | preset: 'react-native', 4 | testEnvironment: 'node', 5 | cacheDirectory: '.jest/cache', 6 | coverageDirectory: './coverage/', 7 | clearMocks: true, 8 | collectCoverage: true, 9 | collectCoverageFrom: ['./src/**/*.(js|ts)'], 10 | setupFilesAfterEnv: ['./test/setup.ts'], 11 | moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'json', 'node'], 12 | modulePathIgnorePatterns: ['examples'], 13 | transform: { 14 | '^.+\\.jsx$': 'babel-jest', 15 | '^.+\\.tsx?$': ['ts-jest', { tsconfig: 'tsconfig.test.json' }], 16 | }, 17 | }; 18 | -------------------------------------------------------------------------------- /react-native.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | dependency: { 3 | platforms: { 4 | ios: { 5 | scriptPhases: [ 6 | { 7 | name: '[instabug-reactnative] Upload Sourcemap', 8 | path: './ios/sourcemaps.sh', 9 | execution_position: 'after_compile', 10 | }, 11 | ], 12 | }, 13 | android: {}, 14 | }, 15 | }, 16 | }; 17 | -------------------------------------------------------------------------------- /rollup.config.js: -------------------------------------------------------------------------------- 1 | import commonjs from '@rollup/plugin-commonjs'; 2 | import json from '@rollup/plugin-json'; 3 | import nodeResolve from '@rollup/plugin-node-resolve'; 4 | import typescript from '@rollup/plugin-typescript'; 5 | import cleanup from 'rollup-plugin-cleanup'; 6 | import shebang from 'rollup-plugin-preserve-shebang'; 7 | 8 | const commonPlugins = [ 9 | shebang(), 10 | json(), 11 | nodeResolve({ preferBuiltins: true }), 12 | commonjs(), 13 | cleanup(), 14 | ]; 15 | 16 | /** @type import('rollup').RollupOptions */ 17 | export default [ 18 | { 19 | input: ['cli/index.ts'], 20 | output: { 21 | dir: 'bin', 22 | format: 'cjs', 23 | }, 24 | plugins: [...commonPlugins, typescript({ tsconfig: './tsconfig.cli.json' })], 25 | }, 26 | { 27 | input: ['cli/upload/index.ts'], 28 | output: { 29 | dir: 'upload', 30 | format: 'cjs', 31 | }, 32 | plugins: [...commonPlugins, typescript({ tsconfig: './tsconfig.upload.json' })], 33 | }, 34 | ]; 35 | -------------------------------------------------------------------------------- /scripts/customize-ios-endpoints.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Replaces the internal Config.plist file inside the Instabug iOS SDK with the 4 | # Instabug.plist file in the example app. 5 | # 6 | # This is a workaround until the iOS SDK is updated to prioritize the custom 7 | # Instabug.plist over the internal Config.plist. 8 | 9 | instabug_plist=examples/default/ios/InstabugExample/Instabug.plist 10 | 11 | if [ ! -f $instabug_plist ]; then 12 | echo "Instabug.plist not found" 13 | exit 1 14 | fi 15 | 16 | for dir in examples/default/ios/Pods/Instabug/Instabug.xcframework/ios-*/ 17 | do 18 | echo "Replacing Config.plist in $dir" 19 | 20 | config_path=$dir/Instabug.framework/InstabugResources.bundle/Config.plist 21 | 22 | if [ ! -f $config_path ]; then 23 | echo "Config.plist not found in $dir" 24 | exit 1 25 | fi 26 | 27 | cp -f $instabug_plist $config_path 28 | done 29 | -------------------------------------------------------------------------------- /scripts/find-token.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # Searches for app token within source files. 4 | 5 | INIT_APP_TOKEN=$( 6 | grep "Instabug.init({" -r -A 6 -m 1 --exclude-dir={node_modules,ios,android} --include=\*.{js,ts,jsx,tsx} ./ | 7 | grep "token:[[:space:]]*[\"\'][0-9a-zA-Z]*[\"\']" | 8 | grep -o "[\"\'][0-9a-zA-Z]*[\"\']" | 9 | cut -d "\"" -f 2 | 10 | cut -d "'" -f 2 11 | ) 12 | 13 | if [ ! -z "${INIT_APP_TOKEN}" ]; then 14 | echo $INIT_APP_TOKEN 15 | exit 0 16 | fi 17 | 18 | START_APP_TOKEN=$( 19 | grep "Instabug.start(" -r -A 1 -m 1 --exclude-dir={node_modules,ios,android} --include=\*.{js,ts,jsx,tsx} ./ | 20 | grep -o "[\"\'][0-9a-zA-Z]*[\"\']" | 21 | cut -d "\"" -f 2 | 22 | cut -d "'" -f 2 23 | ) 24 | 25 | if [ ! -z "${START_APP_TOKEN}" ]; then 26 | echo $START_APP_TOKEN 27 | exit 0 28 | fi 29 | 30 | echo "Couldn't find Instabug's app token" 31 | exit 1 32 | -------------------------------------------------------------------------------- /scripts/notify-github.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | pr_url="https://api.github.com/repos/$CIRCLE_PROJECT_USERNAME/$CIRCLE_PROJECT_REPONAME/pulls?head=$CIRCLE_PROJECT_USERNAME:$CIRCLE_BRANCH&state=open" 4 | pr_response=$(curl --location --request GET "$pr_url" --header "Authorization: Bearer $RELEASE_GITHUB_TOKEN") 5 | 6 | if [ $(echo "$pr_response" | jq length) -eq 0 ]; then 7 | echo "No PR found to update" 8 | else 9 | pr_comment_url=$(echo "$pr_response" | jq -r ".[]._links.comments.href") 10 | 11 | curl --location --request POST "$pr_comment_url" \ 12 | --header 'Content-Type: application/json' \ 13 | --header "Authorization: Bearer $RELEASE_GITHUB_TOKEN" \ 14 | --data-raw "$1" 15 | fi 16 | -------------------------------------------------------------------------------- /scripts/replace.js: -------------------------------------------------------------------------------- 1 | /** 2 | * A script to replace all occurrences of a string in a file, this is built as a 3 | * replacement for the `sed` command to make it easier to replace strings with 4 | * special characters without the hassle of escaping them, this is important 5 | * when we are replacing strings that aren't known in advance like parameters 6 | * from files. 7 | * 8 | * Usage: node replace.js 9 | */ 10 | 11 | const fs = require('fs'); 12 | const path = require('path'); 13 | const { parseArgs } = require('util'); 14 | 15 | const { values, positionals } = parseArgs({ 16 | allowPositionals: true, 17 | options: { 18 | pattern: { 19 | type: 'boolean', 20 | default: false, 21 | short: 'p', 22 | }, 23 | }, 24 | }); 25 | 26 | const [search, replace, ...files] = positionals; 27 | 28 | /** Whether to replace the search string as a regular expression or as a literal string. */ 29 | const isPattern = values.pattern; 30 | 31 | if (search == null || replace == null || !files.length) { 32 | // The path of the script relative to the directory where the user ran the 33 | // script to be used in the error message demonstrating the usage. 34 | const scriptPath = path.relative(process.cwd(), __filename); 35 | 36 | console.error('Missing arguments.'); 37 | console.table({ search, replace, files }); 38 | 39 | console.error(`Usage: node ${scriptPath} [-p | --pattern] `); 40 | process.exit(1); 41 | } 42 | 43 | for (const file of files) { 44 | try { 45 | const filePath = path.resolve(process.cwd(), file); 46 | 47 | const fileContent = fs.readFileSync(filePath, 'utf8'); 48 | 49 | const searchPattern = isPattern ? new RegExp(search, 'gm') : search; 50 | const newContent = fileContent.replaceAll(searchPattern, replace); 51 | 52 | fs.writeFileSync(filePath, newContent); 53 | } catch (error) { 54 | console.error(`An error occurred while replacing the content of the file: ${file}.`); 55 | console.error(error); 56 | process.exit(1); 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /scripts/snapshot-comment.md: -------------------------------------------------------------------------------- 1 | Your snapshot has been generated! :rocket: 2 | 3 | ### Installation 4 | 5 | You can install the snapshot through NPM: 6 | 7 | ```sh 8 | npm install instabug-reactnative@{VERSION} 9 | ``` 10 | 11 | or Yarn: 12 | 13 | ```sh 14 | yarn add instabug-reactnative@{VERSION} 15 | ``` 16 | -------------------------------------------------------------------------------- /scripts/snapshot-version.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Generates a snapshot version following the format {version}-{pr}{random-3-digit}-SNAPSHOT 4 | # Example: 13.3.0-502861-SNAPSHOT 5 | 6 | pr=$(basename $CIRCLE_PULL_REQUEST) 7 | random=$(($RANDOM % 900 + 100)) 8 | version=$(jq -r '.version' package.json) 9 | suffix="SNAPSHOT" 10 | 11 | SNAPSHOT_VERSION="$version-$pr$random-$suffix" 12 | -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | // Models 2 | import type { InstabugConfig } from './models/InstabugConfig'; 3 | import Report from './models/Report'; 4 | import Trace from './models/Trace'; 5 | // Modules 6 | import * as APM from './modules/APM'; 7 | import * as BugReporting from './modules/BugReporting'; 8 | import * as CrashReporting from './modules/CrashReporting'; 9 | import * as FeatureRequests from './modules/FeatureRequests'; 10 | import * as Instabug from './modules/Instabug'; 11 | import * as NetworkLogger from './modules/NetworkLogger'; 12 | import type { NetworkData, NetworkDataObfuscationHandler } from './modules/NetworkLogger'; 13 | import * as Replies from './modules/Replies'; 14 | import type { Survey } from './modules/Surveys'; 15 | import * as Surveys from './modules/Surveys'; 16 | import * as SessionReplay from './modules/SessionReplay'; 17 | import type { SessionMetadata } from './models/SessionMetadata'; 18 | 19 | export * from './utils/Enums'; 20 | export { 21 | Report, 22 | Trace, 23 | APM, 24 | BugReporting, 25 | CrashReporting, 26 | FeatureRequests, 27 | NetworkLogger, 28 | SessionReplay, 29 | Replies, 30 | Surveys, 31 | }; 32 | export type { InstabugConfig, Survey, NetworkData, NetworkDataObfuscationHandler, SessionMetadata }; 33 | 34 | export default Instabug; 35 | -------------------------------------------------------------------------------- /src/models/FeatureFlag.ts: -------------------------------------------------------------------------------- 1 | export interface FeatureFlag { 2 | /** 3 | * the name of feature flag 4 | */ 5 | name: string; 6 | 7 | /** 8 | * The variant of the feature flag. 9 | * Leave it `undefined` for boolean (kill switch) feature flags. 10 | */ 11 | variant?: string; 12 | } 13 | -------------------------------------------------------------------------------- /src/models/InstabugConfig.ts: -------------------------------------------------------------------------------- 1 | import type { InvocationEvent, LogLevel, NetworkInterceptionMode } from '../utils/Enums'; 2 | 3 | export interface InstabugConfig { 4 | /** 5 | * The token that identifies the app. You can find it on your dashboard. 6 | */ 7 | token: string; 8 | /** 9 | * An array of events that invoke the SDK's UI. 10 | */ 11 | invocationEvents: InvocationEvent[]; 12 | /** 13 | * An optional LogLevel to indicate the verbosity of SDK logs. Default is Error. 14 | */ 15 | debugLogsLevel?: LogLevel; 16 | 17 | /** 18 | * An optional code push version to be used for all reports. 19 | */ 20 | codePushVersion?: string; 21 | 22 | /** 23 | * An optional network interception mode, this determines whether network interception 24 | * is done in the JavaScript side or in the native Android and iOS SDK side. 25 | * 26 | * When set to `NetworkInterceptionMode.native`, configuring network logging 27 | * should be done through native code not JavaScript (e.g. network request obfuscation). 28 | * 29 | * @default NetworkInterceptionMode.javascript 30 | */ 31 | networkInterceptionMode?: NetworkInterceptionMode; 32 | } 33 | -------------------------------------------------------------------------------- /src/models/NonFatalOptions.ts: -------------------------------------------------------------------------------- 1 | import type { NonFatalErrorLevel } from '../utils/Enums'; 2 | 3 | export interface NonFatalOptions { 4 | /** 5 | * An Optional extra user attributes attached to the crash 6 | * */ 7 | userAttributes?: Record; 8 | /** 9 | * An Optional key used to customize how crashes are grouped together 10 | * */ 11 | fingerprint?: string; 12 | /** 13 | * An Optional different severity levels for errors 14 | * */ 15 | level?: NonFatalErrorLevel; 16 | } 17 | -------------------------------------------------------------------------------- /src/models/ReproConfig.ts: -------------------------------------------------------------------------------- 1 | import type { ReproStepsMode } from '../utils/Enums'; 2 | 3 | export interface ReproConfig { 4 | /** 5 | * Repro steps mode for Bug Reporting. 6 | * 7 | * @default ReproStepsMode.enabled 8 | */ 9 | bug?: ReproStepsMode; 10 | 11 | /** 12 | * Repro steps mode for Crash Reporting. 13 | * 14 | * @default ReproStepsMode.enabledWithNoScreenshots 15 | */ 16 | crash?: ReproStepsMode; 17 | 18 | /** 19 | * Repro steps mode for Session Replay. 20 | * 21 | * @default ReproStepsMode.enabled 22 | */ 23 | sessionReplay?: ReproStepsMode; 24 | 25 | /** 26 | * Repro steps mode for Bug Reporting, Crash Reporting, and Session Replay. 27 | * 28 | * When this is set, `bug`, `crash`, and `sessionReplay` will be ignored. 29 | */ 30 | all?: ReproStepsMode; 31 | } 32 | -------------------------------------------------------------------------------- /src/models/SessionMetadata.ts: -------------------------------------------------------------------------------- 1 | import type { LaunchType } from '../utils/Enums'; 2 | 3 | /** 4 | * network log item 5 | */ 6 | export interface NetworkLog { 7 | url: string; 8 | duration: number; 9 | statusCode: number; 10 | } 11 | 12 | export interface SessionMetadata { 13 | /** 14 | * app version of the session 15 | */ 16 | appVersion: string; 17 | /** 18 | * operating system of the session 19 | */ 20 | OS: string; 21 | /** 22 | * mobile device model of the session 23 | */ 24 | device: string; 25 | /** 26 | * session duration in seconds 27 | */ 28 | sessionDurationInSeconds: number; 29 | /** 30 | * list of netwrok requests occurred during the session 31 | */ 32 | networkLogs: NetworkLog[]; 33 | /** 34 | * launch type of the session 35 | */ 36 | launchType: LaunchType; 37 | /** 38 | * is an in-app review occurred in the previous session. 39 | */ 40 | hasLinkToAppReview: boolean; 41 | /** 42 | * app launch duration 43 | */ 44 | launchDuration: number; 45 | /** 46 | * number of bugs in the session (iOS only) 47 | */ 48 | bugsCount?: number; 49 | /** 50 | * number of fetal crashes in the session (iOS only) 51 | */ 52 | fatalCrashCount?: number; 53 | /** 54 | * number of out of memory crashes in the session (iOS only) 55 | */ 56 | oomCrashCount?: number; 57 | } 58 | -------------------------------------------------------------------------------- /src/models/Trace.ts: -------------------------------------------------------------------------------- 1 | import { NativeAPM } from '../native/NativeAPM'; 2 | import type * as APM from '../modules/APM'; 3 | 4 | export default class Trace { 5 | constructor( 6 | public readonly id: string, 7 | public readonly name: string = '', 8 | public readonly attributes: Record = {}, 9 | ) {} 10 | 11 | /** 12 | * Adds an attribute with a specified key and value to the Trace to be sent. 13 | * 14 | * @param key - The key of the attribute. 15 | * @param value - The value of the attribute. 16 | * 17 | * @deprecated Please migrate to the App Flows APIs: {@link APM.startFlow}, {@link APM.endFlow}, and {@link APM.setFlowAttribute}. 18 | */ 19 | setAttribute(key: string, value: string) { 20 | NativeAPM.setExecutionTraceAttribute(this.id, key, value); 21 | this.attributes[key] = value; 22 | } 23 | 24 | /** 25 | * Ends the execution trace. 26 | * 27 | * @deprecated Please migrate to the App Flows APIs: {@link APM.startFlow}, {@link APM.endFlow}, and {@link APM.setFlowAttribute}. 28 | */ 29 | 30 | end() { 31 | NativeAPM.endExecutionTrace(this.id); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/models/W3cExternalTraceAttributes.ts: -------------------------------------------------------------------------------- 1 | export type W3cExternalTraceAttributes = { 2 | /** 3 | * A key that determines if the traceparent header was found 4 | */ 5 | isW3cHeaderFound: boolean | null; 6 | /** 7 | * A unique identifier for the trace generated by the SDK in case of no cought header found 8 | */ 9 | partialId: number | null; 10 | /** 11 | * The start time of the network request 12 | */ 13 | networkStartTimeInSeconds: number | null; 14 | /** 15 | * The traceparent header generated by the SDK 16 | */ 17 | w3cGeneratedHeader: string | null; 18 | /** 19 | * The traceparent header received by the server 20 | */ 21 | w3cCaughtHeader: string | null; 22 | }; 23 | -------------------------------------------------------------------------------- /src/modules/CrashReporting.ts: -------------------------------------------------------------------------------- 1 | import type { ExtendedError } from 'react-native/Libraries/Core/Devtools/parseErrorStack'; 2 | 3 | import { NativeCrashReporting } from '../native/NativeCrashReporting'; 4 | import InstabugUtils from '../utils/InstabugUtils'; 5 | import { Platform } from 'react-native'; 6 | import type { NonFatalOptions } from '../models/NonFatalOptions'; 7 | import { NonFatalErrorLevel } from '../utils/Enums'; 8 | import { Logger } from '../utils/logger'; 9 | 10 | /** 11 | * Enables and disables everything related to crash reporting including intercepting 12 | * errors in the global error handler. It is enabled by default. 13 | * @param isEnabled 14 | */ 15 | export const setEnabled = (isEnabled: boolean) => { 16 | NativeCrashReporting.setEnabled(isEnabled); 17 | }; 18 | 19 | /** 20 | * Send handled JS error object 21 | * @param error Error object to be sent to Instabug's servers 22 | * @param nonFatalOptions extra config for the non-fatal error sent with Error Object 23 | */ 24 | export const reportError = (error: ExtendedError, nonFatalOptions: NonFatalOptions = {}) => { 25 | if (error instanceof Error) { 26 | let level = NonFatalErrorLevel.error; 27 | if (nonFatalOptions.level != null) { 28 | level = nonFatalOptions.level; 29 | } 30 | return InstabugUtils.sendCrashReport(error, (data) => 31 | NativeCrashReporting.sendHandledJSCrash( 32 | data, 33 | nonFatalOptions.userAttributes, 34 | nonFatalOptions.fingerprint, 35 | level, 36 | ), 37 | ); 38 | } else { 39 | Logger.warn( 40 | `IBG-RN: The error ${error} has been omitted because only error type is supported.`, 41 | ); 42 | return; 43 | } 44 | }; 45 | 46 | /** 47 | * Enables and disables capturing native C++ NDK crashes. 48 | * @param isEnabled 49 | */ 50 | export const setNDKCrashesEnabled = (isEnabled: boolean) => { 51 | if (Platform.OS === 'android') { 52 | NativeCrashReporting.setNDKCrashesEnabled(isEnabled); 53 | } 54 | }; 55 | -------------------------------------------------------------------------------- /src/modules/FeatureRequests.ts: -------------------------------------------------------------------------------- 1 | import { NativeFeatureRequests } from '../native/NativeFeatureRequests'; 2 | import type { ActionType } from '../utils/Enums'; 3 | 4 | /** 5 | * Enables and disables everything related to feature requests. 6 | * 7 | * @param isEnabled 8 | */ 9 | export const setEnabled = (isEnabled: boolean) => { 10 | NativeFeatureRequests.setEnabled(isEnabled); 11 | }; 12 | 13 | /** 14 | * Sets whether users are required to enter an email address or not when 15 | * sending reports. 16 | * Defaults to YES. 17 | * 18 | * @param isEmailFieldRequired A boolean to indicate whether email field is required or not. 19 | * @param types An enum that indicates which action types will have the isEmailFieldRequired 20 | */ 21 | export const setEmailFieldRequired = (isEmailFieldRequired: boolean, type: ActionType) => { 22 | NativeFeatureRequests.setEmailFieldRequiredForFeatureRequests(isEmailFieldRequired, [ 23 | type, 24 | ] as ActionType[]); 25 | }; 26 | 27 | /** 28 | * Shows the UI for feature requests list 29 | */ 30 | export const show = () => { 31 | NativeFeatureRequests.show(); 32 | }; 33 | -------------------------------------------------------------------------------- /src/native/NativeAPM.ts: -------------------------------------------------------------------------------- 1 | import type { NativeModule } from 'react-native'; 2 | import { NativeEventEmitter } from 'react-native'; 3 | 4 | import type { W3cExternalTraceAttributes } from '../models/W3cExternalTraceAttributes'; 5 | import { NativeModules } from './NativePackage'; 6 | 7 | export interface ApmNativeModule extends NativeModule { 8 | // Essential APIs // 9 | setEnabled(isEnabled: boolean): void; 10 | 11 | // Network APIs // 12 | networkLogAndroid( 13 | requestStartTime: number, 14 | requestDuration: number, 15 | requestHeaders: string, 16 | requestBody: string, 17 | requestBodySize: number, 18 | requestMethod: string, 19 | requestUrl: string, 20 | requestContentType: string, 21 | responseHeaders: string, 22 | responseBody: string | null, 23 | responseBodySize: number, 24 | statusCode: number, 25 | responseContentType: string, 26 | errorDomain: string, 27 | w3cExternalTraceAttributes: W3cExternalTraceAttributes, 28 | gqlQueryName?: string, 29 | serverErrorMessage?: string, 30 | ): void; 31 | 32 | // App Launches APIs // 33 | setAppLaunchEnabled(isEnabled: boolean): void; 34 | endAppLaunch(): void; 35 | 36 | // Execution Traces APIs // 37 | startExecutionTrace(name: string, timestamp: string): Promise; 38 | setExecutionTraceAttribute(id: string, key: string, value: string): void; 39 | endExecutionTrace(id: string): void; 40 | 41 | // App Flows APIs // 42 | startFlow(name: string): void; 43 | endFlow(name: string): void; 44 | setFlowAttribute(name: string, key: string, value?: string | null): void; 45 | 46 | // UI Traces APIs // 47 | setAutoUITraceEnabled(isEnabled: boolean): void; 48 | startUITrace(name: string): void; 49 | endUITrace(): void; 50 | ibgSleep(): void; 51 | } 52 | 53 | export const NativeAPM = NativeModules.IBGAPM; 54 | 55 | export const emitter = new NativeEventEmitter(NativeAPM); 56 | -------------------------------------------------------------------------------- /src/native/NativeBugReporting.ts: -------------------------------------------------------------------------------- 1 | import { NativeEventEmitter, NativeModule } from 'react-native'; 2 | 3 | import type { 4 | DismissType, 5 | ExtendedBugReportMode, 6 | FloatingButtonPosition, 7 | InvocationEvent, 8 | InvocationOption, 9 | RecordingButtonPosition, 10 | ReportType, 11 | } from '../utils/Enums'; 12 | import { NativeModules } from './NativePackage'; 13 | 14 | export interface BugReportingNativeModule extends NativeModule { 15 | // Essential APIs // 16 | setEnabled(isEnabled: boolean): void; 17 | show(type: ReportType, options: InvocationOption[]): void; 18 | 19 | // Customization APIs // 20 | setInvocationEvents(events: InvocationEvent[]): void; 21 | setOptions(options: InvocationOption[]): void; 22 | setExtendedBugReportMode(mode: ExtendedBugReportMode): void; 23 | setReportTypes(types: ReportType[]): void; 24 | setDisclaimerText(text: string): void; 25 | setCommentMinimumCharacterCount(limit: number, reportTypes: ReportType[]): void; 26 | setFloatingButtonEdge(edge: FloatingButtonPosition, offset: number): void; 27 | setVideoRecordingFloatingButtonPosition(buttonPosition: RecordingButtonPosition): void; 28 | setEnabledAttachmentTypes( 29 | screenshot: boolean, 30 | extraScreenshot: boolean, 31 | galleryImage: boolean, 32 | screenRecording: boolean, 33 | ): void; 34 | 35 | // Screen Recording APIs // 36 | setAutoScreenRecordingEnabled(isEnabled: boolean): void; 37 | setAutoScreenRecordingDuration(maxDuration: number): void; 38 | setViewHierarchyEnabled(isEnabled: boolean): void; 39 | 40 | // Shaking Threshold APIs // 41 | setShakingThresholdForiPhone(threshold: number): void; 42 | setShakingThresholdForiPad(threshold: number): void; 43 | setShakingThresholdForAndroid(threshold: number): void; 44 | 45 | // Callbacks // 46 | setOnInvokeHandler(handler: () => void): void; 47 | setDidSelectPromptOptionHandler(handler: (promptOption: string) => void): void; 48 | setOnSDKDismissedHandler( 49 | handler: (dismissType: DismissType, reportType: ReportType) => void, 50 | ): void; 51 | } 52 | 53 | export const NativeBugReporting = NativeModules.IBGBugReporting; 54 | 55 | export enum NativeEvents { 56 | ON_INVOKE_HANDLER = 'IBGpreInvocationHandler', 57 | ON_DISMISS_HANDLER = 'IBGpostInvocationHandler', 58 | DID_SELECT_PROMPT_OPTION_HANDLER = 'IBGDidSelectPromptOptionHandler', 59 | } 60 | 61 | export const emitter = new NativeEventEmitter(NativeBugReporting); 62 | -------------------------------------------------------------------------------- /src/native/NativeCrashReporting.ts: -------------------------------------------------------------------------------- 1 | import type { NativeModule, Platform } from 'react-native'; 2 | import type { StackFrame } from 'react-native/Libraries/Core/Devtools/parseErrorStack'; 3 | 4 | import { NativeModules } from './NativePackage'; 5 | import type { NonFatalErrorLevel } from '../utils/Enums'; 6 | 7 | export interface CrashData { 8 | message: string; 9 | e_message: string; 10 | e_name: string; 11 | os: (typeof Platform)['OS']; 12 | platform: 'react_native'; 13 | exception: StackFrame[]; 14 | } 15 | 16 | export interface CrashReportingNativeModule extends NativeModule { 17 | setEnabled(isEnabled: boolean): void; 18 | sendJSCrash(data: CrashData | string): Promise; 19 | 20 | sendHandledJSCrash( 21 | data: CrashData | string, 22 | userAttributes?: Record | null, 23 | fingerprint?: string | null, 24 | nonFatalExceptionLevel?: NonFatalErrorLevel | null, 25 | ): Promise; 26 | setNDKCrashesEnabled(isEnabled: boolean): Promise; 27 | } 28 | 29 | export const NativeCrashReporting = NativeModules.IBGCrashReporting; 30 | -------------------------------------------------------------------------------- /src/native/NativeFeatureRequests.ts: -------------------------------------------------------------------------------- 1 | import type { NativeModule } from 'react-native'; 2 | 3 | import type { ActionType } from '../utils/Enums'; 4 | import { NativeModules } from './NativePackage'; 5 | 6 | export interface FeatureRequestsNativeModule extends NativeModule { 7 | setEnabled(isEnabled: boolean): void; 8 | show(): void; 9 | setEmailFieldRequiredForFeatureRequests(isEmailFieldRequired: boolean, types: ActionType[]): void; 10 | } 11 | 12 | export const NativeFeatureRequests = NativeModules.IBGFeatureRequests; 13 | -------------------------------------------------------------------------------- /src/native/NativePackage.ts: -------------------------------------------------------------------------------- 1 | import { NativeModules as ReactNativeModules } from 'react-native'; 2 | 3 | import type { ApmNativeModule } from './NativeAPM'; 4 | import type { BugReportingNativeModule } from './NativeBugReporting'; 5 | import type { CrashReportingNativeModule } from './NativeCrashReporting'; 6 | import type { FeatureRequestsNativeModule } from './NativeFeatureRequests'; 7 | import type { InstabugNativeModule } from './NativeInstabug'; 8 | import type { RepliesNativeModule } from './NativeReplies'; 9 | import type { SurveysNativeModule } from './NativeSurveys'; 10 | import type { SessionReplayNativeModule } from './NativeSessionReplay'; 11 | 12 | export interface InstabugNativePackage { 13 | IBGAPM: ApmNativeModule; 14 | IBGBugReporting: BugReportingNativeModule; 15 | IBGCrashReporting: CrashReportingNativeModule; 16 | IBGFeatureRequests: FeatureRequestsNativeModule; 17 | Instabug: InstabugNativeModule; 18 | IBGReplies: RepliesNativeModule; 19 | IBGSurveys: SurveysNativeModule; 20 | IBGSessionReplay: SessionReplayNativeModule; 21 | } 22 | 23 | export const NativeModules = ReactNativeModules as InstabugNativePackage; 24 | -------------------------------------------------------------------------------- /src/native/NativeReplies.ts: -------------------------------------------------------------------------------- 1 | import { NativeEventEmitter, NativeModule } from 'react-native'; 2 | 3 | import { NativeModules } from './NativePackage'; 4 | 5 | export interface RepliesNativeModule extends NativeModule { 6 | // Essential APIs // 7 | setEnabled(isEnabled: boolean): void; 8 | show(): void; 9 | hasChats(): Promise; 10 | getUnreadRepliesCount(): Promise; 11 | 12 | // Callbacks // 13 | setOnNewReplyReceivedHandler(handler: () => void): void; 14 | 15 | // Notifications APIs // 16 | setPushNotificationsEnabled(isEnabled: boolean): void; 17 | setInAppNotificationEnabled(isEnabled: boolean): void; 18 | 19 | // Android Notifications APIs // 20 | setInAppNotificationSound(isEnabled: boolean): void; 21 | setPushNotificationRegistrationToken(token: string): void; 22 | showNotification(data: Record): void; 23 | setNotificationIcon(resourceId: number): void; 24 | setPushNotificationChannelId(id: string): void; 25 | setSystemReplyNotificationSoundEnabled(isEnabled: boolean): void; 26 | } 27 | 28 | export const NativeReplies = NativeModules.IBGReplies; 29 | 30 | export enum NativeEvents { 31 | ON_REPLY_RECEIVED_HANDLER = 'IBGOnNewReplyReceivedCallback', 32 | } 33 | 34 | export const emitter = new NativeEventEmitter(NativeReplies); 35 | -------------------------------------------------------------------------------- /src/native/NativeSessionReplay.ts: -------------------------------------------------------------------------------- 1 | import { NativeEventEmitter } from 'react-native'; 2 | import type { NativeModule } from 'react-native'; 3 | 4 | import { NativeModules } from './NativePackage'; 5 | 6 | export interface SessionReplayNativeModule extends NativeModule { 7 | setEnabled(isEnabled: boolean): void; 8 | setNetworkLogsEnabled(isEnabled: boolean): void; 9 | setInstabugLogsEnabled(isEnabled: boolean): void; 10 | setUserStepsEnabled(isEnabled: boolean): void; 11 | getSessionReplayLink(): Promise; 12 | setSyncCallback(): Promise; 13 | evaluateSync(shouldSync: boolean): void; 14 | } 15 | 16 | export const NativeSessionReplay = NativeModules.IBGSessionReplay; 17 | export enum NativeEvents { 18 | SESSION_REPLAY_ON_SYNC_CALLBACK_INVOCATION = 'IBGSessionReplayOnSyncCallback', 19 | } 20 | 21 | export const emitter = new NativeEventEmitter(NativeSessionReplay); 22 | -------------------------------------------------------------------------------- /src/native/NativeSurveys.ts: -------------------------------------------------------------------------------- 1 | import { NativeEventEmitter, NativeModule } from 'react-native'; 2 | 3 | import { NativeModules } from './NativePackage'; 4 | 5 | export interface Survey { 6 | title: string; 7 | } 8 | 9 | export interface SurveysNativeModule extends NativeModule { 10 | // Essential APIs // 11 | setEnabled(isEnabled: boolean): void; 12 | setAutoShowingEnabled(autoShowingSurveysEnabled: boolean): void; 13 | showSurvey(surveyToken: string): void; 14 | showSurveysIfAvailable(): void; 15 | getAvailableSurveys(): Promise; 16 | hasRespondedToSurvey(surveyToken: string): Promise; 17 | 18 | // Misc APIs // 19 | setShouldShowWelcomeScreen(shouldShowWelcomeScreen: boolean): void; 20 | setAppStoreURL(appStoreURL: string): void; 21 | 22 | // Callbacks // 23 | setOnShowHandler(onShowHandler: () => void): void; 24 | setOnDismissHandler(onDismissHandler: () => void): void; 25 | } 26 | 27 | export const NativeSurveys = NativeModules.IBGSurveys; 28 | 29 | export enum NativeEvents { 30 | WILL_SHOW_SURVEY_HANDLER = 'IBGWillShowSurvey', 31 | DID_DISMISS_SURVEY_HANDLER = 'IBGDidDismissSurvey', 32 | } 33 | 34 | export const emitter = new NativeEventEmitter(NativeSurveys); 35 | -------------------------------------------------------------------------------- /src/promise.d.ts: -------------------------------------------------------------------------------- 1 | declare module 'promise/setimmediate/rejection-tracking' { 2 | export interface RejectionTrackingOptions { 3 | allRejections?: boolean; 4 | whitelist?: Function[]; 5 | onUnhandled?: (id: number, error: unknown) => void; 6 | onHandled?: (id: number, error: unknown) => void; 7 | } 8 | 9 | export function enable(options?: RejectionTrackingOptions): void; 10 | export function disable(): void; 11 | } 12 | -------------------------------------------------------------------------------- /src/utils/FeatureFlags.ts: -------------------------------------------------------------------------------- 1 | import { NativeInstabug } from '../native/NativeInstabug'; 2 | import { _registerW3CFlagsChangeListener } from '../modules/Instabug'; 3 | 4 | export const FeatureFlags = { 5 | isW3ExternalTraceID: () => NativeInstabug.isW3ExternalTraceIDEnabled(), 6 | isW3ExternalGeneratedHeader: () => NativeInstabug.isW3ExternalGeneratedHeaderEnabled(), 7 | isW3CaughtHeader: () => NativeInstabug.isW3CaughtHeaderEnabled(), 8 | }; 9 | 10 | export const registerW3CFlagsListener = () => { 11 | _registerW3CFlagsChangeListener( 12 | (res: { 13 | isW3ExternalTraceIDEnabled: boolean; 14 | isW3ExternalGeneratedHeaderEnabled: boolean; 15 | isW3CaughtHeaderEnabled: boolean; 16 | }) => { 17 | FeatureFlags.isW3ExternalTraceID = async () => { 18 | return res.isW3ExternalTraceIDEnabled; 19 | }; 20 | FeatureFlags.isW3ExternalGeneratedHeader = async () => { 21 | return res.isW3ExternalGeneratedHeaderEnabled; 22 | }; 23 | FeatureFlags.isW3CaughtHeader = async () => { 24 | return res.isW3CaughtHeaderEnabled; 25 | }; 26 | }, 27 | ); 28 | }; 29 | -------------------------------------------------------------------------------- /src/utils/InstabugConstants.ts: -------------------------------------------------------------------------------- 1 | const InstabugConstants = { 2 | GRAPHQL_HEADER: 'ibg-graphql-header', 3 | 4 | // TODO: dyanmically get the max size from the native SDK and update the error message to reflect the dynamic size. 5 | MAX_NETWORK_BODY_SIZE_IN_BYTES: 1024 * 10, // 10 KB 6 | MAX_RESPONSE_BODY_SIZE_EXCEEDED_MESSAGE: 7 | 'The response body has not been logged because it exceeds the maximum size of 10 Kb', 8 | MAX_REQUEST_BODY_SIZE_EXCEEDED_MESSAGE: 9 | 'The request body has not been logged because it exceeds the maximum size of 10 Kb', 10 | SET_USER_ATTRIBUTES_ERROR_TYPE_MESSAGE: 11 | 'IBG-RN: Expected key and value passed to setUserAttribute to be of type string', 12 | REMOVE_USER_ATTRIBUTES_ERROR_TYPE_MESSAGE: 13 | 'IBG-RN: Expected key and value passed to removeUserAttribute to be of type string', 14 | DEFAULT_METRO_PORT: '8081', 15 | }; 16 | 17 | export default InstabugConstants; 18 | -------------------------------------------------------------------------------- /src/utils/config.ts: -------------------------------------------------------------------------------- 1 | import InstabugConstants from './InstabugConstants'; 2 | import { LogLevel } from './Enums'; 3 | 4 | export const InstabugRNConfig = { 5 | metroDevServerPort: InstabugConstants.DEFAULT_METRO_PORT, 6 | debugLogsLevel: LogLevel.error, 7 | }; 8 | -------------------------------------------------------------------------------- /src/utils/logger.ts: -------------------------------------------------------------------------------- 1 | import { InstabugRNConfig } from './config'; 2 | import { LogLevel } from './Enums'; 3 | 4 | export class Logger { 5 | private static shouldLog(level: LogLevel): boolean { 6 | const currentLevel = InstabugRNConfig.debugLogsLevel; 7 | 8 | // Return true if the current log level is equal to or more verbose than the requested level 9 | const logLevelHierarchy: Record = { 10 | [LogLevel.verbose]: 3, 11 | [LogLevel.debug]: 2, 12 | [LogLevel.error]: 1, 13 | [LogLevel.none]: 0, 14 | }; 15 | 16 | return logLevelHierarchy[currentLevel] >= logLevelHierarchy[level]; 17 | } 18 | 19 | // General logging method that takes a logging function as an argument 20 | private static logMessage( 21 | level: LogLevel, 22 | logMethod: (...args: any[]) => void, 23 | message?: any, 24 | ...optionalParams: any[] 25 | ): void { 26 | if (this.shouldLog(level)) { 27 | logMethod(message, ...optionalParams); 28 | } 29 | } 30 | 31 | static error(message?: any, ...optionalParams: any[]) { 32 | this.logMessage(LogLevel.error, console.error, message, ...optionalParams); // Pass console.error for errors 33 | } 34 | 35 | static info(message?: any, ...optionalParams: any[]) { 36 | this.logMessage(LogLevel.verbose, console.info, message, ...optionalParams); // Pass console.info for info 37 | } 38 | 39 | static log(message?: any, ...optionalParams: any[]) { 40 | this.logMessage(LogLevel.verbose, console.log, message, ...optionalParams); // Default log method 41 | } 42 | 43 | static warn(message?: any, ...optionalParams: any[]) { 44 | this.logMessage(LogLevel.debug, console.warn, message, ...optionalParams); // Use console.warn for debug 45 | } 46 | 47 | static trace(message?: any, ...optionalParams: any[]) { 48 | this.logMessage(LogLevel.debug, console.trace, message, ...optionalParams); // Use console.trace for debugging 49 | } 50 | 51 | static debug(message?: any, ...optionalParams: any[]) { 52 | this.logMessage(LogLevel.debug, console.debug, message, ...optionalParams); // Use console.debug for debug logs 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /test/mocks/fakeNetworkRequest.ts: -------------------------------------------------------------------------------- 1 | import type { Interceptor, ReplyHeaders } from 'nock'; 2 | 3 | let xhr: XMLHttpRequest; 4 | 5 | export default { 6 | open(method: string, url: string) { 7 | xhr = new global.XMLHttpRequest(); 8 | xhr.open(method, url); 9 | }, 10 | send(data?: any) { 11 | xhr.send(data); 12 | }, 13 | setRequestHeaders(headers: Record) { 14 | for (let i = 0; i < Object.keys(headers).length; i++) { 15 | const key = Object.keys(headers)[i]; 16 | xhr.setRequestHeader(key, headers[key]); 17 | } 18 | }, 19 | setResponseType(type: XMLHttpRequestResponseType) { 20 | xhr.responseType = type; 21 | }, 22 | mockHasError() { 23 | // @ts-ignore 24 | xhr._hasError = true; 25 | }, 26 | mockXHRStatus(status: number | null) { 27 | // @ts-ignore 28 | xhr.status = status; 29 | }, 30 | mockResponse( 31 | request: Interceptor, 32 | status: number = 200, 33 | body: string | Buffer = 'ok', 34 | headers: ReplyHeaders = {}, 35 | ) { 36 | request.once().reply(status, body, headers); 37 | }, 38 | }; 39 | -------------------------------------------------------------------------------- /test/mocks/mockAPM.ts: -------------------------------------------------------------------------------- 1 | import type { ApmNativeModule } from '../../src/native/NativeAPM'; 2 | 3 | const mockAPM: ApmNativeModule = { 4 | addListener: jest.fn(), 5 | removeListeners: jest.fn(), 6 | setEnabled: jest.fn(), 7 | setAppLaunchEnabled: jest.fn(), 8 | setAutoUITraceEnabled: jest.fn(), 9 | startExecutionTrace: jest.fn(), 10 | setExecutionTraceAttribute: jest.fn(), 11 | endExecutionTrace: jest.fn(), 12 | startFlow: jest.fn(), 13 | setFlowAttribute: jest.fn(), 14 | endFlow: jest.fn(), 15 | startUITrace: jest.fn(), 16 | endUITrace: jest.fn(), 17 | endAppLaunch: jest.fn(), 18 | ibgSleep: jest.fn(), 19 | networkLogAndroid: jest.fn(), 20 | }; 21 | 22 | export default mockAPM; 23 | -------------------------------------------------------------------------------- /test/mocks/mockBugReporting.ts: -------------------------------------------------------------------------------- 1 | import type { BugReportingNativeModule } from '../../src/native/NativeBugReporting'; 2 | 3 | const mockBugReporting: BugReportingNativeModule = { 4 | addListener: jest.fn(), 5 | removeListeners: jest.fn(), 6 | setEnabled: jest.fn(), 7 | setInvocationEvents: jest.fn(), 8 | setOptions: jest.fn(), 9 | setFloatingButtonEdge: jest.fn(), 10 | setShakingThresholdForiPhone: jest.fn(), 11 | setShakingThresholdForiPad: jest.fn(), 12 | setShakingThresholdForAndroid: jest.fn(), 13 | setExtendedBugReportMode: jest.fn(), 14 | setReportTypes: jest.fn(), 15 | show: jest.fn(), 16 | setOnInvokeHandler: jest.fn(), 17 | setOnSDKDismissedHandler: jest.fn(), 18 | setAutoScreenRecordingEnabled: jest.fn(), 19 | setAutoScreenRecordingDuration: jest.fn(), 20 | setViewHierarchyEnabled: jest.fn(), 21 | setEnabledAttachmentTypes: jest.fn(), 22 | setDidSelectPromptOptionHandler: jest.fn(), 23 | setVideoRecordingFloatingButtonPosition: jest.fn(), 24 | setDisclaimerText: jest.fn(), 25 | setCommentMinimumCharacterCount: jest.fn(), 26 | }; 27 | 28 | export default mockBugReporting; 29 | -------------------------------------------------------------------------------- /test/mocks/mockCrashReporting.ts: -------------------------------------------------------------------------------- 1 | import type { CrashReportingNativeModule } from '../../src/native/NativeCrashReporting'; 2 | 3 | const mockCrashReporting: CrashReportingNativeModule = { 4 | addListener: jest.fn(), 5 | removeListeners: jest.fn(), 6 | setEnabled: jest.fn(), 7 | sendHandledJSCrash: jest.fn(), 8 | sendJSCrash: jest.fn(), 9 | setNDKCrashesEnabled: jest.fn(), 10 | }; 11 | 12 | export default mockCrashReporting; 13 | -------------------------------------------------------------------------------- /test/mocks/mockDevMode.ts: -------------------------------------------------------------------------------- 1 | export function mockDevMode(isDev: boolean) { 2 | const original = __DEV__; 3 | 4 | // Using Object.defineProperty to avoid TypeScript errors 5 | Object.defineProperty(global, '__DEV__', { value: isDev }); 6 | 7 | return { 8 | mockRestore: () => { 9 | Object.defineProperty(global, '__DEV__', { value: original }); 10 | }, 11 | }; 12 | } 13 | -------------------------------------------------------------------------------- /test/mocks/mockFeatureRequests.ts: -------------------------------------------------------------------------------- 1 | import type { FeatureRequestsNativeModule } from '../../src/native/NativeFeatureRequests'; 2 | 3 | const mockFeatureRequests: FeatureRequestsNativeModule = { 4 | addListener: jest.fn(), 5 | removeListeners: jest.fn(), 6 | setEmailFieldRequiredForFeatureRequests: jest.fn(), 7 | show: jest.fn(), 8 | setEnabled: jest.fn(), 9 | }; 10 | 11 | export default mockFeatureRequests; 12 | -------------------------------------------------------------------------------- /test/mocks/mockHermesInternal.ts: -------------------------------------------------------------------------------- 1 | import type { HermesInternalType } from '../../src/utils/UnhandledRejectionTracking'; 2 | 3 | export function mockHermesInternal(hermes: HermesInternalType) { 4 | const original = (global as any).HermesInternal; 5 | 6 | // Using Object.defineProperty to avoid TypeScript errors 7 | Object.defineProperty(global, 'HermesInternal', { value: hermes, writable: true }); 8 | 9 | return { 10 | mockRestore: () => { 11 | Object.defineProperty(global, 'HermesInternal', { value: original, writable: true }); 12 | }, 13 | }; 14 | } 15 | -------------------------------------------------------------------------------- /test/mocks/mockInstabugUtils.ts: -------------------------------------------------------------------------------- 1 | jest.mock('../../src/utils/InstabugUtils', () => { 2 | const actual = jest.requireActual('../../src/utils/InstabugUtils'); 3 | 4 | return { 5 | ...actual, 6 | parseErrorStack: jest.fn(), 7 | captureJsErrors: jest.fn(), 8 | getActiveRouteName: jest.fn(), 9 | stringifyIfNotString: jest.fn(), 10 | sendCrashReport: jest.fn(), 11 | getStackTrace: jest.fn().mockReturnValue('javascriptStackTrace'), 12 | getFullRoute: jest.fn().mockImplementation(() => 'ScreenName'), 13 | reportNetworkLog: jest.fn(), 14 | isContentTypeNotAllowed: jest.fn(), 15 | }; 16 | }); 17 | -------------------------------------------------------------------------------- /test/mocks/mockNativeModules.ts: -------------------------------------------------------------------------------- 1 | import type { InstabugNativePackage } from '../../src/native/NativePackage'; 2 | import mockAPM from './mockAPM'; 3 | import mockBugReporting from './mockBugReporting'; 4 | import mockCrashReporting from './mockCrashReporting'; 5 | import mockFeatureRequests from './mockFeatureRequests'; 6 | import mockSessionReplay from './mockSessionReplay'; 7 | import mockInstabug from './mockInstabug'; 8 | import mockReplies from './mockReplies'; 9 | import mockSurveys from './mockSurveys'; 10 | 11 | jest.mock('react-native', () => { 12 | const RN = jest.requireActual('react-native'); 13 | const mockNativeModules: InstabugNativePackage = { 14 | IBGAPM: mockAPM, 15 | IBGBugReporting: mockBugReporting, 16 | IBGCrashReporting: mockCrashReporting, 17 | IBGFeatureRequests: mockFeatureRequests, 18 | IBGSessionReplay: mockSessionReplay, 19 | Instabug: mockInstabug, 20 | IBGReplies: mockReplies, 21 | IBGSurveys: mockSurveys, 22 | }; 23 | 24 | Object.assign(RN.NativeModules, mockNativeModules); 25 | 26 | return RN; 27 | }); 28 | -------------------------------------------------------------------------------- /test/mocks/mockNetworkLogger.ts: -------------------------------------------------------------------------------- 1 | jest.mock('../../src/modules/NetworkLogger'); 2 | -------------------------------------------------------------------------------- /test/mocks/mockParseErrorStackLib.ts: -------------------------------------------------------------------------------- 1 | jest.mock('react-native/Libraries/Core/Devtools/parseErrorStack', () => { 2 | const { Platform } = jest.requireActual('react-native'); 3 | 4 | // This mock's goal is to provide a parseErrorStack function that adapts to the mock React Native version 5 | // This mock should work as long as the tests run with React Native version < 0.64 6 | return jest.fn((error) => { 7 | const originalParseErrorStack = jest.requireActual( 8 | 'react-native/Libraries/Core/Devtools/parseErrorStack', 9 | ); 10 | 11 | if (!Platform.hasOwnProperty('constants') || Platform.constants.reactNativeVersion.minor < 64) { 12 | return originalParseErrorStack(error.stack); 13 | } 14 | 15 | return originalParseErrorStack(error); 16 | }); 17 | }); 18 | -------------------------------------------------------------------------------- /test/mocks/mockPromiseRejectionTracking.ts: -------------------------------------------------------------------------------- 1 | jest.mock('promise/setimmediate/rejection-tracking'); 2 | -------------------------------------------------------------------------------- /test/mocks/mockReplies.ts: -------------------------------------------------------------------------------- 1 | import type { RepliesNativeModule } from '../../src/native/NativeReplies'; 2 | 3 | const mockReplies: RepliesNativeModule = { 4 | addListener: jest.fn(), 5 | removeListeners: jest.fn(), 6 | setEnabled: jest.fn(), 7 | hasChats: jest.fn(), 8 | show: jest.fn(), 9 | setOnNewReplyReceivedHandler: jest.fn(), 10 | getUnreadRepliesCount: jest.fn(), 11 | setInAppNotificationEnabled: jest.fn(), 12 | setInAppNotificationSound: jest.fn(), 13 | setPushNotificationsEnabled: jest.fn(), 14 | setPushNotificationRegistrationToken: jest.fn(), 15 | showNotification: jest.fn(), 16 | setNotificationIcon: jest.fn(), 17 | setPushNotificationChannelId: jest.fn(), 18 | setSystemReplyNotificationSoundEnabled: jest.fn(), 19 | }; 20 | 21 | export default mockReplies; 22 | -------------------------------------------------------------------------------- /test/mocks/mockSessionReplay.ts: -------------------------------------------------------------------------------- 1 | import type { SessionReplayNativeModule } from '../../src/native/NativeSessionReplay'; 2 | 3 | const mockSessionReplay: SessionReplayNativeModule = { 4 | addListener: jest.fn(), 5 | removeListeners: jest.fn(), 6 | setEnabled: jest.fn(), 7 | setNetworkLogsEnabled: jest.fn(), 8 | setInstabugLogsEnabled: jest.fn(), 9 | setUserStepsEnabled: jest.fn(), 10 | getSessionReplayLink: jest.fn().mockReturnValue('link'), 11 | setSyncCallback: jest.fn(), 12 | evaluateSync: jest.fn(), 13 | }; 14 | 15 | export default mockSessionReplay; 16 | -------------------------------------------------------------------------------- /test/mocks/mockSurveys.ts: -------------------------------------------------------------------------------- 1 | import type { SurveysNativeModule } from '../../src/native/NativeSurveys'; 2 | 3 | const mockSurveys: SurveysNativeModule = { 4 | addListener: jest.fn(), 5 | removeListeners: jest.fn(), 6 | setEnabled: jest.fn(), 7 | setAppStoreURL: jest.fn(), 8 | showSurveysIfAvailable: jest.fn(), 9 | getAvailableSurveys: jest.fn(), 10 | setAutoShowingEnabled: jest.fn(), 11 | setOnShowHandler: jest.fn(), 12 | setOnDismissHandler: jest.fn(), 13 | showSurvey: jest.fn(), 14 | hasRespondedToSurvey: jest.fn(), 15 | setShouldShowWelcomeScreen: jest.fn(), 16 | }; 17 | 18 | export default mockSurveys; 19 | -------------------------------------------------------------------------------- /test/mocks/mockXhrNetworkInterceptor.ts: -------------------------------------------------------------------------------- 1 | jest.mock('../../src/utils/XhrNetworkInterceptor', () => { 2 | return { 3 | setOnDoneCallback: jest.fn(), 4 | setOnProgressCallback: jest.fn(), 5 | enableInterception: jest.fn(), 6 | disableInterception: jest.fn(), 7 | }; 8 | }); 9 | -------------------------------------------------------------------------------- /test/models/Trace.spec.ts: -------------------------------------------------------------------------------- 1 | import Trace from '../../src/models/Trace'; 2 | import { NativeAPM } from '../../src/native/NativeAPM'; 3 | 4 | describe('Trace Model', () => { 5 | it('should set the id, name and attributes if passed', () => { 6 | const id = 'trace-id'; 7 | const name = 'my-trace'; 8 | const attributes = { screen: 'login' }; 9 | const trace = new Trace(id, name, attributes); 10 | 11 | expect(trace.id).toBe(id); 12 | expect(trace.name).toBe(name); 13 | expect(trace.attributes).toBe(attributes); 14 | }); 15 | 16 | it('should set execution trace attributes', () => { 17 | const attribute = { key: 'isAuthenticated', value: 'yes' }; 18 | 19 | const trace = new Trace('trace-id'); 20 | trace.setAttribute(attribute.key, attribute.value); 21 | 22 | expect(trace.attributes[attribute.key]).toBe(attribute.value); 23 | expect(NativeAPM.setExecutionTraceAttribute).toBeCalledTimes(1); 24 | expect(NativeAPM.setExecutionTraceAttribute).toBeCalledWith( 25 | trace.id, 26 | attribute.key, 27 | attribute.value, 28 | ); 29 | }); 30 | 31 | it('should end execution trace', () => { 32 | const trace = new Trace('trace-id'); 33 | 34 | trace.end(); 35 | 36 | expect(NativeAPM.endExecutionTrace).toBeCalledTimes(1); 37 | expect(NativeAPM.endExecutionTrace).toBeCalledWith(trace.id); 38 | }); 39 | }); 40 | -------------------------------------------------------------------------------- /test/modules/CrashReporting.spec.ts: -------------------------------------------------------------------------------- 1 | import '../mocks/mockNativeModules'; 2 | 3 | import * as CrashReporting from '../../src/modules/CrashReporting'; 4 | import { NativeCrashReporting } from '../../src/native/NativeCrashReporting'; 5 | import { Platform } from 'react-native'; 6 | import { NonFatalErrorLevel } from '../../src'; 7 | import { getCrashDataFromError } from '../../src/utils/InstabugUtils'; 8 | 9 | describe('CrashReporting Module', () => { 10 | it('should call the native method setEnabled', () => { 11 | CrashReporting.setEnabled(true); 12 | 13 | expect(NativeCrashReporting.setEnabled).toBeCalledTimes(1); 14 | expect(NativeCrashReporting.setEnabled).toBeCalledWith(true); 15 | }); 16 | 17 | it('should call the native method sendHandledJSCrash with error, fingerprint and level', async () => { 18 | const error = new TypeError('Invalid type'); 19 | const fingerprint = 'fingerprint'; 20 | const level = NonFatalErrorLevel.critical; 21 | 22 | await CrashReporting.reportError(error, { fingerprint, level }); 23 | const crashData = getCrashDataFromError(error); 24 | expect(NativeCrashReporting.sendHandledJSCrash).toBeCalledWith( 25 | crashData, 26 | undefined, 27 | fingerprint, 28 | level, 29 | ); 30 | }); 31 | 32 | it('should call the native method setNDKCrashesEnabled for Android platform', () => { 33 | Platform.OS = 'android'; 34 | CrashReporting.setNDKCrashesEnabled(true); 35 | 36 | expect(NativeCrashReporting.setNDKCrashesEnabled).toBeCalledTimes(1); 37 | expect(NativeCrashReporting.setNDKCrashesEnabled).toBeCalledWith(true); 38 | }); 39 | 40 | it('should not call the native method setNDKCrashesEnabled for ios platform', () => { 41 | Platform.OS = 'ios'; 42 | CrashReporting.setNDKCrashesEnabled(true); 43 | 44 | expect(NativeCrashReporting.setNDKCrashesEnabled).not.toBeCalled(); 45 | }); 46 | }); 47 | -------------------------------------------------------------------------------- /test/modules/FeatureRequests.spec.ts: -------------------------------------------------------------------------------- 1 | import * as FeatureRequests from '../../src/modules/FeatureRequests'; 2 | import { NativeFeatureRequests } from '../../src/native/NativeFeatureRequests'; 3 | import { ActionType } from '../../src/utils/Enums'; 4 | 5 | describe('Feature Requests Module', () => { 6 | it('should call the native method setEmailFieldRequiredForFeatureRequests', () => { 7 | const actionType = ActionType.reportBug; 8 | FeatureRequests.setEmailFieldRequired(true, actionType); 9 | 10 | expect(NativeFeatureRequests.setEmailFieldRequiredForFeatureRequests).toBeCalledTimes(1); 11 | expect(NativeFeatureRequests.setEmailFieldRequiredForFeatureRequests).toBeCalledWith(true, [ 12 | actionType, 13 | ]); 14 | }); 15 | 16 | it('should call the native method showFeatureRequests', () => { 17 | FeatureRequests.show(); 18 | 19 | expect(NativeFeatureRequests.show).toBeCalledTimes(1); 20 | }); 21 | 22 | it('should call the native method setEnabled', () => { 23 | FeatureRequests.setEnabled(true); 24 | 25 | expect(NativeFeatureRequests.setEnabled).toBeCalledTimes(1); 26 | expect(NativeFeatureRequests.setEnabled).toBeCalledWith(true); 27 | }); 28 | }); 29 | -------------------------------------------------------------------------------- /test/modules/SessionReplay.spec.ts: -------------------------------------------------------------------------------- 1 | import * as SessionReplay from '../../src/modules/SessionReplay'; 2 | import { NativeSessionReplay, emitter, NativeEvents } from '../../src/native/NativeSessionReplay'; 3 | 4 | describe('Session Replay Module', () => { 5 | it('should call the native method setEnabled', () => { 6 | SessionReplay.setEnabled(true); 7 | 8 | expect(NativeSessionReplay.setEnabled).toBeCalledTimes(1); 9 | expect(NativeSessionReplay.setEnabled).toBeCalledWith(true); 10 | }); 11 | 12 | it('should call the native method setNetworkLogsEnabled', () => { 13 | SessionReplay.setNetworkLogsEnabled(true); 14 | 15 | expect(NativeSessionReplay.setNetworkLogsEnabled).toBeCalledTimes(1); 16 | expect(NativeSessionReplay.setNetworkLogsEnabled).toBeCalledWith(true); 17 | }); 18 | 19 | it('should call the native method setInstabugLogsEnabled', () => { 20 | SessionReplay.setInstabugLogsEnabled(true); 21 | 22 | expect(NativeSessionReplay.setInstabugLogsEnabled).toBeCalledTimes(1); 23 | expect(NativeSessionReplay.setInstabugLogsEnabled).toBeCalledWith(true); 24 | }); 25 | 26 | it('should call the native method setUserStepsEnabled', () => { 27 | SessionReplay.setUserStepsEnabled(true); 28 | 29 | expect(NativeSessionReplay.setUserStepsEnabled).toBeCalledTimes(1); 30 | expect(NativeSessionReplay.setUserStepsEnabled).toBeCalledWith(true); 31 | }); 32 | 33 | it('should call the native method getSessionReplayLink', () => { 34 | SessionReplay.getSessionReplayLink(); 35 | 36 | expect(NativeSessionReplay.getSessionReplayLink).toBeCalledTimes(1); 37 | expect(NativeSessionReplay.getSessionReplayLink).toReturnWith('link'); 38 | }); 39 | 40 | it('should call the native method setSyncCallback', () => { 41 | const shouldSync = true; 42 | const callback = jest.fn().mockReturnValue(shouldSync); 43 | 44 | SessionReplay.setSyncCallback(callback); 45 | emitter.emit(NativeEvents.SESSION_REPLAY_ON_SYNC_CALLBACK_INVOCATION); 46 | 47 | expect(NativeSessionReplay.setSyncCallback).toBeCalledTimes(1); 48 | expect(emitter.listenerCount(NativeEvents.SESSION_REPLAY_ON_SYNC_CALLBACK_INVOCATION)).toBe(1); 49 | expect(NativeSessionReplay.evaluateSync).toBeCalledTimes(1); 50 | expect(NativeSessionReplay.evaluateSync).toBeCalledWith(shouldSync); 51 | }); 52 | }); 53 | -------------------------------------------------------------------------------- /test/setup.ts: -------------------------------------------------------------------------------- 1 | import './mocks/mockNativeModules'; 2 | import './mocks/mockPromiseRejectionTracking'; 3 | import './mocks/mockParseErrorStackLib'; 4 | 5 | import { Platform } from 'react-native'; 6 | import 'react-native/Libraries/Network/fetch'; 7 | 8 | import nock from 'nock'; 9 | import XHR from 'xhr2'; 10 | 11 | global.XMLHttpRequest = XHR; 12 | 13 | nock.disableNetConnect(); 14 | 15 | beforeEach(() => { 16 | jest.spyOn(Platform, 'constants', 'get').mockReturnValue({ 17 | isTesting: true, 18 | reactNativeVersion: { 19 | major: 0, 20 | minor: 60, 21 | patch: 0, 22 | }, 23 | }); 24 | }); 25 | -------------------------------------------------------------------------------- /test/xhr2.d.ts: -------------------------------------------------------------------------------- 1 | declare module 'xhr2'; 2 | -------------------------------------------------------------------------------- /tsconfig.cli.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "include": ["cli/**/*"], 4 | "exclude": [], 5 | "compilerOptions": { 6 | "lib": ["dom"], 7 | "outDir": "bin", 8 | "module": "esnext" 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "include": ["src/**/*"], 3 | "compilerOptions": { 4 | "module": "esnext", 5 | "target": "esnext", 6 | "lib": ["esnext"], 7 | "jsx": "react-native", 8 | "moduleResolution": "node", 9 | "skipLibCheck": true, 10 | "outDir": "dist", 11 | 12 | // Interop 13 | "esModuleInterop": true, 14 | "forceConsistentCasingInFileNames": true, 15 | 16 | // Emit 17 | "declaration": true, 18 | "importsNotUsedAsValues": "error", 19 | 20 | // Code Style 21 | "allowUnreachableCode": false, 22 | "allowUnusedLabels": false, 23 | "noFallthroughCasesInSwitch": true, 24 | "noImplicitReturns": true, 25 | "noImplicitUseStrict": false, 26 | "noStrictGenericChecks": false, 27 | "noUnusedLocals": true, 28 | "noUnusedParameters": true, 29 | "noImplicitAny": true, 30 | "strict": true 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /tsconfig.test.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "include": ["test/**/*", "src/**/*"] 4 | } 5 | -------------------------------------------------------------------------------- /tsconfig.upload.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "include": ["cli/upload/**/*"], 4 | "exclude": [], 5 | "compilerOptions": { 6 | "lib": ["dom"], 7 | "outDir": "upload", 8 | "module": "esnext" 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /upload/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "main": "index.js", 3 | "module": "index.mjs", 4 | "types": "index.d.ts" 5 | } 6 | --------------------------------------------------------------------------------