├── .babelrc ├── .editorconfig ├── .eslintrc.js ├── .gitignore ├── .prettierrc ├── .travis.yml ├── .vscode └── launch.json ├── LICENSE ├── README.md ├── mocha-babel-hook.js ├── package-lock.json ├── package.json ├── rollup.config.js ├── src ├── index.d.ts ├── index.js ├── index.test-d.ts └── stack-trace-parser.js └── test ├── fixtures └── captured-errors.js └── stack-trace-parser.spec.js /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | [ 4 | "@babel/preset-env", 5 | { 6 | "targets": { 7 | "browsers": ["last 2 versions", "safari >= 7"] 8 | }, 9 | "modules": false 10 | } 11 | ] 12 | ] 13 | } 14 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | end_of_line = lf 5 | insert_final_newline = true 6 | indent_style = space 7 | indent_size = 2 -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | env: { 3 | browser: true, 4 | commonjs: true, 5 | es6: true, 6 | node: true, 7 | mocha: true 8 | }, 9 | extends: ['eslint:recommended', 'plugin:prettier/recommended'], 10 | parserOptions: { 11 | ecmaVersion: 2018, 12 | sourceType: 'module', 13 | }, 14 | rules: { 15 | 'linebreak-style': ['error', 'unix'], 16 | }, 17 | }; 18 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /.history 2 | node_modules 3 | dist 4 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "singleQuote": true, 3 | "trailingComma": "es5" 4 | } 5 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - "6" 4 | - "8" 5 | - "10" 6 | -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "0.2.0", 3 | "configurations": [ 4 | { 5 | "type": "node", 6 | "request": "launch", 7 | "name": "Mocha All", 8 | "program": "${workspaceFolder}/node_modules/mocha/bin/_mocha", 9 | "args": ["--timeout", "999999", "--colors", "--compilers", "js:@babel/register", "'${workspaceFolder}/test/**/*.spec.js'"], 10 | "console": "integratedTerminal", 11 | "internalConsoleOptions": "neverOpen", 12 | "disableOptimisticBPs": true 13 | } 14 | ] 15 | } 16 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2014-2019 Georg Tavonius 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. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![Build Status][build-image]][build-url] [![dependencies][deps-image]][deps-url] [![dev-dependencies][dev-deps-image]][dev-deps-url] 2 | 3 | # StackTrace-Parser 4 | 5 | This parser parses a stack trace from any browser or Node.js and returns an array of hashes each representing a line. 6 | 7 | The goal here is to support every browser even old Internet Explorer stack traces will work. 8 | 9 | ## Install 10 | 11 | ```bashv0.1 12 | npm install stacktrace-parser 13 | ``` 14 | 15 | ## Usage 16 | 17 | ```JavaScript 18 | import * as stackTraceParser from 'stacktrace-parser'; 19 | 20 | try { 21 | throw new Error('My error'); 22 | } catch(ex) { 23 | const stack = stackTraceParser.parse(ex.stack); 24 | } 25 | ``` 26 | 27 | Every line contains five properties: `lineNumber`, `methodName`, `arguments`, `file` and `column` (if applicable). 28 | 29 | ## TODOs 30 | 31 | - parse stack traces from other sources (Ruby, etc) (v0.3) 32 | 33 | ## Contribution 34 | 35 | If you want to contrib, then do you thing, write tests, run `npm run test` ensure that everything is green, 36 | commit and make the pull request. Or just write an issue, or let's talk. 37 | 38 | ## Contributors 39 | 40 | - [Georg Tavonius](https://github.com/calamari) 41 | - [James Ide](https://github.com/ide) 42 | - [Alexander Kotliarskyi](https://github.com/frantic) 43 | - [Dimitri Benin](https://github.com/BendingBender) 44 | - [Tony Brix](https://github.com/UziTech) 45 | 46 | ## LICENSE 47 | 48 | [The MIT License (MIT)](https://github.com/errwischt/stacktrace-parser/blob/master/LICENSE) 49 | 50 | [build-image]: https://img.shields.io/travis/errwischt/stacktrace-parser/master.svg?style=flat-square 51 | [build-url]: https://travis-ci.org/errwischt/stacktrace-parser 52 | [deps-image]: https://img.shields.io/david/errwischt/stacktrace-parser.svg?style=flat-square 53 | [deps-url]: https://david-dm.org/errwischt/stacktrace-parser 54 | [dev-deps-image]: https://img.shields.io/david/dev/errwischt/stacktrace-parser.svg?style=flat-square 55 | [dev-deps-url]: https://david-dm.org/errwischt/stacktrace-parser?type=dev 56 | -------------------------------------------------------------------------------- /mocha-babel-hook.js: -------------------------------------------------------------------------------- 1 | // different babel config for mocha, need to transpile modules 2 | require('@babel/register')({ 3 | presets: ['@babel/preset-env'], 4 | }); 5 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "author": "Georg Tavonius (http://jaz-lounge.com)", 3 | "name": "stacktrace-parser", 4 | "description": "Parses every stack trace into a nicely formatted array of hashes.", 5 | "main": "dist/stack-trace-parser.cjs.js", 6 | "module": "dist/stack-trace-parser.esm.js", 7 | "types": "dist/stack-trace-parser.d.ts", 8 | "scripts": { 9 | "clean": "rimraf dist", 10 | "dev": "rollup -c -w", 11 | "dist": "rollup -c && cpy --rename stack-trace-parser.d.ts src/index.d.ts dist/ && cpy --rename stack-trace-parser.test-d.ts src/index.test-d.ts dist/", 12 | "prepublish": "npm run dist", 13 | "pretest": "npm run dist", 14 | "test": "tsd && mocha --require ./mocha-babel-hook 'test/**/*.spec.js'", 15 | "lint": "eslint --fix '{src,test}/**/*.js'" 16 | }, 17 | "keywords": [ 18 | "errors", 19 | "stacktrace", 20 | "parser", 21 | "exceptions" 22 | ], 23 | "engines": { 24 | "node": ">=6" 25 | }, 26 | "version": "0.1.11", 27 | "files": [ 28 | "dist/stack-trace-parser.cjs.js", 29 | "dist/stack-trace-parser.esm.js", 30 | "dist/stack-trace-parser.d.ts", 31 | "LICENSE", 32 | "README.md" 33 | ], 34 | "dependencies": { 35 | "type-fest": "^0.7.1" 36 | }, 37 | "devDependencies": { 38 | "@babel/core": "^7.9.6", 39 | "@babel/preset-env": "^7.9.6", 40 | "@babel/register": "^7.9.0", 41 | "cpy-cli": "^2.0.0", 42 | "eslint": "^6.8.0", 43 | "eslint-config-prettier": "^6.11.0", 44 | "eslint-plugin-prettier": "^3.1.3", 45 | "expect.js": "^0.3.1", 46 | "mocha": "^10.2.0", 47 | "prettier": "^1.19.1", 48 | "rimraf": "^3.0.2", 49 | "rollup": "^1.32.1", 50 | "rollup-plugin-babel": "^4.4.0", 51 | "rollup-plugin-commonjs": "^10.0.0", 52 | "rollup-plugin-node-resolve": "^5.2.0", 53 | "tsd": "^0.25.0" 54 | }, 55 | "homepage": "https://github.com/errwischt/stacktrace-parser", 56 | "repository": { 57 | "type": "git", 58 | "url": "https://github.com/errwischt/stacktrace-parser" 59 | }, 60 | "bugs": { 61 | "url": "http://github.com/errwischt/stacktrace-parser/issues" 62 | }, 63 | "license": "MIT" 64 | } 65 | -------------------------------------------------------------------------------- /rollup.config.js: -------------------------------------------------------------------------------- 1 | import resolve from 'rollup-plugin-node-resolve'; 2 | import commonjs from 'rollup-plugin-commonjs'; 3 | import babel from 'rollup-plugin-babel'; 4 | import pkg from './package.json'; 5 | 6 | export default [ 7 | // browser-friendly UMD build 8 | { 9 | input: 'src/index.js', 10 | output: { 11 | name: 'stackTraceParser', 12 | file: pkg.browser, 13 | format: 'umd', 14 | }, 15 | plugins: [ 16 | resolve(), 17 | commonjs(), 18 | babel({ 19 | exclude: ['node_modules/**'], 20 | }), 21 | ], 22 | }, 23 | 24 | // CommonJS (for Node) and ES module (for bundlers) build. 25 | { 26 | input: 'src/index.js', 27 | output: [ 28 | { file: pkg.main, format: 'cjs' }, 29 | { file: pkg.module, format: 'es' }, 30 | ], 31 | plugins: [ 32 | babel({ 33 | exclude: ['node_modules/**'], 34 | }), 35 | ], 36 | }, 37 | ]; 38 | -------------------------------------------------------------------------------- /src/index.d.ts: -------------------------------------------------------------------------------- 1 | import { LiteralUnion } from 'type-fest'; 2 | 3 | export interface StackFrame { 4 | file: string | null; 5 | methodName: LiteralUnion<'', string>; 6 | arguments: string[]; 7 | lineNumber: number | null; 8 | column: number | null; 9 | } 10 | 11 | /** 12 | * This parser parses a stack trace from any browser or Node.js and returns an array of hashes representing a line. 13 | * 14 | * @param stackString - The stack to parse, usually from `error.stack` property. 15 | * @returns The parsed stack frames. 16 | */ 17 | export function parse(stackString: string): StackFrame[]; 18 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | export * from './stack-trace-parser'; 2 | -------------------------------------------------------------------------------- /src/index.test-d.ts: -------------------------------------------------------------------------------- 1 | import { expectType } from 'tsd'; 2 | import { LiteralUnion } from 'type-fest'; 3 | import { parse, StackFrame } from '..'; 4 | 5 | expectType(parse(new Error().stack!)); 6 | 7 | const stack = parse(new Error().stack!); 8 | 9 | expectType(stack[0].file); 10 | expectType', string>>(stack[0].methodName); 11 | expectType(stack[0].arguments); 12 | expectType(stack[0].lineNumber); 13 | expectType(stack[0].column); 14 | -------------------------------------------------------------------------------- /src/stack-trace-parser.js: -------------------------------------------------------------------------------- 1 | const UNKNOWN_FUNCTION = ''; 2 | 3 | /** 4 | * This parses the different stack traces and puts them into one format 5 | * This borrows heavily from TraceKit (https://github.com/csnover/TraceKit) 6 | */ 7 | export function parse(stackString) { 8 | const lines = stackString.split('\n'); 9 | 10 | return lines.reduce((stack, line) => { 11 | const parseResult = 12 | parseChrome(line) || 13 | parseWinjs(line) || 14 | parseGecko(line) || 15 | parseNode(line) || 16 | parseJSC(line); 17 | 18 | if (parseResult) { 19 | stack.push(parseResult); 20 | } 21 | 22 | return stack; 23 | }, []); 24 | } 25 | 26 | const chromeRe = /^\s*at (.*?) ?\(((?:file|https?|blob|chrome-extension|native|eval|webpack|rsc||\/|[a-z]:\\|\\\\).*?)(?::(\d+))?(?::(\d+))?\)?\s*$/i; 27 | const chromeEvalRe = /\((\S*)(?::(\d+))(?::(\d+))\)/; 28 | 29 | function parseChrome(line) { 30 | const parts = chromeRe.exec(line); 31 | 32 | if (!parts) { 33 | return null; 34 | } 35 | 36 | const isNative = parts[2] && parts[2].indexOf('native') === 0; // start of line 37 | const isEval = parts[2] && parts[2].indexOf('eval') === 0; // start of line 38 | 39 | const submatch = chromeEvalRe.exec(parts[2]); 40 | if (isEval && submatch != null) { 41 | // throw out eval line/column and use top-most line/column number 42 | parts[2] = submatch[1]; // url 43 | parts[3] = submatch[2]; // line 44 | parts[4] = submatch[3]; // column 45 | } 46 | 47 | return { 48 | file: !isNative ? parts[2] : null, 49 | methodName: parts[1] || UNKNOWN_FUNCTION, 50 | arguments: isNative ? [parts[2]] : [], 51 | lineNumber: parts[3] ? +parts[3] : null, 52 | column: parts[4] ? +parts[4] : null, 53 | }; 54 | } 55 | 56 | const winjsRe = /^\s*at (?:((?:\[object object\])?.+) )?\(?((?:file|ms-appx|https?|webpack|rsc|blob):.*?):(\d+)(?::(\d+))?\)?\s*$/i; 57 | 58 | function parseWinjs(line) { 59 | const parts = winjsRe.exec(line); 60 | 61 | if (!parts) { 62 | return null; 63 | } 64 | 65 | return { 66 | file: parts[2], 67 | methodName: parts[1] || UNKNOWN_FUNCTION, 68 | arguments: [], 69 | lineNumber: +parts[3], 70 | column: parts[4] ? +parts[4] : null, 71 | }; 72 | } 73 | 74 | const geckoRe = /^\s*(.*?)(?:\((.*?)\))?(?:^|@)((?:file|https?|blob|chrome|webpack|rsc|resource|\[native).*?|[^@]*bundle)(?::(\d+))?(?::(\d+))?\s*$/i; 75 | const geckoEvalRe = /(\S+) line (\d+)(?: > eval line \d+)* > eval/i; 76 | 77 | function parseGecko(line) { 78 | const parts = geckoRe.exec(line); 79 | 80 | if (!parts) { 81 | return null; 82 | } 83 | 84 | const isEval = parts[3] && parts[3].indexOf(' > eval') > -1; 85 | 86 | const submatch = geckoEvalRe.exec(parts[3]); 87 | if (isEval && submatch != null) { 88 | // throw out eval line/column and use top-most line number 89 | parts[3] = submatch[1]; 90 | parts[4] = submatch[2]; 91 | parts[5] = null; // no column when eval 92 | } 93 | 94 | return { 95 | file: parts[3], 96 | methodName: parts[1] || UNKNOWN_FUNCTION, 97 | arguments: parts[2] ? parts[2].split(',') : [], 98 | lineNumber: parts[4] ? +parts[4] : null, 99 | column: parts[5] ? +parts[5] : null, 100 | }; 101 | } 102 | 103 | const javaScriptCoreRe = /^\s*(?:([^@]*)(?:\((.*?)\))?@)?(\S.*?):(\d+)(?::(\d+))?\s*$/i; 104 | 105 | function parseJSC(line) { 106 | const parts = javaScriptCoreRe.exec(line); 107 | 108 | if (!parts) { 109 | return null; 110 | } 111 | 112 | return { 113 | file: parts[3], 114 | methodName: parts[1] || UNKNOWN_FUNCTION, 115 | arguments: [], 116 | lineNumber: +parts[4], 117 | column: parts[5] ? +parts[5] : null, 118 | }; 119 | } 120 | 121 | const nodeRe = /^\s*at (?:((?:\[object object\])?[^\\/]+(?: \[as \S+\])?) )?\(?(.*?):(\d+)(?::(\d+))?\)?\s*$/i; 122 | 123 | function parseNode(line) { 124 | const parts = nodeRe.exec(line); 125 | 126 | if (!parts) { 127 | return null; 128 | } 129 | 130 | return { 131 | file: parts[2], 132 | methodName: parts[1] || UNKNOWN_FUNCTION, 133 | arguments: [], 134 | lineNumber: +parts[3], 135 | column: parts[4] ? +parts[4] : null, 136 | }; 137 | } 138 | -------------------------------------------------------------------------------- /test/fixtures/captured-errors.js: -------------------------------------------------------------------------------- 1 | export default { 2 | NODE_12: { 3 | message: 'Just an Exception', 4 | name: 'Error', 5 | stack: 6 | 'Error: Just an Exception\n' + 7 | ' at promiseMe (/home/xyz/hack/asyncnode.js:11:9)\n' + 8 | ' at async main (/home/xyz/hack/asyncnode.js:15:13)', 9 | }, 10 | NODE_ANONYM: { 11 | message: '', 12 | name: 'Error', 13 | stack: `Error 14 | at Spect.get (C:\\projects\\spect\\src\\index.js:161:26) 15 | at Object.get (C:\\projects\\spect\\src\\index.js:43:36) 16 | at 17 | at (anonymous function).then (C:\\projects\\spect\\src\\index.js:165:33) 18 | at process.runNextTicks [as _tickCallback] (internal/process/task_queues.js:52:5) 19 | at C:\\projects\\spect\\node_modules\\esm\\esm.js:1:34535 20 | at C:\\projects\\spect\\node_modules\\esm\\esm.js:1:34176 21 | at process. (C:\\projects\\spect\\node_modules\\esm\\esm.js:1:34506) 22 | at Function. (C:\\projects\\spect\\node_modules\\esm\\esm.js:1:296856) 23 | at Function. (C:\\projects\\spect\\node_modules\\esm\\esm.js:1:296555)`, 24 | }, 25 | 26 | NODE_SPACE: { 27 | message: '', 28 | name: 'Error', 29 | stack: `Error 30 | at Spect.get (C:\\project files\\spect\\src\\index.js:161:26) 31 | at Object.get (C:\\project files\\spect\\src\\index.js:43:36) 32 | at 33 | at (anonymous function).then (C:\\project files\\spect\\src\\index.js:165:33) 34 | at process.runNextTicks [as _tickCallback] (internal/process/task_queues.js:52:5) 35 | at C:\\project files\\spect\\node_modules\\esm\\esm.js:1:34535 36 | at C:\\project files\\spect\\node_modules\\esm\\esm.js:1:34176 37 | at process. (C:\\project files\\spect\\node_modules\\esm\\esm.js:1:34506) 38 | at Function. (C:\\project files\\spect\\node_modules\\esm\\esm.js:1:296856) 39 | at Function. (C:\\project files\\spect\\node_modules\\esm\\esm.js:1:296555)`, 40 | }, 41 | 42 | OPERA_25: { 43 | message: "Cannot read property 'undef' of null", 44 | name: 'TypeError', 45 | stack: 46 | "TypeError: Cannot read property 'undef' of null\n" + 47 | ' at http://path/to/file.js:47:22\n' + 48 | ' at foo (http://path/to/file.js:52:15)\n' + 49 | ' at bar (http://path/to/file.js:108:168)', 50 | }, 51 | 52 | CHROME_15: { 53 | arguments: ['undef'], 54 | message: "Object # has no method 'undef'", 55 | stack: 56 | "TypeError: Object # has no method 'undef'\n" + 57 | ' at bar (http://path/to/file.js:13:17)\n' + 58 | ' at bar (http://path/to/file.js:16:5)\n' + 59 | ' at foo (http://path/to/file.js:20:5)\n' + 60 | ' at http://path/to/file.js:24:4', 61 | }, 62 | 63 | CHROME_36: { 64 | message: 'Default error', 65 | name: 'Error', 66 | stack: 67 | 'Error: Default error\n' + 68 | ' at dumpExceptionError (http://localhost:8080/file.js:41:27)\n' + 69 | ' at HTMLButtonElement.onclick (http://localhost:8080/file.js:107:146)\n' + 70 | ' at I.e.fn.(anonymous function) [as index] (http://localhost:8080/file.js:10:3651)', 71 | }, 72 | 73 | CHROME_76: { 74 | message: 'BEEP BEEP', 75 | name: 'Error', 76 | stack: 77 | 'Error: BEEP BEEP\n' + 78 | ' at bar (:8:9)\n' + 79 | ' at async foo (:2:3)', 80 | }, 81 | 82 | // can be generated when Webpack is built with source maps 83 | CHROME_XX_WEBPACK: { 84 | message: "Cannot read property 'error' of undefined", 85 | name: 'TypeError', 86 | stack: 87 | "TypeError: Cannot read property 'error' of undefined\n" + 88 | // { devtool: eval }: 89 | ' at TESTTESTTEST.eval(webpack:///./src/components/test/test.jsx?:295:108)\n' + 90 | ' at TESTTESTTEST.render(webpack:///./src/components/test/test.jsx?:272:32)\n' + 91 | ' at TESTTESTTEST.tryRender(webpack:///./~/react-transform-catch-errors/lib/index.js?:34:31)\n' + 92 | ' at TESTTESTTEST.proxiedMethod(webpack:///./~/react-proxy/modules/createPrototypeProxy.js?:44:30)\n' + 93 | // { devtool: source-map }: 94 | ' at Module../pages/index.js (C:\\root\\server\\development\\pages\\index.js:182:7)', 95 | }, 96 | 97 | FIREFOX_3: { 98 | fileName: 'http://127.0.0.1:8000/js/stacktrace.js', 99 | lineNumber: 44, 100 | message: 'this.undef is not a function', 101 | name: 'TypeError', 102 | stack: 103 | '()@http://127.0.0.1:8000/js/stacktrace.js:44\n' + 104 | '(null)@http://127.0.0.1:8000/js/stacktrace.js:31\n' + 105 | 'printStackTrace()@http://127.0.0.1:8000/js/stacktrace.js:18\n' + 106 | 'bar(1)@http://127.0.0.1:8000/js/file.js:13\n' + 107 | 'bar(2)@http://127.0.0.1:8000/js/file.js:16\n' + 108 | 'foo()@http://127.0.0.1:8000/js/file.js:20\n' + 109 | '@http://127.0.0.1:8000/js/file.js:24\n' + 110 | '', 111 | }, 112 | 113 | FIREFOX_7: { 114 | fileName: 'file:///G:/js/stacktrace.js', 115 | lineNumber: 44, 116 | stack: 117 | '()@file:///G:/js/stacktrace.js:44\n' + 118 | '(null)@file:///G:/js/stacktrace.js:31\n' + 119 | 'printStackTrace()@file:///G:/js/stacktrace.js:18\n' + 120 | 'bar(1)@file:///G:/js/file.js:13\n' + 121 | 'bar(2)@file:///G:/js/file.js:16\n' + 122 | 'foo()@file:///G:/js/file.js:20\n' + 123 | '@file:///G:/js/file.js:24\n' + 124 | '', 125 | }, 126 | 127 | FIREFOX_14: { 128 | message: 'x is null', 129 | stack: 130 | '@http://path/to/file.js:48\n' + 131 | 'dumpException3@http://path/to/file.js:52\n' + 132 | 'onclick@http://path/to/file.js:1\n' + 133 | '', 134 | fileName: 'http://path/to/file.js', 135 | lineNumber: 48, 136 | }, 137 | 138 | FIREFOX_31: { 139 | message: 'Default error', 140 | name: 'Error', 141 | stack: 142 | 'foo@http://path/to/file.js:41:13\n' + 143 | 'bar@http://path/to/file.js:1:1\n' + 144 | '.plugin/e.fn[c]/<@http://path/to/file.js:1:1\n' + 145 | '', 146 | fileName: 'http://path/to/file.js', 147 | lineNumber: 41, 148 | columnNumber: 12, 149 | }, 150 | 151 | FIREFOX_43_EVAL: { 152 | columnNumber: 30, 153 | fileName: 'http://localhost:8080/file.js line 25 > eval line 2 > eval', 154 | lineNumber: 1, 155 | message: 'message string', 156 | stack: 157 | 'baz@http://localhost:8080/file.js line 26 > eval line 2 > eval:1:30\n' + 158 | 'foo@http://localhost:8080/file.js line 26 > eval:2:96\n' + 159 | '@http://localhost:8080/file.js line 26 > eval:4:18\n' + 160 | 'speak@http://localhost:8080/file.js:26:17\n' + 161 | '@http://localhost:8080/file.js:33:9', 162 | }, 163 | 164 | // Internal errors sometimes thrown by Firefox 165 | // More here: https://developer.mozilla.org/en-US/docs/Mozilla/Errors 166 | // 167 | // Note that such errors are instanceof "Exception", not "Error" 168 | FIREFOX_44_NS_EXCEPTION: { 169 | message: '', 170 | name: 'NS_ERROR_FAILURE', 171 | stack: 172 | '[2] tag 176 | '', 177 | fileName: 'http://path/to/file.js', 178 | columnNumber: 0, 179 | lineNumber: 703, 180 | result: 2147500037, 181 | }, 182 | 183 | FIREFOX_50_RESOURCE_URL: { 184 | stack: 185 | 'render@resource://path/data/content/bundle.js:5529:16\n' + 186 | 'dispatchEvent@resource://path/data/content/vendor.bundle.js:18:23028\n' + 187 | 'wrapped@resource://path/data/content/bundle.js:7270:25', 188 | fileName: 'resource://path/data/content/bundle.js', 189 | lineNumber: 5529, 190 | columnNumber: 16, 191 | message: 'this.props.raw[this.state.dataSource].rows is undefined', 192 | name: 'TypeError', 193 | }, 194 | 195 | SAFARI_6: { 196 | message: "'null' is not an object (evaluating 'x.undef')", 197 | stack: 198 | '@http://path/to/file.js:48\n' + 199 | 'dumpException3@http://path/to/file.js:52\n' + 200 | 'onclick@http://path/to/file.js:82\n' + 201 | '[native code]', 202 | line: 48, 203 | sourceURL: 'http://path/to/file.js', 204 | }, 205 | 206 | SAFARI_7: { 207 | message: "'null' is not an object (evaluating 'x.undef')", 208 | name: 'TypeError', 209 | stack: 210 | 'http://path/to/file.js:48:22\n' + 211 | 'foo@http://path/to/file.js:52:15\n' + 212 | 'bar@http://path/to/file.js:108:107', 213 | line: 47, 214 | sourceURL: 'http://path/to/file.js', 215 | }, 216 | 217 | SAFARI_8: { 218 | message: "null is not an object (evaluating 'x.undef')", 219 | name: 'TypeError', 220 | stack: 221 | 'http://path/to/file.js:47:22\n' + 222 | 'foo@http://path/to/file.js:52:15\n' + 223 | 'bar@http://path/to/file.js:108:23', 224 | line: 47, 225 | column: 22, 226 | sourceURL: 'http://path/to/file.js', 227 | }, 228 | 229 | SAFARI_8_EVAL: { 230 | message: "Can't find variable: getExceptionProps", 231 | name: 'ReferenceError', 232 | stack: 233 | 'eval code\n' + 234 | 'eval@[native code]\n' + 235 | 'foo@http://path/to/file.js:58:21\n' + 236 | 'bar@http://path/to/file.js:109:91', 237 | line: 1, 238 | column: 18, 239 | }, 240 | 241 | IE_10: { 242 | message: "Unable to get property 'undef' of undefined or null reference", 243 | stack: 244 | "TypeError: Unable to get property 'undef' of undefined or null reference\n" + 245 | ' at Anonymous function (http://path/to/file.js:48:13)\n' + 246 | ' at foo (http://path/to/file.js:46:9)\n' + 247 | ' at bar (http://path/to/file.js:82:1)', 248 | description: 249 | "Unable to get property 'undef' of undefined or null reference", 250 | number: -2146823281, 251 | }, 252 | 253 | IE_11: { 254 | message: "Unable to get property 'undef' of undefined or null reference", 255 | name: 'TypeError', 256 | stack: 257 | "TypeError: Unable to get property 'undef' of undefined or null reference\n" + 258 | ' at Anonymous function (http://path/to/file.js:47:21)\n' + 259 | ' at foo (http://path/to/file.js:45:13)\n' + 260 | ' at bar (http://path/to/file.js:108:1)', 261 | description: 262 | "Unable to get property 'undef' of undefined or null reference", 263 | number: -2146823281, 264 | }, 265 | 266 | IE_11_EVAL: { 267 | message: "'getExceptionProps' is undefined", 268 | name: 'ReferenceError', 269 | stack: 270 | "ReferenceError: 'getExceptionProps' is undefined\n" + 271 | ' at eval code (eval code:1:1)\n' + 272 | ' at foo (http://path/to/file.js:58:17)\n' + 273 | ' at bar (http://path/to/file.js:109:1)', 274 | description: "'getExceptionProps' is undefined", 275 | number: -2146823279, 276 | }, 277 | 278 | CHROME_48_BLOB: { 279 | message: 'Error: test', 280 | name: 'Error', 281 | stack: 282 | 'Error: test\n' + 283 | ' at Error (native)\n' + 284 | ' at s (blob:http%3A//localhost%3A8080/abfc40e9-4742-44ed-9dcd-af8f99a29379:31:29146)\n' + 285 | ' at Object.d [as add] (blob:http%3A//localhost%3A8080/abfc40e9-4742-44ed-9dcd-af8f99a29379:31:30039)\n' + 286 | ' at blob:http%3A//localhost%3A8080/d4eefe0f-361a-4682-b217-76587d9f712a:15:10978\n' + 287 | ' at blob:http%3A//localhost%3A8080/abfc40e9-4742-44ed-9dcd-af8f99a29379:1:6911\n' + 288 | ' at n.fire (blob:http%3A//localhost%3A8080/abfc40e9-4742-44ed-9dcd-af8f99a29379:7:3019)\n' + 289 | ' at n.handle (blob:http%3A//localhost%3A8080/abfc40e9-4742-44ed-9dcd-af8f99a29379:7:2863)', 290 | }, 291 | 292 | CHROME_48_EVAL: { 293 | message: 'message string', 294 | name: 'Error', 295 | stack: 296 | 'Error: message string\n' + 297 | 'at baz (eval at foo (eval at speak (http://localhost:8080/file.js:21:17)), :1:30)\n' + 298 | 'at foo (eval at speak (http://localhost:8080/file.js:21:17), :2:96)\n' + 299 | 'at eval (eval at speak (http://localhost:8080/file.js:21:17), :4:18)\n' + 300 | 'at Object.speak (http://localhost:8080/file.js:21:17)\n' + 301 | 'at http://localhost:8080/file.js:31:13\n', 302 | }, 303 | 304 | PHANTOMJS_1_19: { 305 | stack: 306 | 'Error: foo\n' + 307 | ' at file:///path/to/file.js:878\n' + 308 | ' at foo (http://path/to/file.js:4283)\n' + 309 | ' at http://path/to/file.js:4287', 310 | }, 311 | 312 | ANDROID_REACT_NATIVE: { 313 | message: 'Error: test', 314 | name: 'Error', 315 | stack: 316 | 'Error: test\n' + 317 | 'at render(/home/username/sample-workspace/sampleapp.collect.react/src/components/GpsMonitorScene.js:78:24)\n' + 318 | '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' + 319 | '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' + 320 | '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' + 321 | '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' + 322 | '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' + 323 | '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' + 324 | 'at this(/home/username/sample-workspace/sampleapp.collect.react/node_modules/react-native/Libraries/Renderer/src/renderers/native/ReactNativeBaseComponent.js:74:41)\n', 325 | }, 326 | 327 | ANDROID_REACT_NATIVE_PROD: { 328 | message: 'Error: test', 329 | name: 'Error', 330 | stack: 331 | 'value@index.android.bundle:12:1917\n' + 332 | 'onPress@index.android.bundle:12:2336\n' + 333 | 'touchableHandlePress@index.android.bundle:258:1497\n' + 334 | '[native code]\n' + 335 | '_performSideEffectsForTransition@index.android.bundle:252:8508\n' + 336 | '[native code]\n' + 337 | '_receiveSignal@index.android.bundle:252:7291\n' + 338 | '[native code]\n' + 339 | 'touchableHandleResponderRelease@index.android.bundle:252:4735\n' + 340 | '[native code]\n' + 341 | 'u@index.android.bundle:79:142\n' + 342 | 'invokeGuardedCallback@index.android.bundle:79:459\n' + 343 | 'invokeGuardedCallbackAndCatchFirstError@index.android.bundle:79:580\n' + 344 | 'c@index.android.bundle:95:365\n' + 345 | 'a@index.android.bundle:95:567\n' + 346 | 'v@index.android.bundle:146:501\n' + 347 | 'g@index.android.bundle:146:604\n' + 348 | 'forEach@[native code]\n' + 349 | 'i@index.android.bundle:149:80\n' + 350 | 'processEventQueue@index.android.bundle:146:1432\n' + 351 | 's@index.android.bundle:157:88\n' + 352 | 'handleTopLevel@index.android.bundle:157:174\n' + 353 | 'index.android.bundle:156:572\n' + 354 | 'a@index.android.bundle:93:276\n' + 355 | 'c@index.android.bundle:93:60\n' + 356 | 'perform@index.android.bundle:177:596\n' + 357 | 'batchedUpdates@index.android.bundle:188:464\n' + 358 | 'i@index.android.bundle:176:358\n' + 359 | 'i@index.android.bundle:93:90\n' + 360 | 'u@index.android.bundle:93:150\n' + 361 | '_receiveRootNodeIDEvent@index.android.bundle:156:544\n' + 362 | 'receiveTouches@index.android.bundle:156:918\n' + 363 | 'value@index.android.bundle:29:3016\n' + 364 | 'index.android.bundle:29:955\n' + 365 | 'value@index.android.bundle:29:2417\n' + 366 | 'value@index.android.bundle:29:927\n' + 367 | '[native code]', 368 | }, 369 | 370 | IOS_REACT_NATIVE_1: { 371 | message: 'Error: from issue #11', 372 | stack: ` 373 | _exampleFunction@/home/test/project/App.js:125:13 374 | _depRunCallbacks@/home/test/project/node_modules/dep/index.js:77:45 375 | tryCallTwo@/home/test/project/node_modules/react-native/node_modules/promise/lib/core.js:45:5 376 | doResolve@/home/test/project/node_modules/react-native/node_modules/promise/lib/core.js:200:13 377 | `, 378 | }, 379 | 380 | IOS_REACT_NATIVE_2: { 381 | message: 382 | 'Error: from issue https://github.com/facebook/react-native/issues/24382#issuecomment-489404970', 383 | stack: 384 | 's@33.js:1:531\n' + 385 | 'b@1959.js:1:1469\n' + 386 | 'onSocketClose@2932.js:1:727\n' + 387 | 'value@81.js:1:1505\n' + 388 | '102.js:1:2956\n' + 389 | 'value@89.js:1:1247\n' + 390 | 'value@42.js:1:3311\n' + 391 | '42.js:1:822\n' + 392 | 'value@42.js:1:2565\n' + 393 | 'value@42.js:1:794\n' + 394 | 'value@[native code]', 395 | }, 396 | }; 397 | -------------------------------------------------------------------------------- /test/stack-trace-parser.spec.js: -------------------------------------------------------------------------------- 1 | import expect from 'expect.js'; 2 | import * as stackTraceParser from '../src'; 3 | import CapturedExceptions from './fixtures/captured-errors'; 4 | 5 | describe('stackTraceParser', () => { 6 | it('parses node error with space in path', () => { 7 | const stackFrames = stackTraceParser.parse( 8 | CapturedExceptions.NODE_SPACE.stack 9 | ); 10 | expect(stackFrames.length).to.be(9); 11 | expect(stackFrames).to.eql([ 12 | { 13 | file: 'C:\\project files\\spect\\src\\index.js', 14 | methodName: 'Spect.get', 15 | arguments: [], 16 | lineNumber: 161, 17 | column: 26, 18 | }, 19 | { 20 | file: 'C:\\project files\\spect\\src\\index.js', 21 | methodName: 'Object.get', 22 | arguments: [], 23 | lineNumber: 43, 24 | column: 36, 25 | }, 26 | { 27 | file: 'C:\\project files\\spect\\src\\index.js', 28 | methodName: '(anonymous function).then', 29 | arguments: [], 30 | lineNumber: 165, 31 | column: 33, 32 | }, 33 | { 34 | file: 'internal/process/task_queues.js', 35 | methodName: 'process.runNextTicks [as _tickCallback]', 36 | arguments: [], 37 | lineNumber: 52, 38 | column: 5, 39 | }, 40 | { 41 | file: 'C:\\project files\\spect\\node_modules\\esm\\esm.js', 42 | methodName: '', 43 | arguments: [], 44 | lineNumber: 1, 45 | column: 34535, 46 | }, 47 | { 48 | file: 'C:\\project files\\spect\\node_modules\\esm\\esm.js', 49 | methodName: '', 50 | arguments: [], 51 | lineNumber: 1, 52 | column: 34176, 53 | }, 54 | { 55 | file: 'C:\\project files\\spect\\node_modules\\esm\\esm.js', 56 | methodName: 'process.', 57 | arguments: [], 58 | lineNumber: 1, 59 | column: 34506, 60 | }, 61 | { 62 | file: 'C:\\project files\\spect\\node_modules\\esm\\esm.js', 63 | methodName: 'Function.', 64 | arguments: [], 65 | lineNumber: 1, 66 | column: 296856, 67 | }, 68 | { 69 | file: 'C:\\project files\\spect\\node_modules\\esm\\esm.js', 70 | methodName: 'Function.', 71 | arguments: [], 72 | lineNumber: 1, 73 | column: 296555, 74 | }, 75 | ]); 76 | }); 77 | it('parses JavaScriptCore errors', () => { 78 | const stackFrames = stackTraceParser.parse( 79 | CapturedExceptions.IOS_REACT_NATIVE_1.stack 80 | ); 81 | expect(stackFrames.length).to.be(4); 82 | expect(stackFrames).to.eql([ 83 | { 84 | file: '/home/test/project/App.js', 85 | methodName: '_exampleFunction', 86 | arguments: [], 87 | lineNumber: 125, 88 | column: 13, 89 | }, 90 | { 91 | file: '/home/test/project/node_modules/dep/index.js', 92 | methodName: '_depRunCallbacks', 93 | arguments: [], 94 | lineNumber: 77, 95 | column: 45, 96 | }, 97 | { 98 | file: 99 | '/home/test/project/node_modules/react-native/node_modules/promise/lib/core.js', 100 | methodName: 'tryCallTwo', 101 | arguments: [], 102 | lineNumber: 45, 103 | column: 5, 104 | }, 105 | { 106 | file: 107 | '/home/test/project/node_modules/react-native/node_modules/promise/lib/core.js', 108 | methodName: 'doResolve', 109 | arguments: [], 110 | lineNumber: 200, 111 | column: 13, 112 | }, 113 | ]); 114 | }); 115 | 116 | it('parses an error in react native', () => { 117 | const stackFrames = stackTraceParser.parse( 118 | CapturedExceptions.IOS_REACT_NATIVE_2.stack 119 | ); 120 | 121 | expect(stackFrames.length).to.be(11); 122 | expect(stackFrames).to.eql([ 123 | { 124 | file: '33.js', 125 | methodName: 's', 126 | arguments: [], 127 | lineNumber: 1, 128 | column: 531, 129 | }, 130 | { 131 | file: '1959.js', 132 | methodName: 'b', 133 | arguments: [], 134 | lineNumber: 1, 135 | column: 1469, 136 | }, 137 | { 138 | file: '2932.js', 139 | methodName: 'onSocketClose', 140 | arguments: [], 141 | lineNumber: 1, 142 | column: 727, 143 | }, 144 | { 145 | file: '81.js', 146 | methodName: 'value', 147 | arguments: [], 148 | lineNumber: 1, 149 | column: 1505, 150 | }, 151 | { 152 | file: '102.js', 153 | methodName: '', 154 | arguments: [], 155 | lineNumber: 1, 156 | column: 2956, 157 | }, 158 | { 159 | file: '89.js', 160 | methodName: 'value', 161 | arguments: [], 162 | lineNumber: 1, 163 | column: 1247, 164 | }, 165 | { 166 | file: '42.js', 167 | methodName: 'value', 168 | arguments: [], 169 | lineNumber: 1, 170 | column: 3311, 171 | }, 172 | { 173 | file: '42.js', 174 | methodName: '', 175 | arguments: [], 176 | lineNumber: 1, 177 | column: 822, 178 | }, 179 | { 180 | file: '42.js', 181 | methodName: 'value', 182 | arguments: [], 183 | lineNumber: 1, 184 | column: 2565, 185 | }, 186 | { 187 | file: '42.js', 188 | methodName: 'value', 189 | arguments: [], 190 | lineNumber: 1, 191 | column: 794, 192 | }, 193 | { 194 | file: '[native code]', 195 | methodName: 'value', 196 | arguments: [], 197 | lineNumber: null, 198 | column: null, 199 | }, 200 | ]); 201 | }); 202 | 203 | it('parses very simple JavaScriptCore errors', () => { 204 | const stackFrames = stackTraceParser.parse( 205 | 'global code@stack_traces/test:83:55' 206 | ); 207 | expect(stackFrames.length).to.be(1); 208 | expect(stackFrames).to.eql([ 209 | { 210 | file: 'stack_traces/test', 211 | methodName: 'global code', 212 | arguments: [], 213 | lineNumber: 83, 214 | column: 55, 215 | }, 216 | ]); 217 | }); 218 | 219 | it('parses Safari 6 error', () => { 220 | const stackFrames = stackTraceParser.parse( 221 | CapturedExceptions.SAFARI_6.stack 222 | ); 223 | expect(stackFrames.length).to.be(4); 224 | expect(stackFrames[0]).to.eql({ 225 | file: 'http://path/to/file.js', 226 | methodName: '', 227 | arguments: [], 228 | lineNumber: 48, 229 | column: null, 230 | }); 231 | expect(stackFrames[1]).to.eql({ 232 | file: 'http://path/to/file.js', 233 | methodName: 'dumpException3', 234 | arguments: [], 235 | lineNumber: 52, 236 | column: null, 237 | }); 238 | expect(stackFrames[2]).to.eql({ 239 | file: 'http://path/to/file.js', 240 | methodName: 'onclick', 241 | arguments: [], 242 | lineNumber: 82, 243 | column: null, 244 | }); 245 | expect(stackFrames[3]).to.eql({ 246 | file: '[native code]', 247 | methodName: '', 248 | arguments: [], 249 | lineNumber: null, 250 | column: null, 251 | }); 252 | }); 253 | 254 | it('parses Safari 7 error', () => { 255 | const stackFrames = stackTraceParser.parse( 256 | CapturedExceptions.SAFARI_7.stack 257 | ); 258 | expect(stackFrames.length).to.be(3); 259 | expect(stackFrames[0]).to.eql({ 260 | file: 'http://path/to/file.js', 261 | methodName: '', 262 | arguments: [], 263 | lineNumber: 48, 264 | column: 22, 265 | }); 266 | expect(stackFrames[1]).to.eql({ 267 | file: 'http://path/to/file.js', 268 | methodName: 'foo', 269 | arguments: [], 270 | lineNumber: 52, 271 | column: 15, 272 | }); 273 | expect(stackFrames[2]).to.eql({ 274 | file: 'http://path/to/file.js', 275 | methodName: 'bar', 276 | arguments: [], 277 | lineNumber: 108, 278 | column: 107, 279 | }); 280 | }); 281 | 282 | it('parses Safari 8 error', () => { 283 | const stackFrames = stackTraceParser.parse( 284 | CapturedExceptions.SAFARI_8.stack 285 | ); 286 | expect(stackFrames.length).to.be(3); 287 | expect(stackFrames[0]).to.eql({ 288 | file: 'http://path/to/file.js', 289 | methodName: '', 290 | arguments: [], 291 | lineNumber: 47, 292 | column: 22, 293 | }); 294 | expect(stackFrames[1]).to.eql({ 295 | file: 'http://path/to/file.js', 296 | methodName: 'foo', 297 | arguments: [], 298 | lineNumber: 52, 299 | column: 15, 300 | }); 301 | expect(stackFrames[2]).to.eql({ 302 | file: 'http://path/to/file.js', 303 | methodName: 'bar', 304 | arguments: [], 305 | lineNumber: 108, 306 | column: 23, 307 | }); 308 | }); 309 | 310 | it('parses Safari 8 eval error', () => { 311 | // TODO: Take into account the line and column properties on the error object and use them for the first stack trace. 312 | const stackFrames = stackTraceParser.parse( 313 | CapturedExceptions.SAFARI_8_EVAL.stack 314 | ); 315 | expect(stackFrames.length).to.be(3); 316 | expect(stackFrames[0]).to.eql({ 317 | file: '[native code]', 318 | methodName: 'eval', 319 | arguments: [], 320 | lineNumber: null, 321 | column: null, 322 | }); 323 | expect(stackFrames[1]).to.eql({ 324 | file: 'http://path/to/file.js', 325 | methodName: 'foo', 326 | arguments: [], 327 | lineNumber: 58, 328 | column: 21, 329 | }); 330 | expect(stackFrames[2]).to.eql({ 331 | file: 'http://path/to/file.js', 332 | methodName: 'bar', 333 | arguments: [], 334 | lineNumber: 109, 335 | column: 91, 336 | }); 337 | }); 338 | 339 | it('parses Firefox 3 error', () => { 340 | const stackFrames = stackTraceParser.parse( 341 | CapturedExceptions.FIREFOX_3.stack 342 | ); 343 | expect(stackFrames.length).to.be(7); 344 | expect(stackFrames[0]).to.eql({ 345 | file: 'http://127.0.0.1:8000/js/stacktrace.js', 346 | methodName: '', 347 | arguments: [], 348 | lineNumber: 44, 349 | column: null, 350 | }); 351 | expect(stackFrames[1]).to.eql({ 352 | file: 'http://127.0.0.1:8000/js/stacktrace.js', 353 | methodName: '', 354 | arguments: ['null'], 355 | lineNumber: 31, 356 | column: null, 357 | }); 358 | expect(stackFrames[2]).to.eql({ 359 | file: 'http://127.0.0.1:8000/js/stacktrace.js', 360 | methodName: 'printStackTrace', 361 | arguments: [], 362 | lineNumber: 18, 363 | column: null, 364 | }); 365 | expect(stackFrames[3]).to.eql({ 366 | file: 'http://127.0.0.1:8000/js/file.js', 367 | methodName: 'bar', 368 | arguments: ['1'], 369 | lineNumber: 13, 370 | column: null, 371 | }); 372 | expect(stackFrames[4]).to.eql({ 373 | file: 'http://127.0.0.1:8000/js/file.js', 374 | methodName: 'bar', 375 | arguments: ['2'], 376 | lineNumber: 16, 377 | column: null, 378 | }); 379 | expect(stackFrames[5]).to.eql({ 380 | file: 'http://127.0.0.1:8000/js/file.js', 381 | methodName: 'foo', 382 | arguments: [], 383 | lineNumber: 20, 384 | column: null, 385 | }); 386 | expect(stackFrames[6]).to.eql({ 387 | file: 'http://127.0.0.1:8000/js/file.js', 388 | methodName: '', 389 | arguments: [], 390 | lineNumber: 24, 391 | column: null, 392 | }); 393 | }); 394 | 395 | it('parses Firefox 7 error', () => { 396 | const stackFrames = stackTraceParser.parse( 397 | CapturedExceptions.FIREFOX_7.stack 398 | ); 399 | expect(stackFrames.length).to.be(7); 400 | expect(stackFrames[0]).to.eql({ 401 | file: 'file:///G:/js/stacktrace.js', 402 | methodName: '', 403 | arguments: [], 404 | lineNumber: 44, 405 | column: null, 406 | }); 407 | expect(stackFrames[1]).to.eql({ 408 | file: 'file:///G:/js/stacktrace.js', 409 | methodName: '', 410 | arguments: ['null'], 411 | lineNumber: 31, 412 | column: null, 413 | }); 414 | expect(stackFrames[2]).to.eql({ 415 | file: 'file:///G:/js/stacktrace.js', 416 | methodName: 'printStackTrace', 417 | arguments: [], 418 | lineNumber: 18, 419 | column: null, 420 | }); 421 | expect(stackFrames[3]).to.eql({ 422 | file: 'file:///G:/js/file.js', 423 | methodName: 'bar', 424 | arguments: ['1'], 425 | lineNumber: 13, 426 | column: null, 427 | }); 428 | expect(stackFrames[4]).to.eql({ 429 | file: 'file:///G:/js/file.js', 430 | methodName: 'bar', 431 | arguments: ['2'], 432 | lineNumber: 16, 433 | column: null, 434 | }); 435 | expect(stackFrames[5]).to.eql({ 436 | file: 'file:///G:/js/file.js', 437 | methodName: 'foo', 438 | arguments: [], 439 | lineNumber: 20, 440 | column: null, 441 | }); 442 | expect(stackFrames[6]).to.eql({ 443 | file: 'file:///G:/js/file.js', 444 | methodName: '', 445 | arguments: [], 446 | lineNumber: 24, 447 | column: null, 448 | }); 449 | }); 450 | 451 | it('parses Firefox 14 error', () => { 452 | const stackFrames = stackTraceParser.parse( 453 | CapturedExceptions.FIREFOX_14.stack 454 | ); 455 | expect(stackFrames.length).to.be(3); 456 | expect(stackFrames[0]).to.eql({ 457 | file: 'http://path/to/file.js', 458 | methodName: '', 459 | arguments: [], 460 | lineNumber: 48, 461 | column: null, 462 | }); 463 | expect(stackFrames[1]).to.eql({ 464 | file: 'http://path/to/file.js', 465 | methodName: 'dumpException3', 466 | arguments: [], 467 | lineNumber: 52, 468 | column: null, 469 | }); 470 | expect(stackFrames[2]).to.eql({ 471 | file: 'http://path/to/file.js', 472 | methodName: 'onclick', 473 | arguments: [], 474 | lineNumber: 1, 475 | column: null, 476 | }); 477 | }); 478 | 479 | it('parses Firefox 31 error', () => { 480 | const stackFrames = stackTraceParser.parse( 481 | CapturedExceptions.FIREFOX_31.stack 482 | ); 483 | expect(stackFrames.length).to.be(3); 484 | expect(stackFrames[0]).to.eql({ 485 | file: 'http://path/to/file.js', 486 | methodName: 'foo', 487 | arguments: [], 488 | lineNumber: 41, 489 | column: 13, 490 | }); 491 | expect(stackFrames[1]).to.eql({ 492 | file: 'http://path/to/file.js', 493 | methodName: 'bar', 494 | arguments: [], 495 | lineNumber: 1, 496 | column: 1, 497 | }); 498 | expect(stackFrames[2]).to.eql({ 499 | file: 'http://path/to/file.js', 500 | methodName: '.plugin/e.fn[c]/<', 501 | arguments: [], 502 | lineNumber: 1, 503 | column: 1, 504 | }); 505 | }); 506 | 507 | it('parses Firefox 44 ns exceptions', () => { 508 | const stackFrames = stackTraceParser.parse( 509 | CapturedExceptions.FIREFOX_44_NS_EXCEPTION.stack 510 | ); 511 | expect(stackFrames.length).to.be(4); 512 | expect(stackFrames[0]).to.eql({ 513 | file: 'http://path/to/file.js', 514 | methodName: '[2]', 536 | arguments: [], 537 | lineNumber: 23, 538 | column: 1, 539 | }); 540 | }); 541 | 542 | it('parses Chrome error with no location', () => { 543 | const stackFrames = stackTraceParser.parse( 544 | 'error\n at Array.forEach (native)' 545 | ); 546 | expect(stackFrames.length).to.be(1); 547 | expect(stackFrames[0]).to.eql({ 548 | file: null, 549 | methodName: 'Array.forEach', 550 | arguments: ['native'], 551 | lineNumber: null, 552 | column: null, 553 | }); 554 | }); 555 | 556 | it('parses Chrome 15 error', () => { 557 | const stackFrames = stackTraceParser.parse( 558 | CapturedExceptions.CHROME_15.stack 559 | ); 560 | expect(stackFrames.length).to.be(4); 561 | expect(stackFrames[0]).to.eql({ 562 | file: 'http://path/to/file.js', 563 | methodName: 'bar', 564 | arguments: [], 565 | lineNumber: 13, 566 | column: 17, 567 | }); 568 | expect(stackFrames[1]).to.eql({ 569 | file: 'http://path/to/file.js', 570 | methodName: 'bar', 571 | arguments: [], 572 | lineNumber: 16, 573 | column: 5, 574 | }); 575 | expect(stackFrames[2]).to.eql({ 576 | file: 'http://path/to/file.js', 577 | methodName: 'foo', 578 | arguments: [], 579 | lineNumber: 20, 580 | column: 5, 581 | }); 582 | expect(stackFrames[3]).to.eql({ 583 | file: 'http://path/to/file.js', 584 | methodName: '', 585 | arguments: [], 586 | lineNumber: 24, 587 | column: 4, 588 | }); 589 | }); 590 | 591 | it('parses Chrome 36 error with port numbers', () => { 592 | const stackFrames = stackTraceParser.parse( 593 | CapturedExceptions.CHROME_36.stack 594 | ); 595 | expect(stackFrames.length).to.be(3); 596 | expect(stackFrames[0]).to.eql({ 597 | file: 'http://localhost:8080/file.js', 598 | methodName: 'dumpExceptionError', 599 | arguments: [], 600 | lineNumber: 41, 601 | column: 27, 602 | }); 603 | expect(stackFrames[1]).to.eql({ 604 | file: 'http://localhost:8080/file.js', 605 | methodName: 'HTMLButtonElement.onclick', 606 | arguments: [], 607 | lineNumber: 107, 608 | column: 146, 609 | }); 610 | expect(stackFrames[2]).to.eql({ 611 | file: 'http://localhost:8080/file.js', 612 | methodName: 'I.e.fn.(anonymous function) [as index]', 613 | arguments: [], 614 | lineNumber: 10, 615 | column: 3651, 616 | }); 617 | }); 618 | 619 | it('parses Chrome 76 error with async support', () => { 620 | const stackFrames = stackTraceParser.parse( 621 | CapturedExceptions.CHROME_76.stack 622 | ); 623 | expect(stackFrames.length).to.be(2); 624 | expect(stackFrames[0]).to.eql({ 625 | file: '', 626 | methodName: 'bar', 627 | arguments: [], 628 | lineNumber: 8, 629 | column: 9, 630 | }); 631 | expect(stackFrames[1]).to.eql({ 632 | file: '', 633 | methodName: 'async foo', 634 | arguments: [], 635 | lineNumber: 2, 636 | column: 3, 637 | }); 638 | }); 639 | 640 | it('parses Chrome error with webpack URLs', () => { 641 | const stackFrames = stackTraceParser.parse( 642 | CapturedExceptions.CHROME_XX_WEBPACK.stack 643 | ); 644 | expect(stackFrames.length).to.be(5); 645 | expect(stackFrames[0]).to.eql({ 646 | file: 'webpack:///./src/components/test/test.jsx?', 647 | methodName: 'TESTTESTTEST.eval', 648 | arguments: [], 649 | lineNumber: 295, 650 | column: 108, 651 | }); 652 | expect(stackFrames[1]).to.eql({ 653 | file: 'webpack:///./src/components/test/test.jsx?', 654 | methodName: 'TESTTESTTEST.render', 655 | arguments: [], 656 | lineNumber: 272, 657 | column: 32, 658 | }); 659 | expect(stackFrames[2]).to.eql({ 660 | file: 'webpack:///./~/react-transform-catch-errors/lib/index.js?', 661 | methodName: 'TESTTESTTEST.tryRender', 662 | arguments: [], 663 | lineNumber: 34, 664 | column: 31, 665 | }); 666 | expect(stackFrames[3]).to.eql({ 667 | file: 'webpack:///./~/react-proxy/modules/createPrototypeProxy.js?', 668 | methodName: 'TESTTESTTEST.proxiedMethod', 669 | arguments: [], 670 | lineNumber: 44, 671 | column: 30, 672 | }); 673 | expect(stackFrames[4]).to.eql({ 674 | file: 'C:\\root\\server\\development\\pages\\index.js', 675 | methodName: 'Module../pages/index.js', 676 | arguments: [], 677 | lineNumber: 182, 678 | column: 7, 679 | }); 680 | }); 681 | 682 | it('parses nested eval() from Chrome', () => { 683 | const stackFrames = stackTraceParser.parse( 684 | CapturedExceptions.CHROME_48_EVAL.stack 685 | ); 686 | expect(stackFrames.length).to.be(5); 687 | expect(stackFrames[0]).to.eql({ 688 | file: 'http://localhost:8080/file.js', 689 | methodName: 'baz', 690 | arguments: [], 691 | lineNumber: 21, 692 | column: 17, 693 | }); 694 | expect(stackFrames[1]).to.eql({ 695 | file: 'http://localhost:8080/file.js', 696 | methodName: 'foo', 697 | arguments: [], 698 | lineNumber: 21, 699 | column: 17, 700 | }); 701 | expect(stackFrames[2]).to.eql({ 702 | file: 'http://localhost:8080/file.js', 703 | methodName: 'eval', 704 | arguments: [], 705 | lineNumber: 21, 706 | column: 17, 707 | }); 708 | expect(stackFrames[3]).to.eql({ 709 | file: 'http://localhost:8080/file.js', 710 | methodName: 'Object.speak', 711 | arguments: [], 712 | lineNumber: 21, 713 | column: 17, 714 | }); 715 | expect(stackFrames[4]).to.eql({ 716 | file: 'http://localhost:8080/file.js', 717 | methodName: '', 718 | arguments: [], 719 | lineNumber: 31, 720 | column: 13, 721 | }); 722 | }); 723 | 724 | it('parses Chrome error with blob URLs', () => { 725 | const stackFrames = stackTraceParser.parse( 726 | CapturedExceptions.CHROME_48_BLOB.stack 727 | ); 728 | expect(stackFrames.length).to.be(7); 729 | expect(stackFrames[1]).to.eql({ 730 | file: 731 | 'blob:http%3A//localhost%3A8080/abfc40e9-4742-44ed-9dcd-af8f99a29379', 732 | methodName: 's', 733 | arguments: [], 734 | lineNumber: 31, 735 | column: 29146, 736 | }); 737 | expect(stackFrames[2]).to.eql({ 738 | file: 739 | 'blob:http%3A//localhost%3A8080/abfc40e9-4742-44ed-9dcd-af8f99a29379', 740 | methodName: 'Object.d [as add]', 741 | arguments: [], 742 | lineNumber: 31, 743 | column: 30039, 744 | }); 745 | expect(stackFrames[3]).to.eql({ 746 | file: 747 | 'blob:http%3A//localhost%3A8080/d4eefe0f-361a-4682-b217-76587d9f712a', 748 | methodName: '', 749 | arguments: [], 750 | lineNumber: 15, 751 | column: 10978, 752 | }); 753 | expect(stackFrames[4]).to.eql({ 754 | file: 755 | 'blob:http%3A//localhost%3A8080/abfc40e9-4742-44ed-9dcd-af8f99a29379', 756 | methodName: '', 757 | arguments: [], 758 | lineNumber: 1, 759 | column: 6911, 760 | }); 761 | expect(stackFrames[5]).to.eql({ 762 | file: 763 | 'blob:http%3A//localhost%3A8080/abfc40e9-4742-44ed-9dcd-af8f99a29379', 764 | methodName: 'n.fire', 765 | arguments: [], 766 | lineNumber: 7, 767 | column: 3019, 768 | }); 769 | expect(stackFrames[6]).to.eql({ 770 | file: 771 | 'blob:http%3A//localhost%3A8080/abfc40e9-4742-44ed-9dcd-af8f99a29379', 772 | methodName: 'n.handle', 773 | arguments: [], 774 | lineNumber: 7, 775 | column: 2863, 776 | }); 777 | }); 778 | 779 | it('parses IE 10 error', () => { 780 | const stackFrames = stackTraceParser.parse(CapturedExceptions.IE_10.stack); 781 | expect(stackFrames.length).to.be(3); 782 | // TODO: func should be normalized 783 | expect(stackFrames[0]).to.eql({ 784 | file: 'http://path/to/file.js', 785 | methodName: 'Anonymous function', 786 | arguments: [], 787 | lineNumber: 48, 788 | column: 13, 789 | }); 790 | expect(stackFrames[1]).to.eql({ 791 | file: 'http://path/to/file.js', 792 | methodName: 'foo', 793 | arguments: [], 794 | lineNumber: 46, 795 | column: 9, 796 | }); 797 | expect(stackFrames[2]).to.eql({ 798 | file: 'http://path/to/file.js', 799 | methodName: 'bar', 800 | arguments: [], 801 | lineNumber: 82, 802 | column: 1, 803 | }); 804 | }); 805 | 806 | it('parses IE 11 error', () => { 807 | const stackFrames = stackTraceParser.parse(CapturedExceptions.IE_11.stack); 808 | expect(stackFrames.length).to.be(3); 809 | // TODO: func should be normalized 810 | expect(stackFrames[0]).to.eql({ 811 | file: 'http://path/to/file.js', 812 | methodName: 'Anonymous function', 813 | arguments: [], 814 | lineNumber: 47, 815 | column: 21, 816 | }); 817 | expect(stackFrames[1]).to.eql({ 818 | file: 'http://path/to/file.js', 819 | methodName: 'foo', 820 | arguments: [], 821 | lineNumber: 45, 822 | column: 13, 823 | }); 824 | expect(stackFrames[2]).to.eql({ 825 | file: 'http://path/to/file.js', 826 | methodName: 'bar', 827 | arguments: [], 828 | lineNumber: 108, 829 | column: 1, 830 | }); 831 | }); 832 | 833 | it('parses IE 11 eval error', () => { 834 | const stackFrames = stackTraceParser.parse( 835 | CapturedExceptions.IE_11_EVAL.stack 836 | ); 837 | expect(stackFrames.length).to.be(3); 838 | expect(stackFrames[0]).to.eql({ 839 | file: 'eval code', 840 | methodName: 'eval code', 841 | arguments: [], 842 | lineNumber: 1, 843 | column: 1, 844 | }); 845 | expect(stackFrames[1]).to.eql({ 846 | file: 'http://path/to/file.js', 847 | methodName: 'foo', 848 | arguments: [], 849 | lineNumber: 58, 850 | column: 17, 851 | }); 852 | expect(stackFrames[2]).to.eql({ 853 | file: 'http://path/to/file.js', 854 | methodName: 'bar', 855 | arguments: [], 856 | lineNumber: 109, 857 | column: 1, 858 | }); 859 | }); 860 | 861 | it('parses Opera 25 error', () => { 862 | const stackFrames = stackTraceParser.parse( 863 | CapturedExceptions.OPERA_25.stack 864 | ); 865 | expect(stackFrames.length).to.be(3); 866 | expect(stackFrames[0]).to.eql({ 867 | file: 'http://path/to/file.js', 868 | methodName: '', 869 | arguments: [], 870 | lineNumber: 47, 871 | column: 22, 872 | }); 873 | expect(stackFrames[1]).to.eql({ 874 | file: 'http://path/to/file.js', 875 | methodName: 'foo', 876 | arguments: [], 877 | lineNumber: 52, 878 | column: 15, 879 | }); 880 | expect(stackFrames[2]).to.eql({ 881 | file: 'http://path/to/file.js', 882 | methodName: 'bar', 883 | arguments: [], 884 | lineNumber: 108, 885 | column: 168, 886 | }); 887 | }); 888 | 889 | it('parses PhantomJS 1.19 error', () => { 890 | const stackFrames = stackTraceParser.parse( 891 | CapturedExceptions.PHANTOMJS_1_19.stack 892 | ); 893 | expect(stackFrames.length).to.be(3); 894 | expect(stackFrames[0]).to.eql({ 895 | file: 'file:///path/to/file.js', 896 | methodName: '', 897 | arguments: [], 898 | lineNumber: 878, 899 | column: null, 900 | }); 901 | expect(stackFrames[1]).to.eql({ 902 | file: 'http://path/to/file.js', 903 | methodName: 'foo', 904 | arguments: [], 905 | lineNumber: 4283, 906 | column: null, 907 | }); 908 | expect(stackFrames[2]).to.eql({ 909 | file: 'http://path/to/file.js', 910 | methodName: '', 911 | arguments: [], 912 | lineNumber: 4287, 913 | column: null, 914 | }); 915 | }); 916 | 917 | it('parses Firefox errors with resource: URLs', () => { 918 | const stackFrames = stackTraceParser.parse( 919 | CapturedExceptions.FIREFOX_50_RESOURCE_URL.stack 920 | ); 921 | expect(stackFrames.length).to.be(3); 922 | expect(stackFrames[0]).to.eql({ 923 | file: 'resource://path/data/content/bundle.js', 924 | methodName: 'render', 925 | arguments: [], 926 | lineNumber: 5529, 927 | column: 16, 928 | }); 929 | }); 930 | 931 | it('parses Firefox errors with eval URLs', () => { 932 | const stackFrames = stackTraceParser.parse( 933 | CapturedExceptions.FIREFOX_43_EVAL.stack 934 | ); 935 | expect(stackFrames.length).to.be(5); 936 | expect(stackFrames[0]).to.eql({ 937 | file: 'http://localhost:8080/file.js', 938 | methodName: 'baz', 939 | arguments: [], 940 | lineNumber: 26, 941 | column: null, 942 | }); 943 | expect(stackFrames[1]).to.eql({ 944 | file: 'http://localhost:8080/file.js', 945 | methodName: 'foo', 946 | arguments: [], 947 | lineNumber: 26, 948 | column: null, 949 | }); 950 | expect(stackFrames[2]).to.eql({ 951 | file: 'http://localhost:8080/file.js', 952 | methodName: '', 953 | arguments: [], 954 | lineNumber: 26, 955 | column: null, 956 | }); 957 | expect(stackFrames[3]).to.eql({ 958 | file: 'http://localhost:8080/file.js', 959 | methodName: 'speak', 960 | arguments: [], 961 | lineNumber: 26, 962 | column: 17, 963 | }); 964 | expect(stackFrames[4]).to.eql({ 965 | file: 'http://localhost:8080/file.js', 966 | methodName: '', 967 | arguments: [], 968 | lineNumber: 33, 969 | column: 9, 970 | }); 971 | }); 972 | 973 | it('parses React Native errors on Android', () => { 974 | const stackFrames = stackTraceParser.parse( 975 | CapturedExceptions.ANDROID_REACT_NATIVE.stack 976 | ); 977 | expect(stackFrames.length).to.be(8); 978 | expect(stackFrames[0]).to.eql({ 979 | file: 980 | '/home/username/sample-workspace/sampleapp.collect.react/src/components/GpsMonitorScene.js', 981 | methodName: 'render', 982 | arguments: [], 983 | lineNumber: 78, 984 | column: 24, 985 | }); 986 | expect(stackFrames[7]).to.eql({ 987 | file: 988 | '/home/username/sample-workspace/sampleapp.collect.react/node_modules/react-native/Libraries/Renderer/src/renderers/native/ReactNativeBaseComponent.js', 989 | methodName: 'this', 990 | arguments: [], 991 | lineNumber: 74, 992 | column: 41, 993 | }); 994 | }); 995 | 996 | it('parses React Native errors on Android Production', () => { 997 | const stackFrames = stackTraceParser.parse( 998 | CapturedExceptions.ANDROID_REACT_NATIVE_PROD.stack 999 | ); 1000 | expect(stackFrames.length).to.be(37); 1001 | expect(stackFrames[0]).to.eql({ 1002 | file: 'index.android.bundle', 1003 | methodName: 'value', 1004 | arguments: [], 1005 | lineNumber: 12, 1006 | column: 1917, 1007 | }); 1008 | expect(stackFrames[35]).to.eql({ 1009 | file: 'index.android.bundle', 1010 | methodName: 'value', 1011 | arguments: [], 1012 | lineNumber: 29, 1013 | column: 927, 1014 | }); 1015 | expect(stackFrames[36]).to.eql({ 1016 | file: '[native code]', 1017 | methodName: '', 1018 | arguments: [], 1019 | lineNumber: null, 1020 | column: null, 1021 | }); 1022 | }); 1023 | 1024 | it('parses node.js async errors available with version 12', () => { 1025 | const stackFrames = stackTraceParser.parse( 1026 | CapturedExceptions.NODE_12.stack 1027 | ); 1028 | expect(stackFrames.length).to.be(2); 1029 | expect(stackFrames[0]).to.eql({ 1030 | file: '/home/xyz/hack/asyncnode.js', 1031 | methodName: 'promiseMe', 1032 | arguments: [], 1033 | lineNumber: 11, 1034 | column: 9, 1035 | }); 1036 | expect(stackFrames[1]).to.eql({ 1037 | file: '/home/xyz/hack/asyncnode.js', 1038 | methodName: 'async main', 1039 | arguments: [], 1040 | lineNumber: 15, 1041 | column: 13, 1042 | }); 1043 | }); 1044 | 1045 | it('parses node.js errors with calls as well', () => { 1046 | const stackFrames = stackTraceParser.parse( 1047 | CapturedExceptions.NODE_ANONYM.stack 1048 | ); 1049 | // console.log(stackFrames); 1050 | expect(stackFrames.length).to.be(9); 1051 | expect(stackFrames[0]).to.eql({ 1052 | file: 'C:\\projects\\spect\\src\\index.js', 1053 | methodName: 'Spect.get', 1054 | arguments: [], 1055 | lineNumber: 161, 1056 | column: 26, 1057 | }); 1058 | expect(stackFrames[2]).to.eql({ 1059 | file: 'C:\\projects\\spect\\src\\index.js', 1060 | methodName: '(anonymous function).then', 1061 | arguments: [], 1062 | lineNumber: 165, 1063 | column: 33, 1064 | }); 1065 | expect(stackFrames[4]).to.eql({ 1066 | file: 'C:\\projects\\spect\\node_modules\\esm\\esm.js', 1067 | methodName: '', 1068 | arguments: [], 1069 | lineNumber: 1, 1070 | column: 34535, 1071 | }); 1072 | expect(stackFrames[6]).to.eql({ 1073 | file: 'C:\\projects\\spect\\node_modules\\esm\\esm.js', 1074 | methodName: 'process.', 1075 | arguments: [], 1076 | lineNumber: 1, 1077 | column: 34506, 1078 | }); 1079 | }); 1080 | 1081 | const data = { 1082 | 'Anonymous sources': [ 1083 | { 1084 | from: `x 1085 | at new (http://www.example.com/test.js:2:1 1086 | at :1:2`, 1087 | to: [ 1088 | { 1089 | file: 'http://www.example.com/test.js', 1090 | methodName: 'new ', 1091 | arguments: [], 1092 | lineNumber: 2, 1093 | column: 1, 1094 | }, 1095 | { 1096 | file: '', 1097 | methodName: '', 1098 | arguments: [], 1099 | lineNumber: 1, 1100 | column: 2, 1101 | }, 1102 | ], 1103 | }, 1104 | ], 1105 | 'Node.js': [ 1106 | { 1107 | from: `ReferenceError: test is not defined 1108 | at repl:1:2 1109 | at REPLServer.self.eval (repl.js:110:21) 1110 | at Interface. (repl.js:239:12) 1111 | at Interface.EventEmitter.emit (events.js:95:17) 1112 | at emitKey (readline.js:1095:12)`, 1113 | to: [ 1114 | { 1115 | file: 'repl', 1116 | methodName: '', 1117 | arguments: [], 1118 | lineNumber: 1, 1119 | column: 2, 1120 | }, 1121 | { 1122 | file: 'repl.js', 1123 | methodName: 'REPLServer.self.eval', 1124 | arguments: [], 1125 | lineNumber: 110, 1126 | column: 21, 1127 | }, 1128 | { 1129 | file: 'repl.js', 1130 | methodName: 'Interface.', 1131 | arguments: [], 1132 | lineNumber: 239, 1133 | column: 12, 1134 | }, 1135 | { 1136 | file: 'events.js', 1137 | methodName: 'Interface.EventEmitter.emit', 1138 | arguments: [], 1139 | lineNumber: 95, 1140 | column: 17, 1141 | }, 1142 | { 1143 | file: 'readline.js', 1144 | methodName: 'emitKey', 1145 | arguments: [], 1146 | lineNumber: 1095, 1147 | column: 12, 1148 | }, 1149 | ], 1150 | }, 1151 | { 1152 | from: `ReferenceError: breakDown is not defined 1153 | at null._onTimeout (repl:1:25) 1154 | at Timer.listOnTimeout [as ontimeout] (timers.js:110:15)`, 1155 | to: [ 1156 | { 1157 | file: 'repl', 1158 | methodName: 'null._onTimeout', 1159 | arguments: [], 1160 | lineNumber: 1, 1161 | column: 25, 1162 | }, 1163 | { 1164 | file: 'timers.js', 1165 | methodName: 'Timer.listOnTimeout [as ontimeout]', 1166 | arguments: [], 1167 | lineNumber: 110, 1168 | column: 15, 1169 | }, 1170 | ], 1171 | }, 1172 | ], 1173 | 'io.js': [ 1174 | // io.js 2.4.0 1175 | { 1176 | from: `ReferenceError: test is not defined 1177 | at repl:1:1 1178 | at REPLServer.defaultEval (repl.js:154:27) 1179 | at bound (domain.js:254:14) 1180 | at REPLServer.runBound [as eval] (domain.js:267:12) 1181 | at REPLServer. (repl.js:308:12) 1182 | at emitOne (events.js:77:13) 1183 | at REPLServer.emit (events.js:169:7) 1184 | at REPLServer.Interface._onLine (readline.js:210:10) 1185 | at REPLServer.Interface._line (readline.js:549:8) 1186 | at REPLServer.Interface._ttyWrite (readline.js:826:14)`, 1187 | to: [ 1188 | { 1189 | file: 'repl', 1190 | methodName: '', 1191 | arguments: [], 1192 | lineNumber: 1, 1193 | column: 1, 1194 | }, 1195 | { 1196 | file: 'repl.js', 1197 | methodName: 'REPLServer.defaultEval', 1198 | arguments: [], 1199 | lineNumber: 154, 1200 | column: 27, 1201 | }, 1202 | { 1203 | file: 'domain.js', 1204 | methodName: 'bound', 1205 | arguments: [], 1206 | lineNumber: 254, 1207 | column: 14, 1208 | }, 1209 | { 1210 | file: 'domain.js', 1211 | methodName: 'REPLServer.runBound [as eval]', 1212 | arguments: [], 1213 | lineNumber: 267, 1214 | column: 12, 1215 | }, 1216 | { 1217 | file: 'repl.js', 1218 | methodName: 'REPLServer.', 1219 | arguments: [], 1220 | lineNumber: 308, 1221 | column: 12, 1222 | }, 1223 | { 1224 | file: 'events.js', 1225 | methodName: 'emitOne', 1226 | arguments: [], 1227 | lineNumber: 77, 1228 | column: 13, 1229 | }, 1230 | { 1231 | file: 'events.js', 1232 | methodName: 'REPLServer.emit', 1233 | arguments: [], 1234 | lineNumber: 169, 1235 | column: 7, 1236 | }, 1237 | { 1238 | file: 'readline.js', 1239 | methodName: 'REPLServer.Interface._onLine', 1240 | arguments: [], 1241 | lineNumber: 210, 1242 | column: 10, 1243 | }, 1244 | { 1245 | file: 'readline.js', 1246 | methodName: 'REPLServer.Interface._line', 1247 | arguments: [], 1248 | lineNumber: 549, 1249 | column: 8, 1250 | }, 1251 | { 1252 | file: 'readline.js', 1253 | methodName: 'REPLServer.Interface._ttyWrite', 1254 | arguments: [], 1255 | lineNumber: 826, 1256 | column: 14, 1257 | }, 1258 | ], 1259 | }, 1260 | ], 1261 | }; 1262 | 1263 | Object.keys(data).forEach(browser => { 1264 | describe('can parse stack trace of ' + browser, () => { 1265 | const browserTests = data[browser]; 1266 | 1267 | browserTests.forEach(browserTest => { 1268 | it(browserTest.from, () => { 1269 | const result = stackTraceParser.parse(browserTest.from); 1270 | 1271 | expect(result.length).to.equal(browserTest.to.length); 1272 | expect(result).to.eql(browserTest.to); 1273 | }); 1274 | }); 1275 | }); 1276 | }); 1277 | }); 1278 | --------------------------------------------------------------------------------