Current Dev State

This commit is contained in:
Tim Lorsbach
2025-06-23 20:13:54 +02:00
parent b4f9bb277d
commit ded50edaa2
22617 changed files with 4345095 additions and 174 deletions

View File

@ -0,0 +1,43 @@
The ISC License
Copyright (c) Isaac Z. Schlueter and Contributors
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
----
The reporters in this package are based on Mocha, in accordance with the
terms of Mocha's license:
(The MIT License)
Copyright (c) 2011-2015 TJ Holowaychuk <tj@vision-media.ca>
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
'Software'), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

View File

@ -0,0 +1,27 @@
# tap-mocha-reporter
Format a TAP stream using Mocha's set of reporters
## USAGE
On the command line, pipe TAP in, and it'll do its thing.
```bash
tap test/*.js | tap-mocha-reporter
```
You can also specify a reporter with the first argument. The default
is `spec`.
```bash
tap test/*.js | tap-mocha-reporter nyan
```
Programmatically, you can use this as a transform stream.
```javascript
var TSR = require('tap-mocha-reporter')
fs.createReadStream('saved-test-output.tap')
.pipe(TSR('dot'))
```

106
static/js/ketcher2/node_modules/tap-mocha-reporter/index.js generated vendored Executable file
View File

@ -0,0 +1,106 @@
#!/usr/bin/env node
module.exports = Formatter
var util = require('util')
var reporters = require('./lib/reporters/index.js')
Formatter.types = Object.keys(reporters).sort()
var Writable = require('stream').Writable
if (!Writable) {
try {
Writable = require('readable-stream').Writable
} catch (er) {
throw new Error('Please install "readable-stream" to use this module ' +
'with Node.js v0.8 and before')
}
}
var Runner = require('./lib/runner.js')
var Parser = require('tap-parser')
util.inherits(Formatter, Writable)
var exitCode
function Formatter (type, options) {
if (!(this instanceof Formatter)) {
return new Formatter(type, options)
}
if (!reporters[type]) {
console.error('Unknown format type: %s\n\n%s', type, avail())
type = 'silent'
}
this.writable = true
// don't actually need a reporter to report the tap we're getting
// just parse it so that we exit with the correct code, but otherwise
// dump it straight through to stdout.
if (type === 'tap') {
var p = new Parser()
this.write = function (chunk) {
process.stdout.write(chunk)
return p.write(chunk)
}
this.end = p.end.bind(p)
p.on('complete', function () {
if (!p.ok)
exitCode = 1
})
return this
}
var runner = this.runner = new Runner(options)
this.reporter = new reporters[type](this.runner, {})
Writable.call(this, options)
runner.on('end', function () {
if (!runner.parser.ok)
exitCode = 1
})
}
process.on('exit', function (code) {
if (!code && exitCode)
process.exit(exitCode)
})
Formatter.prototype.write = function () {
return this.runner.write.apply(this.runner, arguments)
}
Formatter.prototype.end = function () {
return this.runner.end.apply(this.runner, arguments)
}
function avail () {
var types = Formatter.types.reduce(function (str, t) {
var ll = str.split('\n').pop().length + t.length
if (ll < 40)
return str + ' ' + t
else
return str + '\n' + t
}, '').trim()
return 'Available format types:\n\n' + types
}
function usage (err) {
console[err ? 'error' : 'log'](function () {/*
Usage:
tap-mocha-reporter <type>
Reads TAP data on stdin, and formats to stdout using the specified
reporter. (Note that some reporters write to files instead of stdout.)
%s
*/}.toString().split('\n').slice(1, -1).join('\n'), avail())
}
if (require.main === module) {
var type = process.argv[2]
if (!type)
return usage()
process.stdin.pipe(new Formatter(type))
}

View File

@ -0,0 +1,4 @@
module.exports = function(type){
return function(){
}
};

View File

@ -0,0 +1,11 @@
'use strict';
var matchOperatorsRe = /[|\\{}()[\]^$+*?.]/g;
module.exports = function (str) {
if (typeof str !== 'string') {
throw new TypeError('Expected a string');
}
return str.replace(matchOperatorsRe, '\\$&');
};

View File

@ -0,0 +1,177 @@
/**
* Module exports.
*/
exports.EventEmitter = EventEmitter;
/**
* Check if `obj` is an array.
*/
function isArray(obj) {
return '[object Array]' == {}.toString.call(obj);
}
/**
* Event emitter constructor.
*
* @api public
*/
function EventEmitter(){};
/**
* Adds a listener.
*
* @api public
*/
EventEmitter.prototype.on = function (name, fn) {
if (!this.$events) {
this.$events = {};
}
if (!this.$events[name]) {
this.$events[name] = fn;
} else if (isArray(this.$events[name])) {
this.$events[name].push(fn);
} else {
this.$events[name] = [this.$events[name], fn];
}
return this;
};
EventEmitter.prototype.addListener = EventEmitter.prototype.on;
/**
* Adds a volatile listener.
*
* @api public
*/
EventEmitter.prototype.once = function (name, fn) {
var self = this;
function on () {
self.removeListener(name, on);
fn.apply(this, arguments);
};
on.listener = fn;
this.on(name, on);
return this;
};
/**
* Removes a listener.
*
* @api public
*/
EventEmitter.prototype.removeListener = function (name, fn) {
if (this.$events && this.$events[name]) {
var list = this.$events[name];
if (isArray(list)) {
var pos = -1;
for (var i = 0, l = list.length; i < l; i++) {
if (list[i] === fn || (list[i].listener && list[i].listener === fn)) {
pos = i;
break;
}
}
if (pos < 0) {
return this;
}
list.splice(pos, 1);
if (!list.length) {
delete this.$events[name];
}
} else if (list === fn || (list.listener && list.listener === fn)) {
delete this.$events[name];
}
}
return this;
};
/**
* Removes all listeners for an event.
*
* @api public
*/
EventEmitter.prototype.removeAllListeners = function (name) {
if (name === undefined) {
this.$events = {};
return this;
}
if (this.$events && this.$events[name]) {
this.$events[name] = null;
}
return this;
};
/**
* Gets all listeners for a certain event.
*
* @api public
*/
EventEmitter.prototype.listeners = function (name) {
if (!this.$events) {
this.$events = {};
}
if (!this.$events[name]) {
this.$events[name] = [];
}
if (!isArray(this.$events[name])) {
this.$events[name] = [this.$events[name]];
}
return this.$events[name];
};
/**
* Emits an event.
*
* @api public
*/
EventEmitter.prototype.emit = function (name) {
if (!this.$events) {
return false;
}
var handler = this.$events[name];
if (!handler) {
return false;
}
var args = [].slice.call(arguments, 1);
if ('function' == typeof handler) {
handler.apply(this, args);
} else if (isArray(handler)) {
var listeners = handler.slice();
for (var i = 0, l = listeners.length; i < l; i++) {
listeners[i].apply(this, args);
}
} else {
return false;
}
return true;
};

View File

View File

@ -0,0 +1,125 @@
/**
* Expose `Progress`.
*/
module.exports = Progress;
/**
* Initialize a new `Progress` indicator.
*/
function Progress() {
this.percent = 0;
this.size(0);
this.fontSize(11);
this.font('helvetica, arial, sans-serif');
}
/**
* Set progress size to `n`.
*
* @param {Number} n
* @return {Progress} for chaining
* @api public
*/
Progress.prototype.size = function(n){
this._size = n;
return this;
};
/**
* Set text to `str`.
*
* @param {String} str
* @return {Progress} for chaining
* @api public
*/
Progress.prototype.text = function(str){
this._text = str;
return this;
};
/**
* Set font size to `n`.
*
* @param {Number} n
* @return {Progress} for chaining
* @api public
*/
Progress.prototype.fontSize = function(n){
this._fontSize = n;
return this;
};
/**
* Set font `family`.
*
* @param {String} family
* @return {Progress} for chaining
*/
Progress.prototype.font = function(family){
this._font = family;
return this;
};
/**
* Update percentage to `n`.
*
* @param {Number} n
* @return {Progress} for chaining
*/
Progress.prototype.update = function(n){
this.percent = n;
return this;
};
/**
* Draw on `ctx`.
*
* @param {CanvasRenderingContext2d} ctx
* @return {Progress} for chaining
*/
Progress.prototype.draw = function(ctx){
try {
var percent = Math.min(this.percent, 100)
, size = this._size
, half = size / 2
, x = half
, y = half
, rad = half - 1
, fontSize = this._fontSize;
ctx.font = fontSize + 'px ' + this._font;
var angle = Math.PI * 2 * (percent / 100);
ctx.clearRect(0, 0, size, size);
// outer circle
ctx.strokeStyle = '#9f9f9f';
ctx.beginPath();
ctx.arc(x, y, rad, 0, angle, false);
ctx.stroke();
// inner circle
ctx.strokeStyle = '#eee';
ctx.beginPath();
ctx.arc(x, y, rad - 1, 0, angle, true);
ctx.stroke();
// text
var text = this._text || (percent | 0) + '%'
, w = ctx.measureText(text).width;
ctx.fillText(
text
, x - w / 2 + 1
, y + fontSize / 2 - 1);
} catch (ex) {} //don't fail if we can't render progress
return this;
};

View File

@ -0,0 +1,12 @@
exports.isatty = function(){
return true;
};
exports.getWindowSize = function(){
if ('innerHeight' in global) {
return [global.innerHeight, global.innerWidth];
} else {
// In a Web Worker, the DOM Window is not available.
return [640, 480];
}
};

View File

@ -0,0 +1,87 @@
// A formatter is a Duplex stream that TAP data is written into,
// and then something else (presumably not-TAP) is read from.
//
// See tap-classic.js for an example of a formatter in use.
var Duplex = require('stream').Duplex
if (!Duplex) {
try {
Duplex = require('readable-stream').Duplex
} catch (er) {
throw new Error('Please install "readable-stream" to use this module ' +
'with Node.js v0.8 and before')
}
}
var util = require('util')
var Parser = require('tap-parser')
util.inherits(Formatter, Duplex)
module.exports = Formatter
function Formatter(options, parser, parent) {
if (!(this instanceof Formatter))
return new Formatter(options, parser, parent)
if (!parser)
parser = new Parser()
Duplex.call(this, options)
this.child = null
this.parent = parent || null
this.level = parser.level
this.parser = parser
attachEvents(this, parser, options)
if (options.init)
options.init.call(this)
}
function attachEvents (self, parser, options) {
var events = [
'version', 'plan', 'assert', 'comment',
'complete', 'extra', 'bailout'
]
parser.on('child', function (childparser) {
self.child = new Formatter(options, childparser, self)
if (options.child)
options.child.call(self, self.child)
})
events.forEach(function (ev) {
if (typeof options[ev] === 'function')
parser.on(ev, options[ev].bind(self))
})
// proxy all stream events directly
var streamEvents = [
'pipe', 'prefinish', 'finish', 'unpipe', 'close'
]
streamEvents.forEach(function (ev) {
parser.on(ev, function () {
var args = [ev]
args.push.apply(args, arguments)
self.emit.apply(self, args)
})
})
}
Formatter.prototype.write = function (c, e, cb) {
return this.parser.write(c, e, cb)
}
Formatter.prototype.end = function (c, e, cb) {
return this.parser.end(c, e, cb)
}
Formatter.prototype._read = function () {}
// child formatters always push data to the root obj
Formatter.prototype.push = function (c) {
if (this.parent)
return this.parent.push(c)
Duplex.prototype.push.call(this, c)
}

View File

@ -0,0 +1,109 @@
/**
* Helpers.
*/
var s = 1000;
var m = s * 60;
var h = m * 60;
var d = h * 24;
var y = d * 365.25;
/**
* Parse or format the given `val`.
*
* Options:
*
* - `long` verbose formatting [false]
*
* @param {String|Number} val
* @param {Object} options
* @return {String|Number}
* @api public
*/
module.exports = function(val, options){
options = options || {};
if ('string' == typeof val) return parse(val);
return options['long'] ? longFormat(val) : shortFormat(val);
};
/**
* Parse the given `str` and return milliseconds.
*
* @param {String} str
* @return {Number}
* @api private
*/
function parse(str) {
var match = /^((?:\d+)?\.?\d+) *(ms|seconds?|s|minutes?|m|hours?|h|days?|d|years?|y)?$/i.exec(str);
if (!match) return;
var n = parseFloat(match[1]);
var type = (match[2] || 'ms').toLowerCase();
switch (type) {
case 'years':
case 'year':
case 'y':
return n * y;
case 'days':
case 'day':
case 'd':
return n * d;
case 'hours':
case 'hour':
case 'h':
return n * h;
case 'minutes':
case 'minute':
case 'm':
return n * m;
case 'seconds':
case 'second':
case 's':
return n * s;
case 'ms':
return n;
}
}
/**
* Short format for `ms`.
*
* @param {Number} ms
* @return {String}
* @api private
*/
function shortFormat(ms) {
if (ms >= d) return Math.round(ms / d) + 'd';
if (ms >= h) return Math.round(ms / h) + 'h';
if (ms >= m) return Math.round(ms / m) + 'm';
if (ms >= s) return Math.round(ms / s) + 's';
return ms + 'ms';
}
/**
* Long format for `ms`.
*
* @param {Number} ms
* @return {String}
* @api private
*/
function longFormat(ms) {
return plural(ms, d, 'day')
|| plural(ms, h, 'hour')
|| plural(ms, m, 'minute')
|| plural(ms, s, 'second')
|| ms + ' ms';
}
/**
* Pluralization helper.
*/
function plural(ms, n, name) {
if (ms < n) return;
if (ms < n * 1.5) return Math.floor(ms / n) + ' ' + name;
return Math.ceil(ms / n) + ' ' + name + 's';
}

View File

@ -0,0 +1,463 @@
/**
* Module dependencies.
*/
var tty = require('tty')
, diff = require('diff')
, ms = require('../ms')
, utils = require('../utils')
, supportsColor = require('color-support')()
/**
* Save timer references to avoid Sinon interfering (see GH-237).
*/
var Date = global.Date
, setTimeout = global.setTimeout
, setInterval = global.setInterval
, clearTimeout = global.clearTimeout
, clearInterval = global.clearInterval;
/**
* Check if both stdio streams are associated with a tty.
*/
var isatty = tty.isatty(1);
/**
* Expose `Base`.
*/
exports = module.exports = Base;
/**
* Enable coloring by default, except in the browser interface.
*/
exports.useColors = process.env
? (supportsColor || (process.env.TAP_COLORS !== undefined))
: false;
if (exports.useColors && +process.env.TAP_COLORS === 0)
exports.useColors = false
/**
* Inline diffs instead of +/-
*/
exports.inlineDiffs = false;
/**
* Default color map.
*/
exports.colors = {
'pass': 90
, 'fail': 31
, 'bright pass': 92
, 'bright fail': 91
, 'bright yellow': 93
, 'pending': 35
, 'skip': 36
, 'suite': 0
, 'error title': 0
, 'error message': 31
, 'error stack': 90
, 'checkmark': 32
, 'fast': 90
, 'medium': 33
, 'slow': 31
, 'green': 32
, 'light': 90
, 'diff gutter': 90
, 'diff added': 42
, 'diff removed': 41
};
/**
* Default symbol map.
*/
exports.symbols = {
ok: '✓',
err: '✖',
dot: ''
};
// With node.js on Windows: use symbols available in terminal default fonts
if ('win32' == process.platform) {
exports.symbols.ok = '\u221A';
exports.symbols.err = '\u00D7';
exports.symbols.dot = '.';
}
/**
* Color `str` with the given `type`,
* allowing colors to be disabled,
* as well as user-defined color
* schemes.
*
* @param {String} type
* @param {String} str
* @return {String}
* @api private
*/
var color = exports.color = function(type, str) {
if (!exports.useColors) return String(str);
return '\u001b[' + exports.colors[type] + 'm' + str + '\u001b[0m';
};
/**
* Expose term window size, with some
* defaults for when stderr is not a tty.
*/
exports.window = {
width: isatty
? process.stdout.getWindowSize
? process.stdout.getWindowSize(1)[0]
: tty.getWindowSize()[1]
: 75
};
/**
* Expose some basic cursor interactions
* that are common among reporters.
*/
exports.cursor = {
hide: function(){
isatty && process.stdout.write('\u001b[?25l');
},
show: function(){
isatty && process.stdout.write('\u001b[?25h');
},
deleteLine: function(){
isatty && process.stdout.write('\u001b[2K');
},
beginningOfLine: function(){
isatty && process.stdout.write('\u001b[0G');
},
CR: function(){
if (isatty) {
exports.cursor.deleteLine();
exports.cursor.beginningOfLine();
} else {
process.stdout.write('\r');
}
}
};
/**
* Outut the given `failures` as a list.
*
* @param {Array} failures
* @api public
*/
exports.list = function(failures){
console.log();
failures.forEach(function(test, i){
// format
var fmt = color('error title', ' %s) %s:\n')
+ color('error message', ' %s')
+ color('error stack', '\n%s\n');
// msg
var err = test.err
, message = err.message || ''
, stack = err.stack || message
var index = stack.indexOf(message) + message.length
, msg = stack.slice(0, index)
, actual = err.actual
, expected = err.expected
, escape = true;
// uncaught
if (err.uncaught) {
msg = 'Uncaught ' + msg;
}
// explicitly show diff
if (err.showDiff && sameType(actual, expected)) {
if ('string' !== typeof actual) {
escape = false;
err.actual = actual = utils.stringify(actual);
err.expected = expected = utils.stringify(expected);
}
fmt = color('error title', ' %s) %s:\n%s') + color('error stack', '\n%s\n');
var match = message.match(/^([^:]+): expected/);
msg = '\n ' + color('error message', match ? match[1] : msg);
if (exports.inlineDiffs) {
msg += inlineDiff(err, escape);
} else {
msg += unifiedDiff(err, escape);
}
}
// indent stack trace without msg
stack = utils.stackTraceFilter()(stack.slice(index ? index + 1 : index)
.replace(/^/gm, ' '));
console.log(fmt, (i + 1), test.fullTitle(), msg, stack);
});
};
/**
* Initialize a new `Base` reporter.
*
* All other reporters generally
* inherit from this reporter, providing
* stats such as test duration, number
* of tests passed / failed etc.
*
* @param {Runner} runner
* @api public
*/
function Base(runner) {
var self = this
, stats = this.stats = { suites: 0, tests: 0, passes: 0, pending: 0, failures: 0 }
, failures = this.failures = [];
if (!runner) return;
this.runner = runner;
runner.stats = stats;
runner.on('start', function(){
stats.start = new Date;
});
runner.on('suite', function(suite){
stats.suites = stats.suites || 0;
suite.root || stats.suites++;
});
runner.on('test end', function(test){
stats.tests = stats.tests || 0;
stats.tests++;
});
runner.on('pass', function(test){
stats.passes = stats.passes || 0;
var medium = test.slow() / 2;
test.speed = test.duration > test.slow()
? 'slow'
: test.duration > medium
? 'medium'
: 'fast';
stats.passes++;
});
runner.on('fail', function(test, err){
stats.failures = stats.failures || 0;
stats.failures++;
test.err = err;
failures.push(test);
});
runner.on('end', function(){
stats.end = new Date;
if (!stats.duration)
stats.duration = stats.end - stats.start;
});
runner.on('pending', function(){
stats.pending++;
});
}
/**
* Output common epilogue used by many of
* the bundled reporters.
*
* @api public
*/
Base.prototype.epilogue = function(){
var stats = this.stats;
var tests;
var fmt;
console.log();
// passes
fmt = color('bright pass', ' ')
+ color('green', ' %d passing')
+ color('light', ' (%s)');
console.log(fmt,
stats.passes || 0,
ms(stats.duration));
// pending
if (stats.pending) {
fmt = color('pending', ' ')
+ color('pending', ' %d pending');
console.log(fmt, stats.pending);
}
// failures
if (stats.failures) {
fmt = color('fail', ' %d failing');
console.log(fmt, stats.failures);
Base.list(this.failures);
}
};
/**
* Pad the given `str` to `len`.
*
* @param {String} str
* @param {String} len
* @return {String}
* @api private
*/
function pad(str, len) {
str = String(str);
return Array(len - str.length + 1).join(' ') + str;
}
/**
* Returns an inline diff between 2 strings with coloured ANSI output
*
* @param {Error} Error with actual/expected
* @return {String} Diff
* @api private
*/
function inlineDiff(err, escape) {
var msg = errorDiff(err, 'WordsWithSpace', escape);
// linenos
var lines = msg.split('\n');
if (lines.length > 4) {
var width = String(lines.length).length;
msg = lines.map(function(str, i){
return pad(++i, width) + ' |' + ' ' + str;
}).join('\n');
}
// legend
msg = '\n'
+ color('diff removed', 'actual')
+ ' '
+ color('diff added', 'expected')
+ '\n\n'
+ msg
+ '\n';
// indent
msg = msg.replace(/^/gm, ' ');
return msg;
}
/**
* Returns a unified diff between 2 strings
*
* @param {Error} Error with actual/expected
* @return {String} Diff
* @api private
*/
function unifiedDiff(err, escape) {
var indent = ' ';
function cleanUp(line) {
if (escape) {
line = escapeInvisibles(line);
}
if (line[0] === '+') return indent + colorLines('diff added', line);
if (line[0] === '-') return indent + colorLines('diff removed', line);
if (line.match(/\@\@/)) return null;
if (line.match(/\\ No newline/)) return null;
else return indent + line;
}
function notBlank(line) {
return line != null;
}
var msg = diff.createPatch('string', err.actual, err.expected);
var lines = msg.split('\n').splice(4);
return '\n '
+ colorLines('diff added', '+ expected') + ' '
+ colorLines('diff removed', '- actual')
+ '\n\n'
+ lines.map(cleanUp).filter(notBlank).join('\n');
}
/**
* Return a character diff for `err`.
*
* @param {Error} err
* @return {String}
* @api private
*/
function errorDiff(err, type, escape) {
var actual = escape ? escapeInvisibles(err.actual) : err.actual;
var expected = escape ? escapeInvisibles(err.expected) : err.expected;
return diff['diff' + type](actual, expected).map(function(str){
if (str.added) return colorLines('diff added', str.value);
if (str.removed) return colorLines('diff removed', str.value);
return str.value;
}).join('');
}
/**
* Returns a string with all invisible characters in plain text
*
* @param {String} line
* @return {String}
* @api private
*/
function escapeInvisibles(line) {
return line.replace(/\t/g, '<tab>')
.replace(/\r/g, '<CR>')
.replace(/\n/g, '<LF>\n');
}
/**
* Color lines for `str`, using the color `name`.
*
* @param {String} name
* @param {String} str
* @return {String}
* @api private
*/
function colorLines(name, str) {
return str.split('\n').map(function(str){
return color(name, str);
}).join('\n');
}
/**
* Check that a / b have the same type.
*
* @param {Object} a
* @param {Object} b
* @return {Boolean}
* @api private
*/
function sameType(a, b) {
a = Object.prototype.toString.call(a);
b = Object.prototype.toString.call(b);
return a == b;
}

View File

@ -0,0 +1,408 @@
exports = module.exports = Classic
var Base = require('./base')
, cursor = Base.cursor
, color = Base.color
, yaml = require('js-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, palette) {
// 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
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 patch = diff.createPatch('', wanted, found)
//console.error(patch)
var width = 0
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 && i > 4
}).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 =
bg + added + '+++ found' +
(Base.useColors
? repeat(width - '+++ found'.length + 1, ' ')
: '') +
reset + '\n' +
bg + removed + '--- wanted' +
(Base.useColors
? repeat(width - '--- wanted'.length + 1, ' ')
: '') +
reset + '\n'
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')) {
printDiff = true
var found = t.diag.found
var wanted = t.diag.wanted
failMsg += indent(doDiff(found, wanted), 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'))
continue
o[i] = t.diag[i]
print = true
}
if (print)
failMsg += indent(yaml.safeDump(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.safeDump(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')
}

View File

@ -0,0 +1,62 @@
/**
* Module dependencies.
*/
var Base = require('./base')
, utils = require('../utils');
/**
* Expose `Doc`.
*/
exports = module.exports = Doc;
/**
* Initialize a new `Doc` reporter.
*
* @param {Runner} runner
* @api public
*/
function Doc(runner) {
Base.call(this, runner);
var self = this
, stats = this.stats
, total = runner.total
, indents = 2;
function indent() {
return Array(indents).join(' ');
}
runner.on('suite', function(suite){
if (suite.root) return;
++indents;
console.log('%s<section class="suite">', indent());
++indents;
console.log('%s<h1>%s</h1>', indent(), utils.escape(suite.title));
console.log('%s<dl>', indent());
});
runner.on('suite end', function(suite){
if (suite.root) return;
console.log('%s</dl>', indent());
--indents;
console.log('%s</section>', indent());
--indents;
});
runner.on('pass', function(test){
console.log('%s <dt>%s</dt>', indent(), utils.escape(test.title));
var code = utils.escape(utils.clean(test.fn.toString()));
console.log('%s <dd><pre><code>%s</code></pre></dd>', indent(), code);
});
runner.on('fail', function(test, err){
console.log('%s <dt class="error">%s</dt>', indent(), utils.escape(test.title));
var code = utils.escape(utils.clean(test.fn.toString()));
console.log('%s <dd class="error"><pre><code>%s</code></pre></dd>', indent(), code);
console.log('%s <dd class="error">%s</dd>', indent(), utils.escape(err));
});
}

View File

@ -0,0 +1,62 @@
/**
* Module dependencies.
*/
var Base = require('./base')
, color = Base.color;
/**
* Expose `Dot`.
*/
exports = module.exports = Dot;
/**
* Initialize a new `Dot` matrix test reporter.
*
* @param {Runner} runner
* @api public
*/
function Dot(runner) {
Base.call(this, runner);
var self = this
, stats = this.stats
, width = Base.window.width * .75 | 0
, n = -1;
runner.on('start', function(){
process.stdout.write('\n');
});
runner.on('pending', function(test){
if (++n % width == 0) process.stdout.write('\n ');
process.stdout.write(color('pending', Base.symbols.dot));
});
runner.on('pass', function(test){
if (++n % width == 0) process.stdout.write('\n ');
if ('slow' == test.speed) {
process.stdout.write(color('bright yellow', Base.symbols.dot));
} else {
process.stdout.write(color(test.speed, Base.symbols.dot));
}
});
runner.on('fail', function(test, err){
if (++n % width == 0) process.stdout.write('\n ');
process.stdout.write(color('fail', Base.symbols.dot));
});
runner.on('end', function(){
console.log();
self.epilogue();
});
}
/**
* Inherit from `Base.prototype`.
*/
Dot.prototype.__proto__ = Base.prototype;

View File

@ -0,0 +1,50 @@
exports = module.exports = Dump
var Base = require('./base')
, cursor = Base.cursor
, color = Base.color
, useColors = Base.useColors
, util = require('util')
function Dump(runner) {
Base.call(this, runner);
var events = [
'start',
'version',
'suite',
'suite end',
'test',
'pending',
'pass',
'fail',
'test end',
];
var i = process.argv.indexOf('dump')
if (i !== -1) {
var args = process.argv.slice(i + 1)
if (args.length)
events = args
}
runner.on('line', function (c) {
if (c.trim())
process.stderr.write(Base.color('bright yellow', c))
})
events.forEach(function (ev) {
runner.on(ev, function (obj) {
console.log(ev)
if (arguments.length) {
console.log(util.inspect(obj, false, Infinity, useColors))
console.log()
}
})
})
runner.on('end', function () {
console.log('end')
console.log(runner.stats)
console.log()
})
}

View File

@ -0,0 +1,16 @@
exports.dot = require('./dot.js')
exports.doc = require('./doc.js')
exports.tap = true
exports.json = require('./json.js')
exports.list = require('./list.js')
exports.min = require('./min.js')
exports.spec = require('./spec.js')
exports.nyan = require('./nyan.js')
exports.xunit = require('./xunit.js')
exports.markdown = require('./markdown.js')
exports.progress = require('./progress.js')
exports.landing = require('./landing.js')
exports.jsonstream = require('./json-stream.js')
exports.dump = require('./dump.js')
exports.classic = require('./classic.js')
exports.silent = require('./silent.js')

View File

@ -0,0 +1,62 @@
/**
* Module dependencies.
*/
var Base = require('./base')
, color = Base.color;
/**
* Expose `List`.
*/
exports = module.exports = List;
/**
* Initialize a new `List` test reporter.
*
* @param {Runner} runner
* @api public
*/
function List(runner) {
Base.call(this, runner);
var self = this
, stats = this.stats
, total = runner.total;
runner.on('start', function(){
console.log(JSON.stringify(['start', { total: total }]));
});
runner.on('pass', function(test){
console.log(JSON.stringify(['pass', clean(test)]));
});
runner.on('fail', function(test, err){
test = clean(test);
test.err = err.message;
console.log(JSON.stringify(['fail', test]));
});
runner.on('end', function(){
process.stdout.write(JSON.stringify(['end', self.stats]));
});
}
/**
* Return a plain-object representation of `test`
* free of cyclic properties etc.
*
* @param {Object} test
* @return {Object}
* @api private
*/
function clean(test) {
return {
title: test.title
, fullTitle: test.fullTitle()
, duration: test.duration
}
}

View File

@ -0,0 +1,92 @@
/**
* Module dependencies.
*/
var Base = require('./base')
, cursor = Base.cursor
, color = Base.color;
/**
* Expose `JSON`.
*/
exports = module.exports = JSONReporter;
/**
* Initialize a new `JSON` reporter.
*
* @param {Runner} runner
* @api public
*/
function JSONReporter(runner) {
var self = this;
Base.call(this, runner);
var tests = []
, pending = []
, failures = []
, passes = [];
runner.on('test end', function(test){
tests.push(test);
});
runner.on('pass', function(test){
passes.push(test);
});
runner.on('fail', function(test){
failures.push(test);
});
runner.on('pending', function(test){
pending.push(test);
});
runner.on('end', function(){
var obj = {
stats: self.stats,
tests: tests.map(clean),
pending: pending.map(clean),
failures: failures.map(clean),
passes: passes.map(clean)
};
runner.testResults = obj;
process.stdout.write(JSON.stringify(obj, null, 2));
});
}
/**
* Return a plain-object representation of `test`
* free of cyclic properties etc.
*
* @param {Object} test
* @return {Object}
* @api private
*/
function clean(test) {
return {
title: test.title,
fullTitle: test.fullTitle(),
duration: test.duration,
err: errorJSON(test.err || {})
}
}
/**
* Transform `error` into a JSON object.
* @param {Error} err
* @return {Object}
*/
function errorJSON(err) {
var res = {};
Object.getOwnPropertyNames(err).forEach(function(key) {
res[key] = err[key];
}, err);
return res;
}

View File

@ -0,0 +1,96 @@
/**
* Module dependencies.
*/
var Base = require('./base')
, cursor = Base.cursor
, color = Base.color;
/**
* Expose `Landing`.
*/
exports = module.exports = Landing;
/**
* Airplane color.
*/
Base.colors.plane = 0;
/**
* Airplane crash color.
*/
Base.colors['plane crash'] = 31;
/**
* Runway color.
*/
Base.colors.runway = 90;
/**
* Initialize a new `Landing` reporter.
*
* @param {Runner} runner
* @api public
*/
function Landing(runner) {
Base.call(this, runner);
var self = this
, stats = this.stats
, width = Base.window.width * .75 | 0
, total = runner.total
, stream = process.stdout
, plane = color('plane', '✈')
, crashed = -1
, n = 0;
function runway() {
var buf = Array(width).join('-');
return ' ' + color('runway', buf);
}
runner.on('start', function(){
stream.write('\n\n\n ');
cursor.hide();
});
runner.on('test end', function(test){
// check if the plane crashed
var col = -1 == crashed
? width * ++n / total | 0
: crashed;
// show the crash
if ('failed' == test.state) {
plane = color('plane crash', '✈');
crashed = col;
}
// render landing strip
stream.write('\u001b['+(width+1)+'D\u001b[2A');
stream.write(runway());
stream.write('\n ');
stream.write(color('runway', Array(col).join('⋅')));
stream.write(plane)
stream.write(color('runway', Array(width - col).join('⋅') + '\n'));
stream.write(runway());
stream.write('\u001b[0m');
});
runner.on('end', function(){
cursor.show();
console.log();
self.epilogue();
});
}
/**
* Inherit from `Base.prototype`.
*/
Landing.prototype.__proto__ = Base.prototype;

View File

@ -0,0 +1,63 @@
/**
* Module dependencies.
*/
var Base = require('./base')
, cursor = Base.cursor
, color = Base.color;
/**
* Expose `List`.
*/
exports = module.exports = List;
/**
* Initialize a new `List` test reporter.
*
* @param {Runner} runner
* @api public
*/
function List(runner) {
Base.call(this, runner);
var self = this
, stats = this.stats
, n = 0;
runner.on('start', function(){
console.log();
});
runner.on('test', function(test){
process.stdout.write(color('pass', ' ' + test.fullTitle() + ': '));
});
runner.on('pending', function(test){
var fmt = color('checkmark', ' -')
+ color('pending', ' %s');
console.log(fmt, test.fullTitle());
});
runner.on('pass', function(test){
var fmt = color('checkmark', ' '+Base.symbols.dot)
+ color('pass', ' %s')
+ (test.duration ? color('pass', ': ') + color(test.speed, '%dms') : '');
cursor.CR();
console.log(fmt, test.fullTitle(), test.duration || '');
});
runner.on('fail', function(test, err){
cursor.CR();
console.log(color('fail', ' %d) %s'), ++n, test.fullTitle());
});
runner.on('end', self.epilogue.bind(self));
}
/**
* Inherit from `Base.prototype`.
*/
List.prototype.__proto__ = Base.prototype;

View File

@ -0,0 +1,126 @@
/**
* Module dependencies.
*/
var Base = require('./base')
, utils = require('../utils');
/**
* Constants
*/
var SUITE_PREFIX = '$';
/**
* Expose `Markdown`.
*/
exports = module.exports = Markdown;
/**
* Initialize a new `Markdown` reporter.
*
* @param {Runner} runner
* @api public
*/
function Markdown(runner) {
Base.call(this, runner);
var self = this
, stats = this.stats
, level = 0
, buf = '';
function title(str) {
return Array(level + 1).join('#') + ' ' + str;
}
function indent() {
return Array(level).join(' ');
}
function mapTOC(suite, obj) {
var ret = obj,
key = SUITE_PREFIX + suite.title;
obj = obj[key] = obj[key] || { suite: suite };
suite.suites.forEach(function(suite){
mapTOC(suite, obj);
});
return ret;
}
function stringifyTOC(obj, level) {
++level;
var buf = '';
var link;
for (var key in obj) {
if ('suite' == key) continue;
if (key !== SUITE_PREFIX) {
link = ' - [' + key.substring(1) + ']';
link += '(#' + utils.slug(obj[key].suite.fullTitle()) + ')\n';
buf += Array(level).join(' ') + link;
}
buf += stringifyTOC(obj[key], level);
}
return buf;
}
function generateTOC() {
return suites.map(generateTOC_).join('')
}
function generateTOC_(suite) {
var obj = mapTOC(suite, {});
return stringifyTOC(obj, 0);
}
var suites = []
var currentSuite = null
runner.on('suite', function(suite){
currentSuite = suite
if (suite.root) {
suites.push(suite)
}
++level;
var slug = utils.slug(suite.fullTitle());
buf += '<a name="' + slug + '"></a>' + '\n';
buf += title(suite.title) + '\n';
});
runner.on('suite end', function(suite){
if (suite.ok) {
buf += '\nok - ' + suite.title + '\n'
} else {
buf += '\nnot ok - ' + suite.title + '\n'
}
--level;
});
runner.on('test', function(test){
if (!test.ok || test.pending) {
var code = utils.clean(test.fn.toString());
buf += test.title + '.\n';
if (code) {
buf += '\n```js\n';
buf += code + '\n';
buf += '```\n';
}
var stack = test.err && test.err.stack
if (!stack) {
stack = test.result && test.result.diag && test.result.diag.stack
}
if (stack) {
buf += '\n```\n' + stack + '\n```\n';
}
buf += '\n\n';
}
});
runner.on('end', function(){
process.stdout.write('# TOC\n');
process.stdout.write(generateTOC());
process.stdout.write('\n\n');
process.stdout.write(buf);
});
}

View File

@ -0,0 +1,37 @@
/**
* Module dependencies.
*/
var Base = require('./base');
/**
* Expose `Min`.
*/
exports = module.exports = Min;
/**
* Initialize a new `Min` minimal test reporter (best used with --watch).
*
* @param {Runner} runner
* @api public
*/
function Min(runner) {
Base.call(this, runner);
runner.on('start', function(){
// clear screen
process.stdout.write('\u001b[2J');
// set cursor position
process.stdout.write('\u001b[1;3H');
});
runner.on('end', this.epilogue.bind(this));
}
/**
* Inherit from `Base.prototype`.
*/
Min.prototype.__proto__ = Base.prototype;

View File

@ -0,0 +1,260 @@
/**
* Module dependencies.
*/
var Base = require('./base');
/**
* Expose `Dot`.
*/
exports = module.exports = NyanCat;
/**
* Initialize a new `Dot` matrix test reporter.
*
* @param {Runner} runner
* @api public
*/
function NyanCat(runner) {
Base.call(this, runner);
var self = this
, stats = this.stats
, width = Base.window.width * .75 | 0
, rainbowColors = this.rainbowColors = self.generateColors()
, colorIndex = this.colorIndex = 0
, numerOfLines = this.numberOfLines = 4
, trajectories = this.trajectories = [[], [], [], []]
, nyanCatWidth = this.nyanCatWidth = 11
, trajectoryWidthMax = this.trajectoryWidthMax = (width - nyanCatWidth)
, scoreboardWidth = this.scoreboardWidth = 5
, tick = this.tick = 0
, n = 0;
runner.on('start', function(){
Base.cursor.hide();
self.draw();
});
runner.on('pending', function(test){
self.draw();
});
runner.on('pass', function(test){
self.draw();
});
runner.on('fail', function(test, err){
self.draw();
});
runner.on('end', function(){
Base.cursor.show();
for (var i = 0; i < self.numberOfLines; i++) write('\n');
self.epilogue();
});
}
/**
* Draw the nyan cat
*
* @api private
*/
NyanCat.prototype.draw = function(){
this.appendRainbow();
this.drawScoreboard();
this.drawRainbow();
this.drawNyanCat();
this.tick = !this.tick;
};
/**
* Draw the "scoreboard" showing the number
* of passes, failures and pending tests.
*
* @api private
*/
NyanCat.prototype.drawScoreboard = function(){
var stats = this.stats;
function draw(type, n) {
write(' ');
write(Base.color(type, n));
write('\n');
}
draw('green', stats.passes);
draw('fail', stats.failures);
draw('pending', stats.pending);
write('\n');
this.cursorUp(this.numberOfLines);
};
/**
* Append the rainbow.
*
* @api private
*/
NyanCat.prototype.appendRainbow = function(){
var segment = this.tick ? '_' : '-';
var rainbowified = this.rainbowify(segment);
for (var index = 0; index < this.numberOfLines; index++) {
var trajectory = this.trajectories[index];
if (trajectory.length >= this.trajectoryWidthMax) trajectory.shift();
trajectory.push(rainbowified);
}
};
/**
* Draw the rainbow.
*
* @api private
*/
NyanCat.prototype.drawRainbow = function(){
var self = this;
this.trajectories.forEach(function(line, index) {
write('\u001b[' + self.scoreboardWidth + 'C');
write(line.join(''));
write('\n');
});
this.cursorUp(this.numberOfLines);
};
/**
* Draw the nyan cat
*
* @api private
*/
NyanCat.prototype.drawNyanCat = function() {
var self = this;
var startWidth = this.scoreboardWidth + this.trajectories[0].length;
var dist = '\u001b[' + startWidth + 'C';
var padding = '';
write(dist);
write('_,------,');
write('\n');
write(dist);
padding = self.tick ? ' ' : ' ';
write('_|' + padding + '/\\_/\\ ');
write('\n');
write(dist);
padding = self.tick ? '_' : '__';
var tail = self.tick ? '~' : '^';
var face;
write(tail + '|' + padding + this.face() + ' ');
write('\n');
write(dist);
padding = self.tick ? ' ' : ' ';
write(padding + '"" "" ');
write('\n');
this.cursorUp(this.numberOfLines);
};
/**
* Draw nyan cat face.
*
* @return {String}
* @api private
*/
NyanCat.prototype.face = function() {
var stats = this.stats;
if (stats.failures) {
return '( x .x)';
} else if (stats.pending) {
return '( o .o)';
} else if(stats.passes) {
return '( ^ .^)';
} else {
return '( - .-)';
}
};
/**
* Move cursor up `n`.
*
* @param {Number} n
* @api private
*/
NyanCat.prototype.cursorUp = function(n) {
write('\u001b[' + n + 'A');
};
/**
* Move cursor down `n`.
*
* @param {Number} n
* @api private
*/
NyanCat.prototype.cursorDown = function(n) {
write('\u001b[' + n + 'B');
};
/**
* Generate rainbow colors.
*
* @return {Array}
* @api private
*/
NyanCat.prototype.generateColors = function(){
var colors = [];
for (var i = 0; i < (6 * 7); i++) {
var pi3 = Math.floor(Math.PI / 3);
var n = (i * (1.0 / 6));
var r = Math.floor(3 * Math.sin(n) + 3);
var g = Math.floor(3 * Math.sin(n + 2 * pi3) + 3);
var b = Math.floor(3 * Math.sin(n + 4 * pi3) + 3);
colors.push(36 * r + 6 * g + b + 16);
}
return colors;
};
/**
* Apply rainbow to the given `str`.
*
* @param {String} str
* @return {String}
* @api private
*/
NyanCat.prototype.rainbowify = function(str){
if (!Base.useColors)
return str;
var color = this.rainbowColors[this.colorIndex % this.rainbowColors.length];
this.colorIndex += 1;
return '\u001b[38;5;' + color + 'm' + str + '\u001b[0m';
};
/**
* Stdout helper.
*/
function write(string) {
process.stdout.write(string);
}
/**
* Inherit from `Base.prototype`.
*/
NyanCat.prototype.__proto__ = Base.prototype;

View File

@ -0,0 +1,92 @@
/**
* Module dependencies.
*/
var Base = require('./base')
, cursor = Base.cursor
, color = Base.color;
/**
* Expose `Progress`.
*/
exports = module.exports = Progress;
/**
* General progress bar color.
*/
Base.colors.progress = 90;
/**
* Initialize a new `Progress` bar test reporter.
*
* @param {Runner} runner
* @param {Object} options
* @api public
*/
function Progress(runner, options) {
Base.call(this, runner);
var self = this
, options = options || {}
, stats = this.stats
, width = Base.window.width * .50 | 0
, total = runner.total
, complete = 0
, max = Math.max
, lastN = -1;
// default chars
options.open = options.open || '[';
options.complete = options.complete || '▬';
options.incomplete = options.incomplete || Base.symbols.dot;
options.close = options.close || ']';
options.verbose = false;
// tests started
runner.on('start', function(){
console.log();
cursor.hide();
});
// tests complete
runner.on('test end', function(){
complete++;
var incomplete = total - complete
, percent = complete / total
, n = width * percent | 0
, i = width - n;
if (lastN === n && !options.verbose) {
// Don't re-render the line if it hasn't changed
return;
}
lastN = n;
cursor.CR();
process.stdout.write('\u001b[J');
process.stdout.write(color('progress', ' ' + options.open));
process.stdout.write(Array(n).join(options.complete));
process.stdout.write(Array(i).join(options.incomplete));
process.stdout.write(color('progress', options.close));
if (options.verbose) {
process.stdout.write(color('progress', ' ' + complete + ' of ' + total));
}
});
// tests are complete, output some stats
// and the failures if any
runner.on('end', function(){
cursor.show();
console.log();
self.epilogue();
});
}
/**
* Inherit from `Base.prototype`.
*/
Progress.prototype.__proto__ = Base.prototype;

View File

@ -0,0 +1 @@
exports = module.exports = function () {}

View File

@ -0,0 +1,82 @@
/**
* Module dependencies.
*/
var Base = require('./base')
, cursor = Base.cursor
, color = Base.color;
/**
* Expose `Spec`.
*/
exports = module.exports = Spec;
/**
* Initialize a new `Spec` test reporter.
*
* @param {Runner} runner
* @api public
*/
function Spec(runner) {
Base.call(this, runner);
var self = this
, stats = this.stats
, indents = 0
, n = 0;
function indent() {
return Array(indents).join(' ')
}
runner.on('start', function(){
console.log();
});
runner.on('suite', function(suite){
++indents;
console.log(color('suite', '%s%s'), indent(), suite.title);
});
runner.on('suite end', function(suite){
--indents;
if (1 == indents) console.log();
});
runner.on('pending', function(test){
var fmt = indent() + color('pending', ' - %s');
console.log(fmt, test.title);
});
runner.on('pass', function(test){
if ('fast' == test.speed) {
var fmt = indent()
+ color('checkmark', ' ' + Base.symbols.ok)
+ color('pass', ' %s');
cursor.CR();
console.log(fmt, test.title);
} else {
var fmt = indent()
+ color('checkmark', ' ' + Base.symbols.ok)
+ color('pass', ' %s')
+ color(test.speed, ' (%dms)');
cursor.CR();
console.log(fmt, test.title, test.duration);
}
});
runner.on('fail', function(test, err){
cursor.CR();
console.log(indent() + color('fail', ' %d) %s'), ++n, test.title);
});
runner.on('end', self.epilogue.bind(self));
}
/**
* Inherit from `Base.prototype`.
*/
Spec.prototype.__proto__ = Base.prototype;

View File

@ -0,0 +1,51 @@
doctype html
html
head
title Coverage
meta(charset='utf-8')
include script.html
include style.html
body
#coverage
h1#overview Coverage
include menu
#stats(class=coverageClass(cov.coverage))
.percentage #{cov.coverage | 0}%
.sloc= cov.sloc
.hits= cov.hits
.misses= cov.misses
#files
for file in cov.files
.file
h2(id=file.filename)= file.filename
#stats(class=coverageClass(file.coverage))
.percentage #{file.coverage | 0}%
.sloc= file.sloc
.hits= file.hits
.misses= file.misses
table#source
thead
tr
th Line
th Hits
th Source
tbody
for line, number in file.source
if line.coverage > 0
tr.hit
td.line= number
td.hits= line.coverage
td.source= line.source
else if 0 === line.coverage
tr.miss
td.line= number
td.hits 0
td.source= line.source
else
tr
td.line= number
td.hits
td.source= line.source || ' '

View File

@ -0,0 +1,13 @@
#menu
li
a(href='#overview') overview
for file in cov.files
li
span.cov(class=coverageClass(file.coverage)) #{file.coverage | 0}
a(href='##{file.filename}')
segments = file.filename.split('/')
basename = segments.pop()
if segments.length
span.dirname= segments.join('/') + '/'
span.basename= basename
a#logo(href='http://visionmedia.github.io/mocha/') m

View File

@ -0,0 +1,34 @@
<script>
headings = [];
onload = function(){
headings = document.querySelectorAll('h2');
};
onscroll = function(e){
var heading = find(window.scrollY);
if (!heading) return;
var links = document.querySelectorAll('#menu a')
, link;
for (var i = 0, len = links.length; i < len; ++i) {
link = links[i];
link.className = link.getAttribute('href') == '#' + heading.id
? 'active'
: '';
}
};
function find(y) {
var i = headings.length
, heading;
while (i--) {
heading = headings[i];
if (y >= heading.offsetTop) {
return heading;
}
}
}
</script>

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,149 @@
/**
* Module dependencies.
*/
var Base = require('./base')
, utils = require('../utils')
, fs = require('fs')
, escape = utils.escape;
/**
* Save timer references to avoid Sinon interfering (see GH-237).
*/
var Date = global.Date
, setTimeout = global.setTimeout
, setInterval = global.setInterval
, clearTimeout = global.clearTimeout
, clearInterval = global.clearInterval;
/**
* Expose `XUnit`.
*/
exports = module.exports = XUnit;
/**
* Initialize a new `XUnit` reporter.
*
* @param {Runner} runner
* @api public
*/
function XUnit(runner, options) {
Base.call(this, runner);
var stats = this.stats
, tests = []
, self = this;
if (options.reporterOptions && options.reporterOptions.output) {
if (! fs.createWriteStream) {
throw new Error('file output not supported in browser');
}
self.fileStream = fs.createWriteStream(options.reporterOptions.output);
}
runner.on('pending', function(test){
tests.push(test);
});
runner.on('pass', function(test){
tests.push(test);
});
runner.on('fail', function(test){
tests.push(test);
});
runner.on('end', function(){
self.write(tag('testsuite', {
name: 'TAP Tests'
, tests: stats.tests
, failures: stats.failures
, errors: stats.failures
, skipped: stats.tests - stats.failures - stats.passes
, timestamp: (new Date).toUTCString()
, time: (stats.duration / 1000) || 0
}, false));
tests.forEach(function(t) { self.test(t); });
self.write('</testsuite>');
});
}
/**
* Override done to close the stream (if it's a file).
*/
XUnit.prototype.done = function(failures, fn) {
if (this.fileStream) {
this.fileStream.end(function() {
fn(failures);
});
} else {
fn(failures);
}
};
/**
* Inherit from `Base.prototype`.
*/
XUnit.prototype.__proto__ = Base.prototype;
/**
* Write out the given line
*/
XUnit.prototype.write = function(line) {
if (this.fileStream) {
this.fileStream.write(line + '\n');
} else {
console.log(line);
}
};
/**
* Output tag for the given `test.`
*/
XUnit.prototype.test = function(test, ostream) {
var attrs = {
classname: test.parent.fullTitle()
, name: test.title
, time: (test.duration / 1000) || 0
};
if ('failed' == test.state) {
var err = test.err;
this.write(tag('testcase', attrs, false, tag('failure', {}, false, cdata(escape(err.message) + "\n" + err.stack))));
} else if (test.pending) {
this.write(tag('testcase', attrs, false, tag('skipped', {}, true)));
} else {
this.write(tag('testcase', attrs, true) );
}
};
/**
* HTML tag helper.
*/
function tag(name, attrs, close, content) {
var end = close ? '/>' : '>'
, pairs = []
, tag;
for (var key in attrs) {
pairs.push(key + '="' + escape(attrs[key]) + '"');
}
tag = '<' + name + (pairs.length ? ' ' + pairs.join(' ') : '') + end;
if (content) tag += content + '</' + name + end;
return tag;
}
/**
* Return cdata escaped CDATA `str`.
*/
function cdata(str) {
return '<![CDATA[' + escape(str) + ']]>';
}

View File

@ -0,0 +1,347 @@
// A facade from the tap-parser to the Mocha "Runner" object.
// Note that pass/fail/suite events need to also mock the "Runnable"
// objects (either "Suite" or "Test") since these have functions
// which are called by the formatters.
module.exports = Runner
// relevant events:
//
// start()
// Start of the top-level test set
//
// end()
// End of the top-level test set.
//
// fail(test, err)
// any "not ok" test that is not the trailing test for a suite
// of >0 test points.
//
// pass(test)
// any "ok" test point that is not the trailing test for a suite
// of >0 tests
//
// pending(test)
// Any "todo" test
//
// suite(suite)
// A suite is a child test with >0 test points. This is a little bit
// tricky, because TAP will provide a "child" event before we know
// that it's a "suite". We see the "# Subtest: name" comment as the
// first thing in the subtest. Then, when we get our first test point,
// we know that it's a suite, and can emit the event with the mock suite.
//
// suite end(suite)
// Emitted when we end the subtest
//
// test(test)
// Any test point which is not the trailing test for a suite.
//
// test end(test)
// Emitted immediately after the "test" event because test points are
// not async in TAP.
var util = require('util')
var Test = require('./test.js')
var Suite = require('./suite.js')
var Writable = require('stream').Writable
if (!Writable) {
try {
Writable = require('readable-stream').Writable
} catch (er) {
throw new Error('Please install "readable-stream" to use this module ' +
'with Node.js v0.8 and before')
}
}
var Parser = require('tap-parser')
// $1 = number, $2 = units
var timere = /^#\s*time=((?:0|[1-9][0-9]*?)(?:\.[0-9]+)?)(ms|s)?$/
util.inherits(Runner, Writable)
function Runner (options) {
if (!(this instanceof Runner))
return new Runner(options)
var parser = this.parser = new Parser(options)
this.startTime = new Date()
attachEvents(this, parser, 0)
Writable.call(this, options)
}
Runner.prototype.write = function () {
if (!this.emittedStart) {
this.emittedStart = true
this.emit('start')
}
return this.parser.write.apply(this.parser, arguments)
}
Runner.prototype.end = function () {
return this.parser.end.apply(this.parser, arguments)
}
Parser.prototype.fullTitle = function () {
if (!this.parent)
return this.name || ''
else
return (this.parent.fullTitle() + ' ' + (this.name || '')).trim()
}
function attachEvents (runner, parser, level) {
parser.runner = runner
if (level === 0) {
parser.on('line', function (c) {
runner.emit('line', c)
})
parser.on('version', function (v) {
runner.emit('version', v)
})
parser.on('complete', function (res) {
runner.emit('end')
})
parser.on('comment', function (c) {
var tmatch = c.trim().match(timere)
if (tmatch) {
var t = +tmatch[1]
if (tmatch[2] === 's')
t *= 1000
parser.time = t
if (runner.stats)
runner.stats.duration = t
}
})
}
parser.emittedSuite = false
parser.didAssert = false
parser.name = parser.name || ''
parser.doingChild = null
parser.on('complete', function (res) {
if (!res.ok) {
var fail = { ok: false, diag: {} }
var count = res.count
if (res.plan) {
var plan = res.plan.end - res.plan.start + 1
if (count !== plan) {
fail.name = 'test count !== plan'
fail.diag = {
found: count,
wanted: plan
}
} else {
// probably handled on child parser
return
}
} else {
fail.name = 'missing plan'
}
fail.diag.results = res
emitTest(parser, fail)
}
})
parser.on('child', function (child) {
child.parent = parser
attachEvents(runner, child, level + 1)
// if we're in a suite, but we haven't emitted it yet, then we
// know that an assert will follow this child, even if there are
// no others. That means that we will definitely have a 'suite'
// event to emit.
emitSuite(this)
this.didAssert = true
this.doingChild = child
})
if (!parser.name) {
parser.on('comment', function (c) {
if (!this.name && c.match(/^# Subtest: /)) {
c = c.trim().replace(/^# Subtest: /, '')
this.name = c
}
})
}
// Just dump all non-parsing stuff to stderr
parser.on('extra', function (c) {
process.stderr.write(c)
})
parser.on('assert', function (result) {
emitSuite(this)
// no need to print the trailing assert for subtests
// we've already emitted a 'suite end' event for this.
// UNLESS, there were no other asserts, AND it's root level
if (this.doingChild) {
var suite = this.doingChild.suite
if (this.doingChild.name === result.name) {
if (suite) {
if (result.time)
suite.duration = result.time
// If it's ok so far, but the ending result is not-ok, then
// that means that it exited non-zero. Emit the test so
// that we can print it as a failure.
if (suite.ok && !result.ok)
emitTest(this, result)
}
}
var emitOn = this
var dc = this.doingChild
this.doingChild = null
if (!dc.didAssert && dc.level === 1) {
emitOn = dc
} else if (dc.didAssert) {
if (dc.suite)
runner.emit('suite end', dc.suite)
return
} else {
emitOn = this
}
emitSuite(emitOn)
emitTest(emitOn, result)
if (emitOn !== this && emitOn.suite) {
runner.emit('suite end', emitOn.suite)
delete emitOn.suite
}
if (dc.suite) {
runner.emit('suite end', dc.suite)
}
return
}
this.didAssert = true
this.doingChild = null
emitTest(this, result)
})
parser.on('complete', function (results) {
this.results = results
})
parser.on('bailout', function (reason) {
var suite = this.suite
runner.emit('bailout', reason, suite)
if (suite)
this.suite = suite.parent
})
// proxy all stream events directly
var streamEvents = [
'pipe', 'prefinish', 'finish', 'unpipe', 'close'
]
streamEvents.forEach(function (ev) {
parser.on(ev, function () {
var args = [ev]
args.push.apply(args, arguments)
runner.emit.apply(runner, args)
})
})
}
function emitSuite (parser) {
if (!parser.emittedSuite && parser.name) {
parser.emittedSuite = true
var suite = parser.suite = new Suite(parser)
if (parser.parent && parser.parent.suite)
parser.parent.suite.suites.push(suite)
if (parser.runner.stats)
parser.runner.stats.suites ++
parser.runner.emit('suite', suite)
}
}
function emitTest (parser, result) {
var runner = parser.runner
var test = new Test(result, parser)
if (parser.suite) {
parser.suite.tests.push(test)
if (!result.ok) {
for (var p = parser; p && p.suite; p = p.parent) {
p.suite.ok = false
}
}
parser.suite.ok = parser.suite.ok && result.ok
}
runner.emit('test', test)
if (result.skip || result.todo) {
runner.emit('pending', test)
} else if (result.ok) {
runner.emit('pass', test)
} else {
var error = getError(result)
runner.emit('fail', test, error)
}
runner.emit('test end', test)
}
function getError (result) {
var err
function reviveStack (stack) {
if (!stack)
return null
return stack.trim().split('\n').map(function (line) {
return ' at ' + line
}).join('\n')
}
if (result.diag && result.diag.error) {
err = {
name: result.diag.error.name || 'Error',
message: result.diag.error.message,
toString: function () {
return this.name + ': ' + this.message
},
stack: result.diag.error.stack
}
} else {
err = {
message: (result.name || '(unnamed error)').replace(/^Error: /, ''),
toString: function () {
return 'Error: ' + this.message
},
stack: result.diag && result.diag.stack
}
}
var diag = result.diag
if (err.stack)
err.stack = err.toString() + '\n' + reviveStack(err.stack)
if (diag) {
var hasFound = diag.hasOwnProperty('found')
var hasWanted = diag.hasOwnProperty('wanted')
if (hasFound)
err.actual = diag.found
if (hasWanted)
err.expected = diag.wanted
if (hasFound && hasWanted)
err.showDiff = true
}
return err
}

View File

@ -0,0 +1,22 @@
// minimal mock of mocha's Suite class for formatters
module.exports = Suite
function Suite (parent) {
if (!parent.parent || !parent.parent.emittedSuite)
this.root = true
else
this.root = false
this.title = parent.name || ''
this.suites = []
this.tests = []
this.ok = true
}
Suite.prototype.fullTitle = function () {
if (!this.parent)
return (this.title || '').trim()
else
return (this.parent.fullTitle() + ' ' + (this.title || '')).trim()
}

View File

@ -0,0 +1,41 @@
// minimal mock of the mocha Test class for formatters
module.exports = Test
function Test (result, parent) {
this.result = result
this._slow = 75
this.duration = result.time
this.title = result.name
this.state = result.ok ? 'pass' : 'failed'
this.pending = result.todo || result.skip || false
if (result.diag && result.diag.source) {
var source = result.diag.source
this.fn = {
toString: function () {
return 'function(){' + source + '\n}'
}
}
}
Object.defineProperty(this, 'parent', {
value: parent,
writable: true,
configurable: true,
enumerable: false
})
}
Test.prototype.fullTitle = function () {
return (this.parent.fullTitle() + ' ' + (this.title || '')).trim()
}
Test.prototype.slow = function (ms){
return 75
}
Test.prototype.fn = {
toString: function () {
return 'function () {\n}'
}
}

View File

@ -0,0 +1,699 @@
/**
* Module dependencies.
*/
var fs = require('fs')
, path = require('path')
, basename = path.basename
, exists = fs.existsSync || path.existsSync
, glob = require('glob')
, join = path.join
, debug = require('debug')('mocha:watch');
/**
* Ignored directories.
*/
var ignore = ['node_modules', '.git'];
/**
* Escape special characters in the given string of html.
*
* @param {String} html
* @return {String}
* @api private
*/
exports.escape = function(html){
return String(html)
.replace('&', '&amp;')
.replace('"', '&quot;')
.replace('<', '&lt;')
.replace('>', '&gt;');
};
/**
* Array#forEach (<=IE8)
*
* @param {Array} array
* @param {Function} fn
* @param {Object} scope
* @api private
*/
exports.forEach = function(arr, fn, scope){
for (var i = 0, l = arr.length; i < l; i++)
fn.call(scope, arr[i], i);
};
/**
* Array#map (<=IE8)
*
* @param {Array} array
* @param {Function} fn
* @param {Object} scope
* @api private
*/
exports.map = function(arr, fn, scope){
var result = [];
for (var i = 0, l = arr.length; i < l; i++)
result.push(fn.call(scope, arr[i], i, arr));
return result;
};
/**
* Array#indexOf (<=IE8)
*
* @parma {Array} arr
* @param {Object} obj to find index of
* @param {Number} start
* @api private
*/
exports.indexOf = function(arr, obj, start){
for (var i = start || 0, l = arr.length; i < l; i++) {
if (arr[i] === obj)
return i;
}
return -1;
};
/**
* Array#reduce (<=IE8)
*
* @param {Array} array
* @param {Function} fn
* @param {Object} initial value
* @api private
*/
exports.reduce = function(arr, fn, val){
var rval = val;
for (var i = 0, l = arr.length; i < l; i++) {
rval = fn(rval, arr[i], i, arr);
}
return rval;
};
/**
* Array#filter (<=IE8)
*
* @param {Array} array
* @param {Function} fn
* @api private
*/
exports.filter = function(arr, fn){
var ret = [];
for (var i = 0, l = arr.length; i < l; i++) {
var val = arr[i];
if (fn(val, i, arr)) ret.push(val);
}
return ret;
};
/**
* Object.keys (<=IE8)
*
* @param {Object} obj
* @return {Array} keys
* @api private
*/
exports.keys = Object.keys || function(obj) {
var keys = []
, has = Object.prototype.hasOwnProperty; // for `window` on <=IE8
for (var key in obj) {
if (has.call(obj, key)) {
keys.push(key);
}
}
return keys;
};
/**
* Watch the given `files` for changes
* and invoke `fn(file)` on modification.
*
* @param {Array} files
* @param {Function} fn
* @api private
*/
exports.watch = function(files, fn){
var options = { interval: 100 };
files.forEach(function(file){
debug('file %s', file);
fs.watchFile(file, options, function(curr, prev){
if (prev.mtime < curr.mtime) fn(file);
});
});
};
/**
* Array.isArray (<=IE8)
*
* @param {Object} obj
* @return {Boolean}
* @api private
*/
var isArray = Array.isArray || function (obj) {
return '[object Array]' == {}.toString.call(obj);
};
/**
* @description
* Buffer.prototype.toJSON polyfill
* @type {Function}
*/
if(typeof Buffer !== 'undefined' && Buffer.prototype) {
Buffer.prototype.toJSON = Buffer.prototype.toJSON || function () {
return Array.prototype.slice.call(this, 0);
};
}
/**
* Ignored files.
*/
function ignored(path){
return !~ignore.indexOf(path);
}
/**
* Lookup files in the given `dir`.
*
* @return {Array}
* @api private
*/
exports.files = function(dir, ext, ret){
ret = ret || [];
ext = ext || ['js'];
var re = new RegExp('\\.(' + ext.join('|') + ')$');
fs.readdirSync(dir)
.filter(ignored)
.forEach(function(path){
path = join(dir, path);
if (fs.statSync(path).isDirectory()) {
exports.files(path, ext, ret);
} else if (path.match(re)) {
ret.push(path);
}
});
return ret;
};
/**
* Compute a slug from the given `str`.
*
* @param {String} str
* @return {String}
* @api private
*/
exports.slug = function(str){
return str
.toLowerCase()
.replace(/ +/g, '-')
.replace(/[^-\w]/g, '');
};
/**
* Strip the function definition from `str`,
* and re-indent for pre whitespace.
*/
exports.clean = function(str) {
str = str
.replace(/\r\n?|[\n\u2028\u2029]/g, "\n").replace(/^\uFEFF/, '')
.replace(/^function *\(.*\) *{|\(.*\) *=> *{?/, '')
.replace(/\s+\}$/, '');
var spaces = str.match(/^\n?( *)/)[1].length
, tabs = str.match(/^\n?(\t*)/)[1].length
, re = new RegExp('^\n?' + (tabs ? '\t' : ' ') + '{' + (tabs ? tabs : spaces) + '}', 'gm');
str = str.replace(re, '');
return exports.trim(str);
};
/**
* Trim the given `str`.
*
* @param {String} str
* @return {String}
* @api private
*/
exports.trim = function(str){
return str.replace(/^\s+|\s+$/g, '');
};
/**
* Parse the given `qs`.
*
* @param {String} qs
* @return {Object}
* @api private
*/
exports.parseQuery = function(qs){
return exports.reduce(qs.replace('?', '').split('&'), function(obj, pair){
var i = pair.indexOf('=')
, key = pair.slice(0, i)
, val = pair.slice(++i);
obj[key] = decodeURIComponent(val);
return obj;
}, {});
};
/**
* Highlight the given string of `js`.
*
* @param {String} js
* @return {String}
* @api private
*/
function highlight(js) {
return js
.replace(/</g, '&lt;')
.replace(/>/g, '&gt;')
.replace(/\/\/(.*)/gm, '<span class="comment">//$1</span>')
.replace(/('.*?')/gm, '<span class="string">$1</span>')
.replace(/(\d+\.\d+)/gm, '<span class="number">$1</span>')
.replace(/(\d+)/gm, '<span class="number">$1</span>')
.replace(/\bnew[ \t]+(\w+)/gm, '<span class="keyword">new</span> <span class="init">$1</span>')
.replace(/\b(function|new|throw|return|var|if|else)\b/gm, '<span class="keyword">$1</span>')
}
/**
* Highlight the contents of tag `name`.
*
* @param {String} name
* @api private
*/
exports.highlightTags = function(name) {
var code = document.getElementById('mocha').getElementsByTagName(name);
for (var i = 0, len = code.length; i < len; ++i) {
code[i].innerHTML = highlight(code[i].innerHTML);
}
};
/**
* If a value could have properties, and has none, this function is called, which returns
* a string representation of the empty value.
*
* Functions w/ no properties return `'[Function]'`
* Arrays w/ length === 0 return `'[]'`
* Objects w/ no properties return `'{}'`
* All else: return result of `value.toString()`
*
* @param {*} value Value to inspect
* @param {string} [type] The type of the value, if known.
* @returns {string}
*/
var emptyRepresentation = function emptyRepresentation(value, type) {
type = type || exports.type(value);
switch(type) {
case 'function':
return '[Function]';
case 'object':
return '{}';
case 'array':
return '[]';
default:
return value.toString();
}
};
/**
* Takes some variable and asks `{}.toString()` what it thinks it is.
* @param {*} value Anything
* @example
* type({}) // 'object'
* type([]) // 'array'
* type(1) // 'number'
* type(false) // 'boolean'
* type(Infinity) // 'number'
* type(null) // 'null'
* type(new Date()) // 'date'
* type(/foo/) // 'regexp'
* type('type') // 'string'
* type(global) // 'global'
* @api private
* @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/toString
* @returns {string}
*/
exports.type = function type(value) {
if (typeof Buffer !== 'undefined' && Buffer.isBuffer(value)) {
return 'buffer';
}
return Object.prototype.toString.call(value)
.replace(/^\[.+\s(.+?)\]$/, '$1')
.toLowerCase();
};
/**
* @summary Stringify `value`.
* @description Different behavior depending on type of value.
* - If `value` is undefined or null, return `'[undefined]'` or `'[null]'`, respectively.
* - If `value` is not an object, function or array, return result of `value.toString()` wrapped in double-quotes.
* - If `value` is an *empty* object, function, or array, return result of function
* {@link emptyRepresentation}.
* - If `value` has properties, call {@link exports.canonicalize} on it, then return result of
* JSON.stringify().
*
* @see exports.type
* @param {*} value
* @return {string}
* @api private
*/
exports.stringify = function(value) {
var type = exports.type(value);
if (!~exports.indexOf(['object', 'array', 'function'], type)) {
if(type != 'buffer') {
return jsonStringify(value);
}
var json = value.toJSON();
// Based on the toJSON result
return jsonStringify(json.data && json.type ? json.data : json, 2)
.replace(/,(\n|$)/g, '$1');
}
for (var prop in value) {
if (Object.prototype.hasOwnProperty.call(value, prop)) {
return jsonStringify(exports.canonicalize(value), 2)
.replace(/,(\n|$)/g, '$1');
}
}
return emptyRepresentation(value, type);
};
/**
* @description
* like JSON.stringify but more sense.
* @param {Object} object
* @param {Number=} spaces
* @param {number=} depth
* @returns {*}
* @private
*/
function jsonStringify(object, spaces, depth) {
if(typeof spaces == 'undefined') return _stringify(object); // primitive types
depth = depth || 1;
var space = spaces * depth
, str = isArray(object) ? '[' : '{'
, end = isArray(object) ? ']' : '}'
, length = object.length || exports.keys(object).length
, repeat = function(s, n) { return new Array(n).join(s); }; // `.repeat()` polyfill
function _stringify(val) {
switch (exports.type(val)) {
case 'null':
case 'undefined':
val = '[' + val + ']';
break;
case 'array':
case 'object':
val = jsonStringify(val, spaces, depth + 1);
break;
case 'boolean':
case 'regexp':
case 'number':
val = val === 0 && (1/val) === -Infinity // `-0`
? '-0'
: val.toString();
break;
case 'date':
val = '[Date: ' + val.toISOString() + ']';
break;
case 'buffer':
var json = val.toJSON();
// Based on the toJSON result
json = json.data && json.type ? json.data : json;
val = '[Buffer: ' + jsonStringify(json, 2, depth + 1) + ']';
break;
default:
val = (val == '[Function]' || val == '[Circular]')
? val
: '"' + val + '"'; //string
}
return val;
}
for(var i in object) {
if(!Object.prototype.hasOwnProperty.call(object, i)) continue; // not my business
--length;
str += '\n ' + repeat(' ', space)
+ (isArray(object) ? '' : '"' + i + '": ') // key
+ _stringify(object[i]) // value
+ (length ? ',' : ''); // comma
}
return str + (str.length != 1 // [], {}
? '\n' + repeat(' ', --space) + end
: end);
}
/**
* Return if obj is a Buffer
* @param {Object} arg
* @return {Boolean}
* @api private
*/
exports.isBuffer = function (arg) {
return typeof Buffer !== 'undefined' && Buffer.isBuffer(arg);
};
/**
* @summary Return a new Thing that has the keys in sorted order. Recursive.
* @description If the Thing...
* - has already been seen, return string `'[Circular]'`
* - is `undefined`, return string `'[undefined]'`
* - is `null`, return value `null`
* - is some other primitive, return the value
* - is not a primitive or an `Array`, `Object`, or `Function`, return the value of the Thing's `toString()` method
* - is a non-empty `Array`, `Object`, or `Function`, return the result of calling this function again.
* - is an empty `Array`, `Object`, or `Function`, return the result of calling `emptyRepresentation()`
*
* @param {*} value Thing to inspect. May or may not have properties.
* @param {Array} [stack=[]] Stack of seen values
* @return {(Object|Array|Function|string|undefined)}
* @see {@link exports.stringify}
* @api private
*/
exports.canonicalize = function(value, stack) {
var canonicalizedObj,
type = exports.type(value),
prop,
withStack = function withStack(value, fn) {
stack.push(value);
fn();
stack.pop();
};
stack = stack || [];
if (exports.indexOf(stack, value) !== -1) {
return '[Circular]';
}
switch(type) {
case 'undefined':
case 'buffer':
case 'null':
canonicalizedObj = value;
break;
case 'array':
withStack(value, function () {
canonicalizedObj = exports.map(value, function (item) {
return exports.canonicalize(item, stack);
});
});
break;
case 'function':
for (prop in value) {
canonicalizedObj = {};
break;
}
if (!canonicalizedObj) {
canonicalizedObj = emptyRepresentation(value, type);
break;
}
/* falls through */
case 'object':
canonicalizedObj = canonicalizedObj || {};
withStack(value, function () {
exports.forEach(exports.keys(value).sort(), function (key) {
canonicalizedObj[key] = exports.canonicalize(value[key], stack);
});
});
break;
case 'date':
case 'number':
case 'regexp':
case 'boolean':
canonicalizedObj = value;
break;
default:
canonicalizedObj = value.toString();
}
return canonicalizedObj;
};
/**
* Lookup file names at the given `path`.
*/
exports.lookupFiles = function lookupFiles(path, extensions, recursive) {
var files = [];
var re = new RegExp('\\.(' + extensions.join('|') + ')$');
if (!exists(path)) {
if (exists(path + '.js')) {
path += '.js';
} else {
files = glob.sync(path);
if (!files.length) throw new Error("cannot resolve path (or pattern) '" + path + "'");
return files;
}
}
try {
var stat = fs.statSync(path);
if (stat.isFile()) return path;
}
catch (ignored) {
return;
}
fs.readdirSync(path).forEach(function(file) {
file = join(path, file);
try {
var stat = fs.statSync(file);
if (stat.isDirectory()) {
if (recursive) {
files = files.concat(lookupFiles(file, extensions, recursive));
}
return;
}
}
catch (ignored) {
return;
}
if (!stat.isFile() || !re.test(file) || basename(file)[0] === '.') return;
files.push(file);
});
return files;
};
/**
* Generate an undefined error with a message warning the user.
*
* @return {Error}
*/
exports.undefinedError = function() {
return new Error('Caught undefined error, did you throw without specifying what?');
};
/**
* Generate an undefined error if `err` is not defined.
*
* @param {Error} err
* @return {Error}
*/
exports.getError = function(err) {
return err || exports.undefinedError();
};
/**
* @summary
* This Filter based on `mocha-clean` module.(see: `github.com/rstacruz/mocha-clean`)
* @description
* When invoking this function you get a filter function that get the Error.stack as an input,
* and return a prettify output.
* (i.e: strip Mocha, node_modules, bower and componentJS from stack trace).
* @returns {Function}
*/
exports.stackTraceFilter = function() {
var slash = '/'
, is = typeof document === 'undefined'
? { node: true }
: { browser: true }
, cwd = is.node
? process.cwd() + slash
: location.href.replace(/\/[^\/]*$/, '/');
function isNodeModule (line) {
return (~line.indexOf('node_modules'));
}
function isMochaInternal (line) {
return (~line.indexOf('node_modules' + slash + 'tap-mocha-reporter')) ||
(~line.indexOf('components' + slash + 'mochajs')) ||
(~line.indexOf('components' + slash + 'mocha'));
}
// node_modules, bower, componentJS
function isBrowserModule(line) {
return (~line.indexOf('node_modules')) ||
(~line.indexOf('components'));
}
function isNodeInternal (line) {
return (~line.indexOf('(timers.js:')) ||
(~line.indexOf('(domain.js:')) ||
(~line.indexOf('(events.js:')) ||
(~line.indexOf('(node.js:')) ||
(~line.indexOf('(module.js:')) ||
(~line.indexOf('at node.js:')) ||
(~line.indexOf('GeneratorFunctionPrototype.next (native)')) ||
false
}
return function(stack) {
stack = stack.split('\n');
stack = stack.reduce(function (list, line) {
if (is.node && (isNodeModule(line) ||
isMochaInternal(line) ||
isNodeInternal(line)))
return list;
if (is.browser && (isBrowserModule(line)))
return list;
// Clean up cwd(absolute)
list.push(line.replace(cwd, ''));
return list;
}, []);
return stack.join('\n');
}
};

View File

@ -0,0 +1,72 @@
{
"_from": "tap-mocha-reporter@^3.0.6",
"_id": "tap-mocha-reporter@3.0.6",
"_inBundle": false,
"_integrity": "sha512-UImgw3etckDQCoqZIAIKcQDt0w1JLVs3v0yxLlmwvGLZl6MGFxF7JME5PElXjAoDklVDU42P3vVu5jgr37P4Yg==",
"_location": "/tap-mocha-reporter",
"_phantomChildren": {},
"_requested": {
"type": "range",
"registry": true,
"raw": "tap-mocha-reporter@^3.0.6",
"name": "tap-mocha-reporter",
"escapedName": "tap-mocha-reporter",
"rawSpec": "^3.0.6",
"saveSpec": null,
"fetchSpec": "^3.0.6"
},
"_requiredBy": [
"/tap"
],
"_resolved": "https://registry.npmjs.org/tap-mocha-reporter/-/tap-mocha-reporter-3.0.6.tgz",
"_shasum": "12abe97ff409a5a6ecc3d70b6dba34d82184a770",
"_spec": "tap-mocha-reporter@^3.0.6",
"_where": "/home/manfred/enviPath/ketcher2/ketcher/node_modules/tap",
"author": {
"name": "Isaac Z. Schlueter",
"email": "i@izs.me",
"url": "http://blog.izs.me/"
},
"bin": {
"tap-mocha-reporter": "index.js"
},
"bugs": {
"url": "https://github.com/isaacs/tap-mocha-reporter/issues"
},
"bundleDependencies": false,
"dependencies": {
"color-support": "^1.1.0",
"debug": "^2.1.3",
"diff": "^1.3.2",
"escape-string-regexp": "^1.0.3",
"glob": "^7.0.5",
"js-yaml": "^3.3.1",
"readable-stream": "^2.1.5",
"tap-parser": "^5.1.0",
"unicode-length": "^1.0.0"
},
"deprecated": false,
"description": "Format a TAP stream using Mocha's set of reporters",
"devDependencies": {
"tap": "^6.2.0"
},
"files": [
"index.js",
"lib"
],
"homepage": "https://github.com/isaacs/tap-mocha-reporter",
"license": "ISC",
"main": "index.js",
"name": "tap-mocha-reporter",
"optionalDependencies": {
"readable-stream": "^2.1.5"
},
"repository": {
"type": "git",
"url": "git+https://github.com/isaacs/tap-mocha-reporter.git"
},
"scripts": {
"test": "tap test/*.js"
},
"version": "3.0.6"
}