diff --git a/lib/reporters/tap.js b/lib/reporters/tap.js index 6a2bf0832..0dc4efeff 100644 --- a/lib/reporters/tap.js +++ b/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 { @@ -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 = [ @@ -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'); diff --git a/package.json b/package.json index 04d762493..b2538179b 100644 --- a/package.json +++ b/package.json @@ -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", diff --git a/test/reporters/tap.js b/test/reporters/tap.js index ececd9a6c..c9619bd3e 100644 --- a/test/reporters/tap.js +++ b/test/reporters/tap.js @@ -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(); @@ -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();