0613af708d92f2e1e962116199e963b2222cb1ec.svn-base
10.6 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
exports = module.exports = Classic
var Base = require('./base')
, cursor = Base.cursor
, color = Base.color
, yaml = require('tap-yaml')
, util = require('util')
, fancy = Base.useColors && !process.env.TRAVIS
, ms = require('../ms.js')
, diff = require('diff')
, utils = require('../utils.js')
, uclen = require('unicode-length').get
, colorSupport = require('color-support')()
function repeat (n, c) {
return new Array(Math.max(n + 1, 0)).join(c)
}
function hasOwnProperty (obj, key) {
return Object.prototype.hasOwnProperty.call(obj, key)
}
function doDiff (found, wanted, patch) {
// TODO: Make this a configurable thing or something?
//
// Choosing a palette for diffs in a test-runner context
// is really tricky. The temptation is to make it look
// exactly like `git diff`, but the default red and green
// are very confusing with the colors used to indicate
// pass/fail.
//
// So, I decided to experiment with setting a background to
// distinguish the diff section from the rest of the test
// output. The obvious choice, then, was to mimick GitHub's
// diff styling.
//
// The problem there is that, while those of us with an
// abundance of cones tend to find that palette most pleasing,
// it's virtually impossible for people with various sorts of
// red/green colorblindness to distinguish those colors.
//
// The resulting option, with a somewhat pink-ish red and a
// somewhat yellow-ish green, seems to be a pretty good
// compromise between esthetics and accessibility. In a poll
// on twitter, it was the only one that no color-sighted people
// strongly objected to, and no color-blind people had
// significant trouble interpreting. The twitter poll agrees
// with the results of Sim Daltonism, which showed that this
// palette was easily distinguishable across all forms of color
// deficiency.
// TODO: add a TrueColor one that looks nicer
var palette = colorSupport.has256 ? 6 : 1
var plain = ''
var reset = '\u001b[m'
switch (palette) {
case 1:
// most git-like. r/g, no background, no bold
var bg = ''
var removed = '\u001b[31m'
var added = '\u001b[32m'
break
case 2:
// dark option, maybe confusing with pass/fail colors?
var bg = '\u001b[48;5;234m'
var removed = '\u001b[31;1m'
var added = '\u001b[32;1m'
break
case 3:
// pastel option, most githubby
var removed = '\u001b[48;5;224m\u001b[38;5;52m'
var added = '\u001b[48;5;194m\u001b[38;5;22m'
var plain = '\u001b[38;5;233m'
var bg = '\u001b[48;5;255m'
break
case 4:
// orange/cyan pastel option, most r/g-colorblind friendly
var removed = '\u001b[48;5;223m\u001b[38;5;52m'
var added = '\u001b[48;5;158m\u001b[38;5;22m'
var plain = '\u001b[38;5;233m'
var bg = '\u001b[48;5;255m'
break
case 5:
// pastel option, green is bluish, red is just red
var removed = '\u001b[48;5;224m\u001b[38;5;52m'
var added = '\u001b[48;5;158m\u001b[38;5;22m'
var plain = '\u001b[38;5;233m'
var bg = '\u001b[48;5;255m'
break
case 6:
// pastel option, red is purplish, green is yellowish
var removed = '\u001b[48;5;218m\u001b[38;5;52m'
var added = '\u001b[48;5;193m\u001b[38;5;22m'
var plain = '\u001b[38;5;233m'
var bg = '\u001b[48;5;255m'
break
case 7:
// pastel option, red is purplish, green is just green
var removed = '\u001b[48;5;218m\u001b[38;5;52m'
var added = '\u001b[48;5;194m\u001b[38;5;22m'
var plain = '\u001b[38;5;233m'
var bg = '\u001b[48;5;255m'
break
case 8:
// pastel, red and blue
var removed = '\u001b[48;5;224m\u001b[38;5;52m'
var added = '\u001b[48;5;189m\u001b[38;5;17m'
var plain = '\u001b[38;5;233m'
var bg = '\u001b[48;5;255m'
break
case 9:
// pastel option, red is purplish, green is yellowish
var removed = '\u001b[48;5;224m\u001b[38;5;52m'
var added = '\u001b[48;5;193m\u001b[38;5;22m'
var plain = '\u001b[38;5;233m'
var bg = '\u001b[48;5;255m'
break
}
var maxLen = process.stdout.columns || 0
if (maxLen >= 5)
maxLen -= 5
if (!Base.useColors) {
bg = removed = added = reset = plain = ''
maxLen = 0
}
// If they are not strings, or only differ in trailing whitespace,
// then stringify them so that we can see the difference.
if (typeof found !== 'string' ||
typeof wanted !== 'string' ||
found.trim() === wanted.trim()) {
found = utils.stringify(found)
wanted = utils.stringify(wanted)
}
var width = 0
patch = patch || (
['--- wanted', '+++ found'].concat(
diff.createPatch('', wanted, found).split(/\n/).slice(5)
).join('\n'))
patch = patch.split('\n').map(function (line, index) {
if (uclen(line) > width)
width = Math.min(maxLen, uclen(line))
if (line.match(/^\=+$/) ||
line === '\\ No newline at end of file')
return null
else
return line
}).filter(function (line, i) {
return line
}).map(function (line) {
if (uclen(line) <= width)
line += repeat(width - uclen(line) + 1, ' ')
return line
}).map(function (line) {
if (line.charAt(0) === '+')
return bg + added + line + reset
else if (line.charAt(0) === '-')
return bg + removed + line + reset
else
return bg + plain + line + reset
}).join('\n')
var pref =''
return pref + patch
}
util.inherits(Classic, Base)
function Classic (runner) {
Base.call(this, runner);
var self = this
var grandTotal = 0
var grandPass = 0
var bailed = false
var hadFails = false
var currentSuite = null
var tests = []
var skipped = 0
var skipMsg = []
var todo = []
var fails = []
var total = 0
var pass = 0
var tickDots = 0
var tickColor = 'checkmark'
runner.on('bailout', function (bailout, suite) {
if (currentSuite)
runner.emit('suite end', currentSuite)
if (bailed)
return
bailed = true
console.log(Base.color('fail', 'Bail out! ' + bailout))
})
runner.on('suite', function (suite) {
if (!suite.root)
return
if (fancy) {
process.stdout.write(suite.title + ' ')
tickDots = 0
tickColor = 'checkmark'
}
currentSuite = suite
tests = []
todo = []
fails = []
skipMsg = []
skipped = 0
pass = 0
total = 0
})
runner.on('suite end', function (suite) {
if (!suite.root)
return
if (fancy)
Base.cursor.beginningOfLine()
currentSuite = null
var len = 60
var title = suite.title || '(unnamed)'
var num = pass + '/' + total
var dots = len - uclen(title) - uclen(num) - 3
if (dots < 2)
dots = 2
dots = ' ' + repeat(dots, '.') + ' '
if (fails.length)
num = Base.color('fail', num)
else if (pass === total)
num = Base.color('checkmark', num)
else
num = Base.color('pending', num)
var fmt = title + dots + num
if (suite.duration / total > 250)
fmt += Base.color('slow', ' ' + ms(Math.round(suite.duration)))
console.log(fmt)
if (fails.length) {
var failMsg = ''
fails.forEach(function (t) {
if (t.parent)
failMsg += t.parent + '\n'
failMsg += Base.color('fail', 'not ok ' + t.name) + '\n'
if (t.diag) {
var printDiff = false
if (hasOwnProperty(t.diag, 'found') &&
hasOwnProperty(t.diag, 'wanted') ||
hasOwnProperty(t.diag, 'diff')) {
printDiff = true
var found = t.diag.found
var wanted = t.diag.wanted
var diff = doDiff(found, wanted, t.diag.diff)
failMsg += indent(diff, 2) + '\n'
}
var o = {}
var print = false
for (var i in t.diag) {
// Don't re-print what we already showed in the diff
if (printDiff && ( i === 'found' || i === 'wanted' || i === 'pattern' || i === 'diff'))
continue
o[i] = t.diag[i]
print = true
}
if (print)
failMsg += indent(yaml.stringify(o), 2) + '\n'
}
})
console.log(indent(failMsg, 2))
}
if (todo.length) {
var todoMsg = ''
var bullet = Base.color('pending', '~ ')
todo.forEach(function (t) {
if (t.todo !== true)
t.name += ' - ' + Base.color('pending', t.todo)
todoMsg += bullet + t.name + '\n'
if (t.diag)
todoMsg += indent(yaml.stringify(t.diag), 4) + '\n'
})
console.log(indent(todoMsg, 2))
}
if (skipped) {
var fmt = Base.color('skip', indent('Skipped: %d', 2))
console.log(fmt, skipped)
if (skipMsg.length)
console.log(indent(skipMsg.join('\n'), 4))
console.log('')
}
})
runner.on('test', function (test) {
total ++
grandTotal ++
var t = test.result
if (fancy && currentSuite) {
var max = 57 - uclen(currentSuite.title)
if (max < 3)
max = 3
if (tickDots > max) {
tickDots = 0
Base.cursor.deleteLine()
Base.cursor.beginningOfLine();
process.stdout.write(currentSuite.title + ' ')
}
tickDots ++
if (t.todo &&
(tickColor === 'checkmark' || tickColor === 'skip'))
tickColor = 'pending'
else if (t.skip && tickColor === 'checkmark')
tickColor = 'skip'
else if (!t.ok)
tickColor = 'fail'
process.stdout.write(Base.color(tickColor, '.'))
}
if (t.skip) {
skipped += 1
if (!/^filter(( out)?: \/.+\/|: only)$/.test(t.skip)) {
if (t.skip !== true)
skipMsg.push(t.name + ' ' + Base.color('skip', t.skip))
else
skipMsg.push(t.name)
}
}
else if (t.todo)
todo.push(t)
else if (!t.ok) {
t.parent = []
var p = test.parent
while (p && p !== currentSuite) {
var n = p.title || p.name || p.fullTitle()
if (n)
t.parent.unshift(n)
p = p.parent
}
t.parent.shift()
t.parent = t.parent.join(' > ')
fails.push(t)
hadFails = true
}
else {
pass ++
grandPass ++
}
})
runner.on('end', function () {
total = grandTotal
pass = grandPass
tests = []
todo = []
fails = []
skipMsg = []
skipped = 0
if (hadFails)
fails = [,,,]
runner.emit('suite end', { title: 'total', root: true })
self.failures = []
self.epilogue();
if (grandTotal === grandPass) {
console.log(Base.color('checkmark', '\n ok'))
}
})
}
function indent (str, n) {
var ind = repeat(n, ' ')
str = ind + str.split('\n').join('\n' + ind)
return str.replace(/(\n\s*)+$/, '\n')
}