From 547acd64e3d95925d80d5eec7f679f401cff6d67 Mon Sep 17 00:00:00 2001 From: James Talmage Date: Fri, 8 Jan 2016 20:36:20 -0500 Subject: [PATCH] write some long stack trace tests --- index.js | 40 +++++++++-- package.json | 8 +-- test/_utils.js | 10 +++ test/long-stack-traces.js | 138 ++++++++++++++++++++++++++++++++++++++ test/test.js | 8 +-- 5 files changed, 184 insertions(+), 20 deletions(-) create mode 100644 test/_utils.js create mode 100644 test/long-stack-traces.js diff --git a/index.js b/index.js index 2caf0d5..f97cd87 100644 --- a/index.js +++ b/index.js @@ -34,7 +34,12 @@ StackUtils.prototype.clean = function (stack) { stack = stack.slice(1); } - stack = stack.map(function (st) { + var outdent = false; + var lastNonAtLine = null; + var result = []; + + stack.forEach(function (st1) { + var st = st1; var isInternal = this._internals.some(function (internal) { return internal.test(st); }); @@ -43,13 +48,36 @@ StackUtils.prototype.clean = function (stack) { return null; } - return st.trim() - .replace(/^\s*at /, '') + var isAtLine = /^\s*at /.test(st); + + if (outdent) { + st = st.replace(/\s+$/, '').replace(/^(\s+)at /, '$1'); + } else { + st = st.trim(); + if (isAtLine) { + st = st.substring(3); + } + } + + st = st .replace(/\\/g, '/') .replace(this._cwd + '/', ''); - }, this).filter(function (st) { - return st; - }).join('\n').trim(); + + if (st) { + if (isAtLine) { + if (lastNonAtLine) { + result.push(lastNonAtLine); + lastNonAtLine = null; + } + result.push(st); + } else { + outdent = true; + lastNonAtLine = st; + } + } + }, this); + + stack = result.join('\n').trim(); if (stack) { return stack + '\n'; diff --git a/package.json b/package.json index 29d5ab7..d0273ee 100644 --- a/package.json +++ b/package.json @@ -22,13 +22,6 @@ "keywords": [ "" ], - "config": { - "nyc": { - "exclude": [ - "test/fixtures/*" - ] - } - }, "dependencies": {}, "devDependencies": { "ava": "^0.8.0", @@ -37,6 +30,7 @@ "flatten": "0.0.1", "nested-error-stacks": "^1.0.2", "nyc": "^5.2.0", + "pify": "^2.3.0", "q": "^1.4.1", "xo": "^0.12.1" } diff --git a/test/_utils.js b/test/_utils.js new file mode 100644 index 0000000..e9e3ff7 --- /dev/null +++ b/test/_utils.js @@ -0,0 +1,10 @@ +var flatten = require('flatten'); +var path = require('path'); + +module.exports.join = join; +module.exports.fixtureDir = path.join(__dirname, 'fixtures'); + +function join() { + var args = Array.prototype.slice.call(arguments); + return flatten(args).join('\n') + '\n'; +} diff --git a/test/long-stack-traces.js b/test/long-stack-traces.js new file mode 100644 index 0000000..953f507 --- /dev/null +++ b/test/long-stack-traces.js @@ -0,0 +1,138 @@ +import test from 'ava'; +import StackUtils from '../'; +import longStackTraces from './fixtures/long-stack-traces'; +import pify from 'pify'; +const nestedErrors = pify(require('./fixtures/nested-errors'), Promise); + +import {join, fixtureDir} from './_utils'; + +function internals() { + return StackUtils.nodeInternals().concat([ + /[\\\/]long-stack-traces\.js:[0-9]+:[0-9]+\)?$/, + /[\\\/]internal-error\.js:[0-9]+:[0-9]+\)?$/, + /[\\\/]internal-then\.js:[0-9]+:[0-9]+\)?$/, + /[\\\/]node_modules[\\\/]/, + // TODO: Should any of these be default internals? + /[\\\/]\.node-spawn-wrap-\w+-\w+[\\\/]node:[0-9]+:[0-9]+\)?$/, + /internal[\\\/]module\.js:[0-9]+:[0-9]+\)?$/, + /node\.js:[0-9]+:[0-9]+\)?$/ + ]); +} + +const stackUtils = new StackUtils({internals: internals(), cwd: fixtureDir}); + +test('outdents lines after first "From previous event:"', async t => { + const cleanedStack = stackUtils.clean(await longStackTraces.bluebird); + const expected = join([ + 'mostInner (produce-long-stack-traces.js:10:5)', + 'From previous event:', + ' evenMoreInner (produce-long-stack-traces.js:9:29)', + 'From previous event:', + ' inner (produce-long-stack-traces.js:8:28)', + 'From previous event:', + ' outer (produce-long-stack-traces.js:7:27)', + 'From previous event:', + ' Object. (produce-long-stack-traces.js:6:36)' + ]); + + t.is(cleanedStack, expected); +}); + +test('removes empty "From previous event:" sections from the bottom', async t => { + const stack = await longStackTraces.bluebird.bottom; + const cleanedStack = stackUtils.clean(stack); + + const expected = join([ + 'mostInner (produce-long-stack-traces.js:43:6)', + 'From previous event:', + ' evenMoreInner (produce-long-stack-traces.js:42:30)', + 'From previous event:', + ' inner (produce-long-stack-traces.js:41:29)', + 'From previous event:', + ' outer (produce-long-stack-traces.js:40:28)' + ]); + + t.is(cleanedStack, expected); +}); + +test('removes empty "From previous event:" sections from the top', async t => { + const stack = await longStackTraces.bluebird.top; + const cleanedStack = stackUtils.clean(stack); + + const expected = join([ + 'From previous event:', + ' evenMoreInner (produce-long-stack-traces.js:33:29)', + 'From previous event:', + ' inner (produce-long-stack-traces.js:32:28)', + 'From previous event:', + ' outer (produce-long-stack-traces.js:31:27)', + 'From previous event:', + ' Object. (produce-long-stack-traces.js:30:40)' + ]); + + t.is(cleanedStack, expected); +}); + +test('removes empty "From previous event:" sections from the middle', async t => { + const stack = await longStackTraces.bluebird.middle; + const cleanedStack = stackUtils.clean(stack); + + const expected = join([ + 'mostInner (produce-long-stack-traces.js:22:5)', + 'From previous event:', + ' evenMoreInner (produce-long-stack-traces.js:21:29)', + 'From previous event:', + ' inner (produce-long-stack-traces.js:20:10)', + 'From previous event:', + ' outer (produce-long-stack-traces.js:19:27)', + 'From previous event:', + ' Object. (produce-long-stack-traces.js:18:43)' + ]); + + t.is(cleanedStack, expected); +}); + +test.cb('removes empty "Caused by:" sections from the top', t => { + nestedErrors.top(stack => { + const cleanedStack = stackUtils.clean(stack); + + const expected = join([ + 'Caused By: Error: baz', + ' Object.module.exports.top (nested-errors.js:36:5)' + ]); + + t.is(cleanedStack, expected); + t.end(); + }); +}); + +test.cb('removes empty "Caused by:" sections from the bottom', t => { + nestedErrors.bottom(stack => { + const cleanedStack = stackUtils.clean(stack); + + const expected = join([ + 'nested (nested-errors.js:9:6)', + 'moreNested (nested-errors.js:15:3)', + 'Caused By: BarError: bar: internal', + ' moreNested (nested-errors.js:15:6)' + ]); + + t.is(cleanedStack, expected); + t.end(); + }); +}); + +test.cb('removes empty "Caused by:" sections from the middle', t => { + nestedErrors.middle(stack => { + const cleanedStack = stackUtils.clean(stack); + + const expected = join([ + 'nested-errors.js:41:6', + 'Caused By: Error: bar', + ' Object.module.exports.middle (nested-errors.js:42:5)' + ]); + + t.is(cleanedStack, expected); + t.end(); + }); +}); diff --git a/test/test.js b/test/test.js index fdf448a..9a764e4 100644 --- a/test/test.js +++ b/test/test.js @@ -1,12 +1,11 @@ import path from 'path'; import test from 'ava'; -import flatten from 'flatten'; import StackUtils from '../'; import CaptureFixture from './fixtures/capture-fixture'; +import {join, fixtureDir} from './_utils'; const LinuxStack1 = join(linuxStack1(), internalStack()); const WindowsStack1 = join(windowsStack1(), internalStack()); -const fixtureDir = path.join(__dirname, 'fixtures'); const version = process.version.slice(1).split('.').map(function (val) { return parseInt(val, 10); @@ -294,11 +293,6 @@ test('parseLine: handles native errors', t => { }); }); -function join() { - var args = Array.prototype.slice.call(arguments); - return flatten(args).join('\n') + '\n'; -} - function linuxStack1() { return [ 'Error: foo',