diff --git a/plugins/react-native.js b/plugins/react-native.js index 03881a5d75ed..2312457c0439 100644 --- a/plugins/react-native.js +++ b/plugins/react-native.js @@ -33,9 +33,13 @@ var ASYNC_STORAGE_KEY = '--raven-js-global-error-payload--'; * Strip device-specific IDs from React Native file:// paths */ function normalizeUrl(url, pathStripRe) { - return url - .replace(/^file\:\/\//, '') - .replace(pathStripRe, ''); + if (url.indexOf('/') !== -1) { + return url + .replace(/^file\:\/\//, '') + .replace(pathStripRe, ''); + } else { + return '/' + url; + } } /** diff --git a/test/vendor/fixtures/captured-errors.js b/test/vendor/fixtures/captured-errors.js index 0e04bc7a355c..686842afedc3 100644 --- a/test/vendor/fixtures/captured-errors.js +++ b/test/vendor/fixtures/captured-errors.js @@ -400,4 +400,48 @@ CapturedExceptions.ANDROID_REACT_NATIVE = { }; +CapturedExceptions.ANDROID_REACT_NATIVE_PROD = { + message: 'Error: test', + name: 'Error', + stack: 'value@index.android.bundle:12:1917\n' + + 'onPress@index.android.bundle:12:2336\n' + + 'touchableHandlePress@index.android.bundle:258:1497\n' + + '[native code]\n' + + '_performSideEffectsForTransition@index.android.bundle:252:8508\n' + + '[native code]\n' + + '_receiveSignal@index.android.bundle:252:7291\n' + + '[native code]\n' + + 'touchableHandleResponderRelease@index.android.bundle:252:4735\n' + + '[native code]\n' + + 'u@index.android.bundle:79:142\n' + + 'invokeGuardedCallback@index.android.bundle:79:459\n' + + 'invokeGuardedCallbackAndCatchFirstError@index.android.bundle:79:580\n' + + 'c@index.android.bundle:95:365\n' + + 'a@index.android.bundle:95:567\n' + + 'v@index.android.bundle:146:501\n' + + 'g@index.android.bundle:146:604\n' + + 'forEach@[native code]\n' + + 'i@index.android.bundle:149:80\n' + + 'processEventQueue@index.android.bundle:146:1432\n' + + 's@index.android.bundle:157:88\n' + + 'handleTopLevel@index.android.bundle:157:174\n' + + 'index.android.bundle:156:572\n' + + 'a@index.android.bundle:93:276\n' + + 'c@index.android.bundle:93:60\n' + + 'perform@index.android.bundle:177:596\n' + + 'batchedUpdates@index.android.bundle:188:464\n' + + 'i@index.android.bundle:176:358\n' + + 'i@index.android.bundle:93:90\n' + + 'u@index.android.bundle:93:150\n' + + '_receiveRootNodeIDEvent@index.android.bundle:156:544\n' + + 'receiveTouches@index.android.bundle:156:918\n' + + 'value@index.android.bundle:29:3016\n' + + 'index.android.bundle:29:955\n' + + 'value@index.android.bundle:29:2417\n' + + 'value@index.android.bundle:29:927\n' + + '[native code]' +}; + + + module.exports = CapturedExceptions; diff --git a/test/vendor/tracekit-parser.test.js b/test/vendor/tracekit-parser.test.js index ce1a61dd0ca7..a559f1836557 100644 --- a/test/vendor/tracekit-parser.test.js +++ b/test/vendor/tracekit-parser.test.js @@ -257,5 +257,14 @@ describe('TraceKit', function () { assert.deepEqual(stackFrames.stack[0], { url: '/home/username/sample-workspace/sampleapp.collect.react/src/components/GpsMonitorScene.js', func: 'render', args: [], line: 78, column: 24 }); assert.deepEqual(stackFrames.stack[7], { url: '/home/username/sample-workspace/sampleapp.collect.react/node_modules/react-native/Libraries/Renderer/src/renderers/native/ReactNativeBaseComponent.js', func: 'this', args: [], line: 74, column: 41 }); }); + + it('should parse React Native errors on Android Production', function () { + var stackFrames = TraceKit.computeStackTrace(CapturedExceptions.ANDROID_REACT_NATIVE_PROD); + assert.ok(stackFrames); + assert.deepEqual(stackFrames.stack.length, 37); + assert.deepEqual(stackFrames.stack[0], { url: 'index.android.bundle', func: 'value', args: [], line: 12, column: 1917 }); + assert.deepEqual(stackFrames.stack[35], { url: 'index.android.bundle', func: 'value', args: [], line: 29, column: 927 }); + assert.deepEqual(stackFrames.stack[36], { url: '[native code]', func: '?', args: [], line: null, column: null }); + }); }); }); diff --git a/vendor/TraceKit/tracekit.js b/vendor/TraceKit/tracekit.js index 4393aaf3f6cc..11d8bd4cdc79 100644 --- a/vendor/TraceKit/tracekit.js +++ b/vendor/TraceKit/tracekit.js @@ -370,7 +370,7 @@ TraceKit.computeStackTrace = (function computeStackTraceWrapper() { if (typeof ex.stack === 'undefined' || !ex.stack) return; var chrome = /^\s*at (.*?) ?\(((?:file|https?|blob|chrome-extension|native|eval|webpack||\/).*?)(?::(\d+))?(?::(\d+))?\)?\s*$/i, - gecko = /^\s*(.*?)(?:\((.*?)\))?(?:^|@)((?:file|https?|blob|chrome|webpack|resource|\[native).*?)(?::(\d+))?(?::(\d+))?\s*$/i, + gecko = /^\s*(.*?)(?:\((.*?)\))?(?:^|@)((?:file|https?|blob|chrome|webpack|resource|\[native).*?|[^@]*bundle)(?::(\d+))?(?::(\d+))?\s*$/i, winjs = /^\s*at (?:((?:\[object object\])?.+) )?\(?((?:file|ms-appx|https?|webpack|blob):.*?):(\d+)(?::(\d+))?\)?\s*$/i, // Used to additionally parse URL/line/column from eval frames