@academyjs/rover
Version:
Rover allows you to learn programming interactively.
426 lines • 14 kB
JavaScript
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
__setModuleDefault(result, mod);
return result;
};
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const events_1 = require("events");
const pending_1 = __importDefault(require("./pending"));
const ms_1 = __importDefault(require("ms"));
const utils = __importStar(require("./utils"));
const errors_1 = require("./errors");
/**
* Save timer references to avoid Sinon interfering (see GH-237).
* @private
*/
const { Date, setTimeout, clearTimeout } = global;
const toString = Object.prototype.toString;
const constants = utils.defineConstants(
/**
* {@link Runnable}-related constants.
* @public
* @memberof Runnable
* @readonly
* @static
* @alias constants
* @enum {string}
*/
{
/**
* Value of `state` prop when a `Runnable` has failed
*/
STATE_FAILED: "failed",
/**
* Value of `state` prop when a `Runnable` has passed
*/
STATE_PASSED: "passed",
/**
* Value of `state` prop when a `Runnable` has been skipped by user
*/
STATE_PENDING: "pending",
});
class Runnable extends events_1.EventEmitter {
/**
* Initialize a new `Runnable` with the given `title` and callback `fn`.
*
* @class
* @extends external:EventEmitter
* @public
* @param {String} title
* @param {Function} fn
*/
constructor(title, description, fn) {
super();
/**
* Set or get current retry
*
* @private
*/
this.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.
*
* @memberof Mocha.Runnable
* @public
* @return {string}
*/
this.fullTitle = function () {
return this.titlePath().join(" ");
};
/**
* Return the title path generated by concatenating the parent's title path with the title.
*
* @memberof Mocha.Runnable
* @public
* @return {string}
*/
this.titlePath = function () {
return this.parent.titlePath().concat([this.title]);
};
/**
* Clear the timeout.
*
* @private
*/
this.clearTimeout = function () {
clearTimeout(this.timer);
};
/**
* Reset the timeout.
*
* @private
*/
this.resetTimeout = function () {
var self = this;
var ms = this.timeout();
if (ms === 0) {
return;
}
this.clearTimeout();
this.timer = setTimeout(function () {
if (self.timeout() === 0) {
return;
}
self.callback(self._timeoutError(ms));
self.timedOut = true;
}, ms);
};
/**
* Set or get a list of whitelisted globals for this test run.
*
* @private
* @param {string[]} globals
*/
this.globals = function (globals) {
if (!arguments.length) {
return this._allowedGlobals;
}
this._allowedGlobals = globals;
};
/**
* Run the test and invoke `fn(err)`.
*
* @param {Function} fn
* @private
*/
this.run = function (fn) {
var self = this;
var start = new Date();
var ctx = this.ctx;
var finished;
var errorWasHandled = false;
if (this.isPending())
return fn();
// Sometimes the ctx exists, but it is not runnable
if (ctx && ctx.runnable) {
ctx.runnable(this);
}
// called multiple times
function multiple(err) {
if (errorWasHandled) {
return;
}
errorWasHandled = true;
self.emit("error", (0, errors_1.createMultipleDoneError)(self, err));
}
// finished
function done(err) {
var ms = self.timeout();
if (self.timedOut) {
return;
}
if (finished) {
return multiple(err);
}
self.clearTimeout();
self.duration = new Date() - start;
finished = true;
if (!err && self.duration > ms && ms > 0) {
err = self._timeoutError(ms);
}
fn(err);
}
// for .resetTimeout() and Runner#uncaught()
this.callback = done;
if (this.fn && typeof this.fn.call !== "function") {
done(new TypeError("A runnable must be passed a function as its second argument."));
return;
}
// explicit async with `done` argument
if (this.async) {
this.resetTimeout();
// allows skip() to be used in an explicit async context
this.skip = function asyncSkip() {
this.pending = true;
done();
// halt execution, the uncaught handler will ignore the failure.
throw new pending_1.default("async skip; aborting execution");
};
try {
callFnAsync(this.fn);
}
catch (err) {
// handles async runnables which actually run synchronously
errorWasHandled = true;
if (err instanceof pending_1.default) {
return; // done() is already called in this.skip()
}
else if (this.allowUncaught) {
throw err;
}
done(Runnable.toValueOrError(err));
}
return;
}
// sync or promise-returning
try {
callFn(this.fn);
}
catch (err) {
errorWasHandled = true;
if (err instanceof pending_1.default) {
return done();
}
else if (this.allowUncaught) {
throw err;
}
done(Runnable.toValueOrError(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();
});
}
};
/**
* Instantiates a "timeout" error
*
* @param {number} ms - Timeout (in milliseconds)
* @returns {Error} a "timeout" error
* @private
*/
this._timeoutError = function (ms) {
let msg = `Timeout of ${ms}ms exceeded. For async tests and hooks, ensure "done()" is called; if returning a Promise, ensure it resolves.`;
if (this.file) {
msg += " (" + this.file + ")";
}
return (0, errors_1.createTimeoutError)(msg, ms, this.file);
};
this.title = title;
this.description = description;
this.fn = fn;
this.body = (fn || "").toString();
this.async = fn && fn.length;
this.sync = !this.async;
this._timeout = 2000;
this._slow = 75;
this._retries = -1;
utils.assignNewMochaID(this);
Object.defineProperty(this, "id", {
get() {
return utils.getMochaID(this);
},
});
this.reset();
}
/**
* Resets the state initially or for a next run.
*/
reset() {
this.timedOut = false;
this._currentRetry = 0;
this.pending = false;
delete this.state;
delete this.err;
}
/**
* Get current timeout value in msecs.
*
* @private
* @returns {number} current timeout threshold value
*/
/**
* @summary
* Set timeout threshold value (msecs).
*
* @description
* A string argument can use shorthand (e.g., "2s") and will be converted.
* The value will be clamped to range [<code>0</code>, <code>2^<sup>31</sup>-1</code>].
* If clamped value matches either range endpoint, timeouts will be disabled.
*
* @private
* @see {@link https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/setTimeout#Maximum_delay_value}
* @param {number|string} ms - Timeout threshold value.
* @returns {Runnable} this
* @chainable
*/
timeout(ms) {
if (!arguments.length) {
return this._timeout;
}
if (typeof ms === "string") {
ms = (0, ms_1.default)(ms);
}
// Clamp to range
var INT_MAX = Math.pow(2, 31) - 1;
var range = [0, INT_MAX];
ms = utils.clamp(ms, range);
// see #1652 for reasoning
if (ms === range[0] || ms === range[1]) {
this._timeout = 0;
}
else {
this._timeout = ms;
}
if (this.timer) {
this.resetTimeout();
}
return this;
}
/**
* Set or get slow `ms`.
*
* @private
* @param {number|string} ms
* @return {Runnable|number} ms or Runnable instance.
*/
slow(ms) {
if (!arguments.length || typeof ms === "undefined") {
return this._slow;
}
if (typeof ms === "string") {
ms = (0, ms_1.default)(ms);
}
this._slow = ms;
return this;
}
/**
* Halt and mark as pending.
*/
skip() {
this.pending = true;
throw new pending_1.default("sync skip; aborting execution");
}
/**
* Check if this runnable or its parent suite is marked as pending.
*
* @private
*/
isPending() {
return this.pending || (this.parent && this.parent.isPending());
}
/**
* Return `true` if this Runnable has failed.
*/
isFailed() {
return !this.isPending() && this.state === constants.STATE_FAILED;
}
/**
* Return `true` if this Runnable has passed.
*/
isPassed() {
return !this.isPending() && this.state === constants.STATE_PASSED;
}
/**
* Set or get number of retries.
*/
retries(n) {
if (!arguments.length) {
return this._retries;
}
this._retries = n;
}
}
Runnable.constants = constants;
/**
* Given `value`, return identity if truthy, otherwise create an "invalid exception" error and return that.
* @param {*} [value] - Value to return, if present
* @returns {*|Error} `value`, otherwise an `Error`
* @private
*/
Runnable.toValueOrError = function (value) {
return (value ||
(0, errors_1.createInvalidExceptionError)("Runnable failed with falsy or undefined exception. Please throw an Error instead.", value));
};
exports.default = Runnable;
//# sourceMappingURL=runnable.js.map