Skip to content

Commit 9a4845b

Browse files
Merge branch 'master' into spec-reporter-retries
2 parents 26881ee + 3735873 commit 9a4845b

File tree

4 files changed

+203
-24
lines changed

4 files changed

+203
-24
lines changed

lib/reporters/base.js

+51-19
Original file line numberDiff line numberDiff line change
@@ -221,6 +221,56 @@ var generateDiff = (exports.generateDiff = function (actual, expected) {
221221
}
222222
});
223223

224+
/**
225+
* Traverses err.cause and returns all stack traces
226+
*
227+
* @private
228+
* @param {Error} err
229+
* @param {Set<Error>} [seen]
230+
* @return {{ message: string, msg: string, stack: string }}
231+
*/
232+
var getFullErrorStack = function (err, seen) {
233+
if (seen && seen.has(err)) {
234+
return { message: '', msg: '<circular>', stack: '' };
235+
}
236+
237+
var message;
238+
239+
if (typeof err.inspect === 'function') {
240+
message = err.inspect() + '';
241+
} else if (err.message && typeof err.message.toString === 'function') {
242+
message = err.message + '';
243+
} else {
244+
message = '';
245+
}
246+
247+
var msg;
248+
var stack = err.stack || message;
249+
var index = message ? stack.indexOf(message) : -1;
250+
251+
if (index === -1) {
252+
msg = message;
253+
} else {
254+
index += message.length;
255+
msg = stack.slice(0, index);
256+
// remove msg from stack
257+
stack = stack.slice(index + 1);
258+
259+
if (err.cause) {
260+
seen = seen || new Set();
261+
seen.add(err);
262+
const causeStack = getFullErrorStack(err.cause, seen)
263+
stack += '\n Caused by: ' + causeStack.msg + (causeStack.stack ? '\n' + causeStack.stack : '');
264+
}
265+
}
266+
267+
return {
268+
message,
269+
msg,
270+
stack
271+
};
272+
};
273+
224274
/**
225275
* Outputs the given `failures` as a list.
226276
*
@@ -241,7 +291,6 @@ exports.list = function (failures) {
241291
color('error stack', '\n%s\n');
242292

243293
// msg
244-
var msg;
245294
var err;
246295
if (test.err && test.err.multiple) {
247296
if (multipleTest !== test) {
@@ -252,25 +301,8 @@ exports.list = function (failures) {
252301
} else {
253302
err = test.err;
254303
}
255-
var message;
256-
if (typeof err.inspect === 'function') {
257-
message = err.inspect() + '';
258-
} else if (err.message && typeof err.message.toString === 'function') {
259-
message = err.message + '';
260-
} else {
261-
message = '';
262-
}
263-
var stack = err.stack || message;
264-
var index = message ? stack.indexOf(message) : -1;
265304

266-
if (index === -1) {
267-
msg = message;
268-
} else {
269-
index += message.length;
270-
msg = stack.slice(0, index);
271-
// remove msg from stack
272-
stack = stack.slice(index + 1);
273-
}
305+
var { message, msg, stack } = getFullErrorStack(err);
274306

275307
// uncaught
276308
if (err.uncaught) {

lib/runner.js

+16-5
Original file line numberDiff line numberDiff line change
@@ -443,11 +443,22 @@ Runner.prototype.fail = function (test, err, force) {
443443
err = thrown2Error(err);
444444
}
445445

446-
try {
447-
err.stack =
448-
this.fullStackTrace || !err.stack ? err.stack : stackFilter(err.stack);
449-
} catch (ignore) {
450-
// some environments do not take kindly to monkeying with the stack
446+
// Filter the stack traces
447+
if (!this.fullStackTrace) {
448+
const alreadyFiltered = new Set();
449+
let currentErr = err;
450+
451+
while (currentErr && currentErr.stack && !alreadyFiltered.has(currentErr)) {
452+
alreadyFiltered.add(currentErr);
453+
454+
try {
455+
currentErr.stack = stackFilter(currentErr.stack);
456+
} catch (ignore) {
457+
// some environments do not take kindly to monkeying with the stack
458+
}
459+
460+
currentErr = currentErr.cause;
461+
}
451462
}
452463

453464
this.emit(constants.EVENT_TEST_FAIL, test, err);

test/reporters/base.spec.js

+102
Original file line numberDiff line numberDiff line change
@@ -491,6 +491,108 @@ describe('Base reporter', function () {
491491
expect(errOut, 'to be', '1) test title:\n Error\n foo\n bar');
492492
});
493493

494+
describe('error causes', function () {
495+
it('should append any error cause trail to stack traces', function () {
496+
var err = {
497+
message: 'Error',
498+
stack: 'Error\nfoo\nbar',
499+
showDiff: false,
500+
cause: {
501+
message: 'Cause1',
502+
stack: 'Cause1\nbar\nfoo',
503+
showDiff: false,
504+
cause: {
505+
message: 'Cause2',
506+
stack: 'Cause2\nabc\nxyz',
507+
showDiff: false
508+
}
509+
}
510+
};
511+
var test = makeTest(err);
512+
513+
list([test]);
514+
515+
var errOut = stdout.join('\n').trim();
516+
expect(
517+
errOut,
518+
'to be',
519+
'1) test title:\n Error\n foo\n bar\n Caused by: Cause1\n bar\n foo\n Caused by: Cause2\n abc\n xyz'
520+
);
521+
});
522+
523+
it('should not get stuck in a hypothetical circular error cause trail', function () {
524+
var err1 = {
525+
message: 'Error',
526+
stack: 'Error\nfoo\nbar',
527+
showDiff: false,
528+
};
529+
var err2 = {
530+
message: 'Cause1',
531+
stack: 'Cause1\nbar\nfoo',
532+
showDiff: false,
533+
cause: err1
534+
}
535+
err1.cause = err2;
536+
537+
var test = makeTest(err1);
538+
539+
list([test]);
540+
541+
var errOut = stdout.join('\n').trim();
542+
expect(
543+
errOut,
544+
'to be',
545+
'1) test title:\n Error\n foo\n bar\n Caused by: Cause1\n bar\n foo\n Caused by: <circular>'
546+
);
547+
});
548+
549+
it("should set an empty cause if neither 'inspect' nor 'message' is set", function () {
550+
var err = {
551+
message: 'Error',
552+
stack: 'Error\nfoo\nbar',
553+
showDiff: false,
554+
cause: {
555+
showDiff: false,
556+
}
557+
};
558+
559+
var test = makeTest(err);
560+
561+
list([test]);
562+
563+
var errOut = stdout.join('\n').trim();
564+
expect(
565+
errOut,
566+
'to be',
567+
'1) test title:\n Error\n foo\n bar\n Caused by:'
568+
);
569+
});
570+
571+
it('should not add cause trail if error does not contain message', function () {
572+
var err = {
573+
message: 'Error',
574+
stack: 'foo\nbar',
575+
showDiff: false,
576+
cause: {
577+
message: 'Cause1',
578+
stack: 'Cause1\nbar\nfoo',
579+
showDiff: false,
580+
cause: {
581+
message: 'Cause2',
582+
stack: 'Cause2\nabc\nxyz',
583+
showDiff: false
584+
}
585+
}
586+
};
587+
var test = makeTest(err);
588+
589+
list([test]);
590+
591+
var errOut = stdout.join('\n').trim();
592+
expect(errOut, 'to be', '1) test title:\n Error\n foo\n bar');
593+
});
594+
});
595+
494596
it('should list multiple Errors per test', function () {
495597
var err = new Error('First Error');
496598
err.multiple = [new Error('Second Error - same test')];

test/unit/runner.spec.js

+34
Original file line numberDiff line numberDiff line change
@@ -629,6 +629,22 @@ describe('Runner', function () {
629629
});
630630
runner.fail(hook, err);
631631
});
632+
633+
it('should prettify stack-traces in error cause trail', function (done) {
634+
var hook = new Hook();
635+
hook.parent = suite;
636+
var causeErr = new Error();
637+
// Fake stack-trace
638+
causeErr.stack = stack.join('\n');
639+
var err = new Error();
640+
err.cause = causeErr;
641+
642+
runner.on(EVENT_TEST_FAIL, function (_hook, _err) {
643+
expect(_err.cause.stack, 'to be', stack.slice(0, 3).join('\n'));
644+
done();
645+
});
646+
runner.fail(hook, err);
647+
});
632648
});
633649

634650
describe('long', function () {
@@ -647,6 +663,24 @@ describe('Runner', function () {
647663
});
648664
runner.fail(hook, err);
649665
});
666+
667+
it('should display full stack-traces in error cause trail', function (done) {
668+
var hook = new Hook();
669+
hook.parent = suite;
670+
var causeErr = new Error();
671+
// Fake stack-trace
672+
causeErr.stack = stack.join('\n');
673+
var err = new Error();
674+
err.cause = causeErr;
675+
// Add --stack-trace option
676+
runner.fullStackTrace = true;
677+
678+
runner.on(EVENT_TEST_FAIL, function (_hook, _err) {
679+
expect(_err.cause.stack, 'to be', stack.join('\n'));
680+
done();
681+
});
682+
runner.fail(hook, err);
683+
});
650684
});
651685

652686
describe('ginormous', function () {

0 commit comments

Comments
 (0)