Upsilon-VsCode/Client/node_modules/mocha/lib/runnable.js

396 lines
8.5 KiB
JavaScript
Executable File

'use strict';
/**
* Module dependencies.
*/
var EventEmitter = require('events').EventEmitter;
var Pending = require('./pending');
var debug = require('debug')('mocha:runnable');
var milliseconds = require('./ms');
var utils = require('./utils');
/**
* Save timer references to avoid Sinon interfering (see GH-237).
*/
/* eslint-disable no-unused-vars, no-native-reassign */
var Date = global.Date;
var setTimeout = global.setTimeout;
var setInterval = global.setInterval;
var clearTimeout = global.clearTimeout;
var clearInterval = global.clearInterval;
/* eslint-enable no-unused-vars, no-native-reassign */
/**
* Object#toString().
*/
var toString = Object.prototype.toString;
/**
* Expose `Runnable`.
*/
module.exports = Runnable;
/**
* Initialize a new `Runnable` with the given `title` and callback `fn`.
*
* @param {String} title
* @param {Function} fn
* @api private
* @param {string} title
* @param {Function} fn
*/
function Runnable (title, fn) {
this.title = title;
this.fn = fn;
this.body = (fn || '').toString();
this.async = fn && fn.length;
this.sync = !this.async;
this._timeout = 2000;
this._slow = 75;
this._enableTimeouts = true;
this.timedOut = false;
this._trace = new Error('done() called multiple times');
this._retries = -1;
this._currentRetry = 0;
this.pending = false;
}
/**
* Inherit from `EventEmitter.prototype`.
*/
utils.inherits(Runnable, EventEmitter);
/**
* Set & get timeout `ms`.
*
* @api private
* @param {number|string} ms
* @return {Runnable|number} ms or Runnable instance.
*/
Runnable.prototype.timeout = function (ms) {
if (!arguments.length) {
return this._timeout;
}
// see #1652 for reasoning
if (ms === 0 || ms > Math.pow(2, 31)) {
this._enableTimeouts = false;
}
if (typeof ms === 'string') {
ms = milliseconds(ms);
}
debug('timeout %d', ms);
this._timeout = ms;
if (this.timer) {
this.resetTimeout();
}
return this;
};
/**
* Set or get slow `ms`.
*
* @api private
* @param {number|string} ms
* @return {Runnable|number} ms or Runnable instance.
*/
Runnable.prototype.slow = function (ms) {
if (!arguments.length || typeof ms === 'undefined') {
return this._slow;
}
if (typeof ms === 'string') {
ms = milliseconds(ms);
}
debug('timeout %d', ms);
this._slow = ms;
return this;
};
/**
* Set and get whether timeout is `enabled`.
*
* @api private
* @param {boolean} enabled
* @return {Runnable|boolean} enabled or Runnable instance.
*/
Runnable.prototype.enableTimeouts = function (enabled) {
if (!arguments.length) {
return this._enableTimeouts;
}
debug('enableTimeouts %s', enabled);
this._enableTimeouts = enabled;
return this;
};
/**
* Halt and mark as pending.
*
* @api public
*/
Runnable.prototype.skip = function () {
throw new Pending('sync skip');
};
/**
* Check if this runnable or its parent suite is marked as pending.
*
* @api private
*/
Runnable.prototype.isPending = function () {
return this.pending || (this.parent && this.parent.isPending());
};
/**
* Set or get number of retries.
*
* @api private
*/
Runnable.prototype.retries = function (n) {
if (!arguments.length) {
return this._retries;
}
this._retries = n;
};
/**
* Set or get current retry
*
* @api private
*/
Runnable.prototype.currentRetry = function (n) {
if (!arguments.length) {
return this._currentRetry;
}
this._currentRetry = n;
};
/**
* Return the full title generated by recursively concatenating the parent's
* full title.
*
* @api public
* @return {string}
*/
Runnable.prototype.fullTitle = function () {
return this.titlePath().join(' ');
};
/**
* Return the title path generated by concatenating the parent's title path with the title.
*
* @api public
* @return {string}
*/
Runnable.prototype.titlePath = function () {
return this.parent.titlePath().concat([this.title]);
};
/**
* Clear the timeout.
*
* @api private
*/
Runnable.prototype.clearTimeout = function () {
clearTimeout(this.timer);
};
/**
* Inspect the runnable void of private properties.
*
* @api private
* @return {string}
*/
Runnable.prototype.inspect = function () {
return JSON.stringify(this, function (key, val) {
if (key[0] === '_') {
return;
}
if (key === 'parent') {
return '#<Suite>';
}
if (key === 'ctx') {
return '#<Context>';
}
return val;
}, 2);
};
/**
* Reset the timeout.
*
* @api private
*/
Runnable.prototype.resetTimeout = function () {
var self = this;
var ms = this.timeout() || 1e9;
if (!this._enableTimeouts) {
return;
}
this.clearTimeout();
this.timer = setTimeout(function () {
if (!self._enableTimeouts) {
return;
}
self.callback(new Error('Timeout of ' + ms +
'ms exceeded. For async tests and hooks, ensure "done()" is called; if returning a Promise, ensure it resolves.'));
self.timedOut = true;
}, ms);
};
/**
* Set or get a list of whitelisted globals for this test run.
*
* @api private
* @param {string[]} globals
*/
Runnable.prototype.globals = function (globals) {
if (!arguments.length) {
return this._allowedGlobals;
}
this._allowedGlobals = globals;
};
/**
* Run the test and invoke `fn(err)`.
*
* @param {Function} fn
* @api private
*/
Runnable.prototype.run = function (fn) {
var self = this;
var start = new Date();
var ctx = this.ctx;
var finished;
var emitted;
// Sometimes the ctx exists, but it is not runnable
if (ctx && ctx.runnable) {
ctx.runnable(this);
}
// called multiple times
function multiple (err) {
if (emitted) {
return;
}
emitted = true;
self.emit('error', err || new Error('done() called multiple times; stacktrace may be inaccurate'));
}
// finished
function done (err) {
var ms = self.timeout();
if (self.timedOut) {
return;
}
if (finished) {
return multiple(err || self._trace);
}
self.clearTimeout();
self.duration = new Date() - start;
finished = true;
if (!err && self.duration > ms && self._enableTimeouts) {
err = new Error('Timeout of ' + ms +
'ms exceeded. For async tests and hooks, ensure "done()" is called; if returning a Promise, ensure it resolves.');
}
fn(err);
}
// for .resetTimeout()
this.callback = done;
// explicit async with `done` argument
if (this.async) {
this.resetTimeout();
// allows skip() to be used in an explicit async context
this.skip = function asyncSkip () {
done(new Pending('async skip call'));
// halt execution. the Runnable will be marked pending
// by the previous call, and the uncaught handler will ignore
// the failure.
throw new Pending('async skip; aborting execution');
};
if (this.allowUncaught) {
return callFnAsync(this.fn);
}
try {
callFnAsync(this.fn);
} catch (err) {
emitted = true;
done(utils.getError(err));
}
return;
}
if (this.allowUncaught) {
if (this.isPending()) {
done();
} else {
callFn(this.fn);
}
return;
}
// sync or promise-returning
try {
if (this.isPending()) {
done();
} else {
callFn(this.fn);
}
} catch (err) {
emitted = true;
done(utils.getError(err));
}
function callFn (fn) {
var result = fn.call(ctx);
if (result && typeof result.then === 'function') {
self.resetTimeout();
result
.then(function () {
done();
// Return null so libraries like bluebird do not warn about
// subsequently constructed Promises.
return null;
},
function (reason) {
done(reason || new Error('Promise rejected with no or falsy reason'));
});
} else {
if (self.asyncOnly) {
return done(new Error('--async-only option in use without declaring `done()` or returning a promise'));
}
done();
}
}
function callFnAsync (fn) {
var result = fn.call(ctx, function (err) {
if (err instanceof Error || toString.call(err) === '[object Error]') {
return done(err);
}
if (err) {
if (Object.prototype.toString.call(err) === '[object Object]') {
return done(new Error('done() invoked with non-Error: ' +
JSON.stringify(err)));
}
return done(new Error('done() invoked with non-Error: ' + err));
}
if (result && utils.isPromise(result)) {
return done(new Error('Resolution method is overspecified. Specify a callback *or* return a Promise; not both.'));
}
done();
});
}
};