Skip to content

Commit

Permalink
Core: Exclude or grey internal frames from stack traces in TAP reporter
Browse files Browse the repository at this point in the history
Internal frames are those from qunit.js, or Node.js runtime.

* Remove any internal frames from the top of the stack.
* Grey out later internal frames anywhere in the stack.

This change is applied to the TAP reporter, which the QUnit CLI uses
by default.

Closes #1789.
  • Loading branch information
Krinkle authored Jul 27, 2024
1 parent 3a09b40 commit 9fed286
Show file tree
Hide file tree
Showing 5 changed files with 36 additions and 4 deletions.
3 changes: 2 additions & 1 deletion src/core/reporters/TapReporter.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import kleur from 'kleur';
import { errorString } from '../utilities.js';
import { console } from '../globals.js';
import { annotateStacktrace } from '../stacktrace.js';

const hasOwn = Object.prototype.hasOwnProperty;

Expand Down Expand Up @@ -276,7 +277,7 @@ export default class TapReporter {
out += `\n message: ${prettyYamlValue(errorString(error))}`;
out += `\n severity: ${prettyYamlValue('failed')}`;
if (error && error.stack) {
out += `\n stack: ${prettyYamlValue(error.stack + '\n')}`;
out += `\n stack: ${prettyYamlValue(annotateStacktrace(error, kleur.grey) + '\n')}`;
}
out += '\n ...';
this.log(out);
Expand Down
34 changes: 34 additions & 0 deletions src/core/stacktrace.js
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,40 @@ function qunitFileName () {

const fileName = qunitFileName();

/**
* - For internal errors from QUnit itself, remove the first qunit.js frames.
* - For errors in Node.js, format any remaining qunit.js and node:internal
* frames as internal (i.e. grey out).
*/
export function annotateStacktrace (e, formatInternal) {
if (!e || !e.stack) {
return String(e);
}
const frames = e.stack.split('\n');
const annotated = [];
if (e.toString().indexOf(frames[0]) !== -1) {
// In Firefox and Safari e.stack starts with frame 0, but in V8 (Chrome/Node.js),
// e.stack starts first stringified message. Preserve this separately,
// so that, below, we can distinguish between internal frames on top
// (to remove) vs later internal frames (to format differently).
annotated.push(frames.shift());
}
let initialInternal = true;
for (let i = 0; i < frames.length; i++) {
const frame = frames[i];
const isInternal = (frame.indexOf(fileName) !== -1 || frame.indexOf('node:internal/') !== -1);
if (!isInternal) {
initialInternal = false;
}
// Remove initial internal frames entirely.
if (!initialInternal) {
annotated.push(isInternal ? formatInternal(frame) : frame);
}
}

return annotated.join('\n');
}

export function extractStacktrace (e, offset) {
offset = offset === undefined ? 4 : offset;

Expand Down
1 change: 0 additions & 1 deletion test/cli/fixtures/async-module-error-promise.tap.txt
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ not ok 1 global failure
severity: failed
stack: |
TypeError: QUnit.module() callback must not be async. For async module setup, use hooks. https://qunitjs.com/api/QUnit/module/#hooks
at qunit.js
at /qunit/test/cli/fixtures/async-module-error-promise.js:1:7
at internal
...
Expand Down
1 change: 0 additions & 1 deletion test/cli/fixtures/async-module-error-thenable.tap.txt
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ not ok 1 global failure
severity: failed
stack: |
TypeError: QUnit.module() callback must not be async. For async module setup, use hooks. https://qunitjs.com/api/QUnit/module/#hooks
at qunit.js
at /qunit/test/cli/fixtures/async-module-error-thenable.js:1:7
at internal
...
Expand Down
1 change: 0 additions & 1 deletion test/cli/fixtures/async-module-error.tap.txt
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ not ok 1 global failure
severity: failed
stack: |
TypeError: QUnit.module() callback must not be async. For async module setup, use hooks. https://qunitjs.com/api/QUnit/module/#hooks
at qunit.js
at /qunit/test/cli/fixtures/async-module-error.js:2:7
at internal
...
Expand Down

0 comments on commit 9fed286

Please sign in to comment.