UNPKG

5.29 kBJavaScriptView Raw
1var EventEmitter = require('events').EventEmitter;
2var inherits = require('inherits');
3var through = require('through');
4var resumer = require('resumer');
5var inspect = require('object-inspect');
6var bind = require('function-bind');
7var has = require('has');
8var regexpTest = bind.call(Function.call, RegExp.prototype.test);
9var yamlIndicators = /\:|\-|\?/;
10var nextTick = typeof setImmediate !== 'undefined'
11 ? setImmediate
12 : process.nextTick
13;
14
15module.exports = Results;
16inherits(Results, EventEmitter);
17
18function Results () {
19 if (!(this instanceof Results)) return new Results;
20 this.count = 0;
21 this.fail = 0;
22 this.pass = 0;
23 this._stream = through();
24 this.tests = [];
25 this._only = null;
26}
27
28Results.prototype.createStream = function (opts) {
29 if (!opts) opts = {};
30 var self = this;
31 var output, testId = 0;
32 if (opts.objectMode) {
33 output = through();
34 self.on('_push', function ontest (t, extra) {
35 if (!extra) extra = {};
36 var id = testId++;
37 t.once('prerun', function () {
38 var row = {
39 type: 'test',
40 name: t.name,
41 id: id
42 };
43 if (has(extra, 'parent')) {
44 row.parent = extra.parent;
45 }
46 output.queue(row);
47 });
48 t.on('test', function (st) {
49 ontest(st, { parent: id });
50 });
51 t.on('result', function (res) {
52 res.test = id;
53 res.type = 'assert';
54 output.queue(res);
55 });
56 t.on('end', function () {
57 output.queue({ type: 'end', test: id });
58 });
59 });
60 self.on('done', function () { output.queue(null) });
61 }
62 else {
63 output = resumer();
64 output.queue('TAP version 13\n');
65 self._stream.pipe(output);
66 }
67
68 nextTick(function next() {
69 var t;
70 while (t = getNextTest(self)) {
71 t.run();
72 if (!t.ended) return t.once('end', function(){ nextTick(next); });
73 }
74 self.emit('done');
75 });
76
77 return output;
78};
79
80Results.prototype.push = function (t) {
81 var self = this;
82 self.tests.push(t);
83 self._watch(t);
84 self.emit('_push', t);
85};
86
87Results.prototype.only = function (t) {
88 this._only = t;
89};
90
91Results.prototype._watch = function (t) {
92 var self = this;
93 var write = function (s) { self._stream.queue(s) };
94 t.once('prerun', function () {
95 write('# ' + t.name + '\n');
96 });
97
98 t.on('result', function (res) {
99 if (typeof res === 'string') {
100 write('# ' + res + '\n');
101 return;
102 }
103 write(encodeResult(res, self.count + 1));
104 self.count ++;
105
106 if (res.ok) self.pass ++
107 else self.fail ++
108 });
109
110 t.on('test', function (st) { self._watch(st) });
111};
112
113Results.prototype.close = function () {
114 var self = this;
115 if (self.closed) self._stream.emit('error', new Error('ALREADY CLOSED'));
116 self.closed = true;
117 var write = function (s) { self._stream.queue(s) };
118
119 write('\n1..' + self.count + '\n');
120 write('# tests ' + self.count + '\n');
121 write('# pass ' + self.pass + '\n');
122 if (self.fail) write('# fail ' + self.fail + '\n')
123 else write('\n# ok\n')
124
125 self._stream.queue(null);
126};
127
128function encodeResult (res, count) {
129 var output = '';
130 output += (res.ok ? 'ok ' : 'not ok ') + count;
131 output += res.name ? ' ' + res.name.toString().replace(/\s+/g, ' ') : '';
132
133 if (res.skip) output += ' # SKIP';
134 else if (res.todo) output += ' # TODO';
135
136 output += '\n';
137 if (res.ok) return output;
138
139 var outer = ' ';
140 var inner = outer + ' ';
141 output += outer + '---\n';
142 output += inner + 'operator: ' + res.operator + '\n';
143
144 if (has(res, 'expected') || has(res, 'actual')) {
145 var ex = inspect(res.expected, {depth: res.objectPrintDepth});
146 var ac = inspect(res.actual, {depth: res.objectPrintDepth});
147
148 if (Math.max(ex.length, ac.length) > 65 || invalidYaml(ex) || invalidYaml(ac)) {
149 output += inner + 'expected: |-\n' + inner + ' ' + ex + '\n';
150 output += inner + 'actual: |-\n' + inner + ' ' + ac + '\n';
151 }
152 else {
153 output += inner + 'expected: ' + ex + '\n';
154 output += inner + 'actual: ' + ac + '\n';
155 }
156 }
157 if (res.at) {
158 output += inner + 'at: ' + res.at + '\n';
159 }
160 if (res.operator === 'error' && res.actual && res.actual.stack) {
161 var lines = String(res.actual.stack).split('\n');
162 output += inner + 'stack: |-\n';
163 for (var i = 0; i < lines.length; i++) {
164 output += inner + ' ' + lines[i] + '\n';
165 }
166 }
167
168 output += outer + '...\n';
169 return output;
170}
171
172function getNextTest (results) {
173 if (!results._only) {
174 return results.tests.shift();
175 }
176
177 do {
178 var t = results.tests.shift();
179 if (!t) continue;
180 if (results._only === t) {
181 return t;
182 }
183 } while (results.tests.length !== 0)
184}
185
186function invalidYaml (str) {
187 return regexpTest(yamlIndicators, str);
188}