294 lines
6.9 KiB
JavaScript
294 lines
6.9 KiB
JavaScript
|
'use strict';
|
||
|
/**
|
||
|
* @module TAP
|
||
|
*/
|
||
|
/**
|
||
|
* Module dependencies.
|
||
|
*/
|
||
|
|
||
|
var util = require('util');
|
||
|
var Base = require('./base');
|
||
|
var constants = require('../runner').constants;
|
||
|
var EVENT_TEST_PASS = constants.EVENT_TEST_PASS;
|
||
|
var EVENT_TEST_FAIL = constants.EVENT_TEST_FAIL;
|
||
|
var EVENT_RUN_BEGIN = constants.EVENT_RUN_BEGIN;
|
||
|
var EVENT_RUN_END = constants.EVENT_RUN_END;
|
||
|
var EVENT_TEST_PENDING = constants.EVENT_TEST_PENDING;
|
||
|
var EVENT_TEST_END = constants.EVENT_TEST_END;
|
||
|
var inherits = require('../utils').inherits;
|
||
|
var sprintf = util.format;
|
||
|
|
||
|
/**
|
||
|
* Expose `TAP`.
|
||
|
*/
|
||
|
|
||
|
exports = module.exports = TAP;
|
||
|
|
||
|
/**
|
||
|
* Constructs a new `TAP` reporter instance.
|
||
|
*
|
||
|
* @public
|
||
|
* @class
|
||
|
* @memberof Mocha.reporters
|
||
|
* @extends Mocha.reporters.Base
|
||
|
* @param {Runner} runner - Instance triggers reporter actions.
|
||
|
* @param {Object} [options] - runner options
|
||
|
*/
|
||
|
function TAP(runner, options) {
|
||
|
Base.call(this, runner, options);
|
||
|
|
||
|
var self = this;
|
||
|
var n = 1;
|
||
|
|
||
|
var tapVersion = '12';
|
||
|
if (options && options.reporterOptions) {
|
||
|
if (options.reporterOptions.tapVersion) {
|
||
|
tapVersion = options.reporterOptions.tapVersion.toString();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
this._producer = createProducer(tapVersion);
|
||
|
|
||
|
runner.once(EVENT_RUN_BEGIN, function() {
|
||
|
self._producer.writeVersion();
|
||
|
});
|
||
|
|
||
|
runner.on(EVENT_TEST_END, function() {
|
||
|
++n;
|
||
|
});
|
||
|
|
||
|
runner.on(EVENT_TEST_PENDING, function(test) {
|
||
|
self._producer.writePending(n, test);
|
||
|
});
|
||
|
|
||
|
runner.on(EVENT_TEST_PASS, function(test) {
|
||
|
self._producer.writePass(n, test);
|
||
|
});
|
||
|
|
||
|
runner.on(EVENT_TEST_FAIL, function(test, err) {
|
||
|
self._producer.writeFail(n, test, err);
|
||
|
});
|
||
|
|
||
|
runner.once(EVENT_RUN_END, function() {
|
||
|
self._producer.writeEpilogue(runner.stats);
|
||
|
});
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Inherit from `Base.prototype`.
|
||
|
*/
|
||
|
inherits(TAP, Base);
|
||
|
|
||
|
/**
|
||
|
* Returns a TAP-safe title of `test`.
|
||
|
*
|
||
|
* @private
|
||
|
* @param {Test} test - Test instance.
|
||
|
* @return {String} title with any hash character removed
|
||
|
*/
|
||
|
function title(test) {
|
||
|
return test.fullTitle().replace(/#/g, '');
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Writes newline-terminated formatted string to reporter output stream.
|
||
|
*
|
||
|
* @private
|
||
|
* @param {string} format - `printf`-like format string
|
||
|
* @param {...*} [varArgs] - Format string arguments
|
||
|
*/
|
||
|
function println(format, varArgs) {
|
||
|
var vargs = Array.from(arguments);
|
||
|
vargs[0] += '\n';
|
||
|
process.stdout.write(sprintf.apply(null, vargs));
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Returns a `tapVersion`-appropriate TAP producer instance, if possible.
|
||
|
*
|
||
|
* @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 createProducer(tapVersion) {
|
||
|
var producers = {
|
||
|
'12': new TAP12Producer(),
|
||
|
'13': new TAP13Producer()
|
||
|
};
|
||
|
var producer = producers[tapVersion];
|
||
|
|
||
|
if (!producer) {
|
||
|
throw new Error(
|
||
|
'invalid or unsupported TAP version: ' + JSON.stringify(tapVersion)
|
||
|
);
|
||
|
}
|
||
|
|
||
|
return producer;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @summary
|
||
|
* Constructs a new TAPProducer.
|
||
|
*
|
||
|
* @description
|
||
|
* <em>Only</em> to be used as an abstract base class.
|
||
|
*
|
||
|
* @private
|
||
|
* @constructor
|
||
|
*/
|
||
|
function TAPProducer() {}
|
||
|
|
||
|
/**
|
||
|
* Writes the TAP version to reporter output stream.
|
||
|
*
|
||
|
* @abstract
|
||
|
*/
|
||
|
TAPProducer.prototype.writeVersion = function() {};
|
||
|
|
||
|
/**
|
||
|
* 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);
|
||
|
};
|
||
|
|
||
|
/**
|
||
|
* 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));
|
||
|
};
|
||
|
|
||
|
/**
|
||
|
* Writes that test was skipped to reporter output stream.
|
||
|
*
|
||
|
* @abstract
|
||
|
* @param {number} n - Index of test that was skipped.
|
||
|
* @param {Test} test - Instance containing test information.
|
||
|
*/
|
||
|
TAPProducer.prototype.writePending = function(n, test) {
|
||
|
println('ok %d %s # SKIP -', n, title(test));
|
||
|
};
|
||
|
|
||
|
/**
|
||
|
* Writes that test failed to reporter output stream.
|
||
|
*
|
||
|
* @abstract
|
||
|
* @param {number} n - Index of test that failed.
|
||
|
* @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));
|
||
|
};
|
||
|
|
||
|
/**
|
||
|
* 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);
|
||
|
this.writePlan(stats.passes + stats.failures + stats.pending);
|
||
|
};
|
||
|
|
||
|
/**
|
||
|
* @summary
|
||
|
* Constructs a new TAP12Producer.
|
||
|
*
|
||
|
* @description
|
||
|
* Produces output conforming to the TAP12 specification.
|
||
|
*
|
||
|
* @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) {
|
||
|
println(err.message.replace(/^/gm, ' '));
|
||
|
}
|
||
|
if (err.stack) {
|
||
|
println(err.stack.replace(/^/gm, ' '));
|
||
|
}
|
||
|
};
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Inherit from `TAPProducer.prototype`.
|
||
|
*/
|
||
|
inherits(TAP12Producer, TAPProducer);
|
||
|
|
||
|
/**
|
||
|
* @summary
|
||
|
* Constructs a new TAP13Producer.
|
||
|
*
|
||
|
* @description
|
||
|
* Produces output conforming to the TAP13 specification.
|
||
|
*
|
||
|
* @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);
|
||
|
|
||
|
TAP.description = 'TAP-compatible output';
|