tape-six 0.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +11 -0
- package/README.md +9 -0
- package/bin/tape6-server.js +196 -0
- package/bin/tape6.js +187 -0
- package/index.js +74 -0
- package/package.json +57 -0
- package/src/State.js +93 -0
- package/src/TTYReporter.js +270 -0
- package/src/TapReporter.js +126 -0
- package/src/Tester.js +411 -0
- package/src/deep6/env.js +174 -0
- package/src/deep6/index.js +21 -0
- package/src/deep6/traverse/assemble.js +158 -0
- package/src/deep6/traverse/clone.js +109 -0
- package/src/deep6/traverse/deref.js +104 -0
- package/src/deep6/traverse/preprocess.js +112 -0
- package/src/deep6/traverse/walk.js +262 -0
- package/src/deep6/unifiers/matchCondition.js +16 -0
- package/src/deep6/unifiers/matchInstanceOf.js +16 -0
- package/src/deep6/unifiers/matchString.js +33 -0
- package/src/deep6/unifiers/matchTypeOf.js +16 -0
- package/src/deep6/unifiers/ref.js +21 -0
- package/src/deep6/unify.js +446 -0
- package/src/deep6/utils/replaceVars.js +24 -0
- package/src/node/TestWorker.js +37 -0
- package/src/node/config.js +56 -0
- package/src/node/listing.js +78 -0
- package/src/test.js +187 -0
- package/src/utils/Deferred.js +10 -0
- package/src/utils/EventServer.js +78 -0
- package/src/utils/box.js +101 -0
- package/src/utils/defer.js +33 -0
- package/src/utils/fileSets.js +113 -0
- package/src/utils/formatters.js +30 -0
- package/src/utils/timeout.js +3 -0
- package/src/utils/timer.js +24 -0
- package/src/utils/yamlFormatter.js +150 -0
|
@@ -0,0 +1,270 @@
|
|
|
1
|
+
import {stringRep, normalizeBox, padBox, padBoxLeft, drawBox, stackHorizontally} from './utils/box.js';
|
|
2
|
+
import {formatNumber, formatTime} from './utils/formatters.js';
|
|
3
|
+
|
|
4
|
+
// colors
|
|
5
|
+
|
|
6
|
+
const join = (...args) => args.filter(value => value).join('');
|
|
7
|
+
const to6 = x => Math.min(5, Math.round((Math.max(0, Math.min(255, x)) / 255) * 6));
|
|
8
|
+
const buildColor = (r, g, b) => 16 + 36 * to6(r) + 6 * to6(g) + to6(b);
|
|
9
|
+
|
|
10
|
+
const successStyle = `\x1B[48;5;${buildColor(0, 32, 0)};1;97m`,
|
|
11
|
+
failureStyle = `\x1B[48;5;${buildColor(64, 0, 0)};1;97m`,
|
|
12
|
+
skippedStyle = `\x1B[48;5;${buildColor(0, 0, 64)};1;97m`,
|
|
13
|
+
reset = '\x1B[0m';
|
|
14
|
+
|
|
15
|
+
// main
|
|
16
|
+
|
|
17
|
+
class TTYReporter {
|
|
18
|
+
constructor({
|
|
19
|
+
output = process.stdout,
|
|
20
|
+
renumberAsserts = false,
|
|
21
|
+
failureOnly = false,
|
|
22
|
+
showBanner = true,
|
|
23
|
+
showTime = true,
|
|
24
|
+
showData = false,
|
|
25
|
+
showAssertNumber = false
|
|
26
|
+
} = {}) {
|
|
27
|
+
if (!output || !output.isTTY) throw Error('Module TTYReporter works only with TTY output streams.');
|
|
28
|
+
|
|
29
|
+
this.output = output;
|
|
30
|
+
this.hasColors = this.output.hasColors(256);
|
|
31
|
+
this.renumberAsserts = renumberAsserts;
|
|
32
|
+
this.failureOnly = failureOnly;
|
|
33
|
+
this.showBanner = showBanner;
|
|
34
|
+
this.showTime = showTime;
|
|
35
|
+
this.showData = showData;
|
|
36
|
+
this.showAssertNumber = showAssertNumber;
|
|
37
|
+
|
|
38
|
+
this.depth = this.assertCounter = this.failedAsserts = this.successfulAsserts = this.skippedAsserts = this.todoAsserts = 0;
|
|
39
|
+
this.testCounter = -1;
|
|
40
|
+
this.technicalDepth = 0;
|
|
41
|
+
|
|
42
|
+
this.lines = 0;
|
|
43
|
+
this.testStack = [];
|
|
44
|
+
|
|
45
|
+
// colors
|
|
46
|
+
this.red = this.paint('\x1B[31m');
|
|
47
|
+
this.green = this.paint('\x1B[92m');
|
|
48
|
+
this.blue = this.paint('\x1B[94m');
|
|
49
|
+
this.yellow = this.paint('\x1B[93m');
|
|
50
|
+
this.blackBg = this.paint('\x1B[40m', '\x1B[49m');
|
|
51
|
+
this.lowWhite = this.paint('\x1B[2;37m', '\x1B[22;39m');
|
|
52
|
+
this.brightWhite = this.paint('\x1B[1;97m', '\x1B[22;39m');
|
|
53
|
+
this.brightYellow = this.paint('\x1B[1;93m', '\x1B[22;39m');
|
|
54
|
+
this.brightRed = this.paint('\x1B[91m');
|
|
55
|
+
this.italic = this.paint('\x1B[3m', '\x1B[23m');
|
|
56
|
+
this.warning = this.paint('\x1B[41;1;37m', '\x1B[22;39;49m');
|
|
57
|
+
this.success = this.paint(successStyle, reset);
|
|
58
|
+
this.failure = this.paint(failureStyle, reset);
|
|
59
|
+
this.skipped = this.paint(skippedStyle, reset);
|
|
60
|
+
|
|
61
|
+
this.out('');
|
|
62
|
+
}
|
|
63
|
+
paint(prefix, suffix = '\x1B[39m') {
|
|
64
|
+
return this.hasColors ? text => join(prefix, text, suffix) : text => text;
|
|
65
|
+
}
|
|
66
|
+
formatValue(value) {
|
|
67
|
+
if (typeof value == 'string') return value;
|
|
68
|
+
if (typeof value.type == 'string') return this.blue(JSON.stringify(value));
|
|
69
|
+
return this.red(this.italic(JSON.stringify(value)));
|
|
70
|
+
}
|
|
71
|
+
out(text) {
|
|
72
|
+
if (this.depth < 2 + this.technicalDepth) {
|
|
73
|
+
this.output.write(text + '\n');
|
|
74
|
+
} else {
|
|
75
|
+
this.output.write(stringRep(this.depth - 1 - this.technicalDepth, ' ') + text + '\n');
|
|
76
|
+
}
|
|
77
|
+
++this.lines;
|
|
78
|
+
return this;
|
|
79
|
+
}
|
|
80
|
+
report(event) {
|
|
81
|
+
this.output.moveCursor(0, -1);
|
|
82
|
+
this.output.clearLine(0);
|
|
83
|
+
let text;
|
|
84
|
+
switch (event.type) {
|
|
85
|
+
case 'test':
|
|
86
|
+
this.depth > this.technicalDepth && !this.failureOnly && this.out('\u25CB ' + (event.name || 'anonymous test'));
|
|
87
|
+
++this.depth;
|
|
88
|
+
++this.testCounter;
|
|
89
|
+
this.testStack.push({name: event.name, lines: this.lines, fail: false});
|
|
90
|
+
break;
|
|
91
|
+
case 'end':
|
|
92
|
+
this.testStack.pop();
|
|
93
|
+
--this.depth;
|
|
94
|
+
if (this.depth > this.technicalDepth) {
|
|
95
|
+
if (this.failureOnly) break;
|
|
96
|
+
text = (event.fail ? '✗' : '✓') + ' ' + (event.name || this.italic('anonymous test'));
|
|
97
|
+
text = event.fail ? this.brightRed(text) : this.green(text);
|
|
98
|
+
text += this.makeState(event.data);
|
|
99
|
+
this.showTime && (text += this.lowWhite(' - ' + formatTime(event.diffTime)));
|
|
100
|
+
this.out(text);
|
|
101
|
+
break;
|
|
102
|
+
}
|
|
103
|
+
if (this.depth) break;
|
|
104
|
+
|
|
105
|
+
// summary
|
|
106
|
+
{
|
|
107
|
+
const state = event.data,
|
|
108
|
+
total = state.asserts - state.skipped,
|
|
109
|
+
success = total - state.failed;
|
|
110
|
+
|
|
111
|
+
if (!this.showBanner) {
|
|
112
|
+
this.out(
|
|
113
|
+
this.blackBg(
|
|
114
|
+
' ' +
|
|
115
|
+
(event.fail ? '⛔' : '♥️') +
|
|
116
|
+
' ' +
|
|
117
|
+
this.brightWhite('tests: ' + formatNumber(this.testCounter)) +
|
|
118
|
+
', ' +
|
|
119
|
+
('asserts: ' + formatNumber(state.asserts)) +
|
|
120
|
+
', ' +
|
|
121
|
+
this.green('passed: ' + formatNumber(success)) +
|
|
122
|
+
', ' +
|
|
123
|
+
this.red('failed: ' + formatNumber(state.failed)) +
|
|
124
|
+
', ' +
|
|
125
|
+
this.blue('skipped: ' + formatNumber(state.skipped)) +
|
|
126
|
+
', ' +
|
|
127
|
+
this.brightYellow('todo: ' + formatNumber(this.todoAsserts)) +
|
|
128
|
+
', ' +
|
|
129
|
+
this.lowWhite('time: ' + formatTime(event.diffTime)) +
|
|
130
|
+
' '
|
|
131
|
+
)
|
|
132
|
+
);
|
|
133
|
+
return;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
const paintMethod = event.fail ? 'failure' : 'success';
|
|
137
|
+
let box1 = [event.fail ? 'Need work' : 'All good!'];
|
|
138
|
+
box1 = padBox(box1, 0, 2);
|
|
139
|
+
box1 = drawBox(box1);
|
|
140
|
+
box1 = padBox(box1, 0, 3);
|
|
141
|
+
box1 = normalizeBox(
|
|
142
|
+
[...box1, '', 'Passed: ' + (event.fail ? formatNumber((total > 0 ? success / total : 1) * 100, 1) + '%' : '100%')],
|
|
143
|
+
' ',
|
|
144
|
+
'center'
|
|
145
|
+
);
|
|
146
|
+
box1 = padBox(box1, 2, 0);
|
|
147
|
+
box1 = box1.map(s => this[paintMethod](s));
|
|
148
|
+
box1 = padBoxLeft(box1, 2);
|
|
149
|
+
|
|
150
|
+
let box2 = normalizeBox(
|
|
151
|
+
[
|
|
152
|
+
formatNumber(this.testCounter),
|
|
153
|
+
formatNumber(state.asserts),
|
|
154
|
+
formatNumber(success),
|
|
155
|
+
formatNumber(state.failed),
|
|
156
|
+
formatNumber(state.skipped),
|
|
157
|
+
formatNumber(this.todoAsserts),
|
|
158
|
+
formatTime(event.diffTime)
|
|
159
|
+
],
|
|
160
|
+
' ',
|
|
161
|
+
'left'
|
|
162
|
+
);
|
|
163
|
+
box2 = padBoxLeft(box2, 1);
|
|
164
|
+
box2 = stackHorizontally(normalizeBox(['tests:', 'asserts:', ' passed:', ' failed:', ' skipped:', ' todo:', 'time:']), box2);
|
|
165
|
+
|
|
166
|
+
box2[0] = this.brightWhite(box2[0]);
|
|
167
|
+
// box2[1] = this.brightYellow(box2[1]);
|
|
168
|
+
box2[2] = this.green(box2[2]);
|
|
169
|
+
box2[3] = this.red(box2[3]);
|
|
170
|
+
box2[4] = this.blue(box2[4]);
|
|
171
|
+
box2[5] = this.brightYellow(box2[5]);
|
|
172
|
+
box2[6] = this.lowWhite(box2[6]);
|
|
173
|
+
|
|
174
|
+
box2 = padBox(box2, 1, 3);
|
|
175
|
+
box2 = box2.map(s => this.blackBg(s));
|
|
176
|
+
|
|
177
|
+
const box = stackHorizontally(box1, box2);
|
|
178
|
+
this.out('');
|
|
179
|
+
box.forEach(s => this.out(s));
|
|
180
|
+
this.out('');
|
|
181
|
+
}
|
|
182
|
+
return;
|
|
183
|
+
case 'comment':
|
|
184
|
+
!this.failureOnly && this.out(this.blue(this.italic(event.name || 'empty comment')));
|
|
185
|
+
break;
|
|
186
|
+
case 'bail-out':
|
|
187
|
+
{
|
|
188
|
+
text = 'Bail out!';
|
|
189
|
+
event.name && (text += ' ' + event.name);
|
|
190
|
+
let box = [text];
|
|
191
|
+
box = padBox(box, 0, 1);
|
|
192
|
+
box = drawBox(box);
|
|
193
|
+
box = padBox(box, 0, 1);
|
|
194
|
+
|
|
195
|
+
const currentDepth = this.depth;
|
|
196
|
+
this.depth = 0;
|
|
197
|
+
box.forEach(s => this.out(this.warning(s)));
|
|
198
|
+
this.depth = currentDepth;
|
|
199
|
+
}
|
|
200
|
+
break;
|
|
201
|
+
case 'assert':
|
|
202
|
+
const lastTest = this.testStack[this.testStack.length - 1],
|
|
203
|
+
isFailed = event.fail && !event.skip && !event.todo;
|
|
204
|
+
isFailed ? ++this.failedAsserts : ++this.successfulAsserts;
|
|
205
|
+
event.skip && ++this.skippedAsserts;
|
|
206
|
+
event.todo && ++this.todoAsserts;
|
|
207
|
+
if (!isFailed && this.failureOnly) break;
|
|
208
|
+
text = (event.fail ? '✗' : '✓');
|
|
209
|
+
if (this.showAssertNumber) {
|
|
210
|
+
text += ' ' + (this.renumberAsserts ? ++this.assertCounter : event.id);
|
|
211
|
+
}
|
|
212
|
+
if (event.skip) {
|
|
213
|
+
text += ' SKIP';
|
|
214
|
+
} else if (event.todo) {
|
|
215
|
+
text += ' ' + this.brightYellow('TODO');
|
|
216
|
+
}
|
|
217
|
+
event.name && (text += ' ' + event.name);
|
|
218
|
+
if (event.skip) {
|
|
219
|
+
text = this.blue(text);
|
|
220
|
+
} else {
|
|
221
|
+
text = isFailed ? this.red(text) : this.green(text);
|
|
222
|
+
}
|
|
223
|
+
this.showTime && (text += this.lowWhite(' - ' + formatTime(event.diffTime)));
|
|
224
|
+
event.fail && event.at && (text += this.lowWhite(' - ' + event.at));
|
|
225
|
+
if (this.failureOnly && !lastTest.fail) {
|
|
226
|
+
lastTest.fail = true;
|
|
227
|
+
--this.depth;
|
|
228
|
+
this.out(this.brightRed('✗ ' + (lastTest.name || 'anonymous test')));
|
|
229
|
+
++this.depth;
|
|
230
|
+
}
|
|
231
|
+
this.out(text);
|
|
232
|
+
|
|
233
|
+
if (!event.fail || event.skip || !this.showData) break;
|
|
234
|
+
|
|
235
|
+
this.out(this.lowWhite(' operator: ') + event.operator);
|
|
236
|
+
if (event.hasOwnProperty('expected')) {
|
|
237
|
+
this.out(this.lowWhite(' expected: ') + this.formatValue(event.expected));
|
|
238
|
+
}
|
|
239
|
+
if (event.hasOwnProperty('actual')) {
|
|
240
|
+
this.out(this.lowWhite(' actual: ') + this.formatValue(event.actual));
|
|
241
|
+
}
|
|
242
|
+
const stack = event.actual && event.actual.type === 'Error' && typeof event.actual.stack == 'string' ? event.actual.stack : event.marker.stack;
|
|
243
|
+
if (typeof stack == 'string') {
|
|
244
|
+
this.out(this.lowWhite(' stack: |-'));
|
|
245
|
+
stack.split('\n').forEach(line => this.out(this.lowWhite(' ' + line)));
|
|
246
|
+
}
|
|
247
|
+
break;
|
|
248
|
+
}
|
|
249
|
+
this.showScore();
|
|
250
|
+
}
|
|
251
|
+
showScore() {
|
|
252
|
+
this.out(
|
|
253
|
+
this.success(' ' + this.successfulAsserts + ' ') +
|
|
254
|
+
this.failure(' ' + this.failedAsserts + ' ') +
|
|
255
|
+
(this.skippedAsserts ? this.skipped(' ' + this.skippedAsserts + ' ') : '')
|
|
256
|
+
);
|
|
257
|
+
}
|
|
258
|
+
makeState(state) {
|
|
259
|
+
const success = state.asserts - state.skipped - state.failed;
|
|
260
|
+
if (!success && !state.failed && !state.skipped) return '';
|
|
261
|
+
return (
|
|
262
|
+
' ' +
|
|
263
|
+
this[success ? 'success' : 'blackBg'](' ' + formatNumber(success) + ' ') +
|
|
264
|
+
this[state.failed ? 'failure' : 'blackBg'](' ' + formatNumber(state.failed) + ' ') +
|
|
265
|
+
(state.skipped ? this.blackBg(this.blue(' ' + formatNumber(state.skipped) + ' ')) : '')
|
|
266
|
+
);
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
export default TTYReporter;
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
import yamlFormatter from './utils/yamlFormatter.js';
|
|
2
|
+
|
|
3
|
+
const formatterOptions = {offset: 2};
|
|
4
|
+
|
|
5
|
+
const styles = {
|
|
6
|
+
info: 'color: #44f; font-style: italic;',
|
|
7
|
+
success: 'color: green;',
|
|
8
|
+
failure: 'color: red;',
|
|
9
|
+
summary: 'font-weight: bold;',
|
|
10
|
+
'summary-info': 'font-weight: bold; color: #44f; font-style: italic;',
|
|
11
|
+
'summary-success': 'font-weight: bold; color: green;',
|
|
12
|
+
'summary-failure': 'font-weight: bold; color: red;',
|
|
13
|
+
'summary-result-success': 'font-weight: bold; color: white; background-color: green; padding: 0.5em 1em;',
|
|
14
|
+
'summary-result-failure': 'font-weight: bold; color: white; background-color: red; padding: 0.5em 1em;',
|
|
15
|
+
'bail-out': 'color: white; background-color: red; font-weight: bold; padding: 0.5em 1em;'
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
const logger = (text, style) => {
|
|
19
|
+
const css = typeof style == 'string' && styles[style];
|
|
20
|
+
if (css) {
|
|
21
|
+
console.log('%c' + text, css);
|
|
22
|
+
} else {
|
|
23
|
+
console.log(text);
|
|
24
|
+
}
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
const formatValue = value => {
|
|
28
|
+
if (typeof value == 'string') return value;
|
|
29
|
+
return JSON.stringify(value);
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
class TapReporter {
|
|
33
|
+
constructor({write = logger, useJson = false, renumberAsserts = false} = {}) {
|
|
34
|
+
this.write = write;
|
|
35
|
+
this.renumberAsserts = renumberAsserts;
|
|
36
|
+
this.useJson = useJson;
|
|
37
|
+
this.depth = 0;
|
|
38
|
+
this.assertCounter = 0;
|
|
39
|
+
this.opened = false;
|
|
40
|
+
}
|
|
41
|
+
open() {
|
|
42
|
+
if (!this.opened) {
|
|
43
|
+
this.write('TAP version 13');
|
|
44
|
+
this.opened = true;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
report(event) {
|
|
48
|
+
let text;
|
|
49
|
+
switch (event.type) {
|
|
50
|
+
case 'test':
|
|
51
|
+
this.open();
|
|
52
|
+
event.name && this.write('# start: ' + event.name, 'info');
|
|
53
|
+
++this.depth;
|
|
54
|
+
break;
|
|
55
|
+
case 'comment':
|
|
56
|
+
this.open();
|
|
57
|
+
this.write('# ' + event.name);
|
|
58
|
+
break;
|
|
59
|
+
case 'end':
|
|
60
|
+
--this.depth;
|
|
61
|
+
event.name && this.write('# finish: ' + event.name + ' # time=' + event.diffTime.toFixed(3) + 'ms', 'info');
|
|
62
|
+
if (this.depth) break;
|
|
63
|
+
const state = event.data,
|
|
64
|
+
success = state.asserts - state.failed - state.skipped;
|
|
65
|
+
this.write('1..' + state.asserts, 'summary');
|
|
66
|
+
this.write('# tests ' + state.asserts, 'summary');
|
|
67
|
+
state.skipped && this.write('# skip ' + state.skipped, 'summary-info');
|
|
68
|
+
success && this.write('# pass ' + success, 'summary-success');
|
|
69
|
+
state.failed && this.write('# fail ' + state.failed, 'summary-failure');
|
|
70
|
+
this.write('# ' + (event.fail ? 'not ok' : 'ok'), event.fail ? 'summary-result-failure' : 'summary-result-success');
|
|
71
|
+
this.write('# time=' + event.diffTime.toFixed(3) + 'ms', 'summary-info');
|
|
72
|
+
break;
|
|
73
|
+
case 'bail-out':
|
|
74
|
+
this.open();
|
|
75
|
+
text = 'Bail out!';
|
|
76
|
+
event.name && (text += ' ' + event.name);
|
|
77
|
+
this.write(text, 'bail-out');
|
|
78
|
+
break;
|
|
79
|
+
case 'assert':
|
|
80
|
+
this.open();
|
|
81
|
+
text = (event.fail ? 'not ok' : 'ok') + ' ' + (this.renumberAsserts ? ++this.assertCounter : event.id);
|
|
82
|
+
if (event.skip) {
|
|
83
|
+
text += ' # SKIP';
|
|
84
|
+
} else if (event.todo) {
|
|
85
|
+
text += ' # TODO';
|
|
86
|
+
}
|
|
87
|
+
event.name && (text += ' ' + event.name);
|
|
88
|
+
text += ' # time=' + event.diffTime.toFixed(3) + 'ms';
|
|
89
|
+
this.write(text, event.fail ? 'failure' : 'success');
|
|
90
|
+
if (event.fail) {
|
|
91
|
+
this.write(' ---', 'yaml');
|
|
92
|
+
if (this.useJson) {
|
|
93
|
+
this.write(' operator: ' + event.operator, 'yaml');
|
|
94
|
+
if (event.hasOwnProperty('expected')) {
|
|
95
|
+
this.write(' expected: ' + formatValue(event.expected), 'yaml');
|
|
96
|
+
}
|
|
97
|
+
if (event.hasOwnProperty('actual')) {
|
|
98
|
+
this.write(' actual: ' + formatValue(event.actual), 'yaml');
|
|
99
|
+
}
|
|
100
|
+
event.at && this.write(' at: ' + event.at, 'yaml');
|
|
101
|
+
} else {
|
|
102
|
+
yamlFormatter({operator: event.operator}, formatterOptions).forEach(line => this.write(line, 'yaml'));
|
|
103
|
+
if (event.data) {
|
|
104
|
+
yamlFormatter(
|
|
105
|
+
{
|
|
106
|
+
expected: event.data.expected,
|
|
107
|
+
actual: event.data.actual
|
|
108
|
+
},
|
|
109
|
+
formatterOptions
|
|
110
|
+
).forEach(line => this.write(line, 'yaml'));
|
|
111
|
+
}
|
|
112
|
+
yamlFormatter({at: event.at}, formatterOptions).forEach(line => this.write(line, 'yaml'));
|
|
113
|
+
}
|
|
114
|
+
const stack = event.actual && event.actual.type === 'Error' && typeof event.actual.stack == 'string' ? event.actual.stack : event.marker.stack;
|
|
115
|
+
if (typeof stack == 'string') {
|
|
116
|
+
this.write(' stack: |-', 'yaml');
|
|
117
|
+
stack.split('\n').forEach(line => this.write(' ' + line, 'yaml'));
|
|
118
|
+
}
|
|
119
|
+
this.write(' ...', 'yaml');
|
|
120
|
+
}
|
|
121
|
+
break;
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
export default TapReporter;
|