123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212 |
- var defined = require('defined');
- var EventEmitter = require('events').EventEmitter;
- var inherits = require('inherits');
- var through = require('through');
- var resumer = require('resumer');
- var inspect = require('object-inspect');
- var bind = require('function-bind');
- var has = require('has');
- var regexpTest = bind.call(Function.call, RegExp.prototype.test);
- 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 Results() {
- if (!(this instanceof Results)) return new Results;
- this.count = 0;
- this.fail = 0;
- this.pass = 0;
- this.todo = 0;
- this._stream = through();
- this.tests = [];
- this._only = null;
- this._isRunning = false;
- }
-
- Results.prototype.createStream = function (opts) {
- if (!opts) opts = {};
- var self = this;
- var output, testId = 0;
- if (opts.objectMode) {
- output = through();
- self.on('_push', function ontest(t, extra) {
- if (!extra) extra = {};
- var id = testId++;
- t.once('prerun', function () {
- var row = {
- type: 'test',
- name: t.name,
- id: id,
- skip: t._skip,
- todo: t._todo
- };
- if (has(extra, 'parent')) {
- row.parent = extra.parent;
- }
- output.queue(row);
- });
- t.on('test', function (st) {
- ontest(st, { parent: id });
- });
- t.on('result', function (res) {
- res.test = id;
- res.type = 'assert';
- output.queue(res);
- });
- t.on('end', function () {
- output.queue({ type: 'end', test: id });
- });
- });
- self.on('done', function () { output.queue(null); });
- } else {
- output = resumer();
- output.queue('TAP version 13\n');
- self._stream.pipe(output);
- }
-
- if (!this._isRunning) {
- this._isRunning = true;
- nextTick(function next() {
- var t;
- while (t = getNextTest(self)) {
- t.run();
- if (!t.ended) return t.once('end', function () { nextTick(next); });
- }
- self.emit('done');
- });
- }
-
- return output;
- };
-
- Results.prototype.push = function (t) {
- var self = this;
- self.tests.push(t);
- self._watch(t);
- self.emit('_push', t);
- };
-
- Results.prototype.only = function (t) {
- this._only = t;
- };
-
- Results.prototype._watch = function (t) {
- var self = this;
- var write = function (s) { self._stream.queue(s); };
- t.once('prerun', function () {
- var premsg = '';
- if (t._skip) premsg = 'SKIP ';
- else if (t._todo) premsg = 'TODO ';
- write('# ' + premsg + coalesceWhiteSpaces(t.name) + '\n');
- });
-
- t.on('result', function (res) {
- if (typeof res === 'string') {
- write('# ' + res + '\n');
- return;
- }
- write(encodeResult(res, self.count + 1));
- self.count ++;
-
- if (res.ok || res.todo) self.pass ++;
- else {
- self.fail ++;
- self.emit('fail');
- }
- });
-
- t.on('test', function (st) { self._watch(st); });
- };
-
- Results.prototype.close = function () {
- var self = this;
- if (self.closed) self._stream.emit('error', new Error('ALREADY CLOSED'));
- self.closed = true;
- var write = function (s) { self._stream.queue(s); };
-
- write('\n1..' + self.count + '\n');
- write('# tests ' + self.count + '\n');
- write('# pass ' + (self.pass + self.todo) + '\n');
- if (self.todo) write('# todo ' + self.todo + '\n');
- if (self.fail) write('# fail ' + self.fail + '\n');
- else write('\n# ok\n');
-
- 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) continue;
- if (results._only === t) {
- return t;
- }
- } while (results.tests.length !== 0);
- }
-
- function invalidYaml(str) {
- return regexpTest(yamlIndicators, str);
- }
|