Skip to content

Commit

Permalink
Improve YAML blocks in TAP reporter (#1301)
Browse files Browse the repository at this point in the history
* 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
  • Loading branch information
novemberborn authored and sindresorhus committed Mar 11, 2017
1 parent 157ef25 commit 3279336
Show file tree
Hide file tree
Showing 3 changed files with 95 additions and 38 deletions.
49 changes: 31 additions & 18 deletions lib/reporters/tap.js
@@ -1,14 +1,37 @@
'use strict';
const format = require('util').format;
const indentString = require('indent-string');
const stripAnsi = require('strip-ansi');
const yaml = require('js-yaml');
const extractStack = require('../extract-stack');

// Parses stack trace and extracts original function name, file name and line
function getSourceFromStack(stack, index) {
return stack
.split('\n')
.slice(index, index + 1)
.join('')
.replace(/^\s+ /, '');
function getSourceFromStack(stack) {
return extractStack(stack).split('\n')[0];
}

function dumpError(error, includeMessage) {
const obj = {};
if (error.name) {
obj.name = error.name;
}
if (includeMessage && error.message) {
obj.message = error.message;
}
if (error.operator) {
obj.operator = error.operator;
}
if (typeof error.actual === 'string') { // Be sure to print empty strings, which are falsy
obj.actual = stripAnsi(error.actual);
}
if (typeof error.expected === 'string') { // Be sure to print empty strings, which are falsy
obj.expected = stripAnsi(error.expected);
}
if (error.stack) {
obj.at = getSourceFromStack(error.stack);
}

return ` ---\n${indentString(yaml.safeDump(obj).trim(), 4)}\n ...`;
}

class TapReporter {
Expand Down Expand Up @@ -36,12 +59,7 @@ class TapReporter {
output = [
'# ' + title,
format('not ok %d - %s', ++this.i, title),
' ---',
' operator: ' + test.error.operator,
' expected: ' + test.error.expected,
' actual: ' + test.error.actual,
' at: ' + getSourceFromStack(test.error.stack, 1),
' ...'
dumpError(test.error, true)
];
} else {
output = [
Expand All @@ -59,12 +77,7 @@ class TapReporter {
];
// AvaErrors don't have stack traces
if (err.type !== 'exception' || err.name !== 'AvaError') {
output.push(
' ---',
' name: ' + err.name,
' at: ' + getSourceFromStack(err.stack, 1),
' ...'
);
output.push(dumpError(err, false));
}

return output.join('\n');
Expand Down
1 change: 1 addition & 0 deletions package.json
Expand Up @@ -134,6 +134,7 @@
"is-observable": "^0.2.0",
"is-promise": "^2.1.0",
"jest-snapshot": "^18.1.0",
"js-yaml": "^3.8.2",
"last-line-stream": "^1.0.0",
"lodash.debounce": "^4.0.3",
"lodash.difference": "^4.3.0",
Expand Down
83 changes: 63 additions & 20 deletions test/reporters/tap.js
Expand Up @@ -34,24 +34,69 @@ test('failing test', t => {
const actualOutput = reporter.test({
title: 'failing',
error: {
name: 'AssertionError',
message: 'false == true',
operator: '==',
expected: true,
actual: false,
expected: 'true',
actual: 'false',
stack: ['', 'Test.fn (test.js:1:2)'].join('\n')
}
});

const expectedOutput = [
'# failing',
'not ok 1 - failing',
' ---',
' operator: ==',
' expected: true',
' actual: false',
' at: Test.fn (test.js:1:2)',
' ...'
].join('\n');
const expectedOutput = `# failing
not ok 1 - failing
---
name: AssertionError
message: false == true
operator: ==
actual: 'false'
expected: 'true'
at: 'Test.fn (test.js:1:2)'
...`;

t.is(actualOutput, expectedOutput);
t.end();
});

test('multiline strings in YAML block', t => {
const reporter = new TapReporter();

const actualOutput = reporter.test({
title: 'multiline',
error: {
actual: 'hello\nworld'
}
});

const expectedOutput = `# multiline
not ok 1 - multiline
---
actual: |-
hello
world
...`;

t.is(actualOutput, expectedOutput);
t.end();
});

test('strips ANSI from actual and expected values', t => {
const reporter = new TapReporter();

const actualOutput = reporter.test({
title: 'strip ansi',
error: {
actual: '\u001b[31mhello\u001b[39m',
expected: '\u001b[32mworld\u001b[39m'
}
});

const expectedOutput = `# strip ansi
not ok 1 - strip ansi
---
actual: hello
expected: world
...`;

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

const expectedOutput = [
'# unhandled',
'not ok 1 - unhandled',
' ---',
' name: TypeError',
' at: Test.fn (test.js:1:2)',
' ...'
].join('\n');
const expectedOutput = `# unhandled
not ok 1 - unhandled
---
name: TypeError
at: 'Test.fn (test.js:1:2)'
...`;

t.is(actualOutput, expectedOutput);
t.end();
Expand Down

0 comments on commit 3279336

Please sign in to comment.