From d3c2df92d68b9a724fbb34a2b4962e5626025e95 Mon Sep 17 00:00:00 2001 From: Tobias Mollstam Date: Wed, 8 Aug 2018 09:30:30 +0200 Subject: [PATCH 01/80] added test to make sure first line of report is version line --- test/reporters/tap.spec.js | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/test/reporters/tap.spec.js b/test/reporters/tap.spec.js index 5030cbde5c..945a6d0e83 100644 --- a/test/reporters/tap.spec.js +++ b/test/reporters/tap.spec.js @@ -33,6 +33,17 @@ describe('TAP reporter', function() { }); describe('on start', function() { + it('should write expected version on first line', function() { + runner = createMockRunner('start', 'start'); + runner.grepTotal = function(string) {}; + TAP.call({}, runner); + + var expectedFirstLine = 'TAP version 13\n'; + process.stdout.write = stdoutWrite; + + expect(stdout[0], 'to equal', expectedFirstLine); + }); + it('should hand runners suite into grepTotal and log the total', function() { var expectedSuite = 'some suite'; var expectedTotal = 10; From c067a6efa2963de394042a2fa215e2ca54c4676a Mon Sep 17 00:00:00 2001 From: Tobias Mollstam Date: Wed, 8 Aug 2018 09:31:24 +0200 Subject: [PATCH 02/80] reporter will now print version line as first line --- lib/reporters/tap.js | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/reporters/tap.js b/lib/reporters/tap.js index feaf7ea21a..1c7b55931a 100644 --- a/lib/reporters/tap.js +++ b/lib/reporters/tap.js @@ -32,6 +32,7 @@ function TAP(runner) { var failures = 0; runner.on('start', function() { + console.log('TAP version 13'); var total = runner.grepTotal(runner.suite); console.log('%d..%d', 1, total); }); From 9c78b61bc0ed99857a8013c0d456074e238a52c2 Mon Sep 17 00:00:00 2001 From: Tobias Mollstam Date: Wed, 8 Aug 2018 09:34:13 +0200 Subject: [PATCH 03/80] Changed test to look for plan (1..N) on second line. Was failing since it matched entire output array and first line is now the version line per TAP v13 spec. --- test/reporters/tap.spec.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/reporters/tap.spec.js b/test/reporters/tap.spec.js index 945a6d0e83..175ad4b2c1 100644 --- a/test/reporters/tap.spec.js +++ b/test/reporters/tap.spec.js @@ -56,10 +56,10 @@ describe('TAP reporter', function() { }; TAP.call({}, runner); - var expectedArray = ['1..' + expectedTotal + '\n']; + var expectedSecondLine = '1..' + expectedTotal + '\n'; process.stdout.write = stdoutWrite; - expect(stdout, 'to equal', expectedArray); + expect(stdout[1], 'to equal', expectedSecondLine); expect(expectedString, 'to be', expectedSuite); }); }); From 78ab6c52753051469ab1c0ef74c473471800b264 Mon Sep 17 00:00:00 2001 From: Tobias Mollstam Date: Wed, 8 Aug 2018 10:27:36 +0200 Subject: [PATCH 04/80] Added integration tests to make sure we conform to spec. Only expect now is that version line is first line, more to come. Removed the previously added unit test which now is superflous. --- test/integration/reporters.spec.js | 26 ++++++++++++++++++++++++++ test/reporters/tap.spec.js | 11 ----------- 2 files changed, 26 insertions(+), 11 deletions(-) diff --git a/test/integration/reporters.spec.js b/test/integration/reporters.spec.js index 4e95622dee..8abb9d5f6c 100644 --- a/test/integration/reporters.spec.js +++ b/test/integration/reporters.spec.js @@ -99,4 +99,30 @@ describe('reporters', function() { }); }); }); + + describe('tap', function() { + describe('produces valid TAP v13 output', function() { + var runFixtureAndValidateOutput = function(fixture) { + it('for ' + fixture, function(done) { + var args = ['--reporter=tap']; + run(fixture, args, function(err, res) { + if (err) { + done(err); + return; + } + + // see https://testanything.org/tap-version-13-specification.html + + // first line must be `TAP version 13` + var firstLine = res.output.split('\n', 1)[0]; + expect(firstLine, 'to equal', 'TAP version 13'); + done(); + }); + }); + }; + + runFixtureAndValidateOutput('passing.fixture.js'); + runFixtureAndValidateOutput('uncaught.fixture.js'); + }); + }); }); diff --git a/test/reporters/tap.spec.js b/test/reporters/tap.spec.js index 175ad4b2c1..71b29a386a 100644 --- a/test/reporters/tap.spec.js +++ b/test/reporters/tap.spec.js @@ -33,17 +33,6 @@ describe('TAP reporter', function() { }); describe('on start', function() { - it('should write expected version on first line', function() { - runner = createMockRunner('start', 'start'); - runner.grepTotal = function(string) {}; - TAP.call({}, runner); - - var expectedFirstLine = 'TAP version 13\n'; - process.stdout.write = stdoutWrite; - - expect(stdout[0], 'to equal', expectedFirstLine); - }); - it('should hand runners suite into grepTotal and log the total', function() { var expectedSuite = 'some suite'; var expectedTotal = 10; From b59af191e91ae9994a55735d6dfade0767a5a54f Mon Sep 17 00:00:00 2001 From: Tobias Mollstam Date: Wed, 8 Aug 2018 11:12:34 +0200 Subject: [PATCH 05/80] Extended integration test to validate plan, test is growing... --- test/integration/reporters.spec.js | 55 ++++++++++++++++++++++++++---- 1 file changed, 49 insertions(+), 6 deletions(-) diff --git a/test/integration/reporters.spec.js b/test/integration/reporters.spec.js index 8abb9d5f6c..f25f4313d1 100644 --- a/test/integration/reporters.spec.js +++ b/test/integration/reporters.spec.js @@ -102,7 +102,7 @@ describe('reporters', function() { describe('tap', function() { describe('produces valid TAP v13 output', function() { - var runFixtureAndValidateOutput = function(fixture) { + var runFixtureAndValidateOutput = function(fixture, expected) { it('for ' + fixture, function(done) { var args = ['--reporter=tap']; run(fixture, args, function(err, res) { @@ -111,18 +111,61 @@ describe('reporters', function() { return; } + var outputLines = res.output.split('\n'); + // see https://testanything.org/tap-version-13-specification.html - // first line must be `TAP version 13` - var firstLine = res.output.split('\n', 1)[0]; - expect(firstLine, 'to equal', 'TAP version 13'); + // Version + var expectedVersion = 13; + // first line must be version line + expect( + outputLines[0], + 'to equal', + 'TAP version ' + expectedVersion + ); + + // Plan + var expectedPlan = '1..' + expected.numTests; + // plan must appear once + expect(outputLines, 'to contain', expectedPlan); + expect( + outputLines.filter(l => l === expectedPlan), + 'to have length', + 1 + ); + // plan cannot appear in middle of the output + var testLineMatcher = function(line) { + return line.match(/^not ok/) || line.match(/^ok/); + }; + var firstTestLine = outputLines.findIndex(testLineMatcher); + expect(firstTestLine, 'to be greater than', -1); + var lastTestLine = + outputLines.length - + 1 - + outputLines + .slice() + .reverse() + .findIndex(testLineMatcher); + var planLine = outputLines.findIndex(function(line) { + return line === expectedPlan; + }); + expect( + planLine < firstTestLine || planLine > lastTestLine, + 'to equal', + true + ); + done(); }); }); }; - runFixtureAndValidateOutput('passing.fixture.js'); - runFixtureAndValidateOutput('uncaught.fixture.js'); + runFixtureAndValidateOutput('passing.fixture.js', { + numTests: 2 + }); + runFixtureAndValidateOutput('uncaught.fixture.js', { + numTests: 2 + }); }); }); }); From ddc5b17092770ce93b853b65afa8c145944f78ba Mon Sep 17 00:00:00 2001 From: Tobias Mollstam Date: Wed, 8 Aug 2018 11:21:34 +0200 Subject: [PATCH 06/80] removed some section comments and minor restructure --- test/integration/reporters.spec.js | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/test/integration/reporters.spec.js b/test/integration/reporters.spec.js index f25f4313d1..5a92c4f4ef 100644 --- a/test/integration/reporters.spec.js +++ b/test/integration/reporters.spec.js @@ -111,12 +111,11 @@ describe('reporters', function() { return; } - var outputLines = res.output.split('\n'); + var expectedVersion = 13; + var expectedPlan = '1..' + expected.numTests; - // see https://testanything.org/tap-version-13-specification.html + var outputLines = res.output.split('\n'); - // Version - var expectedVersion = 13; // first line must be version line expect( outputLines[0], @@ -124,8 +123,6 @@ describe('reporters', function() { 'TAP version ' + expectedVersion ); - // Plan - var expectedPlan = '1..' + expected.numTests; // plan must appear once expect(outputLines, 'to contain', expectedPlan); expect( @@ -138,6 +135,7 @@ describe('reporters', function() { return line.match(/^not ok/) || line.match(/^ok/); }; var firstTestLine = outputLines.findIndex(testLineMatcher); + // there must be at least one test line expect(firstTestLine, 'to be greater than', -1); var lastTestLine = outputLines.length - From f030a4b3b9859ab7cfa8d1d500ee21e310ed0c8f Mon Sep 17 00:00:00 2001 From: Tobias Mollstam Date: Wed, 8 Aug 2018 11:53:31 +0200 Subject: [PATCH 07/80] broke out testLineMatcher to testLinePredicate with minor refactor. --- test/integration/reporters.spec.js | 45 ++++++++++++++++++++++++++---- 1 file changed, 40 insertions(+), 5 deletions(-) diff --git a/test/integration/reporters.spec.js b/test/integration/reporters.spec.js index 5a92c4f4ef..6527c8e47b 100644 --- a/test/integration/reporters.spec.js +++ b/test/integration/reporters.spec.js @@ -101,6 +101,10 @@ describe('reporters', function() { }); describe('tap', function() { + var testLinePredicate = function(line) { + return line.match(/^not ok/) != null || line.match(/^ok/) != null; + }; + describe('produces valid TAP v13 output', function() { var runFixtureAndValidateOutput = function(fixture, expected) { it('for ' + fixture, function(done) { @@ -131,10 +135,7 @@ describe('reporters', function() { 1 ); // plan cannot appear in middle of the output - var testLineMatcher = function(line) { - return line.match(/^not ok/) || line.match(/^ok/); - }; - var firstTestLine = outputLines.findIndex(testLineMatcher); + var firstTestLine = outputLines.findIndex(testLinePredicate); // there must be at least one test line expect(firstTestLine, 'to be greater than', -1); var lastTestLine = @@ -143,7 +144,7 @@ describe('reporters', function() { outputLines .slice() .reverse() - .findIndex(testLineMatcher); + .findIndex(testLinePredicate); var planLine = outputLines.findIndex(function(line) { return line === expectedPlan; }); @@ -165,5 +166,39 @@ describe('reporters', function() { numTests: 2 }); }); + + it('places exceptions correctly in YAML blocks', function(done) { + var args = ['--reporter=tap']; + run('uncaught.fixture.js', args, function(err, res) { + if (err) { + done(err); + return; + } + + var outputLines = res.output.split('\n'); + for (var i = 0; i + 1 < outputLines.length; i++) { + console.log('>', outputLines[i]); + console.log('\t', testLinePredicate(outputLines[i])); + console.log('\t', testLinePredicate(outputLines[i + 1])); + if ( + testLinePredicate(outputLines[i]) && + testLinePredicate(outputLines[i + 1]) === false + ) { + var blockLinesStart = i + 1; + var blockLinesEnd = + i + 1 + outputLines.slice(i + 1).findIndex(anythingElsePredicate); + var blockLines = + blockLinesEnd > blockLinesStart + ? outputLines.slice(blockLinesStart, blockLinesEnd) + : outputLines.slice(blockLinesStart); + console.log(i); + console.log(blockLines); + i += blockLines.length; + } + } + + done(); + }); + }); }); }); From 43c94c8411d05db5ff8476a9c98c1e767367d7f9 Mon Sep 17 00:00:00 2001 From: Tobias Mollstam Date: Wed, 8 Aug 2018 12:10:43 +0200 Subject: [PATCH 08/80] added a bunch of predicates to identify lines --- test/integration/reporters.spec.js | 36 +++++++++++++++++++++++++----- 1 file changed, 30 insertions(+), 6 deletions(-) diff --git a/test/integration/reporters.spec.js b/test/integration/reporters.spec.js index 6527c8e47b..bb7063de78 100644 --- a/test/integration/reporters.spec.js +++ b/test/integration/reporters.spec.js @@ -101,9 +101,35 @@ describe('reporters', function() { }); describe('tap', function() { + var not = function(predicate) { + return function() { + return !predicate.apply(this, arguments); + }; + }; + var versionPredicate = function(line) { + return line.match(/^TAP version \d+$/) != null; + }; + var planPredicate = function(line) { + return line.match(/^1\.\.\d+$/) != null; + }; var testLinePredicate = function(line) { return line.match(/^not ok/) != null || line.match(/^ok/) != null; }; + var diagnosticPredicate = function(line) { + return line.match(/^#/) != null; + }; + var bailOutPredicate = function(line) { + return line.match(/^Bail out!/) != null; + }; + var anythingElsePredicate = function(line) { + return ( + versionPredicate(line) === false && + planPredicate(line) === false && + testLinePredicate(line) === false && + diagnosticPredicate(line) === false && + bailOutPredicate(line) === false + ); + }; describe('produces valid TAP v13 output', function() { var runFixtureAndValidateOutput = function(fixture, expected) { @@ -177,22 +203,20 @@ describe('reporters', function() { var outputLines = res.output.split('\n'); for (var i = 0; i + 1 < outputLines.length; i++) { - console.log('>', outputLines[i]); - console.log('\t', testLinePredicate(outputLines[i])); - console.log('\t', testLinePredicate(outputLines[i + 1])); if ( testLinePredicate(outputLines[i]) && testLinePredicate(outputLines[i + 1]) === false ) { var blockLinesStart = i + 1; var blockLinesEnd = - i + 1 + outputLines.slice(i + 1).findIndex(anythingElsePredicate); + i + + 1 + + outputLines.slice(i + 1).findIndex(not(anythingElsePredicate)); var blockLines = blockLinesEnd > blockLinesStart ? outputLines.slice(blockLinesStart, blockLinesEnd) : outputLines.slice(blockLinesStart); - console.log(i); - console.log(blockLines); + i += blockLines.length; } } From 8d369b7cd3ad82f549159aa0736f4f7ca45b8f62 Mon Sep 17 00:00:00 2001 From: Tobias Mollstam Date: Wed, 8 Aug 2018 13:59:54 +0200 Subject: [PATCH 09/80] stacks and errors are now wrapped inside YAML blocks --- lib/reporters/tap.js | 19 ++++++++++++++----- test/integration/reporters.spec.js | 5 ++++- test/reporters/tap.spec.js | 16 ++++++++++++---- 3 files changed, 30 insertions(+), 10 deletions(-) diff --git a/lib/reporters/tap.js b/lib/reporters/tap.js index 1c7b55931a..2244f07a93 100644 --- a/lib/reporters/tap.js +++ b/lib/reporters/tap.js @@ -53,11 +53,20 @@ function TAP(runner) { runner.on('fail', function(test, err) { failures++; console.log('not ok %d %s', n, title(test)); - if (err.message) { - console.log(err.message.replace(/^/gm, ' ')); - } - if (err.stack) { - console.log(err.stack.replace(/^/gm, ' ')); + var emitYamlBlock = err.message != null || err.stack != null; + if (emitYamlBlock) { + console.log(' ---'); + if (err.message) { + console.log(' message: ' + err.message); + } + // stack needs to be last in yaml due to literal style indicator (|) + if (err.stack) { + console.log(' stack: |-'); + if (err.stack) { + console.log(err.stack.replace(/^/gm, ' ')); + } + } + console.log(' ...'); } }); diff --git a/test/integration/reporters.spec.js b/test/integration/reporters.spec.js index bb7063de78..89428478eb 100644 --- a/test/integration/reporters.spec.js +++ b/test/integration/reporters.spec.js @@ -202,6 +202,7 @@ describe('reporters', function() { } var outputLines = res.output.split('\n'); + for (var i = 0; i + 1 < outputLines.length; i++) { if ( testLinePredicate(outputLines[i]) && @@ -216,8 +217,10 @@ describe('reporters', function() { blockLinesEnd > blockLinesStart ? outputLines.slice(blockLinesStart, blockLinesEnd) : outputLines.slice(blockLinesStart); - i += blockLines.length; + + expect(blockLines[0], 'to match', /^\s+\-\-\-/); + expect(blockLines[blockLines.length - 1], 'to match', /^\s+\.\.\./); } } diff --git a/test/reporters/tap.spec.js b/test/reporters/tap.spec.js index 71b29a386a..8845b6b891 100644 --- a/test/reporters/tap.spec.js +++ b/test/reporters/tap.spec.js @@ -121,7 +121,9 @@ describe('TAP reporter', function() { var expectedArray = [ 'not ok ' + countAfterTestEnd + ' ' + expectedTitle + '\n', - ' ' + expectedErrorMessage + '\n' + ' ---\n', + ' message: ' + expectedErrorMessage + '\n', + ' ...\n' ]; expect(stdout, 'to equal', expectedArray); }); @@ -148,7 +150,10 @@ describe('TAP reporter', function() { var expectedArray = [ 'not ok ' + countAfterTestEnd + ' ' + expectedTitle + '\n', - ' ' + expectedStack + '\n' + ' ---\n', + ' stack: |-\n', + ' ' + expectedStack + '\n', + ' ...\n' ]; expect(stdout, 'to equal', expectedArray); }); @@ -185,8 +190,11 @@ describe('TAP reporter', function() { var expectedArray = [ 'not ok ' + countAfterTestEnd + ' ' + expectedTitle + '\n', - ' ' + expectedErrorMessage + '\n', - ' ' + expectedStack + '\n' + ' ---\n', + ' message: ' + expectedErrorMessage + '\n', + ' stack: |-\n', + ' ' + expectedStack + '\n', + ' ...\n' ]; expect(stdout, 'to equal', expectedArray); }); From 96777440946bbd48c52e8c419ec4759a3bdb9816 Mon Sep 17 00:00:00 2001 From: Tobias Mollstam Date: Wed, 8 Aug 2018 14:04:33 +0200 Subject: [PATCH 10/80] changed arrow function to ye olde function to appease bots --- test/integration/reporters.spec.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/test/integration/reporters.spec.js b/test/integration/reporters.spec.js index 89428478eb..3f5efd3de6 100644 --- a/test/integration/reporters.spec.js +++ b/test/integration/reporters.spec.js @@ -156,7 +156,9 @@ describe('reporters', function() { // plan must appear once expect(outputLines, 'to contain', expectedPlan); expect( - outputLines.filter(l => l === expectedPlan), + outputLines.filter(function(l) { + return l === expectedPlan; + }), 'to have length', 1 ); From 30474417e8d90214da6eee168becf9e2a4a829f9 Mon Sep 17 00:00:00 2001 From: Tobias Mollstam Date: Wed, 8 Aug 2018 15:24:54 +0200 Subject: [PATCH 11/80] found linter and made code conform, `npm run test` now clean. --- test/integration/reporters.spec.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/integration/reporters.spec.js b/test/integration/reporters.spec.js index 3f5efd3de6..fc4967a854 100644 --- a/test/integration/reporters.spec.js +++ b/test/integration/reporters.spec.js @@ -221,7 +221,7 @@ describe('reporters', function() { : outputLines.slice(blockLinesStart); i += blockLines.length; - expect(blockLines[0], 'to match', /^\s+\-\-\-/); + expect(blockLines[0], 'to match', /^\s+---/); expect(blockLines[blockLines.length - 1], 'to match', /^\s+\.\.\./); } } From 253d76ac8fd6f5f7c21978a3e54559cd2e0b5dbf Mon Sep 17 00:00:00 2001 From: Tobias Mollstam Date: Thu, 9 Aug 2018 08:25:32 +0200 Subject: [PATCH 12/80] added reporters.fixture.js for testing reporter capabilities --- .../integration/fixtures/reporters.fixture.js | 63 +++++++++++++++++++ 1 file changed, 63 insertions(+) create mode 100644 test/integration/fixtures/reporters.fixture.js diff --git a/test/integration/fixtures/reporters.fixture.js b/test/integration/fixtures/reporters.fixture.js new file mode 100644 index 0000000000..ab16bc0eed --- /dev/null +++ b/test/integration/fixtures/reporters.fixture.js @@ -0,0 +1,63 @@ +'use strict'; + +/** + * This file generates a wide range of output to test reporter functionality. + */ + +describe('Animals', function() { + + it('should consume organic material', function(done) { done(); }); + it('should breathe oxygen', function(done) { + // we're a jellyfish + var actualBreathe = 'nothing'; + var expectedBreathe = 'oxygen'; + expect(actualBreathe, 'to equal', expectedBreathe); + done(); + }); + it('should be able to move', function(done) { done(); }); + it('should reproduce sexually', function(done) { done(); }); + it('should grow from a hollow sphere of cells', function(done) { done(); }); + + describe('Vertebrates', function() { + describe('Mammals', function() { + it('should give birth to live young', function(done) { + var expectedMammal = { + consumesMaterial: 'organic', + breathe: 'oxygen', + reproduction: { + type: 'sexually', + spawnType: 'live', + } + }; + var platypus = JSON.parse(JSON.stringify(expectedMammal)); + platypus['reproduction']['spawnType'] = 'hard-shelled egg'; + + expect(platypus, 'to equal', expectedMammal); + done(); + }); + + describe('Blue Whale', function() { + it('should be the largest of all mammals', function(done) { done(); }); + it('should have a body in some shade of blue', function(done) { + var bodyColor = 'blueish_grey'; + var shadesOfBlue = ['cyan', 'light_blue', 'blue', 'indigo']; + expect(bodyColor, 'to be one of', shadesOfBlue); + + done(); + }); + }); + }); + describe('Birds', function() { + it('should have feathers', function(done) { done(); }); + it('should lay hard-shelled eggs', function(done) { done(); }); + }); + }); + + describe('Tardigrades', function() { + it('should answer to "water bear"', function(done) { done(); }); + it('should be able to survive global mass extinction events', function(done) { + throw new Error("How do we even test for this without causing one?") + done(); + }); + }); +}); From e00b99cbc21b4f1b202b304bc460d65acf27f1c6 Mon Sep 17 00:00:00 2001 From: Tobias Mollstam Date: Thu, 9 Aug 2018 09:26:06 +0200 Subject: [PATCH 13/80] Fixed error message potentially breaking YAML block, some cleanup. err.message is now placed inside YAML block as a literal so that newline doesn't break any YAML parser. --- lib/reporters/tap.js | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/lib/reporters/tap.js b/lib/reporters/tap.js index 2244f07a93..512af6c26a 100644 --- a/lib/reporters/tap.js +++ b/lib/reporters/tap.js @@ -57,14 +57,12 @@ function TAP(runner) { if (emitYamlBlock) { console.log(' ---'); if (err.message) { - console.log(' message: ' + err.message); + console.log(' message: |-'); + console.log(err.message.replace(/^/gm, ' ')); } - // stack needs to be last in yaml due to literal style indicator (|) if (err.stack) { console.log(' stack: |-'); - if (err.stack) { - console.log(err.stack.replace(/^/gm, ' ')); - } + console.log(err.stack.replace(/^/gm, ' ')); } console.log(' ...'); } From 1e559b3c06192fd9d04a3cb0836ed538dd41dc9f Mon Sep 17 00:00:00 2001 From: Tobias Mollstam Date: Thu, 9 Aug 2018 09:52:48 +0200 Subject: [PATCH 14/80] TAP now inherits from Base --- lib/reporters/tap.js | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/lib/reporters/tap.js b/lib/reporters/tap.js index 512af6c26a..c51ffc2c23 100644 --- a/lib/reporters/tap.js +++ b/lib/reporters/tap.js @@ -7,6 +7,7 @@ */ var Base = require('./base'); +var inherits = require('../utils').inherits; /** * Expose `TAP`. @@ -27,6 +28,7 @@ exports = module.exports = TAP; function TAP(runner) { Base.call(this, runner); + var self = this; var n = 1; var passes = 0; var failures = 0; @@ -75,6 +77,11 @@ function TAP(runner) { }); } +/** + * Inherit from `Base.prototype`. + */ +inherits(TAP, Base); + /** * Return a TAP-safe title of `test` * From 419fc3383e64e078e88e61b1226696408e2a8fbe Mon Sep 17 00:00:00 2001 From: Tobias Mollstam Date: Thu, 9 Aug 2018 11:28:58 +0200 Subject: [PATCH 15/80] TAP no longer inherits from Base, decided not to use any Base functionality --- lib/reporters/tap.js | 7 ------- 1 file changed, 7 deletions(-) diff --git a/lib/reporters/tap.js b/lib/reporters/tap.js index c51ffc2c23..512af6c26a 100644 --- a/lib/reporters/tap.js +++ b/lib/reporters/tap.js @@ -7,7 +7,6 @@ */ var Base = require('./base'); -var inherits = require('../utils').inherits; /** * Expose `TAP`. @@ -28,7 +27,6 @@ exports = module.exports = TAP; function TAP(runner) { Base.call(this, runner); - var self = this; var n = 1; var passes = 0; var failures = 0; @@ -77,11 +75,6 @@ function TAP(runner) { }); } -/** - * Inherit from `Base.prototype`. - */ -inherits(TAP, Base); - /** * Return a TAP-safe title of `test` * From 3551db32d6c88a132c9cab2fb38653360f97908d Mon Sep 17 00:00:00 2001 From: Tobias Mollstam Date: Thu, 9 Aug 2018 11:34:40 +0200 Subject: [PATCH 16/80] TAP unit tests now expect message starting with literal --- test/reporters/tap.spec.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/test/reporters/tap.spec.js b/test/reporters/tap.spec.js index 8845b6b891..84d6c5a16b 100644 --- a/test/reporters/tap.spec.js +++ b/test/reporters/tap.spec.js @@ -122,7 +122,8 @@ describe('TAP reporter', function() { var expectedArray = [ 'not ok ' + countAfterTestEnd + ' ' + expectedTitle + '\n', ' ---\n', - ' message: ' + expectedErrorMessage + '\n', + ' message: |-\n', + ' ' + expectedErrorMessage + '\n', ' ...\n' ]; expect(stdout, 'to equal', expectedArray); @@ -191,7 +192,8 @@ describe('TAP reporter', function() { var expectedArray = [ 'not ok ' + countAfterTestEnd + ' ' + expectedTitle + '\n', ' ---\n', - ' message: ' + expectedErrorMessage + '\n', + ' message: |-\n', + ' ' + expectedErrorMessage + '\n', ' stack: |-\n', ' ' + expectedStack + '\n', ' ...\n' From 68d8b9cccd0942b1daeaf32c2c56344cdffbc56b Mon Sep 17 00:00:00 2001 From: Tobias Mollstam Date: Thu, 9 Aug 2018 11:35:50 +0200 Subject: [PATCH 17/80] TAP integration tests use reporters fixture instead of uncaught --- test/integration/reporters.spec.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/test/integration/reporters.spec.js b/test/integration/reporters.spec.js index fc4967a854..12c87fd5d2 100644 --- a/test/integration/reporters.spec.js +++ b/test/integration/reporters.spec.js @@ -190,14 +190,14 @@ describe('reporters', function() { runFixtureAndValidateOutput('passing.fixture.js', { numTests: 2 }); - runFixtureAndValidateOutput('uncaught.fixture.js', { - numTests: 2 + runFixtureAndValidateOutput('reporters.fixture.js', { + numTests: 12 }); }); it('places exceptions correctly in YAML blocks', function(done) { var args = ['--reporter=tap']; - run('uncaught.fixture.js', args, function(err, res) { + run('reporters.fixture.js', args, function(err, res) { if (err) { done(err); return; From 3b5f88f81e18eb352a58ac349a8e066a6683d4ea Mon Sep 17 00:00:00 2001 From: Tobias Mollstam Date: Wed, 19 Sep 2018 09:54:27 +0200 Subject: [PATCH 18/80] refactored yaml indentation to clean things up --- lib/reporters/tap.js | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/lib/reporters/tap.js b/lib/reporters/tap.js index 512af6c26a..51e6fa6a34 100644 --- a/lib/reporters/tap.js +++ b/lib/reporters/tap.js @@ -31,6 +31,10 @@ function TAP(runner) { var passes = 0; var failures = 0; + function yamlIndent(level) { + return Array(level + 1).join(' '); + } + runner.on('start', function() { console.log('TAP version 13'); var total = runner.grepTotal(runner.suite); @@ -55,16 +59,16 @@ function TAP(runner) { console.log('not ok %d %s', n, title(test)); var emitYamlBlock = err.message != null || err.stack != null; if (emitYamlBlock) { - console.log(' ---'); + console.log(yamlIndent(1), '---'); if (err.message) { - console.log(' message: |-'); - console.log(err.message.replace(/^/gm, ' ')); + console.log(yamlIndent(2), 'message: |-'); + console.log(err.message.replace(/^/gm, yamlIndent(3))); } if (err.stack) { - console.log(' stack: |-'); - console.log(err.stack.replace(/^/gm, ' ')); + console.log(yamlIndent(2), 'stack: |-'); + console.log(err.stack.replace(/^/gm, yamlIndent(3))); } - console.log(' ...'); + console.log(yamlIndent(1), '...'); } }); From 3cac7f1d4faca8c6944e3f7c405372c3179f999b Mon Sep 17 00:00:00 2001 From: Tobias Mollstam Date: Wed, 19 Sep 2018 10:17:58 +0200 Subject: [PATCH 19/80] fix to refactored indent which added an extra space on some lines --- lib/reporters/tap.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/reporters/tap.js b/lib/reporters/tap.js index 51e6fa6a34..f2f419e121 100644 --- a/lib/reporters/tap.js +++ b/lib/reporters/tap.js @@ -59,16 +59,16 @@ function TAP(runner) { console.log('not ok %d %s', n, title(test)); var emitYamlBlock = err.message != null || err.stack != null; if (emitYamlBlock) { - console.log(yamlIndent(1), '---'); + console.log(yamlIndent(1) + '---'); if (err.message) { - console.log(yamlIndent(2), 'message: |-'); + console.log(yamlIndent(2) + 'message: |-'); console.log(err.message.replace(/^/gm, yamlIndent(3))); } if (err.stack) { - console.log(yamlIndent(2), 'stack: |-'); + console.log(yamlIndent(2) + 'stack: |-'); console.log(err.stack.replace(/^/gm, yamlIndent(3))); } - console.log(yamlIndent(1), '...'); + console.log(yamlIndent(1) + '...'); } }); From b5b88a1bbd577f1918760b8f6c67634da84236d7 Mon Sep 17 00:00:00 2001 From: Tobias Mollstam Date: Wed, 19 Sep 2018 10:46:24 +0200 Subject: [PATCH 20/80] Retrigger appveyor test From ce24d96d04b1bf1f0eb38c6b4d4ff99b410fbd15 Mon Sep 17 00:00:00 2001 From: Tobias Mollstam Date: Thu, 20 Sep 2018 13:09:09 +0200 Subject: [PATCH 21/80] TAP reporter inherits Base --- lib/reporters/tap.js | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/lib/reporters/tap.js b/lib/reporters/tap.js index f2f419e121..a2ce55fbcd 100644 --- a/lib/reporters/tap.js +++ b/lib/reporters/tap.js @@ -7,6 +7,7 @@ */ var Base = require('./base'); +var inherits = require('../utils').inherits; /** * Expose `TAP`. @@ -89,3 +90,8 @@ function TAP(runner) { function title(test) { return test.fullTitle().replace(/#/g, ''); } + +/** + * Inherit from `Base.prototype`. + */ +inherits(TAP, Base); From d5b519d2ecc39cb23dd905c1ccf309f4a872fb47 Mon Sep 17 00:00:00 2001 From: Tobias Mollstam Date: Thu, 20 Sep 2018 13:17:32 +0200 Subject: [PATCH 22/80] TAP reporter uses runner.stats rather than internal tracking --- lib/reporters/tap.js | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/lib/reporters/tap.js b/lib/reporters/tap.js index a2ce55fbcd..907cc998df 100644 --- a/lib/reporters/tap.js +++ b/lib/reporters/tap.js @@ -29,8 +29,6 @@ function TAP(runner) { Base.call(this, runner); var n = 1; - var passes = 0; - var failures = 0; function yamlIndent(level) { return Array(level + 1).join(' '); @@ -51,12 +49,10 @@ function TAP(runner) { }); runner.on('pass', function(test) { - passes++; console.log('ok %d %s', n, title(test)); }); runner.on('fail', function(test, err) { - failures++; console.log('not ok %d %s', n, title(test)); var emitYamlBlock = err.message != null || err.stack != null; if (emitYamlBlock) { @@ -74,9 +70,9 @@ function TAP(runner) { }); runner.once('end', function() { - console.log('# tests ' + (passes + failures)); - console.log('# pass ' + passes); - console.log('# fail ' + failures); + console.log('# tests ' + (runner.stats.passes + runner.stats.failures)); + console.log('# pass ' + runner.stats.passes); + console.log('# fail ' + runner.stats.failures); }); } From 32424089e90c9669d5628f09956ef6c36a4104d1 Mon Sep 17 00:00:00 2001 From: Tobias Mollstam Date: Thu, 20 Sep 2018 13:59:50 +0200 Subject: [PATCH 23/80] added support for `spec` reporterOption and started implementing `TAP12Producer` and `TAP13Producer`. --- lib/reporters/tap.js | 35 +++- test/reporters/tap.spec.js | 372 ++++++++++++++++++++----------------- 2 files changed, 230 insertions(+), 177 deletions(-) diff --git a/lib/reporters/tap.js b/lib/reporters/tap.js index 907cc998df..b9c073830e 100644 --- a/lib/reporters/tap.js +++ b/lib/reporters/tap.js @@ -25,19 +25,31 @@ exports = module.exports = TAP; * @api public * @param {Runner} runner */ -function TAP(runner) { +function TAP(runner, options) { Base.call(this, runner); + var self = this; var n = 1; function yamlIndent(level) { return Array(level + 1).join(' '); } + var tapSpec = '12'; + if (options && options.reporterOptions) { + if (options.reporterOptions.spec) { + var tapSpec = options.reporterOptions.spec; + } + } + + if (tapSpec === '13') { + this.producer = new TAP13Producer(); + } else { + this.producer = new TAP12Producer(); + } + runner.on('start', function() { - console.log('TAP version 13'); - var total = runner.grepTotal(runner.suite); - console.log('%d..%d', 1, total); + self.producer.writeStart(runner); }); runner.on('test end', function() { @@ -91,3 +103,18 @@ function title(test) { * Inherit from `Base.prototype`. */ inherits(TAP, Base); + +function TAP12Producer() {} + +TAP12Producer.prototype.writeStart = function(runner) { + var total = runner.grepTotal(runner.suite); + console.log('%d..%d', 1, total); +}; + +function TAP13Producer() {} + +TAP13Producer.prototype.writeStart = function(runner) { + console.log('TAP version 13'); + var total = runner.grepTotal(runner.suite); + console.log('%d..%d', 1, total); +}; diff --git a/test/reporters/tap.spec.js b/test/reporters/tap.spec.js index 84d6c5a16b..5d43268f1c 100644 --- a/test/reporters/tap.spec.js +++ b/test/reporters/tap.spec.js @@ -32,116 +32,60 @@ describe('TAP reporter', function() { process.stdout.write = stdoutWrite; }); - describe('on start', function() { - it('should hand runners suite into grepTotal and log the total', function() { - var expectedSuite = 'some suite'; - var expectedTotal = 10; - var expectedString; - runner = createMockRunner('start', 'start'); - runner.suite = expectedSuite; - runner.grepTotal = function(string) { - expectedString = string; - return expectedTotal; - }; - TAP.call({}, runner); - - var expectedSecondLine = '1..' + expectedTotal + '\n'; - process.stdout.write = stdoutWrite; - - expect(stdout[1], 'to equal', expectedSecondLine); - expect(expectedString, 'to be', expectedSuite); - }); - }); - - describe('on pending', function() { - it('should write expected message including count and title', function() { - runner = createMockRunner( - 'start test', - 'test end', - 'pending', - null, - test - ); - runner.suite = ''; - runner.grepTotal = function() {}; - TAP.call({}, runner); - - process.stdout.write = stdoutWrite; - - var expectedMessage = - 'ok ' + countAfterTestEnd + ' ' + expectedTitle + ' # SKIP -\n'; - expect(stdout[0], 'to equal', expectedMessage); - }); - }); - - describe('on pass', function() { - it('should write expected message including count and title', function() { - runner = createMockRunner('start test', 'test end', 'pass', null, test); - - runner.suite = ''; - runner.grepTotal = function() {}; - TAP.call({}, runner); + describe('TAP12 spec', function() { + describe('on start', function() { + it('should hand runners suite into grepTotal and log the total', function() { + var expectedSuite = 'some suite'; + var expectedTotal = 10; + var expectedString; + runner = createMockRunner('start', 'start'); + runner.suite = expectedSuite; + runner.grepTotal = function(string) { + expectedString = string; + return expectedTotal; + }; + TAP.call({}, runner); - process.stdout.write = stdoutWrite; + var expectedArray = ['1..' + expectedTotal + '\n']; + process.stdout.write = stdoutWrite; - var expectedMessage = - 'ok ' + countAfterTestEnd + ' ' + expectedTitle + '\n'; - expect(stdout[0], 'to equal', expectedMessage); + expect(stdout, 'to equal', expectedArray); + expect(expectedString, 'to be', expectedSuite); + }); }); }); - describe('on fail', function() { - describe('if there is an error message', function() { - it('should write expected message and error message', function() { - var expectedTitle = 'some title'; - var countAfterTestEnd = 2; - var expectedErrorMessage = 'some error'; - var test = { - fullTitle: function() { - return expectedTitle; - }, - slow: function() {} - }; - var error = { - message: expectedErrorMessage + describe('TAP13 spec', function() { + var options = {reporterOptions: {spec: '13'}}; + describe('on start', function() { + it('should hand runners suite into grepTotal and log the total', function() { + var expectedSuite = 'some suite'; + var expectedTotal = 10; + var expectedString; + runner = createMockRunner('start', 'start'); + runner.suite = expectedSuite; + runner.grepTotal = function(string) { + expectedString = string; + return expectedTotal; }; - runner.on = function(event, callback) { - if (event === 'test end') { - callback(); - } - if (event === 'fail') { - callback(test, error); - } - }; - runner.suite = ''; - runner.grepTotal = function() {}; - TAP.call({}, runner); + TAP.call({}, runner, options); + var expectedSecondLine = '1..' + expectedTotal + '\n'; process.stdout.write = stdoutWrite; - var expectedArray = [ - 'not ok ' + countAfterTestEnd + ' ' + expectedTitle + '\n', - ' ---\n', - ' message: |-\n', - ' ' + expectedErrorMessage + '\n', - ' ...\n' - ]; - expect(stdout, 'to equal', expectedArray); + expect(stdout[1], 'to equal', expectedSecondLine); + expect(expectedString, 'to be', expectedSuite); }); }); - describe('if there is an error stack', function() { - it('should write expected message and stack', function() { - var expectedStack = 'some stack'; - var error = { - stack: expectedStack - }; + + describe('on pending', function() { + it('should write expected message including count and title', function() { runner = createMockRunner( - 'test end fail', + 'start test', 'test end', - 'fail', + 'pending', null, - test, - error + test ); runner.suite = ''; runner.grepTotal = function() {}; @@ -149,103 +93,185 @@ describe('TAP reporter', function() { process.stdout.write = stdoutWrite; - var expectedArray = [ - 'not ok ' + countAfterTestEnd + ' ' + expectedTitle + '\n', - ' ---\n', - ' stack: |-\n', - ' ' + expectedStack + '\n', - ' ...\n' - ]; - expect(stdout, 'to equal', expectedArray); + var expectedMessage = + 'ok ' + countAfterTestEnd + ' ' + expectedTitle + ' # SKIP -\n'; + expect(stdout[0], 'to equal', expectedMessage); }); }); - describe('if there is an error stack and error message', function() { - it('should write expected message and stack', function() { - var expectedTitle = 'some title'; - var countAfterTestEnd = 2; - var expectedStack = 'some stack'; - var expectedErrorMessage = 'some error'; - var test = { - fullTitle: function() { - return expectedTitle; - }, - slow: function() {} - }; - var error = { - stack: expectedStack, - message: expectedErrorMessage - }; - runner.on = function(event, callback) { - if (event === 'test end') { - callback(); - } - if (event === 'fail') { - callback(test, error); - } - }; + + describe('on pass', function() { + it('should write expected message including count and title', function() { + runner = createMockRunner('start test', 'test end', 'pass', null, test); + runner.suite = ''; runner.grepTotal = function() {}; TAP.call({}, runner); process.stdout.write = stdoutWrite; - var expectedArray = [ - 'not ok ' + countAfterTestEnd + ' ' + expectedTitle + '\n', - ' ---\n', - ' message: |-\n', - ' ' + expectedErrorMessage + '\n', - ' stack: |-\n', - ' ' + expectedStack + '\n', - ' ...\n' - ]; - expect(stdout, 'to equal', expectedArray); + var expectedMessage = + 'ok ' + countAfterTestEnd + ' ' + expectedTitle + '\n'; + expect(stdout[0], 'to equal', expectedMessage); }); }); - describe('if there is no error stack or error message', function() { - it('should write expected message only', function() { - var error = {}; - runner.on = runner.once = function(event, callback) { - if (event === 'test end') { - callback(); - } - if (event === 'fail') { - callback(test, error); - } - }; + + describe('on fail', function() { + describe('if there is an error message', function() { + it('should write expected message and error message', function() { + var expectedTitle = 'some title'; + var countAfterTestEnd = 2; + var expectedErrorMessage = 'some error'; + var test = { + fullTitle: function() { + return expectedTitle; + }, + slow: function() {} + }; + var error = { + message: expectedErrorMessage + }; + runner.on = function(event, callback) { + if (event === 'test end') { + callback(); + } + if (event === 'fail') { + callback(test, error); + } + }; + runner.suite = ''; + runner.grepTotal = function() {}; + TAP.call({}, runner); + + process.stdout.write = stdoutWrite; + + var expectedArray = [ + 'not ok ' + countAfterTestEnd + ' ' + expectedTitle + '\n', + ' ---\n', + ' message: |-\n', + ' ' + expectedErrorMessage + '\n', + ' ...\n' + ]; + expect(stdout, 'to equal', expectedArray); + }); + }); + describe('if there is an error stack', function() { + it('should write expected message and stack', function() { + var expectedStack = 'some stack'; + var error = { + stack: expectedStack + }; + runner = createMockRunner( + 'test end fail', + 'test end', + 'fail', + null, + test, + error + ); + runner.suite = ''; + runner.grepTotal = function() {}; + TAP.call({}, runner); + + process.stdout.write = stdoutWrite; + + var expectedArray = [ + 'not ok ' + countAfterTestEnd + ' ' + expectedTitle + '\n', + ' ---\n', + ' stack: |-\n', + ' ' + expectedStack + '\n', + ' ...\n' + ]; + expect(stdout, 'to equal', expectedArray); + }); + }); + describe('if there is an error stack and error message', function() { + it('should write expected message and stack', function() { + var expectedTitle = 'some title'; + var countAfterTestEnd = 2; + var expectedStack = 'some stack'; + var expectedErrorMessage = 'some error'; + var test = { + fullTitle: function() { + return expectedTitle; + }, + slow: function() {} + }; + var error = { + stack: expectedStack, + message: expectedErrorMessage + }; + runner.on = function(event, callback) { + if (event === 'test end') { + callback(); + } + if (event === 'fail') { + callback(test, error); + } + }; + runner.suite = ''; + runner.grepTotal = function() {}; + TAP.call({}, runner); + + process.stdout.write = stdoutWrite; + + var expectedArray = [ + 'not ok ' + countAfterTestEnd + ' ' + expectedTitle + '\n', + ' ---\n', + ' message: |-\n', + ' ' + expectedErrorMessage + '\n', + ' stack: |-\n', + ' ' + expectedStack + '\n', + ' ...\n' + ]; + expect(stdout, 'to equal', expectedArray); + }); + }); + describe('if there is no error stack or error message', function() { + it('should write expected message only', function() { + var error = {}; + runner.on = runner.once = function(event, callback) { + if (event === 'test end') { + callback(); + } + if (event === 'fail') { + callback(test, error); + } + }; + runner.suite = ''; + runner.grepTotal = function() {}; + TAP.call({}, runner); + + process.stdout.write = stdoutWrite; + + var expectedArray = [ + 'not ok ' + countAfterTestEnd + ' ' + expectedTitle + '\n' + ]; + expect(stdout, 'to equal', expectedArray); + }); + }); + }); + + describe('on end', function() { + it('should write total tests, passes and failures', function() { + var numberOfPasses = 1; + var numberOfFails = 1; + runner = createMockRunner('fail end pass', 'fail', 'end', 'pass', test); runner.suite = ''; runner.grepTotal = function() {}; TAP.call({}, runner); process.stdout.write = stdoutWrite; + var totalTests = numberOfPasses + numberOfFails; var expectedArray = [ - 'not ok ' + countAfterTestEnd + ' ' + expectedTitle + '\n' + 'ok ' + numberOfPasses + ' ' + expectedTitle + '\n', + 'not ok ' + numberOfFails + ' ' + expectedTitle + '\n', + '# tests ' + totalTests + '\n', + '# pass ' + numberOfPasses + '\n', + '# fail ' + numberOfFails + '\n' ]; expect(stdout, 'to equal', expectedArray); }); }); }); - - describe('on end', function() { - it('should write total tests, passes and failures', function() { - var numberOfPasses = 1; - var numberOfFails = 1; - runner = createMockRunner('fail end pass', 'fail', 'end', 'pass', test); - runner.suite = ''; - runner.grepTotal = function() {}; - TAP.call({}, runner); - - process.stdout.write = stdoutWrite; - - var totalTests = numberOfPasses + numberOfFails; - var expectedArray = [ - 'ok ' + numberOfPasses + ' ' + expectedTitle + '\n', - 'not ok ' + numberOfFails + ' ' + expectedTitle + '\n', - '# tests ' + totalTests + '\n', - '# pass ' + numberOfPasses + '\n', - '# fail ' + numberOfFails + '\n' - ]; - expect(stdout, 'to equal', expectedArray); - }); - }); }); From 61e8c799fea79dfcc71221a036d988b2301e2c5d Mon Sep 17 00:00:00 2001 From: Tobias Mollstam Date: Thu, 20 Sep 2018 14:15:13 +0200 Subject: [PATCH 24/80] TAP13 tests now run with proper spec selected --- test/reporters/tap.spec.js | 117 ++++++++++++++++++++++++++++++++++--- 1 file changed, 110 insertions(+), 7 deletions(-) diff --git a/test/reporters/tap.spec.js b/test/reporters/tap.spec.js index 5d43268f1c..b292f9806a 100644 --- a/test/reporters/tap.spec.js +++ b/test/reporters/tap.spec.js @@ -53,6 +53,109 @@ describe('TAP reporter', function() { expect(expectedString, 'to be', expectedSuite); }); }); + + describe('on fail', function() { + describe('if there is an error message', function() { + it('should write expected message and error message', function() { + var expectedTitle = 'some title'; + var countAfterTestEnd = 2; + var expectedErrorMessage = 'some error'; + var test = { + fullTitle: function() { + return expectedTitle; + }, + slow: function() {} + }; + var error = { + message: expectedErrorMessage + }; + runner.on = function(event, callback) { + if (event === 'test end') { + callback(); + } + if (event === 'fail') { + callback(test, error); + } + }; + runner.suite = ''; + runner.grepTotal = function() {}; + TAP.call({}, runner); + + process.stdout.write = stdoutWrite; + + var expectedArray = [ + 'not ok ' + countAfterTestEnd + ' ' + expectedTitle + '\n', + ' ' + expectedErrorMessage + '\n' + ]; + expect(stdout, 'to equal', expectedArray); + }); + }); + describe('if there is an error stack', function() { + it('should write expected message and stack', function() { + var expectedStack = 'some stack'; + var error = { + stack: expectedStack + }; + runner = createMockRunner( + 'test end fail', + 'test end', + 'fail', + null, + test, + error + ); + runner.suite = ''; + runner.grepTotal = function() {}; + TAP.call({}, runner); + + process.stdout.write = stdoutWrite; + + var expectedArray = [ + 'not ok ' + countAfterTestEnd + ' ' + expectedTitle + '\n', + ' ' + expectedStack + '\n' + ]; + expect(stdout, 'to equal', expectedArray); + }); + }); + describe('if there is an error stack and error message', function() { + it('should write expected message and stack', function() { + var expectedTitle = 'some title'; + var countAfterTestEnd = 2; + var expectedStack = 'some stack'; + var expectedErrorMessage = 'some error'; + var test = { + fullTitle: function() { + return expectedTitle; + }, + slow: function() {} + }; + var error = { + stack: expectedStack, + message: expectedErrorMessage + }; + runner.on = function(event, callback) { + if (event === 'test end') { + callback(); + } + if (event === 'fail') { + callback(test, error); + } + }; + runner.suite = ''; + runner.grepTotal = function() {}; + TAP.call({}, runner); + + process.stdout.write = stdoutWrite; + + var expectedArray = [ + 'not ok ' + countAfterTestEnd + ' ' + expectedTitle + '\n', + ' ' + expectedErrorMessage + '\n', + ' ' + expectedStack + '\n' + ]; + expect(stdout, 'to equal', expectedArray); + }); + }); + }); }); describe('TAP13 spec', function() { @@ -89,7 +192,7 @@ describe('TAP reporter', function() { ); runner.suite = ''; runner.grepTotal = function() {}; - TAP.call({}, runner); + TAP.call({}, runner, options); process.stdout.write = stdoutWrite; @@ -105,7 +208,7 @@ describe('TAP reporter', function() { runner.suite = ''; runner.grepTotal = function() {}; - TAP.call({}, runner); + TAP.call({}, runner, options); process.stdout.write = stdoutWrite; @@ -140,7 +243,7 @@ describe('TAP reporter', function() { }; runner.suite = ''; runner.grepTotal = function() {}; - TAP.call({}, runner); + TAP.call({}, runner, options); process.stdout.write = stdoutWrite; @@ -170,7 +273,7 @@ describe('TAP reporter', function() { ); runner.suite = ''; runner.grepTotal = function() {}; - TAP.call({}, runner); + TAP.call({}, runner, options); process.stdout.write = stdoutWrite; @@ -210,7 +313,7 @@ describe('TAP reporter', function() { }; runner.suite = ''; runner.grepTotal = function() {}; - TAP.call({}, runner); + TAP.call({}, runner, options); process.stdout.write = stdoutWrite; @@ -239,7 +342,7 @@ describe('TAP reporter', function() { }; runner.suite = ''; runner.grepTotal = function() {}; - TAP.call({}, runner); + TAP.call({}, runner, options); process.stdout.write = stdoutWrite; @@ -258,7 +361,7 @@ describe('TAP reporter', function() { runner = createMockRunner('fail end pass', 'fail', 'end', 'pass', test); runner.suite = ''; runner.grepTotal = function() {}; - TAP.call({}, runner); + TAP.call({}, runner, options); process.stdout.write = stdoutWrite; From 8dc5b6589396ecea408cd111b5dd9f414cf9b796 Mon Sep 17 00:00:00 2001 From: Tobias Mollstam Date: Thu, 20 Sep 2018 14:16:45 +0200 Subject: [PATCH 25/80] extracted fail reporting to producers --- lib/reporters/tap.js | 50 ++++++++++++++++++++++++++++---------------- 1 file changed, 32 insertions(+), 18 deletions(-) diff --git a/lib/reporters/tap.js b/lib/reporters/tap.js index b9c073830e..7331d8216f 100644 --- a/lib/reporters/tap.js +++ b/lib/reporters/tap.js @@ -31,10 +31,6 @@ function TAP(runner, options) { var self = this; var n = 1; - function yamlIndent(level) { - return Array(level + 1).join(' '); - } - var tapSpec = '12'; if (options && options.reporterOptions) { if (options.reporterOptions.spec) { @@ -65,20 +61,7 @@ function TAP(runner, options) { }); runner.on('fail', function(test, err) { - console.log('not ok %d %s', n, title(test)); - var emitYamlBlock = err.message != null || err.stack != null; - if (emitYamlBlock) { - console.log(yamlIndent(1) + '---'); - if (err.message) { - console.log(yamlIndent(2) + 'message: |-'); - console.log(err.message.replace(/^/gm, yamlIndent(3))); - } - if (err.stack) { - console.log(yamlIndent(2) + 'stack: |-'); - console.log(err.stack.replace(/^/gm, yamlIndent(3))); - } - console.log(yamlIndent(1) + '...'); - } + self.producer.writeFail(n, test, err); }); runner.once('end', function() { @@ -111,10 +94,41 @@ TAP12Producer.prototype.writeStart = function(runner) { console.log('%d..%d', 1, total); }; +TAP12Producer.prototype.writeFail = function(n, test, err) { + console.log('not ok %d %s', n, title(test)); + if (err.message) { + console.log(err.message.replace(/^/gm, ' ')); + } + if (err.stack) { + console.log(err.stack.replace(/^/gm, ' ')); + } +}; + function TAP13Producer() {} +TAP13Producer.prototype.yamlIndent = function(level) { + return Array(level + 1).join(' '); +}; + TAP13Producer.prototype.writeStart = function(runner) { console.log('TAP version 13'); var total = runner.grepTotal(runner.suite); console.log('%d..%d', 1, total); }; + +TAP13Producer.prototype.writeFail = function(n, test, err) { + console.log('not ok %d %s', n, title(test)); + var emitYamlBlock = err.message != null || err.stack != null; + if (emitYamlBlock) { + console.log(this.yamlIndent(1) + '---'); + if (err.message) { + console.log(this.yamlIndent(2) + 'message: |-'); + console.log(err.message.replace(/^/gm, this.yamlIndent(3))); + } + if (err.stack) { + console.log(this.yamlIndent(2) + 'stack: |-'); + console.log(err.stack.replace(/^/gm, this.yamlIndent(3))); + } + console.log(this.yamlIndent(1) + '...'); + } +}; From 4b11a319436732d12e05aae8b6ace6be69440249 Mon Sep 17 00:00:00 2001 From: Tobias Mollstam Date: Thu, 20 Sep 2018 14:25:04 +0200 Subject: [PATCH 26/80] added missing tests to TAP12 spec which are identical to TAP13 expectations --- test/reporters/tap.spec.js | 82 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 82 insertions(+) diff --git a/test/reporters/tap.spec.js b/test/reporters/tap.spec.js index b292f9806a..ef72ee0c26 100644 --- a/test/reporters/tap.spec.js +++ b/test/reporters/tap.spec.js @@ -54,6 +54,43 @@ describe('TAP reporter', function() { }); }); + describe('on pending', function() { + it('should write expected message including count and title', function() { + runner = createMockRunner( + 'start test', + 'test end', + 'pending', + null, + test + ); + runner.suite = ''; + runner.grepTotal = function() {}; + TAP.call({}, runner); + + process.stdout.write = stdoutWrite; + + var expectedMessage = + 'ok ' + countAfterTestEnd + ' ' + expectedTitle + ' # SKIP -\n'; + expect(stdout[0], 'to equal', expectedMessage); + }); + }); + + describe('on pass', function() { + it('should write expected message including count and title', function() { + runner = createMockRunner('start test', 'test end', 'pass', null, test); + + runner.suite = ''; + runner.grepTotal = function() {}; + TAP.call({}, runner); + + process.stdout.write = stdoutWrite; + + var expectedMessage = + 'ok ' + countAfterTestEnd + ' ' + expectedTitle + '\n'; + expect(stdout[0], 'to equal', expectedMessage); + }); + }); + describe('on fail', function() { describe('if there is an error message', function() { it('should write expected message and error message', function() { @@ -155,6 +192,51 @@ describe('TAP reporter', function() { expect(stdout, 'to equal', expectedArray); }); }); + describe('if there is no error stack or error message', function() { + it('should write expected message only', function() { + var error = {}; + runner.on = runner.once = function(event, callback) { + if (event === 'test end') { + callback(); + } + if (event === 'fail') { + callback(test, error); + } + }; + runner.suite = ''; + runner.grepTotal = function() {}; + TAP.call({}, runner); + + process.stdout.write = stdoutWrite; + + var expectedArray = [ + 'not ok ' + countAfterTestEnd + ' ' + expectedTitle + '\n' + ]; + expect(stdout, 'to equal', expectedArray); + }); + }); + }); + describe('on end', function() { + it('should write total tests, passes and failures', function() { + var numberOfPasses = 1; + var numberOfFails = 1; + runner = createMockRunner('fail end pass', 'fail', 'end', 'pass', test); + runner.suite = ''; + runner.grepTotal = function() {}; + TAP.call({}, runner); + + process.stdout.write = stdoutWrite; + + var totalTests = numberOfPasses + numberOfFails; + var expectedArray = [ + 'ok ' + numberOfPasses + ' ' + expectedTitle + '\n', + 'not ok ' + numberOfFails + ' ' + expectedTitle + '\n', + '# tests ' + totalTests + '\n', + '# pass ' + numberOfPasses + '\n', + '# fail ' + numberOfFails + '\n' + ]; + expect(stdout, 'to equal', expectedArray); + }); }); }); From 38b8ce3d16841bfce4edc75f8199874a510999a0 Mon Sep 17 00:00:00 2001 From: Tobias Mollstam Date: Thu, 20 Sep 2018 14:27:01 +0200 Subject: [PATCH 27/80] extracted pending reporting to producers --- lib/reporters/tap.js | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/lib/reporters/tap.js b/lib/reporters/tap.js index 7331d8216f..c88cbb12d8 100644 --- a/lib/reporters/tap.js +++ b/lib/reporters/tap.js @@ -53,7 +53,7 @@ function TAP(runner, options) { }); runner.on('pending', function(test) { - console.log('ok %d %s # SKIP -', n, title(test)); + self.producer.writePending(n, test); }); runner.on('pass', function(test) { @@ -104,6 +104,10 @@ TAP12Producer.prototype.writeFail = function(n, test, err) { } }; +TAP12Producer.prototype.writePending = function(n, test) { + console.log('ok %d %s # SKIP -', n, title(test)); +}; + function TAP13Producer() {} TAP13Producer.prototype.yamlIndent = function(level) { @@ -132,3 +136,7 @@ TAP13Producer.prototype.writeFail = function(n, test, err) { console.log(this.yamlIndent(1) + '...'); } }; + +TAP13Producer.prototype.writePending = function(n, test) { + console.log('ok %d %s # SKIP -', n, title(test)); +}; From 7951bf07165c7494e28058d734eb4b02de7b98f9 Mon Sep 17 00:00:00 2001 From: Tobias Mollstam Date: Thu, 20 Sep 2018 16:16:06 +0200 Subject: [PATCH 28/80] extracted pass reporting to producers --- lib/reporters/tap.js | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/lib/reporters/tap.js b/lib/reporters/tap.js index c88cbb12d8..de844d3839 100644 --- a/lib/reporters/tap.js +++ b/lib/reporters/tap.js @@ -57,7 +57,7 @@ function TAP(runner, options) { }); runner.on('pass', function(test) { - console.log('ok %d %s', n, title(test)); + self.producer.writePass(n, test); }); runner.on('fail', function(test, err) { @@ -108,6 +108,10 @@ TAP12Producer.prototype.writePending = function(n, test) { console.log('ok %d %s # SKIP -', n, title(test)); }; +TAP12Producer.prototype.writePass = function(n, test) { + console.log('ok %d %s', n, title(test)); +}; + function TAP13Producer() {} TAP13Producer.prototype.yamlIndent = function(level) { @@ -140,3 +144,7 @@ TAP13Producer.prototype.writeFail = function(n, test, err) { TAP13Producer.prototype.writePending = function(n, test) { console.log('ok %d %s # SKIP -', n, title(test)); }; + +TAP13Producer.prototype.writePass = function(n, test) { + console.log('ok %d %s', n, title(test)); +}; From ff80cf4b5e29018086abe3e9fa8297e2aeb3fccf Mon Sep 17 00:00:00 2001 From: Tobias Mollstam Date: Thu, 20 Sep 2018 16:30:47 +0200 Subject: [PATCH 29/80] extracted end reporting to producers and refactored to common base --- lib/reporters/tap.js | 57 +++++++++++++++++++++++++++++++------------- 1 file changed, 40 insertions(+), 17 deletions(-) diff --git a/lib/reporters/tap.js b/lib/reporters/tap.js index de844d3839..93da27dcdf 100644 --- a/lib/reporters/tap.js +++ b/lib/reporters/tap.js @@ -65,9 +65,7 @@ function TAP(runner, options) { }); runner.once('end', function() { - console.log('# tests ' + (runner.stats.passes + runner.stats.failures)); - console.log('# pass ' + runner.stats.passes); - console.log('# fail ' + runner.stats.failures); + self.producer.writeEnd(runner); }); } @@ -87,6 +85,36 @@ function title(test) { */ inherits(TAP, Base); +/** + * Initialize a new `TAPProducer`. Should only be used as a base class. + * + * @private + * @class + * @api private + */ +function TAPProducer() {} + +TAPProducer.prototype.writePending = function(n, test) { + console.log('ok %d %s # SKIP -', n, title(test)); +}; + +TAPProducer.prototype.writePass = function(n, test) { + console.log('ok %d %s', n, title(test)); +}; + +TAPProducer.prototype.writeEnd = function(runner) { + console.log('# tests ' + (runner.stats.passes + runner.stats.failures)); + console.log('# pass ' + runner.stats.passes); + console.log('# fail ' + runner.stats.failures); +}; + +/** + * Initialize a new `TAP12Producer` which will produce output conforming to the TAP12 spec. + * + * @private + * @class + * @api private + */ function TAP12Producer() {} TAP12Producer.prototype.writeStart = function(runner) { @@ -104,14 +132,15 @@ TAP12Producer.prototype.writeFail = function(n, test, err) { } }; -TAP12Producer.prototype.writePending = function(n, test) { - console.log('ok %d %s # SKIP -', n, title(test)); -}; - -TAP12Producer.prototype.writePass = function(n, test) { - console.log('ok %d %s', n, title(test)); -}; +inherits(TAP12Producer, TAPProducer); +/** + * Initialize a new `TAP13Producer` which will produce output conforming to the TAP13 spec. + * + * @private + * @class + * @api private + */ function TAP13Producer() {} TAP13Producer.prototype.yamlIndent = function(level) { @@ -141,10 +170,4 @@ TAP13Producer.prototype.writeFail = function(n, test, err) { } }; -TAP13Producer.prototype.writePending = function(n, test) { - console.log('ok %d %s # SKIP -', n, title(test)); -}; - -TAP13Producer.prototype.writePass = function(n, test) { - console.log('ok %d %s', n, title(test)); -}; +inherits(TAP13Producer, TAPProducer); From 0496ef348bd53dd9569f91c1e366e91cda046269 Mon Sep 17 00:00:00 2001 From: Tobias Mollstam Date: Thu, 20 Sep 2018 16:34:33 +0200 Subject: [PATCH 30/80] fixed bad redeclare --- lib/reporters/tap.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/reporters/tap.js b/lib/reporters/tap.js index 93da27dcdf..e8ec3aa3dd 100644 --- a/lib/reporters/tap.js +++ b/lib/reporters/tap.js @@ -34,7 +34,7 @@ function TAP(runner, options) { var tapSpec = '12'; if (options && options.reporterOptions) { if (options.reporterOptions.spec) { - var tapSpec = options.reporterOptions.spec; + tapSpec = options.reporterOptions.spec; } } From 42035e737af1ae1532db20538023bdef89ab2d04 Mon Sep 17 00:00:00 2001 From: Tobias Mollstam Date: Thu, 20 Sep 2018 16:44:02 +0200 Subject: [PATCH 31/80] fix to integrations tests to use TAP13 spec --- test/integration/reporters.spec.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/integration/reporters.spec.js b/test/integration/reporters.spec.js index 12c87fd5d2..cd6910f7df 100644 --- a/test/integration/reporters.spec.js +++ b/test/integration/reporters.spec.js @@ -134,7 +134,7 @@ describe('reporters', function() { describe('produces valid TAP v13 output', function() { var runFixtureAndValidateOutput = function(fixture, expected) { it('for ' + fixture, function(done) { - var args = ['--reporter=tap']; + var args = ['--reporter=tap', '--reporter-options', 'spec=13']; run(fixture, args, function(err, res) { if (err) { done(err); @@ -196,7 +196,7 @@ describe('reporters', function() { }); it('places exceptions correctly in YAML blocks', function(done) { - var args = ['--reporter=tap']; + var args = ['--reporter=tap', '--reporter-options', 'spec=13']; run('reporters.fixture.js', args, function(err, res) { if (err) { done(err); From 03e3440297ea3c36d76433ee4b1a20ee983af888 Mon Sep 17 00:00:00 2001 From: Paul Roebuck Date: Sun, 4 Nov 2018 11:10:11 -0600 Subject: [PATCH 32/80] feat(reporters/tap.js): Make TAP reporter conform to TAP13 Finished up TAPProducer OO overhaul. Documented everything. Renamed reporter option field as `tapVersion`. Additional checking of input. Reworked some event listener arguments. Replaced `console.log` with `process.stdout` stream. Documented everything. --- lib/reporters/tap.js | 272 ++++++++++++++++++++++++++++++------------- 1 file changed, 192 insertions(+), 80 deletions(-) diff --git a/lib/reporters/tap.js b/lib/reporters/tap.js index e8ec3aa3dd..7a591db7bc 100644 --- a/lib/reporters/tap.js +++ b/lib/reporters/tap.js @@ -6,8 +6,10 @@ * Module dependencies. */ +var util = require('util'); var Base = require('./base'); var inherits = require('../utils').inherits; +var sprintf = util.format; /** * Expose `TAP`. @@ -16,36 +18,34 @@ var inherits = require('../utils').inherits; exports = module.exports = TAP; /** - * Initialize a new `TAP` reporter. + * Constructs a new `TAP` reporter instance. * * @public * @class - * @memberof Mocha.reporters * @extends Mocha.reporters.Base - * @api public - * @param {Runner} runner + * @memberof Mocha.reporters + * @param {Runner} runner - Instance triggers reporter actions. + * @param {Object} [options] - runner options */ function TAP(runner, options) { - Base.call(this, runner); + Base.call(this, runner, options); var self = this; var n = 1; - var tapSpec = '12'; + var tapVersion = '12'; if (options && options.reporterOptions) { - if (options.reporterOptions.spec) { - tapSpec = options.reporterOptions.spec; + if (options.reporterOptions.tapVersion) { + tapVersion = options.reporterOptions.tapVersion.toString(); } } - if (tapSpec === '13') { - this.producer = new TAP13Producer(); - } else { - this.producer = new TAP12Producer(); - } + this._producer = createProducer(tapVersion); - runner.on('start', function() { - self.producer.writeStart(runner); + runner.once('start', function() { + var ntests = runner.grepTotal(runner.suite); + self._producer.writeVersion(); + self._producer.writePlan(ntests); }); runner.on('test end', function() { @@ -53,121 +53,233 @@ function TAP(runner, options) { }); runner.on('pending', function(test) { - self.producer.writePending(n, test); + self._producer.writePending(n, test); }); runner.on('pass', function(test) { - self.producer.writePass(n, test); + self._producer.writePass(n, test); }); runner.on('fail', function(test, err) { - self.producer.writeFail(n, test, err); + self._producer.writeFail(n, test, err); }); runner.once('end', function() { - self.producer.writeEnd(runner); + self._producer.writeEpilogue(runner.stats); }); } /** - * Return a TAP-safe title of `test` + * Inherit from `Base.prototype`. + */ +inherits(TAP, Base); + +/** + * Returns a TAP-safe title of `test`. * - * @api private - * @param {Object} test - * @return {String} + * @private + * @param {Test} test - Test instance. + * @return {String} title with any hash character removed */ function title(test) { return test.fullTitle().replace(/#/g, ''); } /** - * Inherit from `Base.prototype`. + * Writes newline-terminated formatted string to reporter output stream. + * + * @private + * @param {string} format - `printf`-like format string + * @param {...*} [varArgs] - Format string arguments */ -inherits(TAP, Base); +function println(format, varArgs) { + var vargs = Array.from(arguments); + vargs[0] += '\n'; + process.stdout.write(sprintf.apply(null, vargs)); +} /** - * Initialize a new `TAPProducer`. Should only be used as a base class. + * Returns a `tapVersion`-appropriate TAP producer instance, if possible. * * @private - * @class - * @api private + * @param {string} tapVersion - Version of TAP specification to produce. + * @returns {TAPProducer} specification-appropriate instance + * @throws {Error} if specification version has no associated producer. */ -function TAPProducer() {} - -TAPProducer.prototype.writePending = function(n, test) { - console.log('ok %d %s # SKIP -', n, title(test)); -}; +function createProducer(tapVersion) { + var producers = { + '12': new TAP12Producer(), + '13': new TAP13Producer() + }; + var producer = producers[tapVersion]; -TAPProducer.prototype.writePass = function(n, test) { - console.log('ok %d %s', n, title(test)); -}; + if (!producer) { + throw new Error( + 'invalid or unsupported TAP version: ' + JSON.stringify(tapVersion) + ); + } -TAPProducer.prototype.writeEnd = function(runner) { - console.log('# tests ' + (runner.stats.passes + runner.stats.failures)); - console.log('# pass ' + runner.stats.passes); - console.log('# fail ' + runner.stats.failures); -}; + return producer; +} /** - * Initialize a new `TAP12Producer` which will produce output conforming to the TAP12 spec. + * @summary + * Constructs a new `TAPProducer` instance. + * + * @description + * Should only be used as an abstract base class. * * @private - * @class - * @api private + * @constructor */ -function TAP12Producer() {} +function TAPProducer() {} -TAP12Producer.prototype.writeStart = function(runner) { - var total = runner.grepTotal(runner.suite); - console.log('%d..%d', 1, total); -}; +/** + * Writes the TAP version to reporter output stream. + * + * @abstract + */ +TAPProducer.prototype.writeVersion = function() {}; -TAP12Producer.prototype.writeFail = function(n, test, err) { - console.log('not ok %d %s', n, title(test)); - if (err.message) { - console.log(err.message.replace(/^/gm, ' ')); - } - if (err.stack) { - console.log(err.stack.replace(/^/gm, ' ')); - } +/** + * Writes the plan to reporter output stream. + * + * @abstract + * @param {number} ntests - Number of tests that are planned to run. + */ +TAPProducer.prototype.writePlan = function(ntests) { + println('%d..%d', 1, ntests); }; -inherits(TAP12Producer, TAPProducer); +/** + * Writes that test passed to reporter output stream. + * + * @abstract + * @param {number} n - Index of test that passed. + * @param {Test} test - Instance containing test information. + */ +TAPProducer.prototype.writePass = function(n, test) { + println('ok %d %s', n, title(test)); +}; /** - * Initialize a new `TAP13Producer` which will produce output conforming to the TAP13 spec. + * Writes that test was skipped to reporter output stream. * - * @private - * @class - * @api private + * @abstract + * @param {number} n - Index of test that was skipped. + * @param {Test} test - Instance containing test information. */ -function TAP13Producer() {} +TAPProducer.prototype.writePending = function(n, test) { + println('ok %d %s # SKIP -', n, title(test)); +}; -TAP13Producer.prototype.yamlIndent = function(level) { - return Array(level + 1).join(' '); +/** + * Writes that test failed to reporter output stream. + * + * @abstract + * @param {number} n - Index of test that was skipped. + * @param {Test} test - Instance containing test information. + * @param {Error} err - Reason the test failed. + */ +TAPProducer.prototype.writeFail = function(n, test, err) { + println('not ok %d %s', n, title(test)); }; -TAP13Producer.prototype.writeStart = function(runner) { - console.log('TAP version 13'); - var total = runner.grepTotal(runner.suite); - console.log('%d..%d', 1, total); +/** + * Writes the summary epilogue to reporter output stream. + * + * @abstract + * @param {Object} stats - Object containing run statistics. + */ +TAPProducer.prototype.writeEpilogue = function(stats) { + // :TBD: Why is this not counting pending tests? + println('# tests ' + (stats.passes + stats.failures)); + println('# pass ' + stats.passes); + // :TBD: Why are we not showing pending results? + println('# fail ' + stats.failures); }; -TAP13Producer.prototype.writeFail = function(n, test, err) { - console.log('not ok %d %s', n, title(test)); - var emitYamlBlock = err.message != null || err.stack != null; - if (emitYamlBlock) { - console.log(this.yamlIndent(1) + '---'); +/** + * @summary + * Constructs a new `TAP12Producer` instance. + * + * @description + * It will produce output conforming to the TAP12 spec. + * + * @private + * @constructor + * @extends TAPProducer + * @see {@link https://testanything.org/tap-specification.html|Specification} + */ +function TAP12Producer() { + /** + * Writes that test failed to reporter output stream, with error formatting. + * @override + */ + this.writeFail = function(n, test, err) { + TAPProducer.prototype.writeFail.call(this, n, test, err); if (err.message) { - console.log(this.yamlIndent(2) + 'message: |-'); - console.log(err.message.replace(/^/gm, this.yamlIndent(3))); + println(err.message.replace(/^/gm, ' ')); } if (err.stack) { - console.log(this.yamlIndent(2) + 'stack: |-'); - console.log(err.stack.replace(/^/gm, this.yamlIndent(3))); + println(err.stack.replace(/^/gm, ' ')); } - console.log(this.yamlIndent(1) + '...'); + }; +} + +/** + * Inherit from `TAPProducer.prototype`. + */ +inherits(TAP12Producer, TAPProducer); + +/** + * @summary + * Constructs a new `TAP13Producer` instance. + * + * @description + * It will produce output conforming to the TAP13 spec. + * + * @private + * @constructor + * @extends TAPProducer + * @see {@link https://testanything.org/tap-version-13-specification.html|Specification} + */ +function TAP13Producer() { + /** + * Writes the TAP version to reporter output stream. + * @override + */ + this.writeVersion = function() { + println('TAP version 13'); + }; + + /** + * Writes that test failed to reporter output stream, with error formatting. + * @override + */ + this.writeFail = function(n, test, err) { + TAPProducer.prototype.writeFail.call(this, n, test, err); + var emitYamlBlock = err.message != null || err.stack != null; + if (emitYamlBlock) { + println(indent(1) + '---'); + if (err.message) { + println(indent(2) + 'message: |-'); + println(err.message.replace(/^/gm, indent(3))); + } + if (err.stack) { + println(indent(2) + 'stack: |-'); + println(err.stack.replace(/^/gm, indent(3))); + } + println(indent(1) + '...'); + } + }; + + function indent(level) { + return Array(level + 1).join(' '); } -}; +} +/** + * Inherit from `TAPProducer.prototype`. + */ inherits(TAP13Producer, TAPProducer); From b060a14880363f5fdb3bc67c41b7a04bbd427b6e Mon Sep 17 00:00:00 2001 From: Paul Roebuck Date: Sun, 4 Nov 2018 11:13:24 -0600 Subject: [PATCH 33/80] test(reporters.spec.js,tap.spec.js): Updated to match `tapVersion` reporter option Converted `spec` to `tapVersion` for `reporterOptions` cmdline args. --- test/integration/reporters.spec.js | 6 ++-- test/reporters/tap.spec.js | 45 +++++++++++++++--------------- 2 files changed, 26 insertions(+), 25 deletions(-) diff --git a/test/integration/reporters.spec.js b/test/integration/reporters.spec.js index cd6910f7df..c78da8ab6b 100644 --- a/test/integration/reporters.spec.js +++ b/test/integration/reporters.spec.js @@ -134,7 +134,8 @@ describe('reporters', function() { describe('produces valid TAP v13 output', function() { var runFixtureAndValidateOutput = function(fixture, expected) { it('for ' + fixture, function(done) { - var args = ['--reporter=tap', '--reporter-options', 'spec=13']; + var args = ['--reporter=tap', '--reporter-options', 'tapVersion=13']; + run(fixture, args, function(err, res) { if (err) { done(err); @@ -196,7 +197,8 @@ describe('reporters', function() { }); it('places exceptions correctly in YAML blocks', function(done) { - var args = ['--reporter=tap', '--reporter-options', 'spec=13']; + var args = ['--reporter=tap', '--reporter-options', 'tapVersion=13']; + run('reporters.fixture.js', args, function(err, res) { if (err) { done(err); diff --git a/test/reporters/tap.spec.js b/test/reporters/tap.spec.js index ef72ee0c26..2ac4eb2c2b 100644 --- a/test/reporters/tap.spec.js +++ b/test/reporters/tap.spec.js @@ -33,8 +33,9 @@ describe('TAP reporter', function() { }); describe('TAP12 spec', function() { + var options = {}; describe('on start', function() { - it('should hand runners suite into grepTotal and log the total', function() { + it('should write the number of tests that it plans to run', function() { var expectedSuite = 'some suite'; var expectedTotal = 10; var expectedString; @@ -44,7 +45,7 @@ describe('TAP reporter', function() { expectedString = string; return expectedTotal; }; - TAP.call({}, runner); + TAP.call({}, runner, options); var expectedArray = ['1..' + expectedTotal + '\n']; process.stdout.write = stdoutWrite; @@ -65,7 +66,7 @@ describe('TAP reporter', function() { ); runner.suite = ''; runner.grepTotal = function() {}; - TAP.call({}, runner); + TAP.call({}, runner, options); process.stdout.write = stdoutWrite; @@ -81,7 +82,7 @@ describe('TAP reporter', function() { runner.suite = ''; runner.grepTotal = function() {}; - TAP.call({}, runner); + TAP.call({}, runner, options); process.stdout.write = stdoutWrite; @@ -109,14 +110,13 @@ describe('TAP reporter', function() { runner.on = function(event, callback) { if (event === 'test end') { callback(); - } - if (event === 'fail') { + } else if (event === 'fail') { callback(test, error); } }; runner.suite = ''; runner.grepTotal = function() {}; - TAP.call({}, runner); + TAP.call({}, runner, options); process.stdout.write = stdoutWrite; @@ -143,7 +143,7 @@ describe('TAP reporter', function() { ); runner.suite = ''; runner.grepTotal = function() {}; - TAP.call({}, runner); + TAP.call({}, runner, options); process.stdout.write = stdoutWrite; @@ -173,14 +173,13 @@ describe('TAP reporter', function() { runner.on = function(event, callback) { if (event === 'test end') { callback(); - } - if (event === 'fail') { + } else if (event === 'fail') { callback(test, error); } }; runner.suite = ''; runner.grepTotal = function() {}; - TAP.call({}, runner); + TAP.call({}, runner, options); process.stdout.write = stdoutWrite; @@ -198,14 +197,13 @@ describe('TAP reporter', function() { runner.on = runner.once = function(event, callback) { if (event === 'test end') { callback(); - } - if (event === 'fail') { + } else if (event === 'fail') { callback(test, error); } }; runner.suite = ''; runner.grepTotal = function() {}; - TAP.call({}, runner); + TAP.call({}, runner, options); process.stdout.write = stdoutWrite; @@ -223,7 +221,7 @@ describe('TAP reporter', function() { runner = createMockRunner('fail end pass', 'fail', 'end', 'pass', test); runner.suite = ''; runner.grepTotal = function() {}; - TAP.call({}, runner); + TAP.call({}, runner, options); process.stdout.write = stdoutWrite; @@ -241,9 +239,13 @@ describe('TAP reporter', function() { }); describe('TAP13 spec', function() { - var options = {reporterOptions: {spec: '13'}}; + var options = { + reporterOptions: { + tapVersion: '13' + } + }; describe('on start', function() { - it('should hand runners suite into grepTotal and log the total', function() { + it('should write the number of tests that it plans to run', function() { var expectedSuite = 'some suite'; var expectedTotal = 10; var expectedString; @@ -318,8 +320,7 @@ describe('TAP reporter', function() { runner.on = function(event, callback) { if (event === 'test end') { callback(); - } - if (event === 'fail') { + } else if (event === 'fail') { callback(test, error); } }; @@ -388,8 +389,7 @@ describe('TAP reporter', function() { runner.on = function(event, callback) { if (event === 'test end') { callback(); - } - if (event === 'fail') { + } else if (event === 'fail') { callback(test, error); } }; @@ -417,8 +417,7 @@ describe('TAP reporter', function() { runner.on = runner.once = function(event, callback) { if (event === 'test end') { callback(); - } - if (event === 'fail') { + } else if (event === 'fail') { callback(test, error); } }; From 76a74bd9acb37395a137c023b0cd7b93f09fa122 Mon Sep 17 00:00:00 2001 From: Tobias Mollstam Date: Wed, 8 Aug 2018 09:30:30 +0200 Subject: [PATCH 34/80] added test to make sure first line of report is version line --- test/reporters/tap.spec.js | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/test/reporters/tap.spec.js b/test/reporters/tap.spec.js index 5030cbde5c..945a6d0e83 100644 --- a/test/reporters/tap.spec.js +++ b/test/reporters/tap.spec.js @@ -33,6 +33,17 @@ describe('TAP reporter', function() { }); describe('on start', function() { + it('should write expected version on first line', function() { + runner = createMockRunner('start', 'start'); + runner.grepTotal = function(string) {}; + TAP.call({}, runner); + + var expectedFirstLine = 'TAP version 13\n'; + process.stdout.write = stdoutWrite; + + expect(stdout[0], 'to equal', expectedFirstLine); + }); + it('should hand runners suite into grepTotal and log the total', function() { var expectedSuite = 'some suite'; var expectedTotal = 10; From 7e72ff9ce13e819dd1d432f9d4df002e856960f6 Mon Sep 17 00:00:00 2001 From: Tobias Mollstam Date: Wed, 8 Aug 2018 09:31:24 +0200 Subject: [PATCH 35/80] reporter will now print version line as first line --- lib/reporters/tap.js | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/reporters/tap.js b/lib/reporters/tap.js index feaf7ea21a..1c7b55931a 100644 --- a/lib/reporters/tap.js +++ b/lib/reporters/tap.js @@ -32,6 +32,7 @@ function TAP(runner) { var failures = 0; runner.on('start', function() { + console.log('TAP version 13'); var total = runner.grepTotal(runner.suite); console.log('%d..%d', 1, total); }); From ce0eecd771f44f9ddad9b8b3ebe0f43af6fcebe5 Mon Sep 17 00:00:00 2001 From: Tobias Mollstam Date: Wed, 8 Aug 2018 09:34:13 +0200 Subject: [PATCH 36/80] Changed test to look for plan (1..N) on second line. Was failing since it matched entire output array and first line is now the version line per TAP v13 spec. --- test/reporters/tap.spec.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/reporters/tap.spec.js b/test/reporters/tap.spec.js index 945a6d0e83..175ad4b2c1 100644 --- a/test/reporters/tap.spec.js +++ b/test/reporters/tap.spec.js @@ -56,10 +56,10 @@ describe('TAP reporter', function() { }; TAP.call({}, runner); - var expectedArray = ['1..' + expectedTotal + '\n']; + var expectedSecondLine = '1..' + expectedTotal + '\n'; process.stdout.write = stdoutWrite; - expect(stdout, 'to equal', expectedArray); + expect(stdout[1], 'to equal', expectedSecondLine); expect(expectedString, 'to be', expectedSuite); }); }); From 9352709a145728ddc13caa307d6a82c30404c7cd Mon Sep 17 00:00:00 2001 From: Tobias Mollstam Date: Wed, 8 Aug 2018 10:27:36 +0200 Subject: [PATCH 37/80] Added integration tests to make sure we conform to spec. Only expect now is that version line is first line, more to come. Removed the previously added unit test which now is superflous. --- test/integration/reporters.spec.js | 26 ++++++++++++++++++++++++++ test/reporters/tap.spec.js | 11 ----------- 2 files changed, 26 insertions(+), 11 deletions(-) diff --git a/test/integration/reporters.spec.js b/test/integration/reporters.spec.js index 4e95622dee..8abb9d5f6c 100644 --- a/test/integration/reporters.spec.js +++ b/test/integration/reporters.spec.js @@ -99,4 +99,30 @@ describe('reporters', function() { }); }); }); + + describe('tap', function() { + describe('produces valid TAP v13 output', function() { + var runFixtureAndValidateOutput = function(fixture) { + it('for ' + fixture, function(done) { + var args = ['--reporter=tap']; + run(fixture, args, function(err, res) { + if (err) { + done(err); + return; + } + + // see https://testanything.org/tap-version-13-specification.html + + // first line must be `TAP version 13` + var firstLine = res.output.split('\n', 1)[0]; + expect(firstLine, 'to equal', 'TAP version 13'); + done(); + }); + }); + }; + + runFixtureAndValidateOutput('passing.fixture.js'); + runFixtureAndValidateOutput('uncaught.fixture.js'); + }); + }); }); diff --git a/test/reporters/tap.spec.js b/test/reporters/tap.spec.js index 175ad4b2c1..71b29a386a 100644 --- a/test/reporters/tap.spec.js +++ b/test/reporters/tap.spec.js @@ -33,17 +33,6 @@ describe('TAP reporter', function() { }); describe('on start', function() { - it('should write expected version on first line', function() { - runner = createMockRunner('start', 'start'); - runner.grepTotal = function(string) {}; - TAP.call({}, runner); - - var expectedFirstLine = 'TAP version 13\n'; - process.stdout.write = stdoutWrite; - - expect(stdout[0], 'to equal', expectedFirstLine); - }); - it('should hand runners suite into grepTotal and log the total', function() { var expectedSuite = 'some suite'; var expectedTotal = 10; From f027dc52d8446a89726506594e323eb8ad30d398 Mon Sep 17 00:00:00 2001 From: Tobias Mollstam Date: Wed, 8 Aug 2018 11:12:34 +0200 Subject: [PATCH 38/80] Extended integration test to validate plan, test is growing... --- test/integration/reporters.spec.js | 55 ++++++++++++++++++++++++++---- 1 file changed, 49 insertions(+), 6 deletions(-) diff --git a/test/integration/reporters.spec.js b/test/integration/reporters.spec.js index 8abb9d5f6c..f25f4313d1 100644 --- a/test/integration/reporters.spec.js +++ b/test/integration/reporters.spec.js @@ -102,7 +102,7 @@ describe('reporters', function() { describe('tap', function() { describe('produces valid TAP v13 output', function() { - var runFixtureAndValidateOutput = function(fixture) { + var runFixtureAndValidateOutput = function(fixture, expected) { it('for ' + fixture, function(done) { var args = ['--reporter=tap']; run(fixture, args, function(err, res) { @@ -111,18 +111,61 @@ describe('reporters', function() { return; } + var outputLines = res.output.split('\n'); + // see https://testanything.org/tap-version-13-specification.html - // first line must be `TAP version 13` - var firstLine = res.output.split('\n', 1)[0]; - expect(firstLine, 'to equal', 'TAP version 13'); + // Version + var expectedVersion = 13; + // first line must be version line + expect( + outputLines[0], + 'to equal', + 'TAP version ' + expectedVersion + ); + + // Plan + var expectedPlan = '1..' + expected.numTests; + // plan must appear once + expect(outputLines, 'to contain', expectedPlan); + expect( + outputLines.filter(l => l === expectedPlan), + 'to have length', + 1 + ); + // plan cannot appear in middle of the output + var testLineMatcher = function(line) { + return line.match(/^not ok/) || line.match(/^ok/); + }; + var firstTestLine = outputLines.findIndex(testLineMatcher); + expect(firstTestLine, 'to be greater than', -1); + var lastTestLine = + outputLines.length - + 1 - + outputLines + .slice() + .reverse() + .findIndex(testLineMatcher); + var planLine = outputLines.findIndex(function(line) { + return line === expectedPlan; + }); + expect( + planLine < firstTestLine || planLine > lastTestLine, + 'to equal', + true + ); + done(); }); }); }; - runFixtureAndValidateOutput('passing.fixture.js'); - runFixtureAndValidateOutput('uncaught.fixture.js'); + runFixtureAndValidateOutput('passing.fixture.js', { + numTests: 2 + }); + runFixtureAndValidateOutput('uncaught.fixture.js', { + numTests: 2 + }); }); }); }); From 5d8845445aa62a0fc02e198e87dfaf10e1582bae Mon Sep 17 00:00:00 2001 From: Tobias Mollstam Date: Wed, 8 Aug 2018 11:21:34 +0200 Subject: [PATCH 39/80] removed some section comments and minor restructure --- test/integration/reporters.spec.js | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/test/integration/reporters.spec.js b/test/integration/reporters.spec.js index f25f4313d1..5a92c4f4ef 100644 --- a/test/integration/reporters.spec.js +++ b/test/integration/reporters.spec.js @@ -111,12 +111,11 @@ describe('reporters', function() { return; } - var outputLines = res.output.split('\n'); + var expectedVersion = 13; + var expectedPlan = '1..' + expected.numTests; - // see https://testanything.org/tap-version-13-specification.html + var outputLines = res.output.split('\n'); - // Version - var expectedVersion = 13; // first line must be version line expect( outputLines[0], @@ -124,8 +123,6 @@ describe('reporters', function() { 'TAP version ' + expectedVersion ); - // Plan - var expectedPlan = '1..' + expected.numTests; // plan must appear once expect(outputLines, 'to contain', expectedPlan); expect( @@ -138,6 +135,7 @@ describe('reporters', function() { return line.match(/^not ok/) || line.match(/^ok/); }; var firstTestLine = outputLines.findIndex(testLineMatcher); + // there must be at least one test line expect(firstTestLine, 'to be greater than', -1); var lastTestLine = outputLines.length - From 425b03f377ec154a3ef95de9c5ce73374e57ac2d Mon Sep 17 00:00:00 2001 From: Tobias Mollstam Date: Wed, 8 Aug 2018 11:53:31 +0200 Subject: [PATCH 40/80] broke out testLineMatcher to testLinePredicate with minor refactor. --- test/integration/reporters.spec.js | 45 ++++++++++++++++++++++++++---- 1 file changed, 40 insertions(+), 5 deletions(-) diff --git a/test/integration/reporters.spec.js b/test/integration/reporters.spec.js index 5a92c4f4ef..6527c8e47b 100644 --- a/test/integration/reporters.spec.js +++ b/test/integration/reporters.spec.js @@ -101,6 +101,10 @@ describe('reporters', function() { }); describe('tap', function() { + var testLinePredicate = function(line) { + return line.match(/^not ok/) != null || line.match(/^ok/) != null; + }; + describe('produces valid TAP v13 output', function() { var runFixtureAndValidateOutput = function(fixture, expected) { it('for ' + fixture, function(done) { @@ -131,10 +135,7 @@ describe('reporters', function() { 1 ); // plan cannot appear in middle of the output - var testLineMatcher = function(line) { - return line.match(/^not ok/) || line.match(/^ok/); - }; - var firstTestLine = outputLines.findIndex(testLineMatcher); + var firstTestLine = outputLines.findIndex(testLinePredicate); // there must be at least one test line expect(firstTestLine, 'to be greater than', -1); var lastTestLine = @@ -143,7 +144,7 @@ describe('reporters', function() { outputLines .slice() .reverse() - .findIndex(testLineMatcher); + .findIndex(testLinePredicate); var planLine = outputLines.findIndex(function(line) { return line === expectedPlan; }); @@ -165,5 +166,39 @@ describe('reporters', function() { numTests: 2 }); }); + + it('places exceptions correctly in YAML blocks', function(done) { + var args = ['--reporter=tap']; + run('uncaught.fixture.js', args, function(err, res) { + if (err) { + done(err); + return; + } + + var outputLines = res.output.split('\n'); + for (var i = 0; i + 1 < outputLines.length; i++) { + console.log('>', outputLines[i]); + console.log('\t', testLinePredicate(outputLines[i])); + console.log('\t', testLinePredicate(outputLines[i + 1])); + if ( + testLinePredicate(outputLines[i]) && + testLinePredicate(outputLines[i + 1]) === false + ) { + var blockLinesStart = i + 1; + var blockLinesEnd = + i + 1 + outputLines.slice(i + 1).findIndex(anythingElsePredicate); + var blockLines = + blockLinesEnd > blockLinesStart + ? outputLines.slice(blockLinesStart, blockLinesEnd) + : outputLines.slice(blockLinesStart); + console.log(i); + console.log(blockLines); + i += blockLines.length; + } + } + + done(); + }); + }); }); }); From 2af2e84417fa994c4bb2311f6d46e20a256827ff Mon Sep 17 00:00:00 2001 From: Tobias Mollstam Date: Wed, 8 Aug 2018 12:10:43 +0200 Subject: [PATCH 41/80] added a bunch of predicates to identify lines --- test/integration/reporters.spec.js | 36 +++++++++++++++++++++++++----- 1 file changed, 30 insertions(+), 6 deletions(-) diff --git a/test/integration/reporters.spec.js b/test/integration/reporters.spec.js index 6527c8e47b..bb7063de78 100644 --- a/test/integration/reporters.spec.js +++ b/test/integration/reporters.spec.js @@ -101,9 +101,35 @@ describe('reporters', function() { }); describe('tap', function() { + var not = function(predicate) { + return function() { + return !predicate.apply(this, arguments); + }; + }; + var versionPredicate = function(line) { + return line.match(/^TAP version \d+$/) != null; + }; + var planPredicate = function(line) { + return line.match(/^1\.\.\d+$/) != null; + }; var testLinePredicate = function(line) { return line.match(/^not ok/) != null || line.match(/^ok/) != null; }; + var diagnosticPredicate = function(line) { + return line.match(/^#/) != null; + }; + var bailOutPredicate = function(line) { + return line.match(/^Bail out!/) != null; + }; + var anythingElsePredicate = function(line) { + return ( + versionPredicate(line) === false && + planPredicate(line) === false && + testLinePredicate(line) === false && + diagnosticPredicate(line) === false && + bailOutPredicate(line) === false + ); + }; describe('produces valid TAP v13 output', function() { var runFixtureAndValidateOutput = function(fixture, expected) { @@ -177,22 +203,20 @@ describe('reporters', function() { var outputLines = res.output.split('\n'); for (var i = 0; i + 1 < outputLines.length; i++) { - console.log('>', outputLines[i]); - console.log('\t', testLinePredicate(outputLines[i])); - console.log('\t', testLinePredicate(outputLines[i + 1])); if ( testLinePredicate(outputLines[i]) && testLinePredicate(outputLines[i + 1]) === false ) { var blockLinesStart = i + 1; var blockLinesEnd = - i + 1 + outputLines.slice(i + 1).findIndex(anythingElsePredicate); + i + + 1 + + outputLines.slice(i + 1).findIndex(not(anythingElsePredicate)); var blockLines = blockLinesEnd > blockLinesStart ? outputLines.slice(blockLinesStart, blockLinesEnd) : outputLines.slice(blockLinesStart); - console.log(i); - console.log(blockLines); + i += blockLines.length; } } From f6cf3cfba191545ae4ff70d7060ef8c7575a8717 Mon Sep 17 00:00:00 2001 From: Tobias Mollstam Date: Wed, 8 Aug 2018 13:59:54 +0200 Subject: [PATCH 42/80] stacks and errors are now wrapped inside YAML blocks --- lib/reporters/tap.js | 19 ++++++++++++++----- test/integration/reporters.spec.js | 5 ++++- test/reporters/tap.spec.js | 16 ++++++++++++---- 3 files changed, 30 insertions(+), 10 deletions(-) diff --git a/lib/reporters/tap.js b/lib/reporters/tap.js index 1c7b55931a..2244f07a93 100644 --- a/lib/reporters/tap.js +++ b/lib/reporters/tap.js @@ -53,11 +53,20 @@ function TAP(runner) { runner.on('fail', function(test, err) { failures++; console.log('not ok %d %s', n, title(test)); - if (err.message) { - console.log(err.message.replace(/^/gm, ' ')); - } - if (err.stack) { - console.log(err.stack.replace(/^/gm, ' ')); + var emitYamlBlock = err.message != null || err.stack != null; + if (emitYamlBlock) { + console.log(' ---'); + if (err.message) { + console.log(' message: ' + err.message); + } + // stack needs to be last in yaml due to literal style indicator (|) + if (err.stack) { + console.log(' stack: |-'); + if (err.stack) { + console.log(err.stack.replace(/^/gm, ' ')); + } + } + console.log(' ...'); } }); diff --git a/test/integration/reporters.spec.js b/test/integration/reporters.spec.js index bb7063de78..89428478eb 100644 --- a/test/integration/reporters.spec.js +++ b/test/integration/reporters.spec.js @@ -202,6 +202,7 @@ describe('reporters', function() { } var outputLines = res.output.split('\n'); + for (var i = 0; i + 1 < outputLines.length; i++) { if ( testLinePredicate(outputLines[i]) && @@ -216,8 +217,10 @@ describe('reporters', function() { blockLinesEnd > blockLinesStart ? outputLines.slice(blockLinesStart, blockLinesEnd) : outputLines.slice(blockLinesStart); - i += blockLines.length; + + expect(blockLines[0], 'to match', /^\s+\-\-\-/); + expect(blockLines[blockLines.length - 1], 'to match', /^\s+\.\.\./); } } diff --git a/test/reporters/tap.spec.js b/test/reporters/tap.spec.js index 71b29a386a..8845b6b891 100644 --- a/test/reporters/tap.spec.js +++ b/test/reporters/tap.spec.js @@ -121,7 +121,9 @@ describe('TAP reporter', function() { var expectedArray = [ 'not ok ' + countAfterTestEnd + ' ' + expectedTitle + '\n', - ' ' + expectedErrorMessage + '\n' + ' ---\n', + ' message: ' + expectedErrorMessage + '\n', + ' ...\n' ]; expect(stdout, 'to equal', expectedArray); }); @@ -148,7 +150,10 @@ describe('TAP reporter', function() { var expectedArray = [ 'not ok ' + countAfterTestEnd + ' ' + expectedTitle + '\n', - ' ' + expectedStack + '\n' + ' ---\n', + ' stack: |-\n', + ' ' + expectedStack + '\n', + ' ...\n' ]; expect(stdout, 'to equal', expectedArray); }); @@ -185,8 +190,11 @@ describe('TAP reporter', function() { var expectedArray = [ 'not ok ' + countAfterTestEnd + ' ' + expectedTitle + '\n', - ' ' + expectedErrorMessage + '\n', - ' ' + expectedStack + '\n' + ' ---\n', + ' message: ' + expectedErrorMessage + '\n', + ' stack: |-\n', + ' ' + expectedStack + '\n', + ' ...\n' ]; expect(stdout, 'to equal', expectedArray); }); From 308b381772e664f0ec1e147f861375512aca9047 Mon Sep 17 00:00:00 2001 From: Tobias Mollstam Date: Wed, 8 Aug 2018 14:04:33 +0200 Subject: [PATCH 43/80] changed arrow function to ye olde function to appease bots --- test/integration/reporters.spec.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/test/integration/reporters.spec.js b/test/integration/reporters.spec.js index 89428478eb..3f5efd3de6 100644 --- a/test/integration/reporters.spec.js +++ b/test/integration/reporters.spec.js @@ -156,7 +156,9 @@ describe('reporters', function() { // plan must appear once expect(outputLines, 'to contain', expectedPlan); expect( - outputLines.filter(l => l === expectedPlan), + outputLines.filter(function(l) { + return l === expectedPlan; + }), 'to have length', 1 ); From 009daed6895061da4adc4375f3169629a3abc3fe Mon Sep 17 00:00:00 2001 From: Tobias Mollstam Date: Wed, 8 Aug 2018 15:24:54 +0200 Subject: [PATCH 44/80] found linter and made code conform, `npm run test` now clean. --- test/integration/reporters.spec.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/integration/reporters.spec.js b/test/integration/reporters.spec.js index 3f5efd3de6..fc4967a854 100644 --- a/test/integration/reporters.spec.js +++ b/test/integration/reporters.spec.js @@ -221,7 +221,7 @@ describe('reporters', function() { : outputLines.slice(blockLinesStart); i += blockLines.length; - expect(blockLines[0], 'to match', /^\s+\-\-\-/); + expect(blockLines[0], 'to match', /^\s+---/); expect(blockLines[blockLines.length - 1], 'to match', /^\s+\.\.\./); } } From ccbd91e819be3d5c6a2f615386d923992b69ed6b Mon Sep 17 00:00:00 2001 From: Tobias Mollstam Date: Thu, 9 Aug 2018 08:25:32 +0200 Subject: [PATCH 45/80] added reporters.fixture.js for testing reporter capabilities --- .../integration/fixtures/reporters.fixture.js | 63 +++++++++++++++++++ 1 file changed, 63 insertions(+) create mode 100644 test/integration/fixtures/reporters.fixture.js diff --git a/test/integration/fixtures/reporters.fixture.js b/test/integration/fixtures/reporters.fixture.js new file mode 100644 index 0000000000..ab16bc0eed --- /dev/null +++ b/test/integration/fixtures/reporters.fixture.js @@ -0,0 +1,63 @@ +'use strict'; + +/** + * This file generates a wide range of output to test reporter functionality. + */ + +describe('Animals', function() { + + it('should consume organic material', function(done) { done(); }); + it('should breathe oxygen', function(done) { + // we're a jellyfish + var actualBreathe = 'nothing'; + var expectedBreathe = 'oxygen'; + expect(actualBreathe, 'to equal', expectedBreathe); + done(); + }); + it('should be able to move', function(done) { done(); }); + it('should reproduce sexually', function(done) { done(); }); + it('should grow from a hollow sphere of cells', function(done) { done(); }); + + describe('Vertebrates', function() { + describe('Mammals', function() { + it('should give birth to live young', function(done) { + var expectedMammal = { + consumesMaterial: 'organic', + breathe: 'oxygen', + reproduction: { + type: 'sexually', + spawnType: 'live', + } + }; + var platypus = JSON.parse(JSON.stringify(expectedMammal)); + platypus['reproduction']['spawnType'] = 'hard-shelled egg'; + + expect(platypus, 'to equal', expectedMammal); + done(); + }); + + describe('Blue Whale', function() { + it('should be the largest of all mammals', function(done) { done(); }); + it('should have a body in some shade of blue', function(done) { + var bodyColor = 'blueish_grey'; + var shadesOfBlue = ['cyan', 'light_blue', 'blue', 'indigo']; + expect(bodyColor, 'to be one of', shadesOfBlue); + + done(); + }); + }); + }); + describe('Birds', function() { + it('should have feathers', function(done) { done(); }); + it('should lay hard-shelled eggs', function(done) { done(); }); + }); + }); + + describe('Tardigrades', function() { + it('should answer to "water bear"', function(done) { done(); }); + it('should be able to survive global mass extinction events', function(done) { + throw new Error("How do we even test for this without causing one?") + done(); + }); + }); +}); From b2746b3bd5dc1a274431637cbf588afb5fe8ba8d Mon Sep 17 00:00:00 2001 From: Tobias Mollstam Date: Thu, 9 Aug 2018 09:26:06 +0200 Subject: [PATCH 46/80] Fixed error message potentially breaking YAML block, some cleanup. err.message is now placed inside YAML block as a literal so that newline doesn't break any YAML parser. --- lib/reporters/tap.js | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/lib/reporters/tap.js b/lib/reporters/tap.js index 2244f07a93..512af6c26a 100644 --- a/lib/reporters/tap.js +++ b/lib/reporters/tap.js @@ -57,14 +57,12 @@ function TAP(runner) { if (emitYamlBlock) { console.log(' ---'); if (err.message) { - console.log(' message: ' + err.message); + console.log(' message: |-'); + console.log(err.message.replace(/^/gm, ' ')); } - // stack needs to be last in yaml due to literal style indicator (|) if (err.stack) { console.log(' stack: |-'); - if (err.stack) { - console.log(err.stack.replace(/^/gm, ' ')); - } + console.log(err.stack.replace(/^/gm, ' ')); } console.log(' ...'); } From 9cb89908111aeb8dbda0538b7c37cbfc20272649 Mon Sep 17 00:00:00 2001 From: Tobias Mollstam Date: Thu, 9 Aug 2018 09:52:48 +0200 Subject: [PATCH 47/80] TAP now inherits from Base --- lib/reporters/tap.js | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/lib/reporters/tap.js b/lib/reporters/tap.js index 512af6c26a..c51ffc2c23 100644 --- a/lib/reporters/tap.js +++ b/lib/reporters/tap.js @@ -7,6 +7,7 @@ */ var Base = require('./base'); +var inherits = require('../utils').inherits; /** * Expose `TAP`. @@ -27,6 +28,7 @@ exports = module.exports = TAP; function TAP(runner) { Base.call(this, runner); + var self = this; var n = 1; var passes = 0; var failures = 0; @@ -75,6 +77,11 @@ function TAP(runner) { }); } +/** + * Inherit from `Base.prototype`. + */ +inherits(TAP, Base); + /** * Return a TAP-safe title of `test` * From 54ca9f73dde44bf24806806e67b80bce442e272a Mon Sep 17 00:00:00 2001 From: Tobias Mollstam Date: Thu, 9 Aug 2018 11:28:58 +0200 Subject: [PATCH 48/80] TAP no longer inherits from Base, decided not to use any Base functionality --- lib/reporters/tap.js | 7 ------- 1 file changed, 7 deletions(-) diff --git a/lib/reporters/tap.js b/lib/reporters/tap.js index c51ffc2c23..512af6c26a 100644 --- a/lib/reporters/tap.js +++ b/lib/reporters/tap.js @@ -7,7 +7,6 @@ */ var Base = require('./base'); -var inherits = require('../utils').inherits; /** * Expose `TAP`. @@ -28,7 +27,6 @@ exports = module.exports = TAP; function TAP(runner) { Base.call(this, runner); - var self = this; var n = 1; var passes = 0; var failures = 0; @@ -77,11 +75,6 @@ function TAP(runner) { }); } -/** - * Inherit from `Base.prototype`. - */ -inherits(TAP, Base); - /** * Return a TAP-safe title of `test` * From 56752f14bc03e987541b9f967b0dbdd8b8f8d9ce Mon Sep 17 00:00:00 2001 From: Tobias Mollstam Date: Thu, 9 Aug 2018 11:34:40 +0200 Subject: [PATCH 49/80] TAP unit tests now expect message starting with literal --- test/reporters/tap.spec.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/test/reporters/tap.spec.js b/test/reporters/tap.spec.js index 8845b6b891..84d6c5a16b 100644 --- a/test/reporters/tap.spec.js +++ b/test/reporters/tap.spec.js @@ -122,7 +122,8 @@ describe('TAP reporter', function() { var expectedArray = [ 'not ok ' + countAfterTestEnd + ' ' + expectedTitle + '\n', ' ---\n', - ' message: ' + expectedErrorMessage + '\n', + ' message: |-\n', + ' ' + expectedErrorMessage + '\n', ' ...\n' ]; expect(stdout, 'to equal', expectedArray); @@ -191,7 +192,8 @@ describe('TAP reporter', function() { var expectedArray = [ 'not ok ' + countAfterTestEnd + ' ' + expectedTitle + '\n', ' ---\n', - ' message: ' + expectedErrorMessage + '\n', + ' message: |-\n', + ' ' + expectedErrorMessage + '\n', ' stack: |-\n', ' ' + expectedStack + '\n', ' ...\n' From 4680170170c712e9c69a2c867a87a03c3e61c29c Mon Sep 17 00:00:00 2001 From: Tobias Mollstam Date: Thu, 9 Aug 2018 11:35:50 +0200 Subject: [PATCH 50/80] TAP integration tests use reporters fixture instead of uncaught --- test/integration/reporters.spec.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/test/integration/reporters.spec.js b/test/integration/reporters.spec.js index fc4967a854..12c87fd5d2 100644 --- a/test/integration/reporters.spec.js +++ b/test/integration/reporters.spec.js @@ -190,14 +190,14 @@ describe('reporters', function() { runFixtureAndValidateOutput('passing.fixture.js', { numTests: 2 }); - runFixtureAndValidateOutput('uncaught.fixture.js', { - numTests: 2 + runFixtureAndValidateOutput('reporters.fixture.js', { + numTests: 12 }); }); it('places exceptions correctly in YAML blocks', function(done) { var args = ['--reporter=tap']; - run('uncaught.fixture.js', args, function(err, res) { + run('reporters.fixture.js', args, function(err, res) { if (err) { done(err); return; From e75408deb859297e43f8d9a9595a8b32bd7b661a Mon Sep 17 00:00:00 2001 From: Tobias Mollstam Date: Wed, 19 Sep 2018 09:54:27 +0200 Subject: [PATCH 51/80] refactored yaml indentation to clean things up --- lib/reporters/tap.js | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/lib/reporters/tap.js b/lib/reporters/tap.js index 512af6c26a..51e6fa6a34 100644 --- a/lib/reporters/tap.js +++ b/lib/reporters/tap.js @@ -31,6 +31,10 @@ function TAP(runner) { var passes = 0; var failures = 0; + function yamlIndent(level) { + return Array(level + 1).join(' '); + } + runner.on('start', function() { console.log('TAP version 13'); var total = runner.grepTotal(runner.suite); @@ -55,16 +59,16 @@ function TAP(runner) { console.log('not ok %d %s', n, title(test)); var emitYamlBlock = err.message != null || err.stack != null; if (emitYamlBlock) { - console.log(' ---'); + console.log(yamlIndent(1), '---'); if (err.message) { - console.log(' message: |-'); - console.log(err.message.replace(/^/gm, ' ')); + console.log(yamlIndent(2), 'message: |-'); + console.log(err.message.replace(/^/gm, yamlIndent(3))); } if (err.stack) { - console.log(' stack: |-'); - console.log(err.stack.replace(/^/gm, ' ')); + console.log(yamlIndent(2), 'stack: |-'); + console.log(err.stack.replace(/^/gm, yamlIndent(3))); } - console.log(' ...'); + console.log(yamlIndent(1), '...'); } }); From ab0db065ac55165cadff799c95f207412c612932 Mon Sep 17 00:00:00 2001 From: Tobias Mollstam Date: Wed, 19 Sep 2018 10:17:58 +0200 Subject: [PATCH 52/80] fix to refactored indent which added an extra space on some lines --- lib/reporters/tap.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/reporters/tap.js b/lib/reporters/tap.js index 51e6fa6a34..f2f419e121 100644 --- a/lib/reporters/tap.js +++ b/lib/reporters/tap.js @@ -59,16 +59,16 @@ function TAP(runner) { console.log('not ok %d %s', n, title(test)); var emitYamlBlock = err.message != null || err.stack != null; if (emitYamlBlock) { - console.log(yamlIndent(1), '---'); + console.log(yamlIndent(1) + '---'); if (err.message) { - console.log(yamlIndent(2), 'message: |-'); + console.log(yamlIndent(2) + 'message: |-'); console.log(err.message.replace(/^/gm, yamlIndent(3))); } if (err.stack) { - console.log(yamlIndent(2), 'stack: |-'); + console.log(yamlIndent(2) + 'stack: |-'); console.log(err.stack.replace(/^/gm, yamlIndent(3))); } - console.log(yamlIndent(1), '...'); + console.log(yamlIndent(1) + '...'); } }); From f00ca30345dc3123e85c08e03408960d9716fa0b Mon Sep 17 00:00:00 2001 From: Tobias Mollstam Date: Thu, 20 Sep 2018 13:09:09 +0200 Subject: [PATCH 53/80] TAP reporter inherits Base --- lib/reporters/tap.js | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/lib/reporters/tap.js b/lib/reporters/tap.js index f2f419e121..a2ce55fbcd 100644 --- a/lib/reporters/tap.js +++ b/lib/reporters/tap.js @@ -7,6 +7,7 @@ */ var Base = require('./base'); +var inherits = require('../utils').inherits; /** * Expose `TAP`. @@ -89,3 +90,8 @@ function TAP(runner) { function title(test) { return test.fullTitle().replace(/#/g, ''); } + +/** + * Inherit from `Base.prototype`. + */ +inherits(TAP, Base); From ae782b8079f883e1235a33f325fe0ba361475da5 Mon Sep 17 00:00:00 2001 From: Tobias Mollstam Date: Thu, 20 Sep 2018 13:17:32 +0200 Subject: [PATCH 54/80] TAP reporter uses runner.stats rather than internal tracking --- lib/reporters/tap.js | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/lib/reporters/tap.js b/lib/reporters/tap.js index a2ce55fbcd..907cc998df 100644 --- a/lib/reporters/tap.js +++ b/lib/reporters/tap.js @@ -29,8 +29,6 @@ function TAP(runner) { Base.call(this, runner); var n = 1; - var passes = 0; - var failures = 0; function yamlIndent(level) { return Array(level + 1).join(' '); @@ -51,12 +49,10 @@ function TAP(runner) { }); runner.on('pass', function(test) { - passes++; console.log('ok %d %s', n, title(test)); }); runner.on('fail', function(test, err) { - failures++; console.log('not ok %d %s', n, title(test)); var emitYamlBlock = err.message != null || err.stack != null; if (emitYamlBlock) { @@ -74,9 +70,9 @@ function TAP(runner) { }); runner.once('end', function() { - console.log('# tests ' + (passes + failures)); - console.log('# pass ' + passes); - console.log('# fail ' + failures); + console.log('# tests ' + (runner.stats.passes + runner.stats.failures)); + console.log('# pass ' + runner.stats.passes); + console.log('# fail ' + runner.stats.failures); }); } From 9e2e5bf8f9b26ee1c639081511e250133e03cdda Mon Sep 17 00:00:00 2001 From: Tobias Mollstam Date: Thu, 20 Sep 2018 13:59:50 +0200 Subject: [PATCH 55/80] added support for `spec` reporterOption and started implementing `TAP12Producer` and `TAP13Producer`. --- lib/reporters/tap.js | 35 +++- test/reporters/tap.spec.js | 372 ++++++++++++++++++++----------------- 2 files changed, 230 insertions(+), 177 deletions(-) diff --git a/lib/reporters/tap.js b/lib/reporters/tap.js index 907cc998df..b9c073830e 100644 --- a/lib/reporters/tap.js +++ b/lib/reporters/tap.js @@ -25,19 +25,31 @@ exports = module.exports = TAP; * @api public * @param {Runner} runner */ -function TAP(runner) { +function TAP(runner, options) { Base.call(this, runner); + var self = this; var n = 1; function yamlIndent(level) { return Array(level + 1).join(' '); } + var tapSpec = '12'; + if (options && options.reporterOptions) { + if (options.reporterOptions.spec) { + var tapSpec = options.reporterOptions.spec; + } + } + + if (tapSpec === '13') { + this.producer = new TAP13Producer(); + } else { + this.producer = new TAP12Producer(); + } + runner.on('start', function() { - console.log('TAP version 13'); - var total = runner.grepTotal(runner.suite); - console.log('%d..%d', 1, total); + self.producer.writeStart(runner); }); runner.on('test end', function() { @@ -91,3 +103,18 @@ function title(test) { * Inherit from `Base.prototype`. */ inherits(TAP, Base); + +function TAP12Producer() {} + +TAP12Producer.prototype.writeStart = function(runner) { + var total = runner.grepTotal(runner.suite); + console.log('%d..%d', 1, total); +}; + +function TAP13Producer() {} + +TAP13Producer.prototype.writeStart = function(runner) { + console.log('TAP version 13'); + var total = runner.grepTotal(runner.suite); + console.log('%d..%d', 1, total); +}; diff --git a/test/reporters/tap.spec.js b/test/reporters/tap.spec.js index 84d6c5a16b..5d43268f1c 100644 --- a/test/reporters/tap.spec.js +++ b/test/reporters/tap.spec.js @@ -32,116 +32,60 @@ describe('TAP reporter', function() { process.stdout.write = stdoutWrite; }); - describe('on start', function() { - it('should hand runners suite into grepTotal and log the total', function() { - var expectedSuite = 'some suite'; - var expectedTotal = 10; - var expectedString; - runner = createMockRunner('start', 'start'); - runner.suite = expectedSuite; - runner.grepTotal = function(string) { - expectedString = string; - return expectedTotal; - }; - TAP.call({}, runner); - - var expectedSecondLine = '1..' + expectedTotal + '\n'; - process.stdout.write = stdoutWrite; - - expect(stdout[1], 'to equal', expectedSecondLine); - expect(expectedString, 'to be', expectedSuite); - }); - }); - - describe('on pending', function() { - it('should write expected message including count and title', function() { - runner = createMockRunner( - 'start test', - 'test end', - 'pending', - null, - test - ); - runner.suite = ''; - runner.grepTotal = function() {}; - TAP.call({}, runner); - - process.stdout.write = stdoutWrite; - - var expectedMessage = - 'ok ' + countAfterTestEnd + ' ' + expectedTitle + ' # SKIP -\n'; - expect(stdout[0], 'to equal', expectedMessage); - }); - }); - - describe('on pass', function() { - it('should write expected message including count and title', function() { - runner = createMockRunner('start test', 'test end', 'pass', null, test); - - runner.suite = ''; - runner.grepTotal = function() {}; - TAP.call({}, runner); + describe('TAP12 spec', function() { + describe('on start', function() { + it('should hand runners suite into grepTotal and log the total', function() { + var expectedSuite = 'some suite'; + var expectedTotal = 10; + var expectedString; + runner = createMockRunner('start', 'start'); + runner.suite = expectedSuite; + runner.grepTotal = function(string) { + expectedString = string; + return expectedTotal; + }; + TAP.call({}, runner); - process.stdout.write = stdoutWrite; + var expectedArray = ['1..' + expectedTotal + '\n']; + process.stdout.write = stdoutWrite; - var expectedMessage = - 'ok ' + countAfterTestEnd + ' ' + expectedTitle + '\n'; - expect(stdout[0], 'to equal', expectedMessage); + expect(stdout, 'to equal', expectedArray); + expect(expectedString, 'to be', expectedSuite); + }); }); }); - describe('on fail', function() { - describe('if there is an error message', function() { - it('should write expected message and error message', function() { - var expectedTitle = 'some title'; - var countAfterTestEnd = 2; - var expectedErrorMessage = 'some error'; - var test = { - fullTitle: function() { - return expectedTitle; - }, - slow: function() {} - }; - var error = { - message: expectedErrorMessage + describe('TAP13 spec', function() { + var options = {reporterOptions: {spec: '13'}}; + describe('on start', function() { + it('should hand runners suite into grepTotal and log the total', function() { + var expectedSuite = 'some suite'; + var expectedTotal = 10; + var expectedString; + runner = createMockRunner('start', 'start'); + runner.suite = expectedSuite; + runner.grepTotal = function(string) { + expectedString = string; + return expectedTotal; }; - runner.on = function(event, callback) { - if (event === 'test end') { - callback(); - } - if (event === 'fail') { - callback(test, error); - } - }; - runner.suite = ''; - runner.grepTotal = function() {}; - TAP.call({}, runner); + TAP.call({}, runner, options); + var expectedSecondLine = '1..' + expectedTotal + '\n'; process.stdout.write = stdoutWrite; - var expectedArray = [ - 'not ok ' + countAfterTestEnd + ' ' + expectedTitle + '\n', - ' ---\n', - ' message: |-\n', - ' ' + expectedErrorMessage + '\n', - ' ...\n' - ]; - expect(stdout, 'to equal', expectedArray); + expect(stdout[1], 'to equal', expectedSecondLine); + expect(expectedString, 'to be', expectedSuite); }); }); - describe('if there is an error stack', function() { - it('should write expected message and stack', function() { - var expectedStack = 'some stack'; - var error = { - stack: expectedStack - }; + + describe('on pending', function() { + it('should write expected message including count and title', function() { runner = createMockRunner( - 'test end fail', + 'start test', 'test end', - 'fail', + 'pending', null, - test, - error + test ); runner.suite = ''; runner.grepTotal = function() {}; @@ -149,103 +93,185 @@ describe('TAP reporter', function() { process.stdout.write = stdoutWrite; - var expectedArray = [ - 'not ok ' + countAfterTestEnd + ' ' + expectedTitle + '\n', - ' ---\n', - ' stack: |-\n', - ' ' + expectedStack + '\n', - ' ...\n' - ]; - expect(stdout, 'to equal', expectedArray); + var expectedMessage = + 'ok ' + countAfterTestEnd + ' ' + expectedTitle + ' # SKIP -\n'; + expect(stdout[0], 'to equal', expectedMessage); }); }); - describe('if there is an error stack and error message', function() { - it('should write expected message and stack', function() { - var expectedTitle = 'some title'; - var countAfterTestEnd = 2; - var expectedStack = 'some stack'; - var expectedErrorMessage = 'some error'; - var test = { - fullTitle: function() { - return expectedTitle; - }, - slow: function() {} - }; - var error = { - stack: expectedStack, - message: expectedErrorMessage - }; - runner.on = function(event, callback) { - if (event === 'test end') { - callback(); - } - if (event === 'fail') { - callback(test, error); - } - }; + + describe('on pass', function() { + it('should write expected message including count and title', function() { + runner = createMockRunner('start test', 'test end', 'pass', null, test); + runner.suite = ''; runner.grepTotal = function() {}; TAP.call({}, runner); process.stdout.write = stdoutWrite; - var expectedArray = [ - 'not ok ' + countAfterTestEnd + ' ' + expectedTitle + '\n', - ' ---\n', - ' message: |-\n', - ' ' + expectedErrorMessage + '\n', - ' stack: |-\n', - ' ' + expectedStack + '\n', - ' ...\n' - ]; - expect(stdout, 'to equal', expectedArray); + var expectedMessage = + 'ok ' + countAfterTestEnd + ' ' + expectedTitle + '\n'; + expect(stdout[0], 'to equal', expectedMessage); }); }); - describe('if there is no error stack or error message', function() { - it('should write expected message only', function() { - var error = {}; - runner.on = runner.once = function(event, callback) { - if (event === 'test end') { - callback(); - } - if (event === 'fail') { - callback(test, error); - } - }; + + describe('on fail', function() { + describe('if there is an error message', function() { + it('should write expected message and error message', function() { + var expectedTitle = 'some title'; + var countAfterTestEnd = 2; + var expectedErrorMessage = 'some error'; + var test = { + fullTitle: function() { + return expectedTitle; + }, + slow: function() {} + }; + var error = { + message: expectedErrorMessage + }; + runner.on = function(event, callback) { + if (event === 'test end') { + callback(); + } + if (event === 'fail') { + callback(test, error); + } + }; + runner.suite = ''; + runner.grepTotal = function() {}; + TAP.call({}, runner); + + process.stdout.write = stdoutWrite; + + var expectedArray = [ + 'not ok ' + countAfterTestEnd + ' ' + expectedTitle + '\n', + ' ---\n', + ' message: |-\n', + ' ' + expectedErrorMessage + '\n', + ' ...\n' + ]; + expect(stdout, 'to equal', expectedArray); + }); + }); + describe('if there is an error stack', function() { + it('should write expected message and stack', function() { + var expectedStack = 'some stack'; + var error = { + stack: expectedStack + }; + runner = createMockRunner( + 'test end fail', + 'test end', + 'fail', + null, + test, + error + ); + runner.suite = ''; + runner.grepTotal = function() {}; + TAP.call({}, runner); + + process.stdout.write = stdoutWrite; + + var expectedArray = [ + 'not ok ' + countAfterTestEnd + ' ' + expectedTitle + '\n', + ' ---\n', + ' stack: |-\n', + ' ' + expectedStack + '\n', + ' ...\n' + ]; + expect(stdout, 'to equal', expectedArray); + }); + }); + describe('if there is an error stack and error message', function() { + it('should write expected message and stack', function() { + var expectedTitle = 'some title'; + var countAfterTestEnd = 2; + var expectedStack = 'some stack'; + var expectedErrorMessage = 'some error'; + var test = { + fullTitle: function() { + return expectedTitle; + }, + slow: function() {} + }; + var error = { + stack: expectedStack, + message: expectedErrorMessage + }; + runner.on = function(event, callback) { + if (event === 'test end') { + callback(); + } + if (event === 'fail') { + callback(test, error); + } + }; + runner.suite = ''; + runner.grepTotal = function() {}; + TAP.call({}, runner); + + process.stdout.write = stdoutWrite; + + var expectedArray = [ + 'not ok ' + countAfterTestEnd + ' ' + expectedTitle + '\n', + ' ---\n', + ' message: |-\n', + ' ' + expectedErrorMessage + '\n', + ' stack: |-\n', + ' ' + expectedStack + '\n', + ' ...\n' + ]; + expect(stdout, 'to equal', expectedArray); + }); + }); + describe('if there is no error stack or error message', function() { + it('should write expected message only', function() { + var error = {}; + runner.on = runner.once = function(event, callback) { + if (event === 'test end') { + callback(); + } + if (event === 'fail') { + callback(test, error); + } + }; + runner.suite = ''; + runner.grepTotal = function() {}; + TAP.call({}, runner); + + process.stdout.write = stdoutWrite; + + var expectedArray = [ + 'not ok ' + countAfterTestEnd + ' ' + expectedTitle + '\n' + ]; + expect(stdout, 'to equal', expectedArray); + }); + }); + }); + + describe('on end', function() { + it('should write total tests, passes and failures', function() { + var numberOfPasses = 1; + var numberOfFails = 1; + runner = createMockRunner('fail end pass', 'fail', 'end', 'pass', test); runner.suite = ''; runner.grepTotal = function() {}; TAP.call({}, runner); process.stdout.write = stdoutWrite; + var totalTests = numberOfPasses + numberOfFails; var expectedArray = [ - 'not ok ' + countAfterTestEnd + ' ' + expectedTitle + '\n' + 'ok ' + numberOfPasses + ' ' + expectedTitle + '\n', + 'not ok ' + numberOfFails + ' ' + expectedTitle + '\n', + '# tests ' + totalTests + '\n', + '# pass ' + numberOfPasses + '\n', + '# fail ' + numberOfFails + '\n' ]; expect(stdout, 'to equal', expectedArray); }); }); }); - - describe('on end', function() { - it('should write total tests, passes and failures', function() { - var numberOfPasses = 1; - var numberOfFails = 1; - runner = createMockRunner('fail end pass', 'fail', 'end', 'pass', test); - runner.suite = ''; - runner.grepTotal = function() {}; - TAP.call({}, runner); - - process.stdout.write = stdoutWrite; - - var totalTests = numberOfPasses + numberOfFails; - var expectedArray = [ - 'ok ' + numberOfPasses + ' ' + expectedTitle + '\n', - 'not ok ' + numberOfFails + ' ' + expectedTitle + '\n', - '# tests ' + totalTests + '\n', - '# pass ' + numberOfPasses + '\n', - '# fail ' + numberOfFails + '\n' - ]; - expect(stdout, 'to equal', expectedArray); - }); - }); }); From 1b065412916afbe7a527a487d6841f68c7bca89e Mon Sep 17 00:00:00 2001 From: Tobias Mollstam Date: Thu, 20 Sep 2018 14:15:13 +0200 Subject: [PATCH 56/80] TAP13 tests now run with proper spec selected --- test/reporters/tap.spec.js | 117 ++++++++++++++++++++++++++++++++++--- 1 file changed, 110 insertions(+), 7 deletions(-) diff --git a/test/reporters/tap.spec.js b/test/reporters/tap.spec.js index 5d43268f1c..b292f9806a 100644 --- a/test/reporters/tap.spec.js +++ b/test/reporters/tap.spec.js @@ -53,6 +53,109 @@ describe('TAP reporter', function() { expect(expectedString, 'to be', expectedSuite); }); }); + + describe('on fail', function() { + describe('if there is an error message', function() { + it('should write expected message and error message', function() { + var expectedTitle = 'some title'; + var countAfterTestEnd = 2; + var expectedErrorMessage = 'some error'; + var test = { + fullTitle: function() { + return expectedTitle; + }, + slow: function() {} + }; + var error = { + message: expectedErrorMessage + }; + runner.on = function(event, callback) { + if (event === 'test end') { + callback(); + } + if (event === 'fail') { + callback(test, error); + } + }; + runner.suite = ''; + runner.grepTotal = function() {}; + TAP.call({}, runner); + + process.stdout.write = stdoutWrite; + + var expectedArray = [ + 'not ok ' + countAfterTestEnd + ' ' + expectedTitle + '\n', + ' ' + expectedErrorMessage + '\n' + ]; + expect(stdout, 'to equal', expectedArray); + }); + }); + describe('if there is an error stack', function() { + it('should write expected message and stack', function() { + var expectedStack = 'some stack'; + var error = { + stack: expectedStack + }; + runner = createMockRunner( + 'test end fail', + 'test end', + 'fail', + null, + test, + error + ); + runner.suite = ''; + runner.grepTotal = function() {}; + TAP.call({}, runner); + + process.stdout.write = stdoutWrite; + + var expectedArray = [ + 'not ok ' + countAfterTestEnd + ' ' + expectedTitle + '\n', + ' ' + expectedStack + '\n' + ]; + expect(stdout, 'to equal', expectedArray); + }); + }); + describe('if there is an error stack and error message', function() { + it('should write expected message and stack', function() { + var expectedTitle = 'some title'; + var countAfterTestEnd = 2; + var expectedStack = 'some stack'; + var expectedErrorMessage = 'some error'; + var test = { + fullTitle: function() { + return expectedTitle; + }, + slow: function() {} + }; + var error = { + stack: expectedStack, + message: expectedErrorMessage + }; + runner.on = function(event, callback) { + if (event === 'test end') { + callback(); + } + if (event === 'fail') { + callback(test, error); + } + }; + runner.suite = ''; + runner.grepTotal = function() {}; + TAP.call({}, runner); + + process.stdout.write = stdoutWrite; + + var expectedArray = [ + 'not ok ' + countAfterTestEnd + ' ' + expectedTitle + '\n', + ' ' + expectedErrorMessage + '\n', + ' ' + expectedStack + '\n' + ]; + expect(stdout, 'to equal', expectedArray); + }); + }); + }); }); describe('TAP13 spec', function() { @@ -89,7 +192,7 @@ describe('TAP reporter', function() { ); runner.suite = ''; runner.grepTotal = function() {}; - TAP.call({}, runner); + TAP.call({}, runner, options); process.stdout.write = stdoutWrite; @@ -105,7 +208,7 @@ describe('TAP reporter', function() { runner.suite = ''; runner.grepTotal = function() {}; - TAP.call({}, runner); + TAP.call({}, runner, options); process.stdout.write = stdoutWrite; @@ -140,7 +243,7 @@ describe('TAP reporter', function() { }; runner.suite = ''; runner.grepTotal = function() {}; - TAP.call({}, runner); + TAP.call({}, runner, options); process.stdout.write = stdoutWrite; @@ -170,7 +273,7 @@ describe('TAP reporter', function() { ); runner.suite = ''; runner.grepTotal = function() {}; - TAP.call({}, runner); + TAP.call({}, runner, options); process.stdout.write = stdoutWrite; @@ -210,7 +313,7 @@ describe('TAP reporter', function() { }; runner.suite = ''; runner.grepTotal = function() {}; - TAP.call({}, runner); + TAP.call({}, runner, options); process.stdout.write = stdoutWrite; @@ -239,7 +342,7 @@ describe('TAP reporter', function() { }; runner.suite = ''; runner.grepTotal = function() {}; - TAP.call({}, runner); + TAP.call({}, runner, options); process.stdout.write = stdoutWrite; @@ -258,7 +361,7 @@ describe('TAP reporter', function() { runner = createMockRunner('fail end pass', 'fail', 'end', 'pass', test); runner.suite = ''; runner.grepTotal = function() {}; - TAP.call({}, runner); + TAP.call({}, runner, options); process.stdout.write = stdoutWrite; From 709f5674d7cec734f593fe8e959e81e0b71d6920 Mon Sep 17 00:00:00 2001 From: Tobias Mollstam Date: Thu, 20 Sep 2018 14:16:45 +0200 Subject: [PATCH 57/80] extracted fail reporting to producers --- lib/reporters/tap.js | 50 ++++++++++++++++++++++++++++---------------- 1 file changed, 32 insertions(+), 18 deletions(-) diff --git a/lib/reporters/tap.js b/lib/reporters/tap.js index b9c073830e..7331d8216f 100644 --- a/lib/reporters/tap.js +++ b/lib/reporters/tap.js @@ -31,10 +31,6 @@ function TAP(runner, options) { var self = this; var n = 1; - function yamlIndent(level) { - return Array(level + 1).join(' '); - } - var tapSpec = '12'; if (options && options.reporterOptions) { if (options.reporterOptions.spec) { @@ -65,20 +61,7 @@ function TAP(runner, options) { }); runner.on('fail', function(test, err) { - console.log('not ok %d %s', n, title(test)); - var emitYamlBlock = err.message != null || err.stack != null; - if (emitYamlBlock) { - console.log(yamlIndent(1) + '---'); - if (err.message) { - console.log(yamlIndent(2) + 'message: |-'); - console.log(err.message.replace(/^/gm, yamlIndent(3))); - } - if (err.stack) { - console.log(yamlIndent(2) + 'stack: |-'); - console.log(err.stack.replace(/^/gm, yamlIndent(3))); - } - console.log(yamlIndent(1) + '...'); - } + self.producer.writeFail(n, test, err); }); runner.once('end', function() { @@ -111,10 +94,41 @@ TAP12Producer.prototype.writeStart = function(runner) { console.log('%d..%d', 1, total); }; +TAP12Producer.prototype.writeFail = function(n, test, err) { + console.log('not ok %d %s', n, title(test)); + if (err.message) { + console.log(err.message.replace(/^/gm, ' ')); + } + if (err.stack) { + console.log(err.stack.replace(/^/gm, ' ')); + } +}; + function TAP13Producer() {} +TAP13Producer.prototype.yamlIndent = function(level) { + return Array(level + 1).join(' '); +}; + TAP13Producer.prototype.writeStart = function(runner) { console.log('TAP version 13'); var total = runner.grepTotal(runner.suite); console.log('%d..%d', 1, total); }; + +TAP13Producer.prototype.writeFail = function(n, test, err) { + console.log('not ok %d %s', n, title(test)); + var emitYamlBlock = err.message != null || err.stack != null; + if (emitYamlBlock) { + console.log(this.yamlIndent(1) + '---'); + if (err.message) { + console.log(this.yamlIndent(2) + 'message: |-'); + console.log(err.message.replace(/^/gm, this.yamlIndent(3))); + } + if (err.stack) { + console.log(this.yamlIndent(2) + 'stack: |-'); + console.log(err.stack.replace(/^/gm, this.yamlIndent(3))); + } + console.log(this.yamlIndent(1) + '...'); + } +}; From c646564487e5b9205097bebcb55c7362d9bf38d9 Mon Sep 17 00:00:00 2001 From: Tobias Mollstam Date: Thu, 20 Sep 2018 14:25:04 +0200 Subject: [PATCH 58/80] added missing tests to TAP12 spec which are identical to TAP13 expectations --- test/reporters/tap.spec.js | 82 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 82 insertions(+) diff --git a/test/reporters/tap.spec.js b/test/reporters/tap.spec.js index b292f9806a..ef72ee0c26 100644 --- a/test/reporters/tap.spec.js +++ b/test/reporters/tap.spec.js @@ -54,6 +54,43 @@ describe('TAP reporter', function() { }); }); + describe('on pending', function() { + it('should write expected message including count and title', function() { + runner = createMockRunner( + 'start test', + 'test end', + 'pending', + null, + test + ); + runner.suite = ''; + runner.grepTotal = function() {}; + TAP.call({}, runner); + + process.stdout.write = stdoutWrite; + + var expectedMessage = + 'ok ' + countAfterTestEnd + ' ' + expectedTitle + ' # SKIP -\n'; + expect(stdout[0], 'to equal', expectedMessage); + }); + }); + + describe('on pass', function() { + it('should write expected message including count and title', function() { + runner = createMockRunner('start test', 'test end', 'pass', null, test); + + runner.suite = ''; + runner.grepTotal = function() {}; + TAP.call({}, runner); + + process.stdout.write = stdoutWrite; + + var expectedMessage = + 'ok ' + countAfterTestEnd + ' ' + expectedTitle + '\n'; + expect(stdout[0], 'to equal', expectedMessage); + }); + }); + describe('on fail', function() { describe('if there is an error message', function() { it('should write expected message and error message', function() { @@ -155,6 +192,51 @@ describe('TAP reporter', function() { expect(stdout, 'to equal', expectedArray); }); }); + describe('if there is no error stack or error message', function() { + it('should write expected message only', function() { + var error = {}; + runner.on = runner.once = function(event, callback) { + if (event === 'test end') { + callback(); + } + if (event === 'fail') { + callback(test, error); + } + }; + runner.suite = ''; + runner.grepTotal = function() {}; + TAP.call({}, runner); + + process.stdout.write = stdoutWrite; + + var expectedArray = [ + 'not ok ' + countAfterTestEnd + ' ' + expectedTitle + '\n' + ]; + expect(stdout, 'to equal', expectedArray); + }); + }); + }); + describe('on end', function() { + it('should write total tests, passes and failures', function() { + var numberOfPasses = 1; + var numberOfFails = 1; + runner = createMockRunner('fail end pass', 'fail', 'end', 'pass', test); + runner.suite = ''; + runner.grepTotal = function() {}; + TAP.call({}, runner); + + process.stdout.write = stdoutWrite; + + var totalTests = numberOfPasses + numberOfFails; + var expectedArray = [ + 'ok ' + numberOfPasses + ' ' + expectedTitle + '\n', + 'not ok ' + numberOfFails + ' ' + expectedTitle + '\n', + '# tests ' + totalTests + '\n', + '# pass ' + numberOfPasses + '\n', + '# fail ' + numberOfFails + '\n' + ]; + expect(stdout, 'to equal', expectedArray); + }); }); }); From 444220741f8807f6ba35b71e1f548e5f2c0fc072 Mon Sep 17 00:00:00 2001 From: Tobias Mollstam Date: Thu, 20 Sep 2018 14:27:01 +0200 Subject: [PATCH 59/80] extracted pending reporting to producers --- lib/reporters/tap.js | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/lib/reporters/tap.js b/lib/reporters/tap.js index 7331d8216f..c88cbb12d8 100644 --- a/lib/reporters/tap.js +++ b/lib/reporters/tap.js @@ -53,7 +53,7 @@ function TAP(runner, options) { }); runner.on('pending', function(test) { - console.log('ok %d %s # SKIP -', n, title(test)); + self.producer.writePending(n, test); }); runner.on('pass', function(test) { @@ -104,6 +104,10 @@ TAP12Producer.prototype.writeFail = function(n, test, err) { } }; +TAP12Producer.prototype.writePending = function(n, test) { + console.log('ok %d %s # SKIP -', n, title(test)); +}; + function TAP13Producer() {} TAP13Producer.prototype.yamlIndent = function(level) { @@ -132,3 +136,7 @@ TAP13Producer.prototype.writeFail = function(n, test, err) { console.log(this.yamlIndent(1) + '...'); } }; + +TAP13Producer.prototype.writePending = function(n, test) { + console.log('ok %d %s # SKIP -', n, title(test)); +}; From f4590ee181a5b5d9130278a0fcddf64a57c0d880 Mon Sep 17 00:00:00 2001 From: Tobias Mollstam Date: Thu, 20 Sep 2018 16:16:06 +0200 Subject: [PATCH 60/80] extracted pass reporting to producers --- lib/reporters/tap.js | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/lib/reporters/tap.js b/lib/reporters/tap.js index c88cbb12d8..de844d3839 100644 --- a/lib/reporters/tap.js +++ b/lib/reporters/tap.js @@ -57,7 +57,7 @@ function TAP(runner, options) { }); runner.on('pass', function(test) { - console.log('ok %d %s', n, title(test)); + self.producer.writePass(n, test); }); runner.on('fail', function(test, err) { @@ -108,6 +108,10 @@ TAP12Producer.prototype.writePending = function(n, test) { console.log('ok %d %s # SKIP -', n, title(test)); }; +TAP12Producer.prototype.writePass = function(n, test) { + console.log('ok %d %s', n, title(test)); +}; + function TAP13Producer() {} TAP13Producer.prototype.yamlIndent = function(level) { @@ -140,3 +144,7 @@ TAP13Producer.prototype.writeFail = function(n, test, err) { TAP13Producer.prototype.writePending = function(n, test) { console.log('ok %d %s # SKIP -', n, title(test)); }; + +TAP13Producer.prototype.writePass = function(n, test) { + console.log('ok %d %s', n, title(test)); +}; From 552091bc505c68580fc95b102a374eff82eb95c2 Mon Sep 17 00:00:00 2001 From: Tobias Mollstam Date: Thu, 20 Sep 2018 16:30:47 +0200 Subject: [PATCH 61/80] extracted end reporting to producers and refactored to common base --- lib/reporters/tap.js | 57 +++++++++++++++++++++++++++++++------------- 1 file changed, 40 insertions(+), 17 deletions(-) diff --git a/lib/reporters/tap.js b/lib/reporters/tap.js index de844d3839..93da27dcdf 100644 --- a/lib/reporters/tap.js +++ b/lib/reporters/tap.js @@ -65,9 +65,7 @@ function TAP(runner, options) { }); runner.once('end', function() { - console.log('# tests ' + (runner.stats.passes + runner.stats.failures)); - console.log('# pass ' + runner.stats.passes); - console.log('# fail ' + runner.stats.failures); + self.producer.writeEnd(runner); }); } @@ -87,6 +85,36 @@ function title(test) { */ inherits(TAP, Base); +/** + * Initialize a new `TAPProducer`. Should only be used as a base class. + * + * @private + * @class + * @api private + */ +function TAPProducer() {} + +TAPProducer.prototype.writePending = function(n, test) { + console.log('ok %d %s # SKIP -', n, title(test)); +}; + +TAPProducer.prototype.writePass = function(n, test) { + console.log('ok %d %s', n, title(test)); +}; + +TAPProducer.prototype.writeEnd = function(runner) { + console.log('# tests ' + (runner.stats.passes + runner.stats.failures)); + console.log('# pass ' + runner.stats.passes); + console.log('# fail ' + runner.stats.failures); +}; + +/** + * Initialize a new `TAP12Producer` which will produce output conforming to the TAP12 spec. + * + * @private + * @class + * @api private + */ function TAP12Producer() {} TAP12Producer.prototype.writeStart = function(runner) { @@ -104,14 +132,15 @@ TAP12Producer.prototype.writeFail = function(n, test, err) { } }; -TAP12Producer.prototype.writePending = function(n, test) { - console.log('ok %d %s # SKIP -', n, title(test)); -}; - -TAP12Producer.prototype.writePass = function(n, test) { - console.log('ok %d %s', n, title(test)); -}; +inherits(TAP12Producer, TAPProducer); +/** + * Initialize a new `TAP13Producer` which will produce output conforming to the TAP13 spec. + * + * @private + * @class + * @api private + */ function TAP13Producer() {} TAP13Producer.prototype.yamlIndent = function(level) { @@ -141,10 +170,4 @@ TAP13Producer.prototype.writeFail = function(n, test, err) { } }; -TAP13Producer.prototype.writePending = function(n, test) { - console.log('ok %d %s # SKIP -', n, title(test)); -}; - -TAP13Producer.prototype.writePass = function(n, test) { - console.log('ok %d %s', n, title(test)); -}; +inherits(TAP13Producer, TAPProducer); From dd1ebc1fa1c6f558716ecfd4b3ba3c3c5896d7c9 Mon Sep 17 00:00:00 2001 From: Tobias Mollstam Date: Thu, 20 Sep 2018 16:34:33 +0200 Subject: [PATCH 62/80] fixed bad redeclare --- lib/reporters/tap.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/reporters/tap.js b/lib/reporters/tap.js index 93da27dcdf..e8ec3aa3dd 100644 --- a/lib/reporters/tap.js +++ b/lib/reporters/tap.js @@ -34,7 +34,7 @@ function TAP(runner, options) { var tapSpec = '12'; if (options && options.reporterOptions) { if (options.reporterOptions.spec) { - var tapSpec = options.reporterOptions.spec; + tapSpec = options.reporterOptions.spec; } } From cb0258d944e5f9fd1b8de476659088aa0b3fdd0a Mon Sep 17 00:00:00 2001 From: Tobias Mollstam Date: Thu, 20 Sep 2018 16:44:02 +0200 Subject: [PATCH 63/80] fix to integrations tests to use TAP13 spec --- test/integration/reporters.spec.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/integration/reporters.spec.js b/test/integration/reporters.spec.js index 12c87fd5d2..cd6910f7df 100644 --- a/test/integration/reporters.spec.js +++ b/test/integration/reporters.spec.js @@ -134,7 +134,7 @@ describe('reporters', function() { describe('produces valid TAP v13 output', function() { var runFixtureAndValidateOutput = function(fixture, expected) { it('for ' + fixture, function(done) { - var args = ['--reporter=tap']; + var args = ['--reporter=tap', '--reporter-options', 'spec=13']; run(fixture, args, function(err, res) { if (err) { done(err); @@ -196,7 +196,7 @@ describe('reporters', function() { }); it('places exceptions correctly in YAML blocks', function(done) { - var args = ['--reporter=tap']; + var args = ['--reporter=tap', '--reporter-options', 'spec=13']; run('reporters.fixture.js', args, function(err, res) { if (err) { done(err); From f472b6bd3181b2a26dbbfe9758705f17747c4c4d Mon Sep 17 00:00:00 2001 From: "P. Roebuck" Date: Wed, 24 Oct 2018 13:50:28 -0500 Subject: [PATCH 64/80] 3483: Squelch CI Growl-related spawn errors (#3517) * ci(.travis.yml): Squelch Growl-related spawn errors Installed binary needed for Linux desktop notification support. * ci(appveyor.yml): Squelch Growl-related spawn errors Installed GfW package needed for Windows desktop notification support. Fixes #3483 --- .travis.yml | 8 ++++++ appveyor.yml | 69 +++++++++++++++++++++++++++++++++++++++++++--------- 2 files changed, 65 insertions(+), 12 deletions(-) diff --git a/.travis.yml b/.travis.yml index 94aa85770e..424f9c5f64 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,3 +1,7 @@ +### +### .travis.yml +### + # these are executed in order. each must pass for the next to be run stages: - smoke # this ensures a "user" install works properly @@ -8,6 +12,10 @@ stages: # defaults language: node_js node_js: '10' +addons: + apt: + packages: + - libnotify-bin # `nvm install` happens before the cache is restored, which means # we must install our own npm elsewhere (`~/npm`) before_install: | diff --git a/appveyor.yml b/appveyor.yml index 2d2e1e459d..c8e4863143 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -1,27 +1,72 @@ -platform: - - x64 +### +### appveyor.yml +### + +## General configuration +version: '{build}' +skip_commits: + message: /\[ci\s+skip\]/ + +## Environment configuration +shallow_clone: true +clone_depth: 1 environment: matrix: - nodejs_version: '10' - nodejs_version: '9' - nodejs_version: '8' - nodejs_version: '6' +matrix: + fast_finish: true install: + ## Manual Growl install + - ps: Add-AppveyorMessage "Installing Growl..." + - ps: $exePath = "$($env:USERPROFILE)\GrowlInstaller.exe" + - ps: (New-Object Net.WebClient).DownloadFile('http://www.growlforwindows.com/gfw/downloads/GrowlInstaller.exe', $exePath) + - ps: mkdir C:\GrowlInstaller | out-null + - ps: 7z x $exePath -oC:\GrowlInstaller | out-null + - ps: cmd /c start /wait msiexec /i C:\GrowlInstaller\Growl_v2.0.msi /quiet + - ps: $env:path = "C:\Program Files (x86)\Growl for Windows;$env:path" + ## Node-related installs + - ps: Add-AppveyorMessage "Installing Node..." + - set PATH=%APPDATA%\npm;C:\MinGW\bin;%PATH% - ps: Install-Product node $env:nodejs_version x64 - - set CI=true - - set PATH=%APPDATA%\npm;c:\MinGW\bin;%PATH% + - ps: Add-AppveyorMessage "Installing npm..." - npm install -g npm@^5 + ## Mocha-related package installs + - ps: Add-AppveyorMessage "Installing Mocha dependencies..." - npm ci --ignore-scripts -matrix: - fast_finish: true -build: off -version: '{build}' -shallow_clone: true -clone_depth: 1 + +## Build configuration +platform: + - x64 +build: script +before_build: + ## Growl requires some time before it's ready to handle notifications + - ps: Start-Process -NoNewWindow Growl + - ps: Add-AppveyorMessage "Started Growl service..." + - ps: Start-Sleep -Milliseconds 2000 +build_script: + ## Placeholder command + - ps: Start-Sleep -Milliseconds 0 + #- ps: Add-AppveyorMessage "Verify Growl responding..." + #- ps: growlnotify test + +## Test configuration +before_test: + - set CI=true test_script: + - ps: Add-AppveyorMessage "Displaying version information" - node --version - npm --version + - ps: Add-AppveyorMessage "Running tests..." - npm start test.node -skip_commits: - message: /\[ci\s+skip\]/ + - ps: Add-AppveyorMessage "Done" + +## Notifications +notifications: + - provider: Email + on_build_success: false + on_build_failure: false + on_build_status_changed: false From 207ed8c31f9c6049610a3ccaa4cf5438bd7dfe5d Mon Sep 17 00:00:00 2001 From: Paul Roebuck Date: Fri, 19 Oct 2018 21:38:59 -0500 Subject: [PATCH 65/80] test(test/reporters/base.spec.js): Fix Base reporter test failure due to timeout The `Base` reporter's specification verifies reporter can interpret Chai custom error messages. This test takes ~480ms (lightly loaded CPU), which is _way_ too close to the 500ms timeout. Change doubles this timeout. #3524 --- test/reporters/base.spec.js | 1 + 1 file changed, 1 insertion(+) diff --git a/test/reporters/base.spec.js b/test/reporters/base.spec.js index 8ff2195f70..f880b7b240 100644 --- a/test/reporters/base.spec.js +++ b/test/reporters/base.spec.js @@ -312,6 +312,7 @@ describe('Base reporter', function() { }); it('should interpret Chai custom error messages', function() { + this.timeout(1000); var chaiExpect = require('chai').expect; try { chaiExpect(43, 'custom error message').to.equal(42); From 02d75eebfa506b12b1f241cf7c239b26066120e8 Mon Sep 17 00:00:00 2001 From: "JeongHoon Byun (aka Outsider)" Date: Fri, 26 Oct 2018 22:56:13 -0700 Subject: [PATCH 66/80] Update "commander" to correct display of falsy default values (#3529) Additionally, this includes minor updates to mocha's help output (by rewording text and/or specifying default values). These were then collected into the website documentation. --- bin/_mocha | 30 ++++++++--- docs/index.md | 132 +++++++++++++++++++++++----------------------- package-lock.json | 18 +++++-- package.json | 2 +- 4 files changed, 103 insertions(+), 79 deletions(-) diff --git a/bin/_mocha b/bin/_mocha index 41e9871b76..355a91b83c 100755 --- a/bin/_mocha +++ b/bin/_mocha @@ -157,9 +157,17 @@ program .option('-f, --fgrep ', 'only run tests containing ') .option('-gc, --expose-gc', 'expose gc extension') .option('-i, --invert', 'inverts --grep and --fgrep matches') - .option('-r, --require ', 'require the given module') - .option('-s, --slow ', '"slow" test threshold in milliseconds [75]') - .option('-t, --timeout ', 'set test-case timeout in milliseconds [2000]') + .option('-r, --require ', 'require the given module', []) + .option( + '-s, --slow ', + 'specify "slow" test threshold in milliseconds', + 75 + ) + .option( + '-t, --timeout ', + 'specify test timeout threshold in milliseconds', + 2000 + ) .option( '-u, --ui ', `specify user-interface (${interfaceNames.join('|')})`, @@ -204,7 +212,7 @@ program '--inspect-brk', 'activate devtools in chrome and break on the first line' ) - .option('--interfaces', 'display available interfaces') + .option('--interfaces', 'output provided interfaces and exit') .option('--no-deprecation', 'silence deprecation warnings') .option( '--exit', @@ -218,10 +226,11 @@ program .option('--prof', 'log statistical profiling information') .option('--log-timer-events', 'Time events including external callbacks') .option('--recursive', 'include sub directories') - .option('--reporters', 'display available reporters') + .option('--reporters', 'output provided reporters and exit') .option( '--retries ', - 'set numbers of time to retry a failed test case' + 'specify number of times to retry a failed test case', + 0 ) .option( '--throw-deprecation', @@ -246,11 +255,16 @@ program ) .option( '--file ', - 'include a file to be ran during the suite', + 'adds file be loaded prior to suite execution', collect, [] ) - .option('--exclude ', 'a file or glob pattern to ignore', collect, []); + .option( + '--exclude ', + 'adds file or glob pattern to ignore', + collect, + [] + ); program._name = 'mocha'; diff --git a/docs/index.md b/docs/index.md index 5fae36b634..367dba678b 100644 --- a/docs/index.md +++ b/docs/index.md @@ -779,73 +779,71 @@ Mocha supports the `err.expected` and `err.actual` properties of any thrown `Ass ## Usage -```text - Usage: mocha [debug] [options] [files] - - Options: - - -V, --version output the version number - -A, --async-only force all tests to take a callback (async) or return a promise - -c, --colors force enabling of colors - -C, --no-colors force disabling of colors - -G, --growl enable growl notification support - -O, --reporter-options reporter-specific options - -R, --reporter specify the reporter to use (default: spec) - -S, --sort sort test files - -b, --bail bail after first test failure - -d, --debug enable node's debugger, synonym for node --debug - -g, --grep only run tests matching - -f, --fgrep only run tests containing - -gc, --expose-gc expose gc extension - -i, --invert inverts --grep and --fgrep matches - -r, --require require the given module - -s, --slow "slow" test threshold in milliseconds [75] - -t, --timeout set test-case timeout in milliseconds [2000] - -u, --ui specify user-interface (bdd|tdd|qunit|exports) (default: bdd) - -w, --watch watch files in the current working directory for changes - --check-leaks check for global variable leaks - --full-trace display the full stack trace - --compilers :,... use the given module(s) to compile files (default: ) - --debug-brk enable node's debugger breaking on the first line - --globals allow the given comma-delimited global [names] (default: ) - --es_staging enable all staged features - --harmony<_classes,_generators,...> all node --harmony* flags are available - --preserve-symlinks Instructs the module loader to preserve symbolic links when resolving and caching modules - --icu-data-dir include ICU data - --inline-diffs display actual/expected differences inline within each string - --no-diff do not show a diff on failure - --inspect activate devtools in chrome - --inspect-brk activate devtools in chrome and break on the first line - --interfaces display available interfaces - --no-deprecation silence deprecation warnings - --exit force shutdown of the event loop after test run: mocha will call process.exit - --no-timeouts disables timeouts, given implicitly with --debug - --no-warnings silence all node process warnings - --opts specify opts path (default: test/mocha.opts) - --perf-basic-prof enable perf linux profiler (basic support) - --napi-modules enable experimental NAPI modules - --prof log statistical profiling information - --log-timer-events Time events including external callbacks - --recursive include sub directories - --reporters display available reporters - --retries set numbers of time to retry a failed test case - --throw-deprecation throw an exception anytime a deprecated function is used - --trace trace function calls - --trace-deprecation show stack traces on deprecations - --trace-warnings show stack traces on node process warnings - --use_strict enforce strict mode - --watch-extensions ,... specify extensions to monitor with --watch (default: js) - --delay wait for async suite definition - --allow-uncaught enable uncaught errors to propagate - --forbid-only causes test marked with only to fail the suite - --forbid-pending causes pending tests and test marked with skip to fail the suite - --file include a file to be ran during the suite (default: ) - --exclude a file or glob pattern to ignore (default: ) - -h, --help output usage information - - Commands: - - init initialize a client-side mocha setup at +```console +Usage: mocha [debug] [options] [files] + +Options: + -V, --version output the version number + -A, --async-only force all tests to take a callback (async) or return a promise + -c, --colors force enabling of colors + -C, --no-colors force disabling of colors + -G, --growl enable growl notification support + -O, --reporter-options reporter-specific options + -R, --reporter specify the reporter to use (default: "spec") + -S, --sort sort test files + -b, --bail bail after first test failure + -d, --debug enable node's debugger, synonym for node --debug + -g, --grep only run tests matching + -f, --fgrep only run tests containing + -gc, --expose-gc expose gc extension + -i, --invert inverts --grep and --fgrep matches + -r, --require require the given module (default: []) + -s, --slow specify "slow" test threshold in milliseconds (default: 75) + -t, --timeout specify test timeout threshold in milliseconds (default: 2000) + -u, --ui specify user-interface (bdd|tdd|qunit|exports) (default: "bdd") + -w, --watch watch files in the current working directory for changes + --check-leaks check for global variable leaks + --full-trace display the full stack trace + --compilers :,... use the given module(s) to compile files (default: []) + --debug-brk enable node's debugger breaking on the first line + --globals allow the given comma-delimited global [names] (default: []) + --es_staging enable all staged features + --harmony<_classes,_generators,...> all node --harmony* flags are available + --preserve-symlinks Instructs the module loader to preserve symbolic links when resolving and caching modules + --icu-data-dir include ICU data + --inline-diffs display actual/expected differences inline within each string + --no-diff do not show a diff on failure + --inspect activate devtools in chrome + --inspect-brk activate devtools in chrome and break on the first line + --interfaces output provided interfaces and exit + --no-deprecation silence deprecation warnings + --exit force shutdown of the event loop after test run: mocha will call process.exit + --no-timeouts disables timeouts, given implicitly with --debug + --no-warnings silence all node process warnings + --opts specify opts path (default: "test/mocha.opts") + --perf-basic-prof enable perf linux profiler (basic support) + --napi-modules enable experimental NAPI modules + --prof log statistical profiling information + --log-timer-events Time events including external callbacks + --recursive include sub directories + --reporters output provided reporters and exit + --retries specify number of times to retry a failed test case (default: 0) + --throw-deprecation throw an exception anytime a deprecated function is used + --trace trace function calls + --trace-deprecation show stack traces on deprecations + --trace-warnings show stack traces on node process warnings + --use_strict enforce strict mode + --watch-extensions ,... specify extensions to monitor with --watch (default: ["js"]) + --delay wait for async suite definition + --allow-uncaught enable uncaught errors to propagate + --forbid-only causes test marked with only to fail the suite + --forbid-pending causes pending tests and test marked with skip to fail the suite + --file adds file be loaded prior to suite execution (default: []) + --exclude adds file or glob pattern to ignore (default: []) + -h, --help output usage information + +Commands: + init initialize a client-side mocha setup at ``` ### `-w, --watch` diff --git a/package-lock.json b/package-lock.json index 782bb42830..97fdde53a1 100644 --- a/package-lock.json +++ b/package-lock.json @@ -2026,9 +2026,9 @@ } }, "commander": { - "version": "2.15.1", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.15.1.tgz", - "integrity": "sha512-VlfT9F3V0v+jr4yxPc5gg9s62/fIVWsd2Bk2iD435um1NlGMYdVCq+MjcXnhYq2icNOizHr1kK+5TI6H0Hy0ag==" + "version": "2.19.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.19.0.tgz", + "integrity": "sha512-6tvAOO+D6OENvRAh524Dh9jcfKTYDQAqvqezbCW82xj5X0pSrcpxtvRKHLG0yBY6SD7PSDrJaj+0AiOcKVd1Xg==" }, "comment-regex": { "version": "1.0.1", @@ -5939,6 +5939,12 @@ "uglify-js": "3.3.x" }, "dependencies": { + "commander": { + "version": "2.15.1", + "resolved": "http://registry.npmjs.org/commander/-/commander-2.15.1.tgz", + "integrity": "sha512-VlfT9F3V0v+jr4yxPc5gg9s62/fIVWsd2Bk2iD435um1NlGMYdVCq+MjcXnhYq2icNOizHr1kK+5TI6H0Hy0ag==", + "dev": true + }, "source-map": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", @@ -17954,6 +17960,12 @@ "source-map": "~0.6.1" }, "dependencies": { + "commander": { + "version": "2.15.1", + "resolved": "http://registry.npmjs.org/commander/-/commander-2.15.1.tgz", + "integrity": "sha512-VlfT9F3V0v+jr4yxPc5gg9s62/fIVWsd2Bk2iD435um1NlGMYdVCq+MjcXnhYq2icNOizHr1kK+5TI6H0Hy0ag==", + "dev": true + }, "source-map": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", diff --git a/package.json b/package.json index 002b55e32b..5cd30f5a17 100644 --- a/package.json +++ b/package.json @@ -461,7 +461,7 @@ }, "dependencies": { "browser-stdout": "1.3.1", - "commander": "2.15.1", + "commander": "2.19.0", "debug": "3.1.0", "diff": "3.5.0", "escape-string-regexp": "1.0.5", From ff6cbe775cc81627eff6e5361e950e7bbfa3f36c Mon Sep 17 00:00:00 2001 From: "P. Roebuck" Date: Tue, 30 Oct 2018 08:52:00 -0500 Subject: [PATCH 67/80] refactor(bin/options.js): Refactor and improve documentation (#3533) --- bin/options.js | 57 ++++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 48 insertions(+), 9 deletions(-) diff --git a/bin/options.js b/bin/options.js index a25a18e28e..0c27ae5fa9 100644 --- a/bin/options.js +++ b/bin/options.js @@ -13,9 +13,54 @@ const fs = require('fs'); module.exports = getOptions; /** - * Get options. + * Default pathname for run-control file. + * + * @constant + * @type {string} + * @default */ +const defaultPathname = 'test/mocha.opts'; +/** + * Reads contents of the run-control file. + * + * @private + * @param {string} pathname - Pathname of run-control file. + * @returns {string} file contents + */ +function readOptionsFile(pathname) { + return fs.readFileSync(pathname, 'utf8'); +} + +/** + * Parses options read from run-control file. + * + * @private + * @param {string} content - Content read from run-control file. + * @returns {string[]} cmdline options (and associated arguments) + */ +function parseOptions(content) { + /* + * Replaces comments with empty strings + * Replaces escaped spaces (e.g., 'xxx\ yyy') with HTML space + * Splits on whitespace, creating array of substrings + * Filters empty string elements from array + * Replaces any HTML space with space + */ + return content + .replace(/^#.*$/gm, '') + .replace(/\\\s/g, '%20') + .split(/\s/) + .filter(Boolean) + .map(value => value.replace(/%20/g, ' ')); +} + +/** + * Prepends options from run-control file to the command line arguments. + * + * @public + * @see {@link https://mochajs.org/#mochaopts|mocha.opts} + */ function getOptions() { if ( process.argv.length === 3 && @@ -26,17 +71,11 @@ function getOptions() { const optsPath = process.argv.indexOf('--opts') === -1 - ? 'test/mocha.opts' + ? defaultPathname : process.argv[process.argv.indexOf('--opts') + 1]; try { - const opts = fs - .readFileSync(optsPath, 'utf8') - .replace(/^#.*$/gm, '') - .replace(/\\\s/g, '%20') - .split(/\s/) - .filter(Boolean) - .map(value => value.replace(/%20/g, ' ')); + const opts = parseOptions(readOptionsFile(optsPath)); process.argv = process.argv .slice(0, 2) From 2f28b49d1c6c061d4b80863d815acdafdb2a804e Mon Sep 17 00:00:00 2001 From: "P. Roebuck" Date: Tue, 30 Oct 2018 11:59:21 -0500 Subject: [PATCH 68/80] refactor(json-stream.js): Consistent output stream usage (#3532) ### Description of the Change * Made all output directly use `process.stdout`. * `process.stdout` and `console.log` take different routes to get to same place * Corrected ctor name. * Updated documentation. ### Alternate Designs N/A ### Benefits Consistency. [Don't cross the streams](https://www.youtube.com/watch?v=wyKQe_i9yyo)! ### Possible Drawbacks N/A ### Applicable issues Fixes #3526 Fixes #3521 semver-patch --- lib/reporters/json-stream.js | 51 ++++++++++++++++++++++-------------- 1 file changed, 32 insertions(+), 19 deletions(-) diff --git a/lib/reporters/json-stream.js b/lib/reporters/json-stream.js index 0edd0cbf88..5b4cfbcebe 100644 --- a/lib/reporters/json-stream.js +++ b/lib/reporters/json-stream.js @@ -9,55 +9,68 @@ var Base = require('./base'); /** - * Expose `List`. + * Expose `JSONStream`. */ -exports = module.exports = List; +exports = module.exports = JSONStream; /** - * Initialize a new `JSONStream` test reporter. + * Constructs a new `JSONStream` reporter instance. * * @public - * @name JSONStream - * @class JSONStream - * @memberof Mocha.reporters + * @class * @extends Mocha.reporters.Base - * @api public - * @param {Runner} runner + * @memberof Mocha.reporters + * @param {Runner} runner - Instance triggers reporter actions. */ -function List(runner) { +function JSONStream(runner) { Base.call(this, runner); var self = this; var total = runner.total; - runner.on('start', function() { - console.log(JSON.stringify(['start', {total: total}])); + runner.once('start', function() { + writeEvent(['start', {total: total}]); }); runner.on('pass', function(test) { - console.log(JSON.stringify(['pass', clean(test)])); + writeEvent(['pass', clean(test)]); }); runner.on('fail', function(test, err) { test = clean(test); test.err = err.message; test.stack = err.stack || null; - console.log(JSON.stringify(['fail', test])); + writeEvent(['fail', test]); }); runner.once('end', function() { - process.stdout.write(JSON.stringify(['end', self.stats])); + writeEvent(['end', self.stats]); }); } /** - * Return a plain-object representation of `test` - * free of cyclic properties etc. + * Mocha event to be written to the output stream. + * @typedef {Array} JSONStream~MochaEvent + */ + +/** + * Writes Mocha event to reporter output stream. + * + * @private + * @param {JSONStream~MochaEvent} event - Mocha event to be output. + */ +function writeEvent(event) { + process.stdout.write(JSON.stringify(event) + '\n'); +} + +/** + * Returns an object literal representation of `test` + * free of cyclic properties, etc. * - * @api private - * @param {Object} test - * @return {Object} + * @private + * @param {Test} test - Instance used as data source. + * @return {Object} object containing pared-down test instance data */ function clean(test) { return { From 2d89a47506696761630457fdcd600be9550fc1c1 Mon Sep 17 00:00:00 2001 From: "P. Roebuck" Date: Fri, 2 Nov 2018 12:48:34 -0500 Subject: [PATCH 69/80] fix(_mocha): Update '--no-timeouts' argument description (#3546) Previously undocumented that use of `--inspect` would disable timeouts. Fixes #3519 --- bin/_mocha | 5 ++++- docs/index.md | 2 +- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/bin/_mocha b/bin/_mocha index 355a91b83c..742c5d70d5 100755 --- a/bin/_mocha +++ b/bin/_mocha @@ -218,7 +218,10 @@ program '--exit', 'force shutdown of the event loop after test run: mocha will call process.exit' ) - .option('--no-timeouts', 'disables timeouts, given implicitly with --debug') + .option( + '--no-timeouts', + 'disables timeouts, given implicitly with --debug/--inspect' + ) .option('--no-warnings', 'silence all node process warnings') .option('--opts ', 'specify opts path', 'test/mocha.opts') .option('--perf-basic-prof', 'enable perf linux profiler (basic support)') diff --git a/docs/index.md b/docs/index.md index 367dba678b..fd4ac9aa94 100644 --- a/docs/index.md +++ b/docs/index.md @@ -818,7 +818,7 @@ Options: --interfaces output provided interfaces and exit --no-deprecation silence deprecation warnings --exit force shutdown of the event loop after test run: mocha will call process.exit - --no-timeouts disables timeouts, given implicitly with --debug + --no-timeouts disables timeouts, given implicitly with --debug/--inspect --no-warnings silence all node process warnings --opts specify opts path (default: "test/mocha.opts") --perf-basic-prof enable perf linux profiler (basic support) From f2f79c0dae57be307a1cb667aca89cdba97ba06f Mon Sep 17 00:00:00 2001 From: "P. Roebuck" Date: Fri, 2 Nov 2018 13:10:10 -0500 Subject: [PATCH 70/80] build(ESLint/Git): Ignore JSDoc output directory (#3544) Prettier-ESLint keeps busying itself with the JSDoc output directory upon any commit, spewing hundreds of errors. This tells both ESLint and Git to ignore the directory. --- .eslintignore | 1 + .gitignore | 1 + 2 files changed, 2 insertions(+) diff --git a/.eslintignore b/.eslintignore index 48c498f279..2226cf1fc2 100644 --- a/.eslintignore +++ b/.eslintignore @@ -2,4 +2,5 @@ coverage/ mocha.js *.fixture.js docs/ +out/ !lib/mocha.js diff --git a/.gitignore b/.gitignore index f11fc29c86..7e1edeb748 100644 --- a/.gitignore +++ b/.gitignore @@ -22,4 +22,5 @@ yarn.lock docs/_site docs/_dist docs/api +out/ .vscode/ From ee4625f509cbde2184a59ce2302a08ad3c6bb5dd Mon Sep 17 00:00:00 2001 From: "P. Roebuck" Date: Fri, 2 Nov 2018 17:16:15 -0500 Subject: [PATCH 71/80] ci(Travis/Appveyor): Update Node versions in CI matrix (#3543) * ci(.travis.yml,appveyor.yml): Update Node versions in CI matrix Make Node-11 the new default and drop Node-9 from matrix. --- .travis.yml | 6 +++--- appveyor.yml | 3 +-- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/.travis.yml b/.travis.yml index 424f9c5f64..f6153f6815 100644 --- a/.travis.yml +++ b/.travis.yml @@ -11,7 +11,7 @@ stages: # defaults language: node_js -node_js: '10' +node_js: '11' addons: apt: packages: @@ -40,7 +40,7 @@ jobs: - &node script: npm start test.node - node_js: '9' + node_js: '10' - <<: *node node_js: '8' @@ -75,7 +75,7 @@ jobs: - node_modules # npm install, unlike npm ci, doesn't wipe node_modules - <<: *smoke - node_js: '9' + node_js: '10' - <<: *smoke node_js: '8' diff --git a/appveyor.yml b/appveyor.yml index c8e4863143..e9f8eba0c0 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -12,8 +12,8 @@ shallow_clone: true clone_depth: 1 environment: matrix: + - nodejs_version: '11' - nodejs_version: '10' - - nodejs_version: '9' - nodejs_version: '8' - nodejs_version: '6' matrix: @@ -69,4 +69,3 @@ notifications: on_build_success: false on_build_failure: false on_build_status_changed: false - From cebb18a988015cf7718b604798f01f7ca3864630 Mon Sep 17 00:00:00 2001 From: Christopher Hiller Date: Fri, 2 Nov 2018 15:51:31 -0700 Subject: [PATCH 72/80] update release instructions [ci skip] --- MAINTAINERS.md | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/MAINTAINERS.md b/MAINTAINERS.md index bee04f42d1..55e5436cfc 100644 --- a/MAINTAINERS.md +++ b/MAINTAINERS.md @@ -279,18 +279,16 @@ This is not necessarily ideal, and we should consider another method of using br 1. Decide whether this is a `patch`, `minor`, or `major` release by the PRs which have been merged since the last release. 1. Checkout `master` in your working copy & pull. 1. Modify `CHANGELOG.md`; follow the existing conventions in that file. Commit this file only; add `[ci skip]` to the commit message to avoid a build. -1. Use `npm version` to bump the version; see `npm version --help` for more info. (Hint--use `-m`: e.g. `npm version patch -m 'Release v%s'`) -1. Push `master` to origin with your new tag; e.g. `git push origin master --tags` +1. Use `npm version` (use `npm@6+`) to bump the version; see `npm version --help` for more info. (Hint--use `-m`: e.g., `npm version patch -m 'Release v%s'`) +1. Push `master` to `origin` with your new tag; e.g. `git push origin master --tags` 1. Copy & paste the added lines to a new GitHub "release". Be sure to add any missing link references (use "preview" button). Save release as draft. 1. Meanwhile, you can check [the build](https://travis-ci.org/mochajs/mocha) on Travis-CI. -1. Once it's green and you're satisfied with the release notes, open your draft release on GitHub, then click "publish" +1. Once the build is green, and you're satisfied with the release notes, open your draft release on GitHub, then click "publish." 1. Back in your working copy, run `npm publish`. -1. Announce the update on Twitter or just tell your dog or something. +1. Announce the update on Twitter or just tell your dog or something. New releases will be automatically tweeted by [@b0neskull](https://twitter.com/b0neskull). *Note: there are too many steps above.* -> As of this writing, `npm version` (using npm@5) is not working well, and you may have to tag manually. Commit the change to the version in `package.json` with a message of the format `Release vX.Y.Z`, then tag the changeset using `vX.Y.Z`. - ## Projects There are [Projects](https://github.com/mochajs/mocha/projects), but we haven't yet settled on how to use them. From 9286fdc5a2f280645667eb18c10847bdb3d229aa Mon Sep 17 00:00:00 2001 From: "JeongHoon Byun (aka Outsider)" Date: Sat, 3 Nov 2018 08:41:27 +0900 Subject: [PATCH 73/80] fix runner to emit start/end event (#3395) Signed-off-by: Outsider --- lib/runner.js | 9 +++++++-- test/unit/mocha.spec.js | 10 ++++++++++ 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/lib/runner.js b/lib/runner.js index d105bca321..dc2092f9fa 100644 --- a/lib/runner.js +++ b/lib/runner.js @@ -851,10 +851,15 @@ Runner.prototype.run = function(fn) { filterOnly(rootSuite); } self.started = true; - self.emit('start'); + Runner.immediately(function() { + self.emit('start'); + }); + self.runSuite(rootSuite, function() { debug('finished running'); - self.emit('end'); + Runner.immediately(function() { + self.emit('end'); + }); }); } diff --git a/test/unit/mocha.spec.js b/test/unit/mocha.spec.js index 3f7145834c..7c91add7f3 100644 --- a/test/unit/mocha.spec.js +++ b/test/unit/mocha.spec.js @@ -34,6 +34,16 @@ describe('Mocha', function() { }); } ); + + it('should emit start event', function(done) { + var mocha = new Mocha(blankOpts); + mocha.run().on('start', done); + }); + + it('should emit end event', function(done) { + var mocha = new Mocha(blankOpts); + mocha.run().on('end', done); + }); }); describe('.addFile()', function() { From d4808a3757ddbe5cc9af061b5b3500fd38b1e861 Mon Sep 17 00:00:00 2001 From: "P. Roebuck" Date: Sat, 3 Nov 2018 20:52:59 -0500 Subject: [PATCH 74/80] Warn that suites cannot return a value (#3550) * Give a `DeprecationWarning` on suite callback returning any value. * Deprecation warning: Show a message only once; use `process.nextTick` when falling back to `console.warn` * Add a test for `utils.deprecate` * style: Make prettier happy * test(deprecate.spec.js): Make PR requested changes for approval (per @boneskull) --- lib/interfaces/common.js | 15 ++++++++-- lib/utils.js | 28 +++++++++++++++++++ test/integration/deprecate.spec.js | 18 ++++++++++++ .../integration/fixtures/deprecate.fixture.js | 9 ++++++ .../suite/suite-returning-value.fixture.js | 5 ++++ test/integration/suite.spec.js | 24 ++++++++++++---- 6 files changed, 91 insertions(+), 8 deletions(-) create mode 100644 test/integration/deprecate.spec.js create mode 100644 test/integration/fixtures/deprecate.fixture.js create mode 100644 test/integration/fixtures/suite/suite-returning-value.fixture.js diff --git a/lib/interfaces/common.js b/lib/interfaces/common.js index 4ca340a608..7997312786 100644 --- a/lib/interfaces/common.js +++ b/lib/interfaces/common.js @@ -1,6 +1,7 @@ 'use strict'; var Suite = require('../suite'); +var utils = require('../utils'); /** * Functions common to more than one interface. @@ -92,6 +93,7 @@ module.exports = function(suites, context, mocha) { /** * Creates a suite. + * * @param {Object} opts Options * @param {string} opts.title Title of Suite * @param {Function} [opts.fn] Suite Function (not always applicable) @@ -109,13 +111,22 @@ module.exports = function(suites, context, mocha) { suite.parent._onlySuites = suite.parent._onlySuites.concat(suite); } if (typeof opts.fn === 'function') { - opts.fn.call(suite); + var result = opts.fn.call(suite); + if (typeof result !== 'undefined') { + utils.deprecate( + 'Deprecation Warning: Suites do not support a return value;' + + opts.title + + ' returned :' + + result + ); + } suites.shift(); } else if (typeof opts.fn === 'undefined' && !suite.pending) { throw new Error( 'Suite "' + suite.fullTitle() + - '" was defined but no callback was supplied. Supply a callback or explicitly skip the suite.' + '" was defined but no callback was supplied. ' + + 'Supply a callback or explicitly skip the suite.' ); } else if (!opts.fn && suite.pending) { suites.shift(); diff --git a/lib/utils.js b/lib/utils.js index e67bf74414..71ea6ce4fb 100644 --- a/lib/utils.js +++ b/lib/utils.js @@ -584,6 +584,34 @@ exports.getError = function(err) { return err || exports.undefinedError(); }; +/** + * Show a deprecation warning. Each distinct message is only displayed once. + * + * @param {string} msg + */ +exports.deprecate = function(msg) { + msg = String(msg); + if (seenDeprecationMsg.hasOwnProperty(msg)) { + return; + } + seenDeprecationMsg[msg] = true; + doDeprecationWarning(msg); +}; + +var seenDeprecationMsg = {}; + +var doDeprecationWarning = process.emitWarning + ? function(msg) { + // Node.js v6+ + process.emitWarning(msg, 'DeprecationWarning'); + } + : function(msg) { + // Everything else + process.nextTick(function() { + console.warn(msg); + }); + }; + /** * @summary * This Filter based on `mocha-clean` module.(see: `github.com/rstacruz/mocha-clean`) diff --git a/test/integration/deprecate.spec.js b/test/integration/deprecate.spec.js new file mode 100644 index 0000000000..db806fcde5 --- /dev/null +++ b/test/integration/deprecate.spec.js @@ -0,0 +1,18 @@ +'use strict'; + +var assert = require('assert'); +var run = require('./helpers').runMocha; +var args = []; + +describe('utils.deprecate test', function() { + it('should print unique deprecation only once', function(done) { + run('deprecate.fixture.js', args, function(err, res) { + if (err) { + return done(err); + } + var result = res.output.match(/deprecated thing/g) || []; + assert.equal(result.length, 2); + done(); + }); + }); +}); diff --git a/test/integration/fixtures/deprecate.fixture.js b/test/integration/fixtures/deprecate.fixture.js new file mode 100644 index 0000000000..cf98884600 --- /dev/null +++ b/test/integration/fixtures/deprecate.fixture.js @@ -0,0 +1,9 @@ +'use strict'; + +var utils = require("../../../lib/utils"); + +it('consolidates identical calls to deprecate', function() { + utils.deprecate("suite foo did a deprecated thing"); + utils.deprecate("suite foo did a deprecated thing"); + utils.deprecate("suite bar did a deprecated thing"); +}); diff --git a/test/integration/fixtures/suite/suite-returning-value.fixture.js b/test/integration/fixtures/suite/suite-returning-value.fixture.js new file mode 100644 index 0000000000..8859cfb5d9 --- /dev/null +++ b/test/integration/fixtures/suite/suite-returning-value.fixture.js @@ -0,0 +1,5 @@ +'use strict'; + +describe('a suite returning a value', function () { + return Promise.resolve(); +}); diff --git a/test/integration/suite.spec.js b/test/integration/suite.spec.js index e8bd34382b..338a198f63 100644 --- a/test/integration/suite.spec.js +++ b/test/integration/suite.spec.js @@ -9,8 +9,7 @@ describe('suite w/no callback', function() { it('should throw a helpful error message when a callback for suite is not supplied', function(done) { run('suite/suite-no-callback.fixture.js', args, function(err, res) { if (err) { - done(err); - return; + return done(err); } var result = res.output.match(/no callback was supplied/) || []; assert.equal(result.length, 1); @@ -24,8 +23,7 @@ describe('skipped suite w/no callback', function() { it('should not throw an error when a callback for skipped suite is not supplied', function(done) { run('suite/suite-skipped-no-callback.fixture.js', args, function(err, res) { if (err) { - done(err); - return; + return done(err); } var pattern = new RegExp('Error', 'g'); var result = res.output.match(pattern) || []; @@ -40,8 +38,7 @@ describe('skipped suite w/ callback', function() { it('should not throw an error when a callback for skipped suite is supplied', function(done) { run('suite/suite-skipped-callback.fixture.js', args, function(err, res) { if (err) { - done(err); - return; + return done(err); } var pattern = new RegExp('Error', 'g'); var result = res.output.match(pattern) || []; @@ -50,3 +47,18 @@ describe('skipped suite w/ callback', function() { }); }); }); + +describe('suite returning a value', function() { + this.timeout(2000); + it('should give a deprecation warning for suite callback returning a value', function(done) { + run('suite/suite-returning-value.fixture.js', args, function(err, res) { + if (err) { + return done(err); + } + var pattern = new RegExp('Deprecation Warning', 'g'); + var result = res.output.match(pattern) || []; + assert.equal(result.length, 1); + done(); + }); + }); +}); From c6c784a913777b6b2ce968eb02aa3af508af9245 Mon Sep 17 00:00:00 2001 From: Paul Roebuck Date: Sun, 4 Nov 2018 11:10:11 -0600 Subject: [PATCH 75/80] feat(reporters/tap.js): Make TAP reporter conform to TAP13 Finished up TAPProducer OO overhaul. Documented everything. Renamed reporter option field as `tapVersion`. Additional checking of input. Reworked some event listener arguments. Replaced `console.log` with `process.stdout` stream. Documented everything. --- lib/reporters/tap.js | 272 ++++++++++++++++++++++++++++++------------- 1 file changed, 192 insertions(+), 80 deletions(-) diff --git a/lib/reporters/tap.js b/lib/reporters/tap.js index e8ec3aa3dd..7a591db7bc 100644 --- a/lib/reporters/tap.js +++ b/lib/reporters/tap.js @@ -6,8 +6,10 @@ * Module dependencies. */ +var util = require('util'); var Base = require('./base'); var inherits = require('../utils').inherits; +var sprintf = util.format; /** * Expose `TAP`. @@ -16,36 +18,34 @@ var inherits = require('../utils').inherits; exports = module.exports = TAP; /** - * Initialize a new `TAP` reporter. + * Constructs a new `TAP` reporter instance. * * @public * @class - * @memberof Mocha.reporters * @extends Mocha.reporters.Base - * @api public - * @param {Runner} runner + * @memberof Mocha.reporters + * @param {Runner} runner - Instance triggers reporter actions. + * @param {Object} [options] - runner options */ function TAP(runner, options) { - Base.call(this, runner); + Base.call(this, runner, options); var self = this; var n = 1; - var tapSpec = '12'; + var tapVersion = '12'; if (options && options.reporterOptions) { - if (options.reporterOptions.spec) { - tapSpec = options.reporterOptions.spec; + if (options.reporterOptions.tapVersion) { + tapVersion = options.reporterOptions.tapVersion.toString(); } } - if (tapSpec === '13') { - this.producer = new TAP13Producer(); - } else { - this.producer = new TAP12Producer(); - } + this._producer = createProducer(tapVersion); - runner.on('start', function() { - self.producer.writeStart(runner); + runner.once('start', function() { + var ntests = runner.grepTotal(runner.suite); + self._producer.writeVersion(); + self._producer.writePlan(ntests); }); runner.on('test end', function() { @@ -53,121 +53,233 @@ function TAP(runner, options) { }); runner.on('pending', function(test) { - self.producer.writePending(n, test); + self._producer.writePending(n, test); }); runner.on('pass', function(test) { - self.producer.writePass(n, test); + self._producer.writePass(n, test); }); runner.on('fail', function(test, err) { - self.producer.writeFail(n, test, err); + self._producer.writeFail(n, test, err); }); runner.once('end', function() { - self.producer.writeEnd(runner); + self._producer.writeEpilogue(runner.stats); }); } /** - * Return a TAP-safe title of `test` + * Inherit from `Base.prototype`. + */ +inherits(TAP, Base); + +/** + * Returns a TAP-safe title of `test`. * - * @api private - * @param {Object} test - * @return {String} + * @private + * @param {Test} test - Test instance. + * @return {String} title with any hash character removed */ function title(test) { return test.fullTitle().replace(/#/g, ''); } /** - * Inherit from `Base.prototype`. + * Writes newline-terminated formatted string to reporter output stream. + * + * @private + * @param {string} format - `printf`-like format string + * @param {...*} [varArgs] - Format string arguments */ -inherits(TAP, Base); +function println(format, varArgs) { + var vargs = Array.from(arguments); + vargs[0] += '\n'; + process.stdout.write(sprintf.apply(null, vargs)); +} /** - * Initialize a new `TAPProducer`. Should only be used as a base class. + * Returns a `tapVersion`-appropriate TAP producer instance, if possible. * * @private - * @class - * @api private + * @param {string} tapVersion - Version of TAP specification to produce. + * @returns {TAPProducer} specification-appropriate instance + * @throws {Error} if specification version has no associated producer. */ -function TAPProducer() {} - -TAPProducer.prototype.writePending = function(n, test) { - console.log('ok %d %s # SKIP -', n, title(test)); -}; +function createProducer(tapVersion) { + var producers = { + '12': new TAP12Producer(), + '13': new TAP13Producer() + }; + var producer = producers[tapVersion]; -TAPProducer.prototype.writePass = function(n, test) { - console.log('ok %d %s', n, title(test)); -}; + if (!producer) { + throw new Error( + 'invalid or unsupported TAP version: ' + JSON.stringify(tapVersion) + ); + } -TAPProducer.prototype.writeEnd = function(runner) { - console.log('# tests ' + (runner.stats.passes + runner.stats.failures)); - console.log('# pass ' + runner.stats.passes); - console.log('# fail ' + runner.stats.failures); -}; + return producer; +} /** - * Initialize a new `TAP12Producer` which will produce output conforming to the TAP12 spec. + * @summary + * Constructs a new `TAPProducer` instance. + * + * @description + * Should only be used as an abstract base class. * * @private - * @class - * @api private + * @constructor */ -function TAP12Producer() {} +function TAPProducer() {} -TAP12Producer.prototype.writeStart = function(runner) { - var total = runner.grepTotal(runner.suite); - console.log('%d..%d', 1, total); -}; +/** + * Writes the TAP version to reporter output stream. + * + * @abstract + */ +TAPProducer.prototype.writeVersion = function() {}; -TAP12Producer.prototype.writeFail = function(n, test, err) { - console.log('not ok %d %s', n, title(test)); - if (err.message) { - console.log(err.message.replace(/^/gm, ' ')); - } - if (err.stack) { - console.log(err.stack.replace(/^/gm, ' ')); - } +/** + * Writes the plan to reporter output stream. + * + * @abstract + * @param {number} ntests - Number of tests that are planned to run. + */ +TAPProducer.prototype.writePlan = function(ntests) { + println('%d..%d', 1, ntests); }; -inherits(TAP12Producer, TAPProducer); +/** + * Writes that test passed to reporter output stream. + * + * @abstract + * @param {number} n - Index of test that passed. + * @param {Test} test - Instance containing test information. + */ +TAPProducer.prototype.writePass = function(n, test) { + println('ok %d %s', n, title(test)); +}; /** - * Initialize a new `TAP13Producer` which will produce output conforming to the TAP13 spec. + * Writes that test was skipped to reporter output stream. * - * @private - * @class - * @api private + * @abstract + * @param {number} n - Index of test that was skipped. + * @param {Test} test - Instance containing test information. */ -function TAP13Producer() {} +TAPProducer.prototype.writePending = function(n, test) { + println('ok %d %s # SKIP -', n, title(test)); +}; -TAP13Producer.prototype.yamlIndent = function(level) { - return Array(level + 1).join(' '); +/** + * Writes that test failed to reporter output stream. + * + * @abstract + * @param {number} n - Index of test that was skipped. + * @param {Test} test - Instance containing test information. + * @param {Error} err - Reason the test failed. + */ +TAPProducer.prototype.writeFail = function(n, test, err) { + println('not ok %d %s', n, title(test)); }; -TAP13Producer.prototype.writeStart = function(runner) { - console.log('TAP version 13'); - var total = runner.grepTotal(runner.suite); - console.log('%d..%d', 1, total); +/** + * Writes the summary epilogue to reporter output stream. + * + * @abstract + * @param {Object} stats - Object containing run statistics. + */ +TAPProducer.prototype.writeEpilogue = function(stats) { + // :TBD: Why is this not counting pending tests? + println('# tests ' + (stats.passes + stats.failures)); + println('# pass ' + stats.passes); + // :TBD: Why are we not showing pending results? + println('# fail ' + stats.failures); }; -TAP13Producer.prototype.writeFail = function(n, test, err) { - console.log('not ok %d %s', n, title(test)); - var emitYamlBlock = err.message != null || err.stack != null; - if (emitYamlBlock) { - console.log(this.yamlIndent(1) + '---'); +/** + * @summary + * Constructs a new `TAP12Producer` instance. + * + * @description + * It will produce output conforming to the TAP12 spec. + * + * @private + * @constructor + * @extends TAPProducer + * @see {@link https://testanything.org/tap-specification.html|Specification} + */ +function TAP12Producer() { + /** + * Writes that test failed to reporter output stream, with error formatting. + * @override + */ + this.writeFail = function(n, test, err) { + TAPProducer.prototype.writeFail.call(this, n, test, err); if (err.message) { - console.log(this.yamlIndent(2) + 'message: |-'); - console.log(err.message.replace(/^/gm, this.yamlIndent(3))); + println(err.message.replace(/^/gm, ' ')); } if (err.stack) { - console.log(this.yamlIndent(2) + 'stack: |-'); - console.log(err.stack.replace(/^/gm, this.yamlIndent(3))); + println(err.stack.replace(/^/gm, ' ')); } - console.log(this.yamlIndent(1) + '...'); + }; +} + +/** + * Inherit from `TAPProducer.prototype`. + */ +inherits(TAP12Producer, TAPProducer); + +/** + * @summary + * Constructs a new `TAP13Producer` instance. + * + * @description + * It will produce output conforming to the TAP13 spec. + * + * @private + * @constructor + * @extends TAPProducer + * @see {@link https://testanything.org/tap-version-13-specification.html|Specification} + */ +function TAP13Producer() { + /** + * Writes the TAP version to reporter output stream. + * @override + */ + this.writeVersion = function() { + println('TAP version 13'); + }; + + /** + * Writes that test failed to reporter output stream, with error formatting. + * @override + */ + this.writeFail = function(n, test, err) { + TAPProducer.prototype.writeFail.call(this, n, test, err); + var emitYamlBlock = err.message != null || err.stack != null; + if (emitYamlBlock) { + println(indent(1) + '---'); + if (err.message) { + println(indent(2) + 'message: |-'); + println(err.message.replace(/^/gm, indent(3))); + } + if (err.stack) { + println(indent(2) + 'stack: |-'); + println(err.stack.replace(/^/gm, indent(3))); + } + println(indent(1) + '...'); + } + }; + + function indent(level) { + return Array(level + 1).join(' '); } -}; +} +/** + * Inherit from `TAPProducer.prototype`. + */ inherits(TAP13Producer, TAPProducer); From c179a34f906b2d6d988226009fada092691148fe Mon Sep 17 00:00:00 2001 From: Paul Roebuck Date: Sun, 4 Nov 2018 11:13:24 -0600 Subject: [PATCH 76/80] test(reporters.spec.js,tap.spec.js): Updated to match `tapVersion` reporter option Converted `spec` to `tapVersion` for `reporterOptions` cmdline args. --- test/integration/reporters.spec.js | 6 ++-- test/reporters/tap.spec.js | 45 +++++++++++++++--------------- 2 files changed, 26 insertions(+), 25 deletions(-) diff --git a/test/integration/reporters.spec.js b/test/integration/reporters.spec.js index cd6910f7df..c78da8ab6b 100644 --- a/test/integration/reporters.spec.js +++ b/test/integration/reporters.spec.js @@ -134,7 +134,8 @@ describe('reporters', function() { describe('produces valid TAP v13 output', function() { var runFixtureAndValidateOutput = function(fixture, expected) { it('for ' + fixture, function(done) { - var args = ['--reporter=tap', '--reporter-options', 'spec=13']; + var args = ['--reporter=tap', '--reporter-options', 'tapVersion=13']; + run(fixture, args, function(err, res) { if (err) { done(err); @@ -196,7 +197,8 @@ describe('reporters', function() { }); it('places exceptions correctly in YAML blocks', function(done) { - var args = ['--reporter=tap', '--reporter-options', 'spec=13']; + var args = ['--reporter=tap', '--reporter-options', 'tapVersion=13']; + run('reporters.fixture.js', args, function(err, res) { if (err) { done(err); diff --git a/test/reporters/tap.spec.js b/test/reporters/tap.spec.js index ef72ee0c26..2ac4eb2c2b 100644 --- a/test/reporters/tap.spec.js +++ b/test/reporters/tap.spec.js @@ -33,8 +33,9 @@ describe('TAP reporter', function() { }); describe('TAP12 spec', function() { + var options = {}; describe('on start', function() { - it('should hand runners suite into grepTotal and log the total', function() { + it('should write the number of tests that it plans to run', function() { var expectedSuite = 'some suite'; var expectedTotal = 10; var expectedString; @@ -44,7 +45,7 @@ describe('TAP reporter', function() { expectedString = string; return expectedTotal; }; - TAP.call({}, runner); + TAP.call({}, runner, options); var expectedArray = ['1..' + expectedTotal + '\n']; process.stdout.write = stdoutWrite; @@ -65,7 +66,7 @@ describe('TAP reporter', function() { ); runner.suite = ''; runner.grepTotal = function() {}; - TAP.call({}, runner); + TAP.call({}, runner, options); process.stdout.write = stdoutWrite; @@ -81,7 +82,7 @@ describe('TAP reporter', function() { runner.suite = ''; runner.grepTotal = function() {}; - TAP.call({}, runner); + TAP.call({}, runner, options); process.stdout.write = stdoutWrite; @@ -109,14 +110,13 @@ describe('TAP reporter', function() { runner.on = function(event, callback) { if (event === 'test end') { callback(); - } - if (event === 'fail') { + } else if (event === 'fail') { callback(test, error); } }; runner.suite = ''; runner.grepTotal = function() {}; - TAP.call({}, runner); + TAP.call({}, runner, options); process.stdout.write = stdoutWrite; @@ -143,7 +143,7 @@ describe('TAP reporter', function() { ); runner.suite = ''; runner.grepTotal = function() {}; - TAP.call({}, runner); + TAP.call({}, runner, options); process.stdout.write = stdoutWrite; @@ -173,14 +173,13 @@ describe('TAP reporter', function() { runner.on = function(event, callback) { if (event === 'test end') { callback(); - } - if (event === 'fail') { + } else if (event === 'fail') { callback(test, error); } }; runner.suite = ''; runner.grepTotal = function() {}; - TAP.call({}, runner); + TAP.call({}, runner, options); process.stdout.write = stdoutWrite; @@ -198,14 +197,13 @@ describe('TAP reporter', function() { runner.on = runner.once = function(event, callback) { if (event === 'test end') { callback(); - } - if (event === 'fail') { + } else if (event === 'fail') { callback(test, error); } }; runner.suite = ''; runner.grepTotal = function() {}; - TAP.call({}, runner); + TAP.call({}, runner, options); process.stdout.write = stdoutWrite; @@ -223,7 +221,7 @@ describe('TAP reporter', function() { runner = createMockRunner('fail end pass', 'fail', 'end', 'pass', test); runner.suite = ''; runner.grepTotal = function() {}; - TAP.call({}, runner); + TAP.call({}, runner, options); process.stdout.write = stdoutWrite; @@ -241,9 +239,13 @@ describe('TAP reporter', function() { }); describe('TAP13 spec', function() { - var options = {reporterOptions: {spec: '13'}}; + var options = { + reporterOptions: { + tapVersion: '13' + } + }; describe('on start', function() { - it('should hand runners suite into grepTotal and log the total', function() { + it('should write the number of tests that it plans to run', function() { var expectedSuite = 'some suite'; var expectedTotal = 10; var expectedString; @@ -318,8 +320,7 @@ describe('TAP reporter', function() { runner.on = function(event, callback) { if (event === 'test end') { callback(); - } - if (event === 'fail') { + } else if (event === 'fail') { callback(test, error); } }; @@ -388,8 +389,7 @@ describe('TAP reporter', function() { runner.on = function(event, callback) { if (event === 'test end') { callback(); - } - if (event === 'fail') { + } else if (event === 'fail') { callback(test, error); } }; @@ -417,8 +417,7 @@ describe('TAP reporter', function() { runner.on = runner.once = function(event, callback) { if (event === 'test end') { callback(); - } - if (event === 'fail') { + } else if (event === 'fail') { callback(test, error); } }; From df03a005d647715ac0af901d59b4439fddbd273b Mon Sep 17 00:00:00 2001 From: Paul Roebuck Date: Mon, 5 Nov 2018 18:25:10 -0600 Subject: [PATCH 77/80] test(reporters/helpers.js): Add update to "helpers.js" from PR #3528 Tried this via rebase, but new function was missing afterwards. --- test/reporters/helpers.js | 48 ++++++++++++++++++++++++++++++++++++--- 1 file changed, 45 insertions(+), 3 deletions(-) diff --git a/test/reporters/helpers.js b/test/reporters/helpers.js index e0c6604216..4da36fa600 100644 --- a/test/reporters/helpers.js +++ b/test/reporters/helpers.js @@ -155,9 +155,51 @@ function makeExpectedTest( }; } +/** + * Creates closure with reference to the reporter class constructor. + * + * @param {Function} ctor - Reporter class constructor + * @return {createRunReporterFunction~runReporter} + */ +function createRunReporterFunction(ctor) { + /** + * Run reporter using stream reassignment to capture output. + * + * @param {Object} stubSelf - Reporter-like stub instance + * @param {Runner} runner - Mock instance + * @param {Object} [options] - Reporter configuration settings + * @param {boolean} [tee=false] - Whether to echo output to screen + * @return {string[]} Lines of output written to `stdout` + */ + var runReporter = function(stubSelf, runner, options, tee) { + var stdout = []; + + // Reassign stream in order to make a copy of all reporter output + var stdoutWrite = process.stdout.write; + process.stdout.write = function(string, enc, callback) { + stdout.push(string); + if (tee) { + stdoutWrite.call(process.stdout, string, enc, callback); + } + }; + + // Invoke reporter + ctor.call(stubSelf, runner, options); + + // Revert stream reassignment here so reporter output + // can't be corrupted if any test assertions throw + process.stdout.write = stdoutWrite; + + return stdout; + }; + + return runReporter; +} + module.exports = { - createMockRunner: createMockRunner, - makeTest: makeTest, createElements: createElements, - makeExpectedTest: makeExpectedTest + createMockRunner: createMockRunner, + createRunReporterFunction: createRunReporterFunction, + makeExpectedTest: makeExpectedTest, + makeTest: makeTest }; From 6d32ec96ca2edb9b090761b894ae4c03611fa86e Mon Sep 17 00:00:00 2001 From: Paul Roebuck Date: Mon, 5 Nov 2018 18:33:27 -0600 Subject: [PATCH 78/80] test(reporters/tap.spec.js): Update TAP reporter test before merge Updated to make use of PR #3528 reporter test improvements. Afterwards, found several places where a test had been relying on a previous test's setup. Added two new tests. Cleanup on aisle 4... --- test/reporters/tap.spec.js | 180 +++++++++++++++++++------------------ 1 file changed, 95 insertions(+), 85 deletions(-) diff --git a/test/reporters/tap.spec.js b/test/reporters/tap.spec.js index 2ac4eb2c2b..67632210e3 100644 --- a/test/reporters/tap.spec.js +++ b/test/reporters/tap.spec.js @@ -4,22 +4,18 @@ var reporters = require('../../').reporters; var TAP = reporters.TAP; var createMockRunner = require('./helpers').createMockRunner; +var makeRunReporter = require('./helpers.js').createRunReporterFunction; describe('TAP reporter', function() { - var stdout; - var stdoutWrite; + var ECHO = true; var runner; + var options = {}; + var runReporter = makeRunReporter(TAP); var expectedTitle = 'some title'; var countAfterTestEnd = 2; var test; beforeEach(function() { - stdout = []; - stdoutWrite = process.stdout.write; - process.stdout.write = function(string, enc, callback) { - stdout.push(string); - stdoutWrite.call(process.stdout, string, enc, callback); - }; test = { fullTitle: function() { return expectedTitle; @@ -29,27 +25,33 @@ describe('TAP reporter', function() { }); afterEach(function() { - process.stdout.write = stdoutWrite; + runner = undefined; + test = undefined; }); describe('TAP12 spec', function() { var options = {}; describe('on start', function() { - it('should write the number of tests that it plans to run', function() { - var expectedSuite = 'some suite'; - var expectedTotal = 10; - var expectedString; + var expectedSuite = 'some suite'; + var expectedTotal = 10; + var expectedString; + var stdout; + + before(function() { runner = createMockRunner('start', 'start'); runner.suite = expectedSuite; runner.grepTotal = function(string) { expectedString = string; return expectedTotal; }; - TAP.call({}, runner, options); + stdout = runReporter({}, runner, options); + }); + it('should not write the TAP specification version', function() { + expect(stdout, 'not to contain', 'TAP version'); + }); + it('should write the number of tests that it plans to run', function() { var expectedArray = ['1..' + expectedTotal + '\n']; - process.stdout.write = stdoutWrite; - expect(stdout, 'to equal', expectedArray); expect(expectedString, 'to be', expectedSuite); }); @@ -66,9 +68,8 @@ describe('TAP reporter', function() { ); runner.suite = ''; runner.grepTotal = function() {}; - TAP.call({}, runner, options); - process.stdout.write = stdoutWrite; + var stdout = runReporter({}, runner, options); var expectedMessage = 'ok ' + countAfterTestEnd + ' ' + expectedTitle + ' # SKIP -\n'; @@ -79,12 +80,10 @@ describe('TAP reporter', function() { describe('on pass', function() { it('should write expected message including count and title', function() { runner = createMockRunner('start test', 'test end', 'pass', null, test); - runner.suite = ''; runner.grepTotal = function() {}; - TAP.call({}, runner, options); - process.stdout.write = stdoutWrite; + var stdout = runReporter({}, runner, options); var expectedMessage = 'ok ' + countAfterTestEnd + ' ' + expectedTitle + '\n'; @@ -95,18 +94,18 @@ describe('TAP reporter', function() { describe('on fail', function() { describe('if there is an error message', function() { it('should write expected message and error message', function() { - var expectedTitle = 'some title'; - var countAfterTestEnd = 2; var expectedErrorMessage = 'some error'; - var test = { - fullTitle: function() { - return expectedTitle; - }, - slow: function() {} - }; var error = { message: expectedErrorMessage }; + runner = createMockRunner( + 'test end fail', + 'test end', + 'fail', + null, + test, + error + ); runner.on = function(event, callback) { if (event === 'test end') { callback(); @@ -116,9 +115,8 @@ describe('TAP reporter', function() { }; runner.suite = ''; runner.grepTotal = function() {}; - TAP.call({}, runner, options); - process.stdout.write = stdoutWrite; + var stdout = runReporter({}, runner, options); var expectedArray = [ 'not ok ' + countAfterTestEnd + ' ' + expectedTitle + '\n', @@ -143,9 +141,8 @@ describe('TAP reporter', function() { ); runner.suite = ''; runner.grepTotal = function() {}; - TAP.call({}, runner, options); - process.stdout.write = stdoutWrite; + var stdout = runReporter({}, runner, options); var expectedArray = [ 'not ok ' + countAfterTestEnd + ' ' + expectedTitle + '\n', @@ -156,20 +153,20 @@ describe('TAP reporter', function() { }); describe('if there is an error stack and error message', function() { it('should write expected message and stack', function() { - var expectedTitle = 'some title'; - var countAfterTestEnd = 2; var expectedStack = 'some stack'; var expectedErrorMessage = 'some error'; - var test = { - fullTitle: function() { - return expectedTitle; - }, - slow: function() {} - }; var error = { stack: expectedStack, message: expectedErrorMessage }; + runner = createMockRunner( + 'test end fail', + 'test end', + 'fail', + null, + test, + error + ); runner.on = function(event, callback) { if (event === 'test end') { callback(); @@ -179,9 +176,8 @@ describe('TAP reporter', function() { }; runner.suite = ''; runner.grepTotal = function() {}; - TAP.call({}, runner, options); - process.stdout.write = stdoutWrite; + var stdout = runReporter({}, runner, options); var expectedArray = [ 'not ok ' + countAfterTestEnd + ' ' + expectedTitle + '\n', @@ -194,6 +190,14 @@ describe('TAP reporter', function() { describe('if there is no error stack or error message', function() { it('should write expected message only', function() { var error = {}; + runner = createMockRunner( + 'test end fail', + 'test end', + 'fail', + null, + test, + error + ); runner.on = runner.once = function(event, callback) { if (event === 'test end') { callback(); @@ -203,9 +207,8 @@ describe('TAP reporter', function() { }; runner.suite = ''; runner.grepTotal = function() {}; - TAP.call({}, runner, options); - process.stdout.write = stdoutWrite; + var stdout = runReporter({}, runner, options); var expectedArray = [ 'not ok ' + countAfterTestEnd + ' ' + expectedTitle + '\n' @@ -221,9 +224,8 @@ describe('TAP reporter', function() { runner = createMockRunner('fail end pass', 'fail', 'end', 'pass', test); runner.suite = ''; runner.grepTotal = function() {}; - TAP.call({}, runner, options); - process.stdout.write = stdoutWrite; + var stdout = runReporter({}, runner, options); var totalTests = numberOfPasses + numberOfFails; var expectedArray = [ @@ -245,21 +247,29 @@ describe('TAP reporter', function() { } }; describe('on start', function() { - it('should write the number of tests that it plans to run', function() { - var expectedSuite = 'some suite'; - var expectedTotal = 10; - var expectedString; + var expectedSuite = 'some suite'; + var expectedTotal = 10; + var expectedString; + var stdout; + + before(function() { runner = createMockRunner('start', 'start'); runner.suite = expectedSuite; runner.grepTotal = function(string) { expectedString = string; return expectedTotal; }; - TAP.call({}, runner, options); - var expectedSecondLine = '1..' + expectedTotal + '\n'; - process.stdout.write = stdoutWrite; + stdout = runReporter({}, runner, options); + }); + it('should write the TAP specification version', function() { + var tapVersion = options.reporterOptions.tapVersion; + var expectedFirstLine = 'TAP version ' + tapVersion + '\n'; + expect(stdout[0], 'to equal', expectedFirstLine); + }); + it('should write the number of tests that it plans to run', function() { + var expectedSecondLine = '1..' + expectedTotal + '\n'; expect(stdout[1], 'to equal', expectedSecondLine); expect(expectedString, 'to be', expectedSuite); }); @@ -276,9 +286,8 @@ describe('TAP reporter', function() { ); runner.suite = ''; runner.grepTotal = function() {}; - TAP.call({}, runner, options); - process.stdout.write = stdoutWrite; + var stdout = runReporter({}, runner, options); var expectedMessage = 'ok ' + countAfterTestEnd + ' ' + expectedTitle + ' # SKIP -\n'; @@ -289,12 +298,10 @@ describe('TAP reporter', function() { describe('on pass', function() { it('should write expected message including count and title', function() { runner = createMockRunner('start test', 'test end', 'pass', null, test); - runner.suite = ''; runner.grepTotal = function() {}; - TAP.call({}, runner, options); - process.stdout.write = stdoutWrite; + var stdout = runReporter({}, runner, options); var expectedMessage = 'ok ' + countAfterTestEnd + ' ' + expectedTitle + '\n'; @@ -305,18 +312,18 @@ describe('TAP reporter', function() { describe('on fail', function() { describe('if there is an error message', function() { it('should write expected message and error message', function() { - var expectedTitle = 'some title'; - var countAfterTestEnd = 2; var expectedErrorMessage = 'some error'; - var test = { - fullTitle: function() { - return expectedTitle; - }, - slow: function() {} - }; var error = { message: expectedErrorMessage }; + runner = createMockRunner( + 'test end fail', + 'test end', + 'fail', + null, + test, + error + ); runner.on = function(event, callback) { if (event === 'test end') { callback(); @@ -326,9 +333,8 @@ describe('TAP reporter', function() { }; runner.suite = ''; runner.grepTotal = function() {}; - TAP.call({}, runner, options); - process.stdout.write = stdoutWrite; + var stdout = runReporter({}, runner, options); var expectedArray = [ 'not ok ' + countAfterTestEnd + ' ' + expectedTitle + '\n', @@ -356,9 +362,8 @@ describe('TAP reporter', function() { ); runner.suite = ''; runner.grepTotal = function() {}; - TAP.call({}, runner, options); - process.stdout.write = stdoutWrite; + var stdout = runReporter({}, runner, options); var expectedArray = [ 'not ok ' + countAfterTestEnd + ' ' + expectedTitle + '\n', @@ -372,20 +377,20 @@ describe('TAP reporter', function() { }); describe('if there is an error stack and error message', function() { it('should write expected message and stack', function() { - var expectedTitle = 'some title'; - var countAfterTestEnd = 2; var expectedStack = 'some stack'; var expectedErrorMessage = 'some error'; - var test = { - fullTitle: function() { - return expectedTitle; - }, - slow: function() {} - }; var error = { stack: expectedStack, message: expectedErrorMessage }; + runner = createMockRunner( + 'test end fail', + 'test end', + 'fail', + null, + test, + error + ); runner.on = function(event, callback) { if (event === 'test end') { callback(); @@ -395,9 +400,8 @@ describe('TAP reporter', function() { }; runner.suite = ''; runner.grepTotal = function() {}; - TAP.call({}, runner, options); - process.stdout.write = stdoutWrite; + var stdout = runReporter({}, runner, options); var expectedArray = [ 'not ok ' + countAfterTestEnd + ' ' + expectedTitle + '\n', @@ -414,6 +418,14 @@ describe('TAP reporter', function() { describe('if there is no error stack or error message', function() { it('should write expected message only', function() { var error = {}; + runner = createMockRunner( + 'test end fail', + 'test end', + 'fail', + null, + test, + error + ); runner.on = runner.once = function(event, callback) { if (event === 'test end') { callback(); @@ -423,9 +435,8 @@ describe('TAP reporter', function() { }; runner.suite = ''; runner.grepTotal = function() {}; - TAP.call({}, runner, options); - process.stdout.write = stdoutWrite; + var stdout = runReporter({}, runner, options); var expectedArray = [ 'not ok ' + countAfterTestEnd + ' ' + expectedTitle + '\n' @@ -442,9 +453,8 @@ describe('TAP reporter', function() { runner = createMockRunner('fail end pass', 'fail', 'end', 'pass', test); runner.suite = ''; runner.grepTotal = function() {}; - TAP.call({}, runner, options); - process.stdout.write = stdoutWrite; + var stdout = runReporter({}, runner, options); var totalTests = numberOfPasses + numberOfFails; var expectedArray = [ From 9d8b18a4194fe25ddfa7a916cb5342209e170646 Mon Sep 17 00:00:00 2001 From: Paul Roebuck Date: Mon, 5 Nov 2018 18:57:47 -0600 Subject: [PATCH 79/80] test(reporters.tap.spec.js): Remove unused variable Some days it's like I just work _for_ the lint tool... --- test/reporters/tap.spec.js | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/test/reporters/tap.spec.js b/test/reporters/tap.spec.js index 67632210e3..c0a5419d37 100644 --- a/test/reporters/tap.spec.js +++ b/test/reporters/tap.spec.js @@ -7,9 +7,7 @@ var createMockRunner = require('./helpers').createMockRunner; var makeRunReporter = require('./helpers.js').createRunReporterFunction; describe('TAP reporter', function() { - var ECHO = true; var runner; - var options = {}; var runReporter = makeRunReporter(TAP); var expectedTitle = 'some title'; var countAfterTestEnd = 2; @@ -31,6 +29,7 @@ describe('TAP reporter', function() { describe('TAP12 spec', function() { var options = {}; + describe('on start', function() { var expectedSuite = 'some suite'; var expectedTotal = 10; @@ -125,6 +124,7 @@ describe('TAP reporter', function() { expect(stdout, 'to equal', expectedArray); }); }); + describe('if there is an error stack', function() { it('should write expected message and stack', function() { var expectedStack = 'some stack'; @@ -151,6 +151,7 @@ describe('TAP reporter', function() { expect(stdout, 'to equal', expectedArray); }); }); + describe('if there is an error stack and error message', function() { it('should write expected message and stack', function() { var expectedStack = 'some stack'; @@ -187,6 +188,7 @@ describe('TAP reporter', function() { expect(stdout, 'to equal', expectedArray); }); }); + describe('if there is no error stack or error message', function() { it('should write expected message only', function() { var error = {}; @@ -217,6 +219,7 @@ describe('TAP reporter', function() { }); }); }); + describe('on end', function() { it('should write total tests, passes and failures', function() { var numberOfPasses = 1; @@ -246,6 +249,7 @@ describe('TAP reporter', function() { tapVersion: '13' } }; + describe('on start', function() { var expectedSuite = 'some suite'; var expectedTotal = 10; @@ -346,6 +350,7 @@ describe('TAP reporter', function() { expect(stdout, 'to equal', expectedArray); }); }); + describe('if there is an error stack', function() { it('should write expected message and stack', function() { var expectedStack = 'some stack'; @@ -375,6 +380,7 @@ describe('TAP reporter', function() { expect(stdout, 'to equal', expectedArray); }); }); + describe('if there is an error stack and error message', function() { it('should write expected message and stack', function() { var expectedStack = 'some stack'; @@ -415,6 +421,7 @@ describe('TAP reporter', function() { expect(stdout, 'to equal', expectedArray); }); }); + describe('if there is no error stack or error message', function() { it('should write expected message only', function() { var error = {}; From 0ce70f8ce260436cb4cec4bdd4d6f01eeb56d1a5 Mon Sep 17 00:00:00 2001 From: Paul Roebuck Date: Sat, 10 Nov 2018 05:02:21 -0600 Subject: [PATCH 80/80] docs(reporters/tap.js): Updated JSDoc --- lib/reporters/tap.js | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/lib/reporters/tap.js b/lib/reporters/tap.js index 7a591db7bc..ccff657e6a 100644 --- a/lib/reporters/tap.js +++ b/lib/reporters/tap.js @@ -18,7 +18,7 @@ var sprintf = util.format; exports = module.exports = TAP; /** - * Constructs a new `TAP` reporter instance. + * Constructs a new TAP reporter with runner instance and reporter options. * * @public * @class @@ -124,10 +124,10 @@ function createProducer(tapVersion) { /** * @summary - * Constructs a new `TAPProducer` instance. + * Constructs a new TAPProducer. * * @description - * Should only be used as an abstract base class. + * Only to be used as an abstract base class. * * @private * @constructor @@ -177,7 +177,7 @@ TAPProducer.prototype.writePending = function(n, test) { * Writes that test failed to reporter output stream. * * @abstract - * @param {number} n - Index of test that was skipped. + * @param {number} n - Index of test that failed. * @param {Test} test - Instance containing test information. * @param {Error} err - Reason the test failed. */ @@ -201,10 +201,10 @@ TAPProducer.prototype.writeEpilogue = function(stats) { /** * @summary - * Constructs a new `TAP12Producer` instance. + * Constructs a new TAP12Producer. * * @description - * It will produce output conforming to the TAP12 spec. + * Produces output conforming to the TAP12 specification. * * @private * @constructor @@ -234,10 +234,10 @@ inherits(TAP12Producer, TAPProducer); /** * @summary - * Constructs a new `TAP13Producer` instance. + * Constructs a new TAP13Producer. * * @description - * It will produce output conforming to the TAP13 spec. + * Produces output conforming to the TAP13 specification. * * @private * @constructor