Skip to content

Commit 3279336

Browse files
novemberbornsindresorhus
authored andcommitted
Improve YAML blocks in TAP reporter (#1301)
* Properly dump errors * Only print actual and expected values if they're present, and strings. This assumes they're serialized in the test workers * Strip ANSI from actual and expected values * Print additional name & message properties * Determine 'at' value similar to serialize-error.js. Recompute since here we want the full line * Print unhandled errors using the same logic
1 parent 157ef25 commit 3279336

File tree

3 files changed

+95
-38
lines changed

3 files changed

+95
-38
lines changed

lib/reporters/tap.js

+31-18
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,37 @@
11
'use strict';
22
const format = require('util').format;
3+
const indentString = require('indent-string');
34
const stripAnsi = require('strip-ansi');
5+
const yaml = require('js-yaml');
6+
const extractStack = require('../extract-stack');
47

58
// Parses stack trace and extracts original function name, file name and line
6-
function getSourceFromStack(stack, index) {
7-
return stack
8-
.split('\n')
9-
.slice(index, index + 1)
10-
.join('')
11-
.replace(/^\s+ /, '');
9+
function getSourceFromStack(stack) {
10+
return extractStack(stack).split('\n')[0];
11+
}
12+
13+
function dumpError(error, includeMessage) {
14+
const obj = {};
15+
if (error.name) {
16+
obj.name = error.name;
17+
}
18+
if (includeMessage && error.message) {
19+
obj.message = error.message;
20+
}
21+
if (error.operator) {
22+
obj.operator = error.operator;
23+
}
24+
if (typeof error.actual === 'string') { // Be sure to print empty strings, which are falsy
25+
obj.actual = stripAnsi(error.actual);
26+
}
27+
if (typeof error.expected === 'string') { // Be sure to print empty strings, which are falsy
28+
obj.expected = stripAnsi(error.expected);
29+
}
30+
if (error.stack) {
31+
obj.at = getSourceFromStack(error.stack);
32+
}
33+
34+
return ` ---\n${indentString(yaml.safeDump(obj).trim(), 4)}\n ...`;
1235
}
1336

1437
class TapReporter {
@@ -36,12 +59,7 @@ class TapReporter {
3659
output = [
3760
'# ' + title,
3861
format('not ok %d - %s', ++this.i, title),
39-
' ---',
40-
' operator: ' + test.error.operator,
41-
' expected: ' + test.error.expected,
42-
' actual: ' + test.error.actual,
43-
' at: ' + getSourceFromStack(test.error.stack, 1),
44-
' ...'
62+
dumpError(test.error, true)
4563
];
4664
} else {
4765
output = [
@@ -59,12 +77,7 @@ class TapReporter {
5977
];
6078
// AvaErrors don't have stack traces
6179
if (err.type !== 'exception' || err.name !== 'AvaError') {
62-
output.push(
63-
' ---',
64-
' name: ' + err.name,
65-
' at: ' + getSourceFromStack(err.stack, 1),
66-
' ...'
67-
);
80+
output.push(dumpError(err, false));
6881
}
6982

7083
return output.join('\n');

package.json

+1
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,7 @@
134134
"is-observable": "^0.2.0",
135135
"is-promise": "^2.1.0",
136136
"jest-snapshot": "^18.1.0",
137+
"js-yaml": "^3.8.2",
137138
"last-line-stream": "^1.0.0",
138139
"lodash.debounce": "^4.0.3",
139140
"lodash.difference": "^4.3.0",

test/reporters/tap.js

+63-20
Original file line numberDiff line numberDiff line change
@@ -34,24 +34,69 @@ test('failing test', t => {
3434
const actualOutput = reporter.test({
3535
title: 'failing',
3636
error: {
37+
name: 'AssertionError',
3738
message: 'false == true',
3839
operator: '==',
39-
expected: true,
40-
actual: false,
40+
expected: 'true',
41+
actual: 'false',
4142
stack: ['', 'Test.fn (test.js:1:2)'].join('\n')
4243
}
4344
});
4445

45-
const expectedOutput = [
46-
'# failing',
47-
'not ok 1 - failing',
48-
' ---',
49-
' operator: ==',
50-
' expected: true',
51-
' actual: false',
52-
' at: Test.fn (test.js:1:2)',
53-
' ...'
54-
].join('\n');
46+
const expectedOutput = `# failing
47+
not ok 1 - failing
48+
---
49+
name: AssertionError
50+
message: false == true
51+
operator: ==
52+
actual: 'false'
53+
expected: 'true'
54+
at: 'Test.fn (test.js:1:2)'
55+
...`;
56+
57+
t.is(actualOutput, expectedOutput);
58+
t.end();
59+
});
60+
61+
test('multiline strings in YAML block', t => {
62+
const reporter = new TapReporter();
63+
64+
const actualOutput = reporter.test({
65+
title: 'multiline',
66+
error: {
67+
actual: 'hello\nworld'
68+
}
69+
});
70+
71+
const expectedOutput = `# multiline
72+
not ok 1 - multiline
73+
---
74+
actual: |-
75+
hello
76+
world
77+
...`;
78+
79+
t.is(actualOutput, expectedOutput);
80+
t.end();
81+
});
82+
83+
test('strips ANSI from actual and expected values', t => {
84+
const reporter = new TapReporter();
85+
86+
const actualOutput = reporter.test({
87+
title: 'strip ansi',
88+
error: {
89+
actual: '\u001b[31mhello\u001b[39m',
90+
expected: '\u001b[32mworld\u001b[39m'
91+
}
92+
});
93+
94+
const expectedOutput = `# strip ansi
95+
not ok 1 - strip ansi
96+
---
97+
actual: hello
98+
expected: world
99+
...`;
55100

56101
t.is(actualOutput, expectedOutput);
57102
t.end();
@@ -66,14 +111,12 @@ test('unhandled error', t => {
66111
stack: ['', 'Test.fn (test.js:1:2)'].join('\n')
67112
});
68113

69-
const expectedOutput = [
70-
'# unhandled',
71-
'not ok 1 - unhandled',
72-
' ---',
73-
' name: TypeError',
74-
' at: Test.fn (test.js:1:2)',
75-
' ...'
76-
].join('\n');
114+
const expectedOutput = `# unhandled
115+
not ok 1 - unhandled
116+
---
117+
name: TypeError
118+
at: 'Test.fn (test.js:1:2)'
119+
...`;
77120

78121
t.is(actualOutput, expectedOutput);
79122
t.end();

0 commit comments

Comments
 (0)