diff --git a/.eslintrc b/.eslintrc index 674fac4a..6e61464a 100644 --- a/.eslintrc +++ b/.eslintrc @@ -21,7 +21,6 @@ "no-negated-condition": "off", "no-param-reassign": "warn", "no-underscore-dangle": "warn", - "no-use-before-define": "warn", "object-curly-newline": "off", "operator-linebreak": ["error", "before"], "sort-keys": "warn", @@ -121,5 +120,11 @@ "camelcase": "off", }, }, + { + "files": ["lib/default_stream.js"], + "rules": { + "no-use-before-define": "warn", + }, + } ], } diff --git a/index.js b/index.js index c6dfd197..2d2d2e53 100644 --- a/index.js +++ b/index.js @@ -13,6 +13,16 @@ var canExit = typeof process !== 'undefined' && process module.exports = (function () { var harness; + + function getHarness(opts) { + if (!opts) { opts = {}; } + opts.autoclose = !canEmitExit; + // this override is here since tests fail via nyc if createHarness is moved upwards + // eslint-disable-next-line no-use-before-define + if (!harness) { harness = createExitHarness(opts); } + return harness; + } + var lazyLoad = function () { // eslint-disable-next-line no-invalid-this return getHarness().apply(this, arguments); @@ -43,62 +53,8 @@ module.exports = (function () { lazyLoad.getHarness = getHarness; return lazyLoad; - - function getHarness(opts) { - if (!opts) { opts = {}; } - opts.autoclose = !canEmitExit; - if (!harness) { harness = createExitHarness(opts); } - return harness; - } }()); -function createExitHarness(conf) { - var config = conf || {}; - var harness = createHarness({ - autoclose: defined(config.autoclose, false), - noOnly: defined(conf.noOnly, defined(process.env.NODE_TAPE_NO_ONLY_TEST, false)) - }); - - var stream = harness.createStream({ objectMode: conf.objectMode }); - var es = stream.pipe(conf.stream || createDefaultStream()); - if (canEmitExit) { - // eslint-disable-next-line no-unused-vars - es.on('error', function (err) { harness._exitCode = 1; }); - } - - var ended = false; - stream.on('end', function () { ended = true; }); - - if (config.exit === false) { return harness; } - if (!canEmitExit || !canExit) { return harness; } - - process.on('exit', function (code) { - // let the process exit cleanly. - if (code !== 0) { - return; - } - - if (!ended) { - var only = harness._results._only; - for (var i = 0; i < harness._tests.length; i++) { - var t = harness._tests[i]; - if (!only || t === only) { - t._exit(); - } - } - } - harness.close(); - process.exit(code || harness._exitCode); // eslint-disable-line no-process-exit - }); - - return harness; -} - -module.exports.createHarness = createHarness; -module.exports.Test = Test; -module.exports.test = module.exports; // tap compat -module.exports.test.skip = Test.skip; - function createHarness(conf_) { var results = createResult(); if (!conf_ || conf_.autoclose !== false) { @@ -152,3 +108,50 @@ function createHarness(conf_) { return test; } + +function createExitHarness(conf) { + var config = conf || {}; + var harness = createHarness({ + autoclose: defined(config.autoclose, false), + noOnly: defined(conf.noOnly, defined(process.env.NODE_TAPE_NO_ONLY_TEST, false)) + }); + + var stream = harness.createStream({ objectMode: conf.objectMode }); + var es = stream.pipe(conf.stream || createDefaultStream()); + if (canEmitExit) { + // eslint-disable-next-line no-unused-vars + es.on('error', function (err) { harness._exitCode = 1; }); + } + + var ended = false; + stream.on('end', function () { ended = true; }); + + if (config.exit === false) { return harness; } + if (!canEmitExit || !canExit) { return harness; } + + process.on('exit', function (code) { + // let the process exit cleanly. + if (code !== 0) { + return; + } + + if (!ended) { + var only = harness._results._only; + for (var i = 0; i < harness._tests.length; i++) { + var t = harness._tests[i]; + if (!only || t === only) { + t._exit(); + } + } + } + harness.close(); + process.exit(code || harness._exitCode); // eslint-disable-line no-process-exit + }); + + return harness; +} + +module.exports.createHarness = createHarness; +module.exports.Test = Test; +module.exports.test = module.exports; // tap compat +module.exports.test.skip = Test.skip; diff --git a/lib/results.js b/lib/results.js index 7554da62..2460a712 100644 --- a/lib/results.js +++ b/lib/results.js @@ -11,13 +11,80 @@ var has = require('has'); var $exec = callBound('RegExp.prototype.exec'); var yamlIndicators = /:|-|\?/; var nextTick = typeof setImmediate !== 'undefined' ? setImmediate : process.nextTick; -module.exports = Results; -inherits(Results, EventEmitter); function coalesceWhiteSpaces(str) { return String(str).replace(/\s+/g, ' '); } +function invalidYaml(str) { + return $exec(yamlIndicators, str) !== null; +} + +function encodeResult(res, count) { + var output = ''; + output += (res.ok ? 'ok ' : 'not ok ') + count; + output += res.name ? ' ' + coalesceWhiteSpaces(res.name) : ''; + + if (res.skip) { + output += ' # SKIP' + (typeof res.skip === 'string' ? ' ' + coalesceWhiteSpaces(res.skip) : ''); + } else if (res.todo) { + output += ' # TODO' + (typeof res.todo === 'string' ? ' ' + coalesceWhiteSpaces(res.todo) : ''); + } + + output += '\n'; + if (res.ok) { return output; } + + var outer = ' '; + var inner = outer + ' '; + output += outer + '---\n'; + output += inner + 'operator: ' + res.operator + '\n'; + + if (has(res, 'expected') || has(res, 'actual')) { + var ex = inspect(res.expected, { depth: res.objectPrintDepth }); + var ac = inspect(res.actual, { depth: res.objectPrintDepth }); + + if (Math.max(ex.length, ac.length) > 65 || invalidYaml(ex) || invalidYaml(ac)) { + output += inner + 'expected: |-\n' + inner + ' ' + ex + '\n'; + output += inner + 'actual: |-\n' + inner + ' ' + ac + '\n'; + } else { + output += inner + 'expected: ' + ex + '\n'; + output += inner + 'actual: ' + ac + '\n'; + } + } + if (res.at) { + output += inner + 'at: ' + res.at + '\n'; + } + + var actualStack = res.actual && (typeof res.actual === 'object' || typeof res.actual === 'function') ? res.actual.stack : undefined; + var errorStack = res.error && res.error.stack; + var stack = defined(actualStack, errorStack); + if (stack) { + var lines = String(stack).split('\n'); + output += inner + 'stack: |-\n'; + for (var i = 0; i < lines.length; i++) { + output += inner + ' ' + lines[i] + '\n'; + } + } + + output += outer + '...\n'; + return output; +} + +function getNextTest(results) { + if (!results._only) { + return results.tests.shift(); + } + + do { + var t = results.tests.shift(); + if (t && results._only === t) { + return t; + } + } while (results.tests.length !== 0); + + return void undefined; +} + function Results() { if (!(this instanceof Results)) { return new Results(); } this.count = 0; @@ -30,6 +97,8 @@ function Results() { this._isRunning = false; } +inherits(Results, EventEmitter); + Results.prototype.createStream = function (opts) { if (!opts) { opts = {}; } var self = this; @@ -155,71 +224,4 @@ Results.prototype.close = function () { self._stream.queue(null); }; -function encodeResult(res, count) { - var output = ''; - output += (res.ok ? 'ok ' : 'not ok ') + count; - output += res.name ? ' ' + coalesceWhiteSpaces(res.name) : ''; - - if (res.skip) { - output += ' # SKIP' + (typeof res.skip === 'string' ? ' ' + coalesceWhiteSpaces(res.skip) : ''); - } else if (res.todo) { - output += ' # TODO' + (typeof res.todo === 'string' ? ' ' + coalesceWhiteSpaces(res.todo) : ''); - } - - output += '\n'; - if (res.ok) { return output; } - - var outer = ' '; - var inner = outer + ' '; - output += outer + '---\n'; - output += inner + 'operator: ' + res.operator + '\n'; - - if (has(res, 'expected') || has(res, 'actual')) { - var ex = inspect(res.expected, { depth: res.objectPrintDepth }); - var ac = inspect(res.actual, { depth: res.objectPrintDepth }); - - if (Math.max(ex.length, ac.length) > 65 || invalidYaml(ex) || invalidYaml(ac)) { - output += inner + 'expected: |-\n' + inner + ' ' + ex + '\n'; - output += inner + 'actual: |-\n' + inner + ' ' + ac + '\n'; - } else { - output += inner + 'expected: ' + ex + '\n'; - output += inner + 'actual: ' + ac + '\n'; - } - } - if (res.at) { - output += inner + 'at: ' + res.at + '\n'; - } - - var actualStack = res.actual && (typeof res.actual === 'object' || typeof res.actual === 'function') ? res.actual.stack : undefined; - var errorStack = res.error && res.error.stack; - var stack = defined(actualStack, errorStack); - if (stack) { - var lines = String(stack).split('\n'); - output += inner + 'stack: |-\n'; - for (var i = 0; i < lines.length; i++) { - output += inner + ' ' + lines[i] + '\n'; - } - } - - output += outer + '...\n'; - return output; -} - -function getNextTest(results) { - if (!results._only) { - return results.tests.shift(); - } - - do { - var t = results.tests.shift(); - if (t && results._only === t) { - return t; - } - } while (results.tests.length !== 0); - - return void undefined; -} - -function invalidYaml(str) { - return $exec(yamlIndicators, str) !== null; -} +module.exports = Results; diff --git a/lib/test.js b/lib/test.js index 9b369492..c43b57a4 100644 --- a/lib/test.js +++ b/lib/test.js @@ -16,16 +16,12 @@ var toLowerCase = callBound('String.prototype.toLowerCase'); var $exec = callBound('RegExp.prototype.exec'); var objectToString = callBound('Object.prototype.toString'); -module.exports = Test; - var nextTick = typeof setImmediate !== 'undefined' ? setImmediate : process.nextTick; var safeSetTimeout = setTimeout; var safeClearTimeout = clearTimeout; -inherits(Test, EventEmitter); - // eslint-disable-next-line no-unused-vars var getTestArgs = function (name_, opts_, cb_) { var name = '(anonymous)'; @@ -94,6 +90,8 @@ function Test(name_, opts_, cb_) { } } +inherits(Test, EventEmitter); + Test.prototype.run = function run() { this.emit('prerun'); if (!this._cb || this._skip) { @@ -183,6 +181,19 @@ Test.prototype._end = function (err) { return; } + function completeEnd() { + if (!self.ended) { self.emit('end'); } + var pendingAsserts = self._pendingAsserts(); + if (!self._planError && self._plan !== undefined && pendingAsserts) { + self._planError = true; + self.fail('plan != count', { + expected: self._plan, + actual: self.assertCount + }); + } + self.ended = true; + } + function next(i) { if (i === self._teardown.length) { completeEnd(); @@ -211,19 +222,6 @@ Test.prototype._end = function (err) { } else { completeEnd(); } - - function completeEnd() { - if (!self.ended) { self.emit('end'); } - var pendingAsserts = self._pendingAsserts(); - if (!self._planError && self._plan !== undefined && pendingAsserts) { - self._planError = true; - self.fail('plan != count', { - expected: self._plan, - actual: self.assertCount - }); - } - self.ended = true; - } }; Test.prototype._exit = function () { @@ -665,4 +663,6 @@ Test.skip = function (name_, _opts, _cb) { return new Test(args.name, args.opts, args.cb); }; +module.exports = Test; + // vim: set softtabstop=4 shiftwidth=4: diff --git a/package.json b/package.json index 1c4e3f5b..eb4ac383 100644 --- a/package.json +++ b/package.json @@ -82,10 +82,9 @@ "assert", "browser" ], - "author": { - "name": "Jordan Harband", - "email": "ljharb@gmail.com", - "url": "http://ljharb.codes" + "author": "Jordan Harband ", + "funding": { + "url": "https://github.com/sponsors/ljharb" }, "license": "MIT" } diff --git a/test/anonymous-fn/test-wrapper.js b/test/anonymous-fn/test-wrapper.js index 0c42f00e..7a35bae9 100644 --- a/test/anonymous-fn/test-wrapper.js +++ b/test/anonymous-fn/test-wrapper.js @@ -1,5 +1,13 @@ 'use strict'; +function setUp() { + // ... example ... +} + +function tearDown() { + // ... example ... +} + // Example of wrapper function that would invoke tape module.exports = function (testCase) { return function (t) { @@ -8,11 +16,3 @@ module.exports = function (testCase) { tearDown(); }; }; - -function setUp() { - // ... example ... -} - -function tearDown() { - // ... example ... -} diff --git a/test/end-as-callback.js b/test/end-as-callback.js index af7babad..42de7066 100644 --- a/test/end-as-callback.js +++ b/test/end-as-callback.js @@ -5,6 +5,45 @@ var forEach = require('for-each'); var tape = require('../'); var concat = require('concat-stream'); +function fakeAsyncTask(name, cb) { + cb(null, 'task' + name); +} + +function fakeAsyncWrite(name, cb) { + cb(null); +} + +function fakeAsyncWriteFail(name, cb) { + cb(new Error('fail')); +} + +/** + * extract the stack trace for the failed test. + * this will change dependent on the environment + * so no point hard-coding it in the test assertion + * see: https://git.io/v6hGG for example + * @param String rows - the tap output lines + * @returns String stacktrace - just the error stack part + */ +function getStackTrace(rows) { + var stacktrace = ' ---\n'; + var extract = false; + forEach(rows.toString('utf8').split('\n'), function (row) { + if (!extract) { + if (row.indexOf('---') > -1) { // start of stack trace + extract = true; + } + } else if (row.indexOf('...') > -1) { // end of stack trace + extract = false; + stacktrace += ' ...'; + } else { + stacktrace += row + '\n'; + } + }); + // console.log(stacktrace); + return stacktrace; +} + tap.test('tape assert.end as callback', function (tt) { var test = tape.createHarness({ exit: false }); @@ -46,42 +85,3 @@ tap.test('tape assert.end as callback', function (tt) { }); }); }); - -function fakeAsyncTask(name, cb) { - cb(null, 'task' + name); -} - -function fakeAsyncWrite(name, cb) { - cb(null); -} - -function fakeAsyncWriteFail(name, cb) { - cb(new Error('fail')); -} - -/** - * extract the stack trace for the failed test. - * this will change dependent on the environment - * so no point hard-coding it in the test assertion - * see: https://git.io/v6hGG for example - * @param String rows - the tap output lines - * @returns String stacktrace - just the error stack part - */ -function getStackTrace(rows) { - var stacktrace = ' ---\n'; - var extract = false; - forEach(rows.toString('utf8').split('\n'), function (row) { - if (!extract) { - if (row.indexOf('---') > -1) { // start of stack trace - extract = true; - } - } else if (row.indexOf('...') > -1) { // end of stack trace - extract = false; - stacktrace += ' ...'; - } else { - stacktrace += row + '\n'; - } - }); - // console.log(stacktrace); - return stacktrace; -} diff --git a/test/require.js b/test/require.js index 724f40cf..c4b48143 100644 --- a/test/require.js +++ b/test/require.js @@ -4,6 +4,12 @@ var tap = require('tap'); var spawn = require('child_process').spawn; var concat = require('concat-stream'); +function tape(args) { + var bin = __dirname + '/../bin/tape'; + + return spawn('node', [bin].concat(args.split(' ')), { cwd: __dirname }); +} + tap.test('requiring a single module', function (t) { t.plan(2); @@ -62,9 +68,3 @@ tap.test('requiring multiple modules', function (t) { t.equal(code, 0); }); }); - -function tape(args) { - var bin = __dirname + '/../bin/tape'; - - return spawn('node', [bin].concat(args.split(' ')), { cwd: __dirname }); -} diff --git a/test/stackTrace.js b/test/stackTrace.js index 0d0723e8..317e2335 100644 --- a/test/stackTrace.js +++ b/test/stackTrace.js @@ -8,6 +8,10 @@ var common = require('./common'); var getDiag = common.getDiag; +function stripAt(body) { + return body.replace(/^\s*at:\s+Test.*$\n/m, ''); +} + tap.test('preserves stack trace with newlines', function (tt) { tt.plan(3); @@ -296,7 +300,3 @@ tap.test('preserves stack trace for failed assertions where actual===falsy', fun t.equal(false, true, 'false should be true'); }); }); - -function stripAt(body) { - return body.replace(/^\s*at:\s+Test.*$\n/m, ''); -}