diff --git a/packages/telemetry/browser-telemetry/__tests__/vendor/TraceKit/CapturedExceptions.ts b/packages/telemetry/browser-telemetry/__tests__/vendor/TraceKit/CapturedExceptions.ts new file mode 100644 index 000000000..cb628dbe9 --- /dev/null +++ b/packages/telemetry/browser-telemetry/__tests__/vendor/TraceKit/CapturedExceptions.ts @@ -0,0 +1,480 @@ +/** + * https://github.com/csnover/TraceKit + * @license MIT + * @namespace TraceKit + */ + +export const OPERA_854 = { + message: + 'Statement on line 44: Type mismatch (usually a non-object value used where an object is required)\n' + + 'Backtrace:\n' + + ' Line 44 of linked script http://path/to/file.js\n' + + ' this.undef();\n' + + ' Line 31 of linked script http://path/to/file.js\n' + + ' ex = ex || this.createException();\n' + + ' Line 18 of linked script http://path/to/file.js\n' + + ' var p = new printStackTrace.implementation(), result = p.run(ex);\n' + + ' Line 4 of inline#1 script in http://path/to/file.js\n' + + ' printTrace(printStackTrace());\n' + + ' Line 7 of inline#1 script in http://path/to/file.js\n' + + ' bar(n - 1);\n' + + ' Line 11 of inline#1 script in http://path/to/file.js\n' + + ' bar(2);\n' + + ' Line 15 of inline#1 script in http://path/to/file.js\n' + + ' foo();\n' + + '', + 'opera#sourceloc': 44, +}; + +export const OPERA_902 = { + message: + 'Statement on line 44: Type mismatch (usually a non-object value used where an object is required)\n' + + 'Backtrace:\n' + + ' Line 44 of linked script http://path/to/file.js\n' + + ' this.undef();\n' + + ' Line 31 of linked script http://path/to/file.js\n' + + ' ex = ex || this.createException();\n' + + ' Line 18 of linked script http://path/to/file.js\n' + + ' var p = new printStackTrace.implementation(), result = p.run(ex);\n' + + ' Line 4 of inline#1 script in http://path/to/file.js\n' + + ' printTrace(printStackTrace());\n' + + ' Line 7 of inline#1 script in http://path/to/file.js\n' + + ' bar(n - 1);\n' + + ' Line 11 of inline#1 script in http://path/to/file.js\n' + + ' bar(2);\n' + + ' Line 15 of inline#1 script in http://path/to/file.js\n' + + ' foo();\n' + + '', + 'opera#sourceloc': 44, +}; + +export const OPERA_927 = { + message: + 'Statement on line 43: Type mismatch (usually a non-object value used where an object is required)\n' + + 'Backtrace:\n' + + ' Line 43 of linked script http://path/to/file.js\n' + + ' bar(n - 1);\n' + + ' Line 31 of linked script http://path/to/file.js\n' + + ' bar(2);\n' + + ' Line 18 of linked script http://path/to/file.js\n' + + ' foo();\n' + + '', + 'opera#sourceloc': 43, +}; + +export const OPERA_964 = { + message: + 'Statement on line 42: Type mismatch (usually non-object value supplied where object required)\n' + + 'Backtrace:\n' + + ' Line 42 of linked script http://path/to/file.js\n' + + ' this.undef();\n' + + ' Line 27 of linked script http://path/to/file.js\n' + + ' ex = ex || this.createException();\n' + + ' Line 18 of linked script http://path/to/file.js: In function printStackTrace\n' + + ' var p = new printStackTrace.implementation(), result = p.run(ex);\n' + + ' Line 4 of inline#1 script in http://path/to/file.js: In function bar\n' + + ' printTrace(printStackTrace());\n' + + ' Line 7 of inline#1 script in http://path/to/file.js: In function bar\n' + + ' bar(n - 1);\n' + + ' Line 11 of inline#1 script in http://path/to/file.js: In function foo\n' + + ' bar(2);\n' + + ' Line 15 of inline#1 script in http://path/to/file.js\n' + + ' foo();\n' + + '', + 'opera#sourceloc': 42, + stacktrace: + ' ... Line 27 of linked script http://path/to/file.js\n' + + ' ex = ex || this.createException();\n' + + ' Line 18 of linked script http://path/to/file.js: In function printStackTrace\n' + + ' var p = new printStackTrace.implementation(), result = p.run(ex);\n' + + ' Line 4 of inline#1 script in http://path/to/file.js: In function bar\n' + + ' printTrace(printStackTrace());\n' + + ' Line 7 of inline#1 script in http://path/to/file.js: In function bar\n' + + ' bar(n - 1);\n' + + ' Line 11 of inline#1 script in http://path/to/file.js: In function foo\n' + + ' bar(2);\n' + + ' Line 15 of inline#1 script in http://path/to/file.js\n' + + ' foo();\n' + + '', +}; + +export const OPERA_10 = { + message: + 'Statement on line 42: Type mismatch (usually non-object value supplied where object required)', + 'opera#sourceloc': 42, + stacktrace: + ' Line 42 of linked script http://path/to/file.js\n' + + ' this.undef();\n' + + ' Line 27 of linked script http://path/to/file.js\n' + + ' ex = ex || this.createException();\n' + + ' Line 18 of linked script http://path/to/file.js: In function printStackTrace\n' + + ' var p = new printStackTrace.implementation(), result = p.run(ex);\n' + + ' Line 4 of inline#1 script in http://path/to/file.js: In function bar\n' + + ' printTrace(printStackTrace());\n' + + ' Line 7 of inline#1 script in http://path/to/file.js: In function bar\n' + + ' bar(n - 1);\n' + + ' Line 11 of inline#1 script in http://path/to/file.js: In function foo\n' + + ' bar(2);\n' + + ' Line 15 of inline#1 script in http://path/to/file.js\n' + + ' foo();\n' + + '', +}; + +export const OPERA_11 = { + message: "'this.undef' is not a function", + stack: + '([arguments not available])@http://path/to/file.js:27\n' + + 'bar([arguments not available])@http://domain.com:1234/path/to/file.js:18\n' + + 'foo([arguments not available])@http://domain.com:1234/path/to/file.js:11\n' + + '@http://path/to/file.js:15\n' + + 'Error created at @http://path/to/file.js:15', + stacktrace: + 'Error thrown at line 42, column 12 in () in http://path/to/file.js:\n' + + ' this.undef();\n' + + 'called from line 27, column 8 in (ex) in http://path/to/file.js:\n' + + ' ex = ex || this.createException();\n' + + 'called from line 18, column 4 in printStackTrace(options) in http://path/to/file.js:\n' + + ' var p = new printStackTrace.implementation(), result = p.run(ex);\n' + + 'called from line 4, column 5 in bar(n) in http://path/to/file.js:\n' + + ' printTrace(printStackTrace());\n' + + 'called from line 7, column 4 in bar(n) in http://path/to/file.js:\n' + + ' bar(n - 1);\n' + + 'called from line 11, column 4 in foo() in http://path/to/file.js:\n' + + ' bar(2);\n' + + 'called from line 15, column 3 in http://path/to/file.js:\n' + + ' foo();', +}; + +export const OPERA_12 = { + message: "Cannot convert 'x' to object", + stack: + '([arguments not available])@http://localhost:8000/ExceptionLab.html:48\n' + + 'dumpException3([arguments not available])@http://localhost:8000/ExceptionLab.html:46\n' + + '([arguments not available])@http://localhost:8000/ExceptionLab.html:1', + stacktrace: + 'Error thrown at line 48, column 12 in (x) in http://localhost:8000/ExceptionLab.html:\n' + + ' x.undef();\n' + + 'called from line 46, column 8 in dumpException3() in http://localhost:8000/ExceptionLab.html:\n' + + ' dumpException((function(x) {\n' + + 'called from line 1, column 0 in (event) in http://localhost:8000/ExceptionLab.html:\n' + + ' dumpException3();', +}; + +export const OPERA_25 = { + message: "Cannot read property 'undef' of null", + name: 'TypeError', + stack: + "TypeError: Cannot read property 'undef' of null\n" + + ' at http://path/to/file.js:47:22\n' + + ' at foo (http://path/to/file.js:52:15)\n' + + ' at bar (http://path/to/file.js:108:168)', +}; + +export const CHROME_15 = { + arguments: ['undef'], + message: "Object # has no method 'undef'", + stack: + "TypeError: Object # has no method 'undef'\n" + + ' at bar (http://path/to/file.js:13:17)\n' + + ' at bar (http://path/to/file.js:16:5)\n' + + ' at foo (http://path/to/file.js:20:5)\n' + + ' at http://path/to/file.js:24:4', +}; + +export const CHROME_36 = { + message: 'Default error', + name: 'Error', + stack: + 'Error: Default error\n' + + ' at dumpExceptionError (http://localhost:8080/file.js:41:27)\n' + + ' at HTMLButtonElement.onclick (http://localhost:8080/file.js:107:146)\n' + + ' at I.e.fn.(anonymous function) [as index] (http://localhost:8080/file.js:10:3651)', +}; + +// can be generated when Webpack is built with { devtool: eval } +export const CHROME_XX_WEBPACK = { + message: "Cannot read property 'error' of undefined", + name: 'TypeError', + stack: + "TypeError: Cannot read property 'error' of undefined\n" + + ' at TESTTESTTEST.eval(webpack:///./src/components/test/test.jsx?:295:108)\n' + + ' at TESTTESTTEST.render(webpack:///./src/components/test/test.jsx?:272:32)\n' + + ' at TESTTESTTEST.tryRender(webpack:///./~/react-transform-catch-errors/lib/index.js?:34:31)\n' + + ' at TESTTESTTEST.proxiedMethod(webpack:///./~/react-proxy/modules/createPrototypeProxy.js?:44:30)', +}; + +export const FIREFOX_3 = { + fileName: 'http://127.0.0.1:8000/js/stacktrace.js', + lineNumber: 44, + message: 'this.undef is not a function', + name: 'TypeError', + stack: + '()@http://127.0.0.1:8000/js/stacktrace.js:44\n' + + '(null)@http://127.0.0.1:8000/js/stacktrace.js:31\n' + + 'printStackTrace()@http://127.0.0.1:8000/js/stacktrace.js:18\n' + + 'bar(1)@http://127.0.0.1:8000/js/file.js:13\n' + + 'bar(2)@http://127.0.0.1:8000/js/file.js:16\n' + + 'foo()@http://127.0.0.1:8000/js/file.js:20\n' + + '@http://127.0.0.1:8000/js/file.js:24\n' + + '', +}; + +export const FIREFOX_7 = { + fileName: 'file:///G:/js/stacktrace.js', + lineNumber: 44, + stack: + '()@file:///G:/js/stacktrace.js:44\n' + + '(null)@file:///G:/js/stacktrace.js:31\n' + + 'printStackTrace()@file:///G:/js/stacktrace.js:18\n' + + 'bar(1)@file:///G:/js/file.js:13\n' + + 'bar(2)@file:///G:/js/file.js:16\n' + + 'foo()@file:///G:/js/file.js:20\n' + + '@file:///G:/js/file.js:24\n' + + '', +}; + +export const FIREFOX_14 = { + message: 'x is null', + stack: + '@http://path/to/file.js:48\n' + + 'dumpException3@http://path/to/file.js:52\n' + + 'onclick@http://path/to/file.js:1\n' + + '', + fileName: 'http://path/to/file.js', + lineNumber: 48, +}; + +export const FIREFOX_31 = { + message: 'Default error', + name: 'Error', + stack: + 'foo@http://path/to/file.js:41:13\n' + + 'bar@http://path/to/file.js:1:1\n' + + '.plugin/e.fn[c]/<@http://path/to/file.js:1:1\n' + + '', + fileName: 'http://path/to/file.js', + lineNumber: 41, + columnNumber: 12, +}; + +export const FIREFOX_43_EVAL = { + columnNumber: 30, + fileName: 'http://localhost:8080/file.js line 25 > eval line 2 > eval', + lineNumber: 1, + message: 'message string', + stack: + 'baz@http://localhost:8080/file.js line 26 > eval line 2 > eval:1:30\n' + + 'foo@http://localhost:8080/file.js line 26 > eval:2:96\n' + + '@http://localhost:8080/file.js line 26 > eval:4:18\n' + + 'speak@http://localhost:8080/file.js:26:17\n' + + '@http://localhost:8080/file.js:33:9', +}; + +// Internal errors sometimes thrown by Firefox +// More here: https://developer.mozilla.org/en-US/docs/Mozilla/Errors +// +// Note that such errors are instanceof "Exception", not "Error" +export const FIREFOX_44_NS_EXCEPTION = { + message: '', + name: 'NS_ERROR_FAILURE', + stack: + '[2] tag + '', + fileName: 'http://path/to/file.js', + columnNumber: 0, + lineNumber: 703, + result: 2147500037, +}; + +export const FIREFOX_50_RESOURCE_URL = { + stack: + 'render@resource://path/data/content/bundle.js:5529:16\n' + + 'dispatchEvent@resource://path/data/content/vendor.bundle.js:18:23028\n' + + 'wrapped@resource://path/data/content/bundle.js:7270:25', + fileName: 'resource://path/data/content/bundle.js', + lineNumber: 5529, + columnNumber: 16, + message: 'this.props.raw[this.state.dataSource].rows is undefined', + name: 'TypeError', +}; + +export const SAFARI_6 = { + message: "'null' is not an object (evaluating 'x.undef')", + stack: + '@http://path/to/file.js:48\n' + + 'dumpException3@http://path/to/file.js:52\n' + + 'onclick@http://path/to/file.js:82\n' + + '[native code]', + line: 48, + sourceURL: 'http://path/to/file.js', +}; + +export const SAFARI_7 = { + message: "'null' is not an object (evaluating 'x.undef')", + name: 'TypeError', + stack: + 'http://path/to/file.js:48:22\n' + + 'foo@http://path/to/file.js:52:15\n' + + 'bar@http://path/to/file.js:108:107', + line: 47, + sourceURL: 'http://path/to/file.js', +}; + +export const SAFARI_8 = { + message: "null is not an object (evaluating 'x.undef')", + name: 'TypeError', + stack: + 'http://path/to/file.js:47:22\n' + + 'foo@http://path/to/file.js:52:15\n' + + 'bar@http://path/to/file.js:108:23', + line: 47, + column: 22, + sourceURL: 'http://path/to/file.js', +}; + +export const SAFARI_8_EVAL = { + message: "Can't find variable: getExceptionProps", + name: 'ReferenceError', + stack: + 'eval code\n' + + 'eval@[native code]\n' + + 'foo@http://path/to/file.js:58:21\n' + + 'bar@http://path/to/file.js:109:91', + line: 1, + column: 18, +}; + +export const IE_9 = { + message: "Unable to get property 'undef' of undefined or null reference", + description: "Unable to get property 'undef' of undefined or null reference", +}; + +export const IE_10 = { + message: "Unable to get property 'undef' of undefined or null reference", + stack: + "TypeError: Unable to get property 'undef' of undefined or null reference\n" + + ' at Anonymous function (http://path/to/file.js:48:13)\n' + + ' at foo (http://path/to/file.js:46:9)\n' + + ' at bar (http://path/to/file.js:82:1)', + description: "Unable to get property 'undef' of undefined or null reference", + number: -2146823281, +}; + +export const IE_11 = { + message: "Unable to get property 'undef' of undefined or null reference", + name: 'TypeError', + stack: + "TypeError: Unable to get property 'undef' of undefined or null reference\n" + + ' at Anonymous function (http://path/to/file.js:47:21)\n' + + ' at foo (http://path/to/file.js:45:13)\n' + + ' at bar (http://path/to/file.js:108:1)', + description: "Unable to get property 'undef' of undefined or null reference", + number: -2146823281, +}; + +export const IE_11_EVAL = { + message: "'getExceptionProps' is undefined", + name: 'ReferenceError', + stack: + "ReferenceError: 'getExceptionProps' is undefined\n" + + ' at eval code (eval code:1:1)\n' + + ' at foo (http://path/to/file.js:58:17)\n' + + ' at bar (http://path/to/file.js:109:1)', + description: "'getExceptionProps' is undefined", + number: -2146823279, +}; + +export const CHROME_48_BLOB = { + message: 'Error: test', + name: 'Error', + stack: + 'Error: test\n' + + ' at Error (native)\n' + + ' at s (blob:http%3A//localhost%3A8080/abfc40e9-4742-44ed-9dcd-af8f99a29379:31:29146)\n' + + ' at Object.d [as add] (blob:http%3A//localhost%3A8080/abfc40e9-4742-44ed-9dcd-af8f99a29379:31:30039)\n' + + ' at blob:http%3A//localhost%3A8080/d4eefe0f-361a-4682-b217-76587d9f712a:15:10978\n' + + ' at blob:http%3A//localhost%3A8080/abfc40e9-4742-44ed-9dcd-af8f99a29379:1:6911\n' + + ' at n.fire (blob:http%3A//localhost%3A8080/abfc40e9-4742-44ed-9dcd-af8f99a29379:7:3019)\n' + + ' at n.handle (blob:http%3A//localhost%3A8080/abfc40e9-4742-44ed-9dcd-af8f99a29379:7:2863)', +}; + +export const CHROME_48_EVAL = { + message: 'message string', + name: 'Error', + stack: + 'Error: message string\n' + + 'at baz (eval at foo (eval at speak (http://localhost:8080/file.js:21:17)), :1:30)\n' + + 'at foo (eval at speak (http://localhost:8080/file.js:21:17), :2:96)\n' + + 'at eval (eval at speak (http://localhost:8080/file.js:21:17), :4:18)\n' + + 'at Object.speak (http://localhost:8080/file.js:21:17)\n' + + 'at http://localhost:8080/file.js:31:13\n', +}; + +export const PHANTOMJS_1_19 = { + stack: + 'Error: foo\n' + + ' at file:///path/to/file.js:878\n' + + ' at foo (http://path/to/file.js:4283)\n' + + ' at http://path/to/file.js:4287', +}; + +export const ANDROID_REACT_NATIVE = { + message: 'Error: test', + name: 'Error', + stack: + 'Error: test\n' + + 'at render(/home/username/sample-workspace/sampleapp.collect.react/src/components/GpsMonitorScene.js:78:24)\n' + + 'at _renderValidatedComponentWithoutOwnerOrContext(/home/username/sample-workspace/sampleapp.collect.react/node_modules/react-native/Libraries/Renderer/src/renderers/shared/stack/reconciler/ReactCompositeComponent.js:1050:29)\n' + + 'at _renderValidatedComponent(/home/username/sample-workspace/sampleapp.collect.react/node_modules/react-native/Libraries/Renderer/src/renderers/shared/stack/reconciler/ReactCompositeComponent.js:1075:15)\n' + + 'at renderedElement(/home/username/sample-workspace/sampleapp.collect.react/node_modules/react-native/Libraries/Renderer/src/renderers/shared/stack/reconciler/ReactCompositeComponent.js:484:29)\n' + + 'at _currentElement(/home/username/sample-workspace/sampleapp.collect.react/node_modules/react-native/Libraries/Renderer/src/renderers/shared/stack/reconciler/ReactCompositeComponent.js:346:40)\n' + + 'at child(/home/username/sample-workspace/sampleapp.collect.react/node_modules/react-native/Libraries/Renderer/src/renderers/shared/stack/reconciler/ReactReconciler.js:68:25)\n' + + 'at children(/home/username/sample-workspace/sampleapp.collect.react/node_modules/react-native/Libraries/Renderer/src/renderers/shared/stack/reconciler/ReactMultiChild.js:264:10)\n' + + 'at this(/home/username/sample-workspace/sampleapp.collect.react/node_modules/react-native/Libraries/Renderer/src/renderers/native/ReactNativeBaseComponent.js:74:41)\n', +}; + +export const 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]', +}; diff --git a/packages/telemetry/browser-telemetry/__tests__/vendor/TraceKit/TraceKit.test.ts b/packages/telemetry/browser-telemetry/__tests__/vendor/TraceKit/TraceKit.test.ts new file mode 100644 index 000000000..ddb6f1532 --- /dev/null +++ b/packages/telemetry/browser-telemetry/__tests__/vendor/TraceKit/TraceKit.test.ts @@ -0,0 +1,117 @@ +import { getTraceKit } from '../../../src/vendor/TraceKit'; + +/* eslint-disable prefer-arrow-callback */ +/* eslint-disable func-names */ +/* eslint-disable no-var */ + +describe('TraceKit', function () { + describe('General', function () { + it('should not remove anonymous functions from the stack', function () { + // mock up an error object with a stack trace that includes both + // named functions and anonymous functions + var stack_str = + '' + + ' Error: \n' + + ' at new (http://example.com/js/test.js:63:1)\n' + // stack[0] + ' at namedFunc0 (http://example.com/js/script.js:10:2)\n' + // stack[1] + ' at http://example.com/js/test.js:65:10\n' + // stack[2] + ' at namedFunc2 (http://example.com/js/script.js:20:5)\n' + // stack[3] + ' at http://example.com/js/test.js:67:5\n' + // stack[4] + ' at namedFunc4 (http://example.com/js/script.js:100001:10002)'; // stack[5] + var mock_err = { stack: stack_str }; + var stackFrames = getTraceKit().computeStackTrace.computeStackTraceFromStackProp( + mock_err as unknown as Error, + ); + + // Make sure TraceKit didn't remove the anonymous functions + // from the stack like it used to :) + expect(stackFrames).toBeTruthy(); + expect(stackFrames?.stack[0].func).toEqual('new '); + expect(stackFrames?.stack[0].url).toEqual('http://example.com/js/test.js'); + expect(stackFrames?.stack[0].line).toBe(63); + expect(stackFrames?.stack[0].column).toBe(1); + + expect(stackFrames?.stack[1].func).toEqual('namedFunc0'); + expect(stackFrames?.stack[1].url).toEqual('http://example.com/js/script.js'); + expect(stackFrames?.stack[1].line).toBe(10); + expect(stackFrames?.stack[1].column).toBe(2); + + expect(stackFrames?.stack[2].func).toEqual('?'); + expect(stackFrames?.stack[2].url).toEqual('http://example.com/js/test.js'); + expect(stackFrames?.stack[2].line).toBe(65); + expect(stackFrames?.stack[2].column).toBe(10); + + expect(stackFrames?.stack[3].func).toEqual('namedFunc2'); + expect(stackFrames?.stack[3].url).toEqual('http://example.com/js/script.js'); + expect(stackFrames?.stack[3].line).toBe(20); + expect(stackFrames?.stack[3].column).toBe(5); + + expect(stackFrames?.stack[4].func).toEqual('?'); + expect(stackFrames?.stack[4].url).toEqual('http://example.com/js/test.js'); + expect(stackFrames?.stack[4].line).toBe(67); + expect(stackFrames?.stack[4].column).toBe(5); + + expect(stackFrames?.stack[5].func).toEqual('namedFunc4'); + expect(stackFrames?.stack[5].url).toEqual('http://example.com/js/script.js'); + expect(stackFrames?.stack[5].line).toBe(100001); + expect(stackFrames?.stack[5].column).toBe(10002); + }); + + it('should handle eval/anonymous strings in Chrome 46', function () { + var stack_str = + '' + + 'ReferenceError: baz is not defined\n' + + ' at bar (http://example.com/js/test.js:19:7)\n' + + ' at foo (http://example.com/js/test.js:23:7)\n' + + ' at eval (eval at (http://example.com/js/test.js:26:5)).toBe(:1:26)\n'; + + var mock_err = { stack: stack_str }; + var stackFrames = getTraceKit().computeStackTrace.computeStackTraceFromStackProp( + mock_err as unknown as Error, + ); + expect(stackFrames).toBeTruthy(); + expect(stackFrames?.stack[0].func).toEqual('bar'); + expect(stackFrames?.stack[0].url).toEqual('http://example.com/js/test.js'); + expect(stackFrames?.stack[0].line).toBe(19); + expect(stackFrames?.stack[0].column).toBe(7); + + expect(stackFrames?.stack[1].func).toEqual('foo'); + expect(stackFrames?.stack[1].url).toEqual('http://example.com/js/test.js'); + expect(stackFrames?.stack[1].line).toBe(23); + expect(stackFrames?.stack[1].column).toBe(7); + + expect(stackFrames?.stack[2].func).toEqual('eval'); + // TODO: fix nested evals + expect(stackFrames?.stack[2].url).toEqual('http://example.com/js/test.js'); + expect(stackFrames?.stack[2].line).toBe(26); + expect(stackFrames?.stack[2].column).toBe(5); + }); + }); + + describe('.computeStackTrace', function () { + it('should handle a native error object', function () { + var ex = new Error('test'); + var stack = getTraceKit().computeStackTrace(ex); + expect(stack.name).toEqual('Error'); + expect(stack.message).toEqual('test'); + }); + + it('should handle a native error object stack from Chrome', function () { + var stackStr = + '' + + 'Error: foo\n' + + ' at :2:11\n' + + ' at Object.InjectedScript._evaluateOn (:904:140)\n' + + ' at Object.InjectedScript._evaluateAndWrap (:837:34)\n' + + ' at Object.InjectedScript.evaluate (:693:21)'; + var mockErr = { + name: 'Error', + message: 'foo', + stack: stackStr, + }; + var stackFrames = getTraceKit().computeStackTrace(mockErr); + expect(stackFrames).toBeTruthy(); + expect(stackFrames.stack[0].url).toEqual(''); + }); + }); +}); diff --git a/packages/telemetry/browser-telemetry/__tests__/vendor/TraceKit/computeStackTrace.test.ts b/packages/telemetry/browser-telemetry/__tests__/vendor/TraceKit/computeStackTrace.test.ts new file mode 100644 index 000000000..46a0d68ef --- /dev/null +++ b/packages/telemetry/browser-telemetry/__tests__/vendor/TraceKit/computeStackTrace.test.ts @@ -0,0 +1,1293 @@ +/** + * https://github.com/csnover/TraceKit + * @license MIT + * @namespace TraceKit + */ +import { getTraceKit } from '../../../src/vendor/TraceKit'; +import * as CapturedExceptions from './CapturedExceptions'; + +/* eslint-disable no-plusplus */ +/* eslint-disable no-useless-escape */ +/* eslint-disable @typescript-eslint/no-use-before-define */ + +describe('computeStackTrace', () => { + describe('domain regex', () => { + const regex = /(.*)\:\/\/([^\/]+)\/{0,1}([\s\S]*)/; + it('should return subdomains properly', () => { + const url = 'https://subdomain.yoursite.com/assets/main.js'; + const domain = 'subdomain.yoursite.com'; + expect(regex.exec(url)![2]).toBe(domain); + }); + it('should return domains correctly with any protocol', () => { + const url = 'http://yoursite.com/assets/main.js'; + const domain = 'yoursite.com'; + expect(regex.exec(url)![2]).toBe(domain); + }); + it('should return the correct domain when directories match the domain', () => { + const url = 'https://mysite.com/mysite/main.js'; + const domain = 'mysite.com'; + expect(regex.exec(url)![2]).toBe(domain); + }); + }); +}); + +describe('Parser', () => { + function foo() { + return bar(); + } + + function bar() { + return baz(); + } + + function baz() { + return getTraceKit().computeStackTrace.ofCaller(); + } + + it('should get the order of functions called right', () => { + const trace = foo(); + const expected = ['baz', 'bar', 'foo']; + for (let i = 1; i <= 3; i++) { + expect(trace.stack[i].func).toBe(expected[i - 1]); + } + }); + + it('should parse Safari 6 error', () => { + const stackFrames = getTraceKit().computeStackTrace( + CapturedExceptions.SAFARI_6 as unknown as Error, + ); + expect(stackFrames).toBeTruthy(); + expect(stackFrames.stack.length).toBe(4); + expect(stackFrames.stack[0]).toEqual({ + url: 'http://path/to/file.js', + func: '?', + args: [], + line: 48, + column: null, + context: null, + }); + expect(stackFrames.stack[1]).toEqual({ + url: 'http://path/to/file.js', + func: 'dumpException3', + args: [], + line: 52, + column: null, + context: null, + }); + expect(stackFrames.stack[2]).toEqual({ + url: 'http://path/to/file.js', + func: 'onclick', + args: [], + line: 82, + column: null, + context: null, + }); + expect(stackFrames.stack[3]).toEqual({ + url: '[native code]', + func: '?', + args: [], + line: null, + column: null, + context: null, + }); + }); + + it('should parse Safari 7 error', () => { + const stackFrames = getTraceKit().computeStackTrace(CapturedExceptions.SAFARI_7); + expect(stackFrames).toBeTruthy(); + expect(stackFrames.stack.length).toBe(3); + expect(stackFrames.stack[0]).toEqual({ + url: 'http://path/to/file.js', + func: '?', + args: [], + line: 48, + column: 22, + context: null, + }); + expect(stackFrames.stack[1]).toEqual({ + url: 'http://path/to/file.js', + func: 'foo', + args: [], + line: 52, + column: 15, + context: null, + }); + expect(stackFrames.stack[2]).toEqual({ + url: 'http://path/to/file.js', + func: 'bar', + args: [], + line: 108, + column: 107, + context: null, + }); + }); + + it('should parse Safari 8 error', () => { + const stackFrames = getTraceKit().computeStackTrace(CapturedExceptions.SAFARI_8); + expect(stackFrames).toBeTruthy(); + expect(stackFrames.stack.length).toBe(3); + expect(stackFrames.stack[0]).toEqual({ + url: 'http://path/to/file.js', + func: '?', + args: [], + line: 47, + column: 22, + context: null, + }); + expect(stackFrames.stack[1]).toEqual({ + url: 'http://path/to/file.js', + func: 'foo', + args: [], + line: 52, + column: 15, + context: null, + }); + expect(stackFrames.stack[2]).toEqual({ + url: 'http://path/to/file.js', + func: 'bar', + args: [], + line: 108, + column: 23, + context: null, + }); + }); + + it('should parse Safari 8 eval error', () => { + // TODO: Take into account the line and column properties on the error object and use them for the first stack trace. + const stackFrames = getTraceKit().computeStackTrace(CapturedExceptions.SAFARI_8_EVAL); + expect(stackFrames).toBeTruthy(); + expect(stackFrames.stack.length).toBe(3); + expect(stackFrames.stack[0]).toEqual({ + url: '[native code]', + func: 'eval', + args: [], + line: null, + column: null, + context: null, + }); + expect(stackFrames.stack[1]).toEqual({ + url: 'http://path/to/file.js', + func: 'foo', + args: [], + line: 58, + column: 21, + context: null, + }); + expect(stackFrames.stack[2]).toEqual({ + url: 'http://path/to/file.js', + func: 'bar', + args: [], + line: 109, + column: 91, + context: null, + }); + }); + + it('should parse Firefox 3 error', () => { + const stackFrames = getTraceKit().computeStackTrace(CapturedExceptions.FIREFOX_3); + expect(stackFrames).toBeTruthy(); + expect(stackFrames.stack.length).toBe(7); + expect(stackFrames.stack[0]).toEqual({ + url: 'http://127.0.0.1:8000/js/stacktrace.js', + func: '?', + args: [], + line: 44, + column: null, + context: null, + }); + expect(stackFrames.stack[1]).toEqual({ + url: 'http://127.0.0.1:8000/js/stacktrace.js', + func: '?', + args: ['null'], + line: 31, + column: null, + context: null, + }); + expect(stackFrames.stack[2]).toEqual({ + url: 'http://127.0.0.1:8000/js/stacktrace.js', + func: 'printStackTrace', + args: [], + line: 18, + column: null, + context: null, + }); + expect(stackFrames.stack[3]).toEqual({ + url: 'http://127.0.0.1:8000/js/file.js', + func: 'bar', + args: ['1'], + line: 13, + column: null, + context: null, + }); + expect(stackFrames.stack[4]).toEqual({ + url: 'http://127.0.0.1:8000/js/file.js', + func: 'bar', + args: ['2'], + line: 16, + column: null, + context: null, + }); + expect(stackFrames.stack[5]).toEqual({ + url: 'http://127.0.0.1:8000/js/file.js', + func: 'foo', + args: [], + line: 20, + column: null, + context: null, + }); + expect(stackFrames.stack[6]).toEqual({ + url: 'http://127.0.0.1:8000/js/file.js', + func: '?', + args: [], + line: 24, + column: null, + context: null, + }); + }); + + it('should parse Firefox 7 error', () => { + const stackFrames = getTraceKit().computeStackTrace( + CapturedExceptions.FIREFOX_7 as unknown as Error, + ); + expect(stackFrames).toBeTruthy(); + expect(stackFrames.stack.length).toBe(7); + expect(stackFrames.stack[0]).toEqual({ + url: 'file:///G:/js/stacktrace.js', + func: '?', + args: [], + line: 44, + column: null, + context: null, + }); + expect(stackFrames.stack[1]).toEqual({ + url: 'file:///G:/js/stacktrace.js', + func: '?', + args: ['null'], + line: 31, + column: null, + context: null, + }); + expect(stackFrames.stack[2]).toEqual({ + url: 'file:///G:/js/stacktrace.js', + func: 'printStackTrace', + args: [], + line: 18, + column: null, + context: null, + }); + expect(stackFrames.stack[3]).toEqual({ + url: 'file:///G:/js/file.js', + func: 'bar', + args: ['1'], + line: 13, + column: null, + context: null, + }); + expect(stackFrames.stack[4]).toEqual({ + url: 'file:///G:/js/file.js', + func: 'bar', + args: ['2'], + line: 16, + column: null, + context: null, + }); + expect(stackFrames.stack[5]).toEqual({ + url: 'file:///G:/js/file.js', + func: 'foo', + args: [], + line: 20, + column: null, + context: null, + }); + expect(stackFrames.stack[6]).toEqual({ + url: 'file:///G:/js/file.js', + func: '?', + args: [], + line: 24, + column: null, + context: null, + }); + }); + + it('should parse Firefox 14 error', () => { + const stackFrames = getTraceKit().computeStackTrace( + CapturedExceptions.FIREFOX_14 as unknown as Error, + ); + expect(stackFrames).toBeTruthy(); + expect(stackFrames.stack.length).toBe(3); + expect(stackFrames.stack[0]).toEqual({ + url: 'http://path/to/file.js', + func: '?', + args: [], + line: 48, + column: null, + context: null, + }); + expect(stackFrames.stack[1]).toEqual({ + url: 'http://path/to/file.js', + func: 'dumpException3', + args: [], + line: 52, + column: null, + context: null, + }); + expect(stackFrames.stack[2]).toEqual({ + url: 'http://path/to/file.js', + func: 'onclick', + args: [], + line: 1, + column: null, + context: null, + }); + }); + + it('should parse Firefox 31 error', () => { + const stackFrames = getTraceKit().computeStackTrace(CapturedExceptions.FIREFOX_31); + expect(stackFrames).toBeTruthy(); + expect(stackFrames.stack.length).toBe(3); + expect(stackFrames.stack[0]).toEqual({ + url: 'http://path/to/file.js', + func: 'foo', + args: [], + line: 41, + column: 13, + context: null, + }); + expect(stackFrames.stack[1]).toEqual({ + url: 'http://path/to/file.js', + func: 'bar', + args: [], + line: 1, + column: 1, + context: null, + }); + expect(stackFrames.stack[2]).toEqual({ + url: 'http://path/to/file.js', + func: '.plugin/e.fn[c]/<', + args: [], + line: 1, + column: 1, + context: null, + }); + }); + + it('should parse Firefox 44 ns exceptions', () => { + const stackFrames = getTraceKit().computeStackTrace(CapturedExceptions.FIREFOX_44_NS_EXCEPTION); + expect(stackFrames).toBeTruthy(); + expect(stackFrames.stack.length).toBe(4); + expect(stackFrames.stack[0]).toEqual({ + url: 'http://path/to/file.js', + func: '[2] { + const stackFrames = getTraceKit().computeStackTrace({ + stack: 'error\n at Array.forEach (native)', + } as unknown as Error); + expect(stackFrames.stack.length).toBe(1); + expect(stackFrames.stack[0]).toEqual({ + url: null, + func: 'Array.forEach', + args: ['native'], + line: null, + column: null, + context: null, + }); + }); + + it('should parse Chrome 15 error', () => { + const stackFrames = getTraceKit().computeStackTrace( + CapturedExceptions.CHROME_15 as unknown as Error, + ); + expect(stackFrames).toBeTruthy(); + expect(stackFrames.stack.length).toBe(4); + expect(stackFrames.stack[0]).toEqual({ + url: 'http://path/to/file.js', + func: 'bar', + args: [], + line: 13, + column: 17, + context: null, + }); + expect(stackFrames.stack[1]).toEqual({ + url: 'http://path/to/file.js', + func: 'bar', + args: [], + line: 16, + column: 5, + context: null, + }); + expect(stackFrames.stack[2]).toEqual({ + url: 'http://path/to/file.js', + func: 'foo', + args: [], + line: 20, + column: 5, + context: null, + }); + expect(stackFrames.stack[3]).toEqual({ + url: 'http://path/to/file.js', + func: '?', + args: [], + line: 24, + column: 4, + context: null, + }); + }); + + it('should parse Chrome 36 error with port numbers', () => { + const stackFrames = getTraceKit().computeStackTrace(CapturedExceptions.CHROME_36); + expect(stackFrames).toBeTruthy(); + expect(stackFrames.stack.length).toBe(3); + expect(stackFrames.stack[0]).toEqual({ + url: 'http://localhost:8080/file.js', + func: 'dumpExceptionError', + args: [], + line: 41, + column: 27, + context: null, + }); + expect(stackFrames.stack[1]).toEqual({ + url: 'http://localhost:8080/file.js', + func: 'HTMLButtonElement.onclick', + args: [], + line: 107, + column: 146, + context: null, + }); + expect(stackFrames.stack[2]).toEqual({ + url: 'http://localhost:8080/file.js', + func: 'I.e.fn.(anonymous function) [as index]', + args: [], + line: 10, + column: 3651, + context: null, + }); + }); + + it('should parse Chrome error with webpack URLs', () => { + const stackFrames = getTraceKit().computeStackTrace(CapturedExceptions.CHROME_XX_WEBPACK); + expect(stackFrames).toBeTruthy(); + expect(stackFrames.stack.length).toBe(4); + expect(stackFrames.stack[0]).toEqual({ + url: 'webpack:///./src/components/test/test.jsx?', + func: 'TESTTESTTEST.eval', + args: [], + line: 295, + column: 108, + context: null, + }); + expect(stackFrames.stack[1]).toEqual({ + url: 'webpack:///./src/components/test/test.jsx?', + func: 'TESTTESTTEST.render', + args: [], + line: 272, + column: 32, + context: null, + }); + expect(stackFrames.stack[2]).toEqual({ + url: 'webpack:///./~/react-transform-catch-errors/lib/index.js?', + func: 'TESTTESTTEST.tryRender', + args: [], + line: 34, + column: 31, + context: null, + }); + expect(stackFrames.stack[3]).toEqual({ + url: 'webpack:///./~/react-proxy/modules/createPrototypeProxy.js?', + func: 'TESTTESTTEST.proxiedMethod', + args: [], + line: 44, + column: 30, + context: null, + }); + }); + + it('should parse nested eval() from Chrome', () => { + const stackFrames = getTraceKit().computeStackTrace(CapturedExceptions.CHROME_48_EVAL); + expect(stackFrames).toBeTruthy(); + expect(stackFrames.stack.length).toBe(5); + expect(stackFrames.stack[0]).toEqual({ + url: 'http://localhost:8080/file.js', + func: 'baz', + args: [], + line: 21, + column: 17, + context: null, + }); + expect(stackFrames.stack[1]).toEqual({ + url: 'http://localhost:8080/file.js', + func: 'foo', + args: [], + line: 21, + column: 17, + context: null, + }); + expect(stackFrames.stack[2]).toEqual({ + url: 'http://localhost:8080/file.js', + func: 'eval', + args: [], + line: 21, + column: 17, + context: null, + }); + expect(stackFrames.stack[3]).toEqual({ + url: 'http://localhost:8080/file.js', + func: 'Object.speak', + args: [], + line: 21, + column: 17, + context: null, + }); + expect(stackFrames.stack[4]).toEqual({ + url: 'http://localhost:8080/file.js', + func: '?', + args: [], + line: 31, + column: 13, + context: null, + }); + }); + + it('should parse Chrome error with blob URLs', () => { + const stackFrames = getTraceKit().computeStackTrace(CapturedExceptions.CHROME_48_BLOB); + expect(stackFrames).toBeTruthy(); + expect(stackFrames.stack.length).toBe(7); + expect(stackFrames.stack[1]).toEqual({ + url: 'blob:http%3A//localhost%3A8080/abfc40e9-4742-44ed-9dcd-af8f99a29379', + func: 's', + args: [], + line: 31, + column: 29146, + context: null, + }); + expect(stackFrames.stack[2]).toEqual({ + url: 'blob:http%3A//localhost%3A8080/abfc40e9-4742-44ed-9dcd-af8f99a29379', + func: 'Object.d [as add]', + args: [], + line: 31, + column: 30039, + context: null, + }); + expect(stackFrames.stack[3]).toEqual({ + url: 'blob:http%3A//localhost%3A8080/d4eefe0f-361a-4682-b217-76587d9f712a', + func: '?', + args: [], + line: 15, + column: 10978, + context: null, + }); + expect(stackFrames.stack[4]).toEqual({ + url: 'blob:http%3A//localhost%3A8080/abfc40e9-4742-44ed-9dcd-af8f99a29379', + func: '?', + args: [], + line: 1, + column: 6911, + context: null, + }); + expect(stackFrames.stack[5]).toEqual({ + url: 'blob:http%3A//localhost%3A8080/abfc40e9-4742-44ed-9dcd-af8f99a29379', + func: 'n.fire', + args: [], + line: 7, + column: 3019, + context: null, + }); + expect(stackFrames.stack[6]).toEqual({ + url: 'blob:http%3A//localhost%3A8080/abfc40e9-4742-44ed-9dcd-af8f99a29379', + func: 'n.handle', + args: [], + line: 7, + column: 2863, + context: null, + }); + }); + + it('should parse empty IE 9 error', () => { + const stackFrames = getTraceKit().computeStackTrace( + CapturedExceptions.IE_9 as unknown as Error, + ); + expect(stackFrames).toBeTruthy(); + if (stackFrames.stack) { + expect(stackFrames.stack.length).toBe(0); + } + }); + + it('should parse IE 10 error', () => { + const stackFrames = getTraceKit().computeStackTrace( + CapturedExceptions.IE_10 as unknown as Error, + ); + expect(stackFrames).toBeTruthy(); + expect(stackFrames.stack.length).toBe(3); + // TODO: func should be normalized + expect(stackFrames.stack[0]).toEqual({ + url: 'http://path/to/file.js', + func: 'Anonymous function', + args: [], + line: 48, + column: 13, + context: null, + }); + expect(stackFrames.stack[1]).toEqual({ + url: 'http://path/to/file.js', + func: 'foo', + args: [], + line: 46, + column: 9, + context: null, + }); + expect(stackFrames.stack[2]).toEqual({ + url: 'http://path/to/file.js', + func: 'bar', + args: [], + line: 82, + column: 1, + context: null, + }); + }); + + it('should parse IE 11 error', () => { + const stackFrames = getTraceKit().computeStackTrace(CapturedExceptions.IE_11); + expect(stackFrames).toBeTruthy(); + expect(stackFrames.stack.length).toBe(3); + // TODO: func should be normalized + expect(stackFrames.stack[0]).toEqual({ + url: 'http://path/to/file.js', + func: 'Anonymous function', + args: [], + line: 47, + column: 21, + context: null, + }); + expect(stackFrames.stack[1]).toEqual({ + url: 'http://path/to/file.js', + func: 'foo', + args: [], + line: 45, + column: 13, + context: null, + }); + expect(stackFrames.stack[2]).toEqual({ + url: 'http://path/to/file.js', + func: 'bar', + args: [], + line: 108, + column: 1, + context: null, + }); + }); + + it('should parse IE 11 eval error', () => { + const stackFrames = getTraceKit().computeStackTrace(CapturedExceptions.IE_11_EVAL); + expect(stackFrames).toBeTruthy(); + expect(stackFrames.stack.length).toBe(3); + expect(stackFrames.stack[0]).toEqual({ + url: 'eval code', + func: 'eval code', + args: [], + line: 1, + column: 1, + context: null, + }); + expect(stackFrames.stack[1]).toEqual({ + url: 'http://path/to/file.js', + func: 'foo', + args: [], + line: 58, + column: 17, + context: null, + }); + expect(stackFrames.stack[2]).toEqual({ + url: 'http://path/to/file.js', + func: 'bar', + args: [], + line: 109, + column: 1, + context: null, + }); + }); + + it('should parse Opera 8.54 error', () => { + const stackFrames = getTraceKit().computeStackTrace( + CapturedExceptions.OPERA_854 as unknown as Error, + ); + expect(stackFrames).toBeTruthy(); + expect(stackFrames.stack.length).toBe(7); + expect(stackFrames.stack[0]).toEqual({ + url: 'http://path/to/file.js', + func: '?', + args: [], + line: 44, + column: null, + context: [' this.undef();'], + }); + expect(stackFrames.stack[1]).toEqual({ + url: 'http://path/to/file.js', + func: '?', + args: [], + line: 31, + column: null, + context: [' ex = ex || this.createException();'], + }); + expect(stackFrames.stack[2]).toEqual({ + url: 'http://path/to/file.js', + func: '?', + args: [], + line: 18, + column: null, + context: [' var p = new printStackTrace.implementation(), result = p.run(ex);'], + }); + expect(stackFrames.stack[3]).toEqual({ + url: 'http://path/to/file.js', + func: '?', + args: [], + line: 4, + column: null, + context: [' printTrace(printStackTrace());'], + }); + expect(stackFrames.stack[4]).toEqual({ + url: 'http://path/to/file.js', + func: '?', + args: [], + line: 7, + column: null, + context: [' bar(n - 1);'], + }); + expect(stackFrames.stack[5]).toEqual({ + url: 'http://path/to/file.js', + func: '?', + args: [], + line: 11, + column: null, + context: [' bar(2);'], + }); + expect(stackFrames.stack[6]).toEqual({ + url: 'http://path/to/file.js', + func: '?', + args: [], + line: 15, + column: null, + context: [' foo();'], + }); + }); + + it('should parse Opera 9.02 error', () => { + const stackFrames = getTraceKit().computeStackTrace( + CapturedExceptions.OPERA_902 as unknown as Error, + ); + expect(stackFrames).toBeTruthy(); + expect(stackFrames.stack.length).toBe(7); + expect(stackFrames.stack[0]).toEqual({ + url: 'http://path/to/file.js', + func: '?', + args: [], + line: 44, + column: null, + context: [' this.undef();'], + }); + expect(stackFrames.stack[1]).toEqual({ + url: 'http://path/to/file.js', + func: '?', + args: [], + line: 31, + column: null, + context: [' ex = ex || this.createException();'], + }); + expect(stackFrames.stack[2]).toEqual({ + url: 'http://path/to/file.js', + func: '?', + args: [], + line: 18, + column: null, + context: [' var p = new printStackTrace.implementation(), result = p.run(ex);'], + }); + expect(stackFrames.stack[3]).toEqual({ + url: 'http://path/to/file.js', + func: '?', + args: [], + line: 4, + column: null, + context: [' printTrace(printStackTrace());'], + }); + expect(stackFrames.stack[4]).toEqual({ + url: 'http://path/to/file.js', + func: '?', + args: [], + line: 7, + column: null, + context: [' bar(n - 1);'], + }); + expect(stackFrames.stack[5]).toEqual({ + url: 'http://path/to/file.js', + func: '?', + args: [], + line: 11, + column: null, + context: [' bar(2);'], + }); + expect(stackFrames.stack[6]).toEqual({ + url: 'http://path/to/file.js', + func: '?', + args: [], + line: 15, + column: null, + context: [' foo();'], + }); + }); + + it('should parse Opera 9.27 error', () => { + const stackFrames = getTraceKit().computeStackTrace( + CapturedExceptions.OPERA_927 as unknown as Error, + ); + expect(stackFrames).toBeTruthy(); + expect(stackFrames.stack.length).toBe(3); + expect(stackFrames.stack[0]).toEqual({ + url: 'http://path/to/file.js', + func: '?', + args: [], + line: 43, + column: null, + context: [' bar(n - 1);'], + }); + expect(stackFrames.stack[1]).toEqual({ + url: 'http://path/to/file.js', + func: '?', + args: [], + line: 31, + column: null, + context: [' bar(2);'], + }); + expect(stackFrames.stack[2]).toEqual({ + url: 'http://path/to/file.js', + func: '?', + args: [], + line: 18, + column: null, + context: [' foo();'], + }); + }); + + it('should parse Opera 9.64 error', () => { + const stackFrames = getTraceKit().computeStackTrace( + CapturedExceptions.OPERA_964 as unknown as Error, + ); + expect(stackFrames).toBeTruthy(); + expect(stackFrames.stack.length).toBe(6); + expect(stackFrames.stack[0]).toEqual({ + url: 'http://path/to/file.js', + func: '?', + args: [], + line: 27, + column: null, + context: [' ex = ex || this.createException();'], + }); + expect(stackFrames.stack[1]).toEqual({ + url: 'http://path/to/file.js', + func: 'printStackTrace', + args: [], + line: 18, + column: null, + context: [' var p = new printStackTrace.implementation(), result = p.run(ex);'], + }); + expect(stackFrames.stack[2]).toEqual({ + url: 'http://path/to/file.js', + func: 'bar', + args: [], + line: 4, + column: null, + context: [' printTrace(printStackTrace());'], + }); + expect(stackFrames.stack[3]).toEqual({ + url: 'http://path/to/file.js', + func: 'bar', + args: [], + line: 7, + column: null, + context: [' bar(n - 1);'], + }); + expect(stackFrames.stack[4]).toEqual({ + url: 'http://path/to/file.js', + func: 'foo', + args: [], + line: 11, + column: null, + context: [' bar(2);'], + }); + expect(stackFrames.stack[5]).toEqual({ + url: 'http://path/to/file.js', + func: '?', + args: [], + line: 15, + column: null, + context: [' foo();'], + }); + }); + + it('should parse Opera 10 error', () => { + const stackFrames = getTraceKit().computeStackTrace( + CapturedExceptions.OPERA_10 as unknown as Error, + ); + expect(stackFrames).toBeTruthy(); + expect(stackFrames.stack.length).toBe(7); + expect(stackFrames.stack[0]).toEqual({ + url: 'http://path/to/file.js', + func: '?', + args: [], + line: 42, + column: null, + context: [' this.undef();'], + }); + expect(stackFrames.stack[1]).toEqual({ + url: 'http://path/to/file.js', + func: '?', + args: [], + line: 27, + column: null, + context: [' ex = ex || this.createException();'], + }); + expect(stackFrames.stack[2]).toEqual({ + url: 'http://path/to/file.js', + func: 'printStackTrace', + args: [], + line: 18, + column: null, + context: [' var p = new printStackTrace.implementation(), result = p.run(ex);'], + }); + expect(stackFrames.stack[3]).toEqual({ + url: 'http://path/to/file.js', + func: 'bar', + args: [], + line: 4, + column: null, + context: [' printTrace(printStackTrace());'], + }); + expect(stackFrames.stack[4]).toEqual({ + url: 'http://path/to/file.js', + func: 'bar', + args: [], + line: 7, + column: null, + context: [' bar(n - 1);'], + }); + expect(stackFrames.stack[5]).toEqual({ + url: 'http://path/to/file.js', + func: 'foo', + args: [], + line: 11, + column: null, + context: [' bar(2);'], + }); + expect(stackFrames.stack[6]).toEqual({ + url: 'http://path/to/file.js', + func: '?', + args: [], + line: 15, + column: null, + context: [' foo();'], + }); + }); + + it('should parse Opera 11 error', () => { + const stackFrames = getTraceKit().computeStackTrace( + CapturedExceptions.OPERA_11 as unknown as Error, + ); + expect(stackFrames).toBeTruthy(); + expect(stackFrames.stack.length).toBe(7); + expect(stackFrames.stack[0]).toEqual({ + url: 'http://path/to/file.js', + func: 'createException', + args: [], + line: 42, + column: 12, + context: [' this.undef();'], + }); + expect(stackFrames.stack[1]).toEqual({ + url: 'http://path/to/file.js', + func: 'run', + args: ['ex'], + line: 27, + column: 8, + context: [' ex = ex || this.createException();'], + }); + expect(stackFrames.stack[2]).toEqual({ + url: 'http://path/to/file.js', + func: 'printStackTrace', + args: ['options'], + line: 18, + column: 4, + context: [' var p = new printStackTrace.implementation(), result = p.run(ex);'], + }); + expect(stackFrames.stack[3]).toEqual({ + url: 'http://path/to/file.js', + func: 'bar', + args: ['n'], + line: 4, + column: 5, + context: [' printTrace(printStackTrace());'], + }); + expect(stackFrames.stack[4]).toEqual({ + url: 'http://path/to/file.js', + func: 'bar', + args: ['n'], + line: 7, + column: 4, + context: [' bar(n - 1);'], + }); + expect(stackFrames.stack[5]).toEqual({ + url: 'http://path/to/file.js', + func: 'foo', + args: [], + line: 11, + column: 4, + context: [' bar(2);'], + }); + expect(stackFrames.stack[6]).toEqual({ + url: 'http://path/to/file.js', + func: '?', + args: [], + line: 15, + column: 3, + context: [' foo();'], + }); + }); + + it('should parse Opera 12 error', () => { + // TODO: Improve anonymous function name. + const stackFrames = getTraceKit().computeStackTrace( + CapturedExceptions.OPERA_12 as unknown as Error, + ); + expect(stackFrames).toBeTruthy(); + expect(stackFrames.stack.length).toBe(3); + expect(stackFrames.stack[0]).toEqual({ + url: 'http://localhost:8000/ExceptionLab.html', + func: '', + args: ['x'], + line: 48, + column: 12, + context: [' x.undef();'], + }); + expect(stackFrames.stack[1]).toEqual({ + url: 'http://localhost:8000/ExceptionLab.html', + func: 'dumpException3', + args: [], + line: 46, + column: 8, + context: [' dumpException((function(x) {'], + }); + expect(stackFrames.stack[2]).toEqual({ + url: 'http://localhost:8000/ExceptionLab.html', + func: '', + args: ['event'], + line: 1, + column: 0, + context: [' dumpException3();'], + }); + }); + + it('should parse Opera 25 error', () => { + const stackFrames = getTraceKit().computeStackTrace(CapturedExceptions.OPERA_25); + expect(stackFrames).toBeTruthy(); + expect(stackFrames.stack.length).toBe(3); + expect(stackFrames.stack[0]).toEqual({ + url: 'http://path/to/file.js', + func: '?', + args: [], + line: 47, + column: 22, + context: null, + }); + expect(stackFrames.stack[1]).toEqual({ + url: 'http://path/to/file.js', + func: 'foo', + args: [], + line: 52, + column: 15, + context: null, + }); + expect(stackFrames.stack[2]).toEqual({ + url: 'http://path/to/file.js', + func: 'bar', + args: [], + line: 108, + column: 168, + context: null, + }); + }); + + it('should parse PhantomJS 1.19 error', () => { + const stackFrames = getTraceKit().computeStackTrace( + CapturedExceptions.PHANTOMJS_1_19 as unknown as Error, + ); + expect(stackFrames).toBeTruthy(); + expect(stackFrames.stack.length).toBe(3); + expect(stackFrames.stack[0]).toEqual({ + url: 'file:///path/to/file.js', + func: '?', + args: [], + line: 878, + column: null, + context: null, + }); + expect(stackFrames.stack[1]).toEqual({ + url: 'http://path/to/file.js', + func: 'foo', + args: [], + line: 4283, + column: null, + context: null, + }); + expect(stackFrames.stack[2]).toEqual({ + url: 'http://path/to/file.js', + func: '?', + args: [], + line: 4287, + column: null, + context: null, + }); + }); + + it('should parse Firefox errors with resource: URLs', () => { + const stackFrames = getTraceKit().computeStackTrace(CapturedExceptions.FIREFOX_50_RESOURCE_URL); + expect(stackFrames).toBeTruthy(); + expect(stackFrames.stack.length).toBe(3); + expect(stackFrames.stack[0]).toEqual({ + url: 'resource://path/data/content/bundle.js', + func: 'render', + args: [], + line: 5529, + column: 16, + context: null, + }); + }); + + it('should parse Firefox errors with eval URLs', () => { + const stackFrames = getTraceKit().computeStackTrace( + CapturedExceptions.FIREFOX_43_EVAL as unknown as Error, + ); + expect(stackFrames).toBeTruthy(); + expect(stackFrames.stack.length).toBe(5); + expect(stackFrames.stack[0]).toEqual({ + url: 'http://localhost:8080/file.js', + func: 'baz', + args: [], + line: 26, + column: null, + context: null, + }); + expect(stackFrames.stack[1]).toEqual({ + url: 'http://localhost:8080/file.js', + func: 'foo', + args: [], + line: 26, + column: null, + context: null, + }); + expect(stackFrames.stack[2]).toEqual({ + url: 'http://localhost:8080/file.js', + func: '?', + args: [], + line: 26, + column: null, + context: null, + }); + expect(stackFrames.stack[3]).toEqual({ + url: 'http://localhost:8080/file.js', + func: 'speak', + args: [], + line: 26, + column: 17, + context: null, + }); + expect(stackFrames.stack[4]).toEqual({ + url: 'http://localhost:8080/file.js', + func: '?', + args: [], + line: 33, + column: 9, + context: null, + }); + }); + + it('should parse React Native errors on Android', () => { + const stackFrames = getTraceKit().computeStackTrace(CapturedExceptions.ANDROID_REACT_NATIVE); + expect(stackFrames).toBeTruthy(); + expect(stackFrames.stack.length).toBe(8); + expect(stackFrames.stack[0]).toEqual({ + url: '/home/username/sample-workspace/sampleapp.collect.react/src/components/GpsMonitorScene.js', + func: 'render', + args: [], + line: 78, + column: 24, + context: null, + }); + expect(stackFrames.stack[7]).toEqual({ + 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, + context: null, + }); + }); + + it('should parse React Native errors on Android Production', () => { + const stackFrames = getTraceKit().computeStackTrace( + CapturedExceptions.ANDROID_REACT_NATIVE_PROD, + ); + expect(stackFrames).toBeTruthy(); + expect(stackFrames.stack.length).toBe(37); + expect(stackFrames.stack[0]).toEqual({ + url: 'index.android.bundle', + func: 'value', + args: [], + line: 12, + column: 1917, + context: null, + }); + expect(stackFrames.stack[35]).toEqual({ + url: 'index.android.bundle', + func: 'value', + args: [], + line: 29, + column: 927, + context: null, + }); + expect(stackFrames.stack[36]).toEqual({ + url: '[native code]', + func: '?', + args: [], + line: null, + column: null, + context: null, + }); + }); +});