-
Notifications
You must be signed in to change notification settings - Fork 1.4k
Show only the useful lines of stack trace - fixes #22 #29
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
Fix "Assertion count does not match planned" error's stack trace not showing
About promises, I couldn't find a way of finding the stack trace for them, and as it's self-explainatory that the the returned promise was rejected, I think we can just ignore printing and stack trace for that. |
Without looking too deeply at the code, is there an option to show full stacks? This is one thing about Mocha I actually hate; a lot of the time I'm writing two modules that complement each other concurrently, and the stack traces never show the other's stack entries unless I use |
@Qix- What would you like to keep? Currently it looks like:
With this PR, it looks like:
All it hides is useless stack info coming from AVA and Node.js core libs. |
@@ -99,14 +101,15 @@ Test.prototype.end = function () { | |||
|
|||
Test.prototype.exit = function () { | |||
if (this.planCount !== null && this.planCount !== this.assertCount) { | |||
console.log(this.planStack); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Leftover
I'd have to see a full stack. If it's just removing lines caused by the tests then that should be just fine. |
Can you provide some code that result in a "full stack"? I definitely agree it should not remove that. |
Well more specifically with Mocha in general, it removes any stack entries that have Just wanting to make sure this isn't the case. |
I see that this approach doesn't work in some cases, consider this example: test('something', function(t) {
t.is('hey', 'hey');
doSomeTest(t, 2);
doSomeTest(t, {});
t.end();
});
function doSomeTest(t, x) {
t.same({x: x}, {x: {}});
} This is the output for this example:
But we should also show the line for:
I think that's what @Qix- means. I'm going to see if I can find a better approach. |
Not quite, but that is true. For instance, // mocha
// Note, `my-module' is not part of the source tree, but it's still mine
// in this example. Maybe a sister/plugin module, etc.
var myModule = require('my-module');
it('should xyz', function () {
myModule(); // throws
}); stack:
It doesn't show where inside the module |
Okay, I think I found the ultimate solution ✊ We can filter out the stack lines which start with the directory of the test file, see: let dir = path.dirname(module.parent.filename);
var split = (result.error.stack || '').split('\n');
let related = split.filter(line => line.indexOf(dir) > -1);
var beautiful = result.error.message + '\n' + related.join('\n'); What do you think? |
Then assertions won't show up correctly. 🍊 |
@Qix- Why? I tested it with the example I said above, it's working correctly, have you tested this? Output:
|
c3cb76b
to
67afa9d
Compare
…ul lines are printed
@sindresorhus Do you think I should squash my commits into one? |
@mdibaiee No need. I squash one merge. Easier to see individual changes the current way. |
What's the plan now? Anything else has to be done in this PR? |
Looks good to me now :) @Qix- ? |
@@ -36,6 +37,18 @@ function stack(results) { | |||
|
|||
i++; | |||
|
|||
// Don't print the full stack but the only useful line showing | |||
// the actual test file stack | |||
var dir = path.dirname(module.parent.filename); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sorry for my thickness, but what is this actually doing? I mean, what is the intent? Isn't it just filtering out anything that's not in the project directory? What is this improving/fixing?
In an edge case, what happens if I require()
something outside of my immediate project directory? It will be dropped, too.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is fixing the issue defined here.
It doesn't necessarily have to show the stack for your required modules, users are least concerned with how the error was generated in the module they are using, they're mostly interested in how their code causes an error, afterall they are testing their own code, not their modules.
It's about defaults, what most people prefer, users usually prefer simplicity, and if they really want the full stack, we can provide an option for that (--full-stack
as you said).
I understand the desire for clean stack traces. I just heavily question the usefulness of this particular approach. Tape's stack errors are even more useless than Mocha's if all they're showing is the deepest user-script call site of the error. Many times, the parts of the stack that make the error's cause immediately clear are the parts above (deeper) the deepest user script. For example:
Without looking at 'use strict';
var path = require('path');
var message = 'TypeError: Cannot convert object to primitive value';
var stack = [
' at String (unknown source)',
' at util.js:38:25',
' at String.replace (native)',
' at Object.<anonymous> (util.js:35:23)',
' at Object.<anonymous> (console.js:25:36)',
' at EventEmitter.<anonymous> (' + __filename + ':16:21)',
].join('\n');
var dir = path.dirname(module.filename);
console.log('DIR', dir);
var split = (stack || '').split('\n');
var related = split.filter(function (line) {
return line.indexOf(dir) > -1;
});
var beautiful = message + '\n' + related.join('\n');
console.log(beautiful); which outputs
I'd have no idea what was going on, making the stack trace absolutely useless - hence why I asked if there was a way to show full traces. If the intent here is to only show relevant stack lines, so that we exclude:
then why not filter on lines that have absolute paths, starting at the first found? The logic here is that, beginning at the deepest source file (going upward through the stack), we filter out anything that isn't a user script. We also show anything deeper than the deepest user script. This is an approach I've used elsewhere for clean error printing and I've never run into problems with not seeing enough information. /\((?:[\\\/](?:(?!node_modules[\\\/]ava[\\\/])[^:\\\/])+)+:\d+:\d+\)/ The above regex looks for all Example: 'use strict';
var path = require('path');
var message = 'TypeError: Cannot convert object to primitive value';
var stack = [
' at String (unknown source)',
' at util.js:38:25',
' at String.replace (native)',
' at Object.<anonymous> (util.js:35:23)',
' at Object.<anonymous> (console.js:25:36)',
' at EventEmitter.<anonymous> (' + __filename + ':16:21)',
' at Object.<anonymous> (console.js:25:36)',
' at Object.<anonymous> (console.js:25:36)',
' at Object.<anonymous> (console.js:25:36)',
' at Object.<anonymous> (console.js:25:36)',
' at EventEmitter.<anonymous> (' + __filename + ':16:21)',
' at EventEmitter.<anonymous> (' + __filename + ':16:21)',
' at Object.<anonymous> (console.js:25:36)',
' at Object.<anonymous> (console.js:25:36)',
' at Object.<anonymous> (console.js:25:36)'
].join('\n');
var reg = /\((?:[\\\/](?:(?!node_modules[\\\/]ava[\\\/])[^:\\\/])+)+:\d+:\d+\)/;
function beautifulStack(stack) {
var found = false;
return stack.split('\n').filter(function (line) {
var relevant = reg.test(line);
found = found || relevant;
return !found || relevant;
}).join('\n');
}
console.log(message + '\n' + beautifulStack(stack)); which yields
For @sindresorhus's stack example, this yields
which, if we replace
Another example, this stacktrace:
gets reduced down to
That seems much more appropriate to me. The point is that reducing the amount of information in errors is going to cause more headache than it solves. It should be intelligent, not "beautiful", as there is beauty in intelligence itself. If it improves the signal-to-noise ratio on callsites, then great - I'm all for it. If it removes pieces of information relevant to debugging my code, it's 100% absolutely useless. |
@Qix- Can you give me the actual test that produces this stack?
|
@mdibaiee No, it's an example stack trace I found on SO. My point is is that you don't need to look at the code to know that the first 5 frames are important for debugging the error. |
@Qix- My point is knowing what your fault was in the error is good enough for debugging most codes, and starting debugging of others. |
@mdibaiee aren't tests a vital part of debugging code? |
@Qix- You know the answer, yes. And how does that relate? |
@mdibaiee Because a first glance view of all of the relevant parts of an error is important. Removing the internals of the deepest user-call will remove the relevant parts of an error. |
I don't think so. I don't understand your worries, almost all commands have a If you really think all relevant paths are important, @sindresorhus if we are going to ship this, should I implement |
Which is what I requested to begin with; however
You don't have the option in there. @mdibaiee add it to this PR. |
I'd also recommend setting infinity length stack traces before filtering. Error.stackTraceLimit = Infinity; |
Minimal vs verbose is a hard one, but from experience with debugging a lot of failed assertions I have to agree with @Qix-. I often have to dig deeper than just the test file. I'm willing to sacrifice visual beauty in favor of practicality here. We should preserve any stack info referencing user code as @Qix- has outlined above. I'm allergic to options and I'd rather go with a good default. We can always change it in the future if it turns out to be the wrong choice. @mdibaiee Would you be willing to update your PR even though you seem to strongly disagree?
👍 |
One last thing (I know I'm persistent) but since the introduction of modules like // in filter function
return str.substr(4, 6) !== 'at' || <other regex stuff>; should suffice. Could probably be worked into the regex, but you get the idea. 👍 For clarification, I'm referring to the fact one could add a line that said
|
👍 I've seen other error modules add stuff too. Better to be on the safe side. |
@sindresorhus So what's the plan now? Don't filter anything out? Use @Qix- 's RegExp? |
Yes |
@sindresorhus Alright, sorry for the delay, I was busy working on another projet, are we ready to go? |
@@ -36,6 +36,19 @@ function stack(results) { | |||
|
|||
i++; | |||
|
|||
// Don't print the full stack but the only useful line showing | |||
// the actual test file stack | |||
var reg = /\((?:[\\\/](?:(?!node_modules[\\\/]ava[\\\/])[^:\\\/])+)+:\d+:\d+\)/; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Mind updating the regex?
(?:^(?! {4}at\b).{6})|(?:\((?:[\\\/](?:(?!node_modules[\\\/]ava[\\\/])[^:\\\/])+)+:\d+:\d+\))
This won't kill the error message and won't filter out lines that don't start with at
.
@sindresorhus done |
98868a6
to
b0c697f
Compare
Update beautifulStack's RegExp Do not modify original error's stack property
No problem! Thanks @mdibaiee 💃 👍 |
✊✌️ |
Fix "Assertion count does not match planned" error's stack trace not showing