Skip to content

Commit

Permalink
add error code for test timeout errors
Browse files Browse the repository at this point in the history
other:

- addresses some issues with API documentation of errors, featuring an example of how to properly document the `Error` object returned by the `create*` functions in `lib/errors.js`
- adds a missing test for the `id` prop of `Runnable`
  • Loading branch information
boneskull committed Nov 13, 2020
1 parent 6d3fe26 commit c6856ba
Show file tree
Hide file tree
Showing 3 changed files with 122 additions and 20 deletions.
95 changes: 90 additions & 5 deletions lib/errors.js
Expand Up @@ -17,6 +17,7 @@ const emitWarning = (msg, type) => {
if (process.emitWarning) {
process.emitWarning(msg, type);
} else {
/* istanbul ignore next */
process.nextTick(function() {
console.warn(type + ': ' + msg);
});
Expand Down Expand Up @@ -53,88 +54,129 @@ const warn = msg => {
};

/**
* When Mocha throw exceptions (or otherwise errors), it attempts to assign a
* `code` property to the `Error` object, for easier handling. These are the
* potential values of `code`.
* When Mocha throws exceptions (or rejects `Promise`s), it attempts to assign a `code` property to the `Error` object, for easier handling. These are the potential values of `code`.
* @public
* @namespace
* @memberof module:lib/errors
*/
var constants = {
/**
* An unrecoverable error.
* @constant
* @default
*/
FATAL: 'ERR_MOCHA_FATAL',

/**
* The type of an argument to a function call is invalid
* @constant
* @default
*/
INVALID_ARG_TYPE: 'ERR_MOCHA_INVALID_ARG_TYPE',

/**
* The value of an argument to a function call is invalid
* @constant
* @default
*/
INVALID_ARG_VALUE: 'ERR_MOCHA_INVALID_ARG_VALUE',

/**
* Something was thrown, but it wasn't an `Error`
* @constant
* @default
*/
INVALID_EXCEPTION: 'ERR_MOCHA_INVALID_EXCEPTION',

/**
* An interface (e.g., `Mocha.interfaces`) is unknown or invalid
* @constant
* @default
*/
INVALID_INTERFACE: 'ERR_MOCHA_INVALID_INTERFACE',

/**
* A reporter (.e.g, `Mocha.reporters`) is unknown or invalid
* @constant
* @default
*/
INVALID_REPORTER: 'ERR_MOCHA_INVALID_REPORTER',

/**
* `done()` was called twice in a `Test` or `Hook` callback
* @constant
* @default
*/
MULTIPLE_DONE: 'ERR_MOCHA_MULTIPLE_DONE',

/**
* No files matched the pattern provided by the user
* @constant
* @default
*/
NO_FILES_MATCH_PATTERN: 'ERR_MOCHA_NO_FILES_MATCH_PATTERN',

/**
* Known, but unsupported behavior of some kind
* @constant
* @default
*/
UNSUPPORTED: 'ERR_MOCHA_UNSUPPORTED',

/**
* Invalid state transition occurring in `Mocha` instance
* @constant
* @default
*/
INSTANCE_ALREADY_RUNNING: 'ERR_MOCHA_INSTANCE_ALREADY_RUNNING',

/**
* Invalid state transition occurring in `Mocha` instance
* @constant
* @default
*/
INSTANCE_ALREADY_DISPOSED: 'ERR_MOCHA_INSTANCE_ALREADY_DISPOSED',

/**
* Use of `only()` w/ `--forbid-only` results in this error.
* @constant
* @default
*/
FORBIDDEN_EXCLUSIVITY: 'ERR_MOCHA_FORBIDDEN_EXCLUSIVITY',

/**
* To be thrown when a user-defined plugin implementation (e.g., `mochaHooks`) is invalid
* @constant
* @default
*/
INVALID_PLUGIN_IMPLEMENTATION: 'ERR_MOCHA_INVALID_PLUGIN_IMPLEMENTATION',

/**
* To be thrown when a builtin or third-party plugin definition (the _definition_ of `mochaHooks`) is invalid
* @constant
* @default
*/
INVALID_PLUGIN_DEFINITION: 'ERR_MOCHA_INVALID_PLUGIN_DEFINITION'
INVALID_PLUGIN_DEFINITION: 'ERR_MOCHA_INVALID_PLUGIN_DEFINITION',

/**
* When a runnable exceeds its allowed run time.
* @constant
* @default
*/
TIMEOUT: 'ERR_MOCHA_TIMEOUT'
};

/**
* A set containing all string values of all Mocha error constants, for use by {@link isMochaError}.
* @private
*/
const MOCHA_ERRORS = new Set(Object.values(constants));

/**
* Creates an error object to be thrown when no files to be tested could be found using specified pattern.
*
* @public
* @static
* @param {string} message - Error message to be displayed.
* @param {string} pattern - User-specified argument value.
* @returns {Error} instance detailing the error condition
Expand Down Expand Up @@ -165,6 +207,7 @@ function createInvalidReporterError(message, reporter) {
* Creates an error object to be thrown when the interface specified in the options was not found.
*
* @public
* @static
* @param {string} message - Error message to be displayed.
* @param {string} ui - User-specified interface value.
* @returns {Error} instance detailing the error condition
Expand All @@ -180,6 +223,7 @@ function createInvalidInterfaceError(message, ui) {
* Creates an error object to be thrown when a behavior, option, or parameter is unsupported.
*
* @public
* @static
* @param {string} message - Error message to be displayed.
* @returns {Error} instance detailing the error condition
*/
Expand All @@ -193,6 +237,7 @@ function createUnsupportedError(message) {
* Creates an error object to be thrown when an argument is missing.
*
* @public
* @static
* @param {string} message - Error message to be displayed.
* @param {string} argument - Argument name.
* @param {string} expected - Expected argument datatype.
Expand All @@ -206,6 +251,7 @@ function createMissingArgumentError(message, argument, expected) {
* Creates an error object to be thrown when an argument did not use the supported type
*
* @public
* @static
* @param {string} message - Error message to be displayed.
* @param {string} argument - Argument name.
* @param {string} expected - Expected argument datatype.
Expand All @@ -224,6 +270,7 @@ function createInvalidArgumentTypeError(message, argument, expected) {
* Creates an error object to be thrown when an argument did not use the supported value
*
* @public
* @static
* @param {string} message - Error message to be displayed.
* @param {string} argument - Argument name.
* @param {string} value - Argument value.
Expand All @@ -243,6 +290,7 @@ function createInvalidArgumentValueError(message, argument, value, reason) {
* Creates an error object to be thrown when an exception was caught, but the `Error` is falsy or undefined.
*
* @public
* @static
* @param {string} message - Error message to be displayed.
* @returns {Error} instance detailing the error condition
*/
Expand All @@ -258,6 +306,7 @@ function createInvalidExceptionError(message, value) {
* Creates an error object to be thrown when an unrecoverable error occurs.
*
* @public
* @static
* @param {string} message - Error message to be displayed.
* @returns {Error} instance detailing the error condition
*/
Expand All @@ -276,6 +325,7 @@ function createFatalError(message, value) {
* @param {string} [pluginId] - Name/path of plugin, if any
* @throws When `pluginType` is not known
* @public
* @static
* @returns {Error}
*/
function createInvalidLegacyPluginError(message, pluginType, pluginId) {
Expand All @@ -297,6 +347,7 @@ function createInvalidLegacyPluginError(message, pluginType, pluginId) {
* @param {string} [pluginId] - Name/path of plugin, if any
* @throws When `pluginType` is not known
* @public
* @static
* @returns {Error}
*/
function createInvalidPluginError(...args) {
Expand All @@ -309,6 +360,7 @@ function createInvalidPluginError(...args) {
* @param {string} message The error message to be displayed.
* @param {boolean} cleanReferencesAfterRun the value of `cleanReferencesAfterRun`
* @param {Mocha} instance the mocha instance that throw this error
* @static
*/
function createMochaInstanceAlreadyDisposedError(
message,
Expand All @@ -325,6 +377,8 @@ function createMochaInstanceAlreadyDisposedError(
/**
* Creates an error object to be thrown when a mocha object's `run` method is called while a test run is in progress.
* @param {string} message The error message to be displayed.
* @static
* @public
*/
function createMochaInstanceAlreadyRunningError(message, instance) {
var err = new Error(message);
Expand All @@ -333,13 +387,14 @@ function createMochaInstanceAlreadyRunningError(message, instance) {
return err;
}

/*
/**
* Creates an error object to be thrown when done() is called multiple times in a test
*
* @public
* @param {Runnable} runnable - Original runnable
* @param {Error} [originalErr] - Original error, if any
* @returns {Error} instance detailing the error condition
* @static
*/
function createMultipleDoneError(runnable, originalErr) {
var title;
Expand Down Expand Up @@ -373,6 +428,7 @@ function createMultipleDoneError(runnable, originalErr) {
/**
* Creates an error object to be thrown when `.only()` is used with
* `--forbid-only`.
* @static
* @public
* @param {Mocha} mocha - Mocha instance
* @returns {Error} Error with code {@link constants.FORBIDDEN_EXCLUSIVITY}
Expand All @@ -389,6 +445,7 @@ function createForbiddenExclusivityError(mocha) {

/**
* Creates an error object to be thrown when a plugin definition is invalid
* @static
* @param {string} msg - Error message
* @param {PluginDefinition} [pluginDef] - Problematic plugin definition
* @public
Expand All @@ -403,6 +460,7 @@ function createInvalidPluginDefinitionError(msg, pluginDef) {

/**
* Creates an error object to be thrown when a plugin implementation (user code) is invalid
* @static
* @param {string} msg - Error message
* @param {Object} [opts] - Plugin definition and user-supplied implementation
* @param {PluginDefinition} [opts.pluginDef] - Plugin Definition
Expand All @@ -421,9 +479,26 @@ function createInvalidPluginImplementationError(
return err;
}

/**
* Creates an error object to be thrown when a runnable exceeds its allowed run time.
* @static
* @param {string} msg - Error message
* @param {number} [timeout] - Timeout in ms
* @param {string} [file] - File, if given
* @returns {MochaTimeoutError}
*/
function createTimeoutError(msg, timeout, file) {
const err = new Error(msg);
err.code = constants.TIMEOUT;
err.timeout = timeout;
err.file = file;
return err;
}

/**
* Returns `true` if an error came out of Mocha.
* _Can suffer from false negatives, but not false positives._
* @static
* @public
* @param {*} err - Error, or anything
* @returns {boolean}
Expand All @@ -449,8 +524,18 @@ module.exports = {
createMochaInstanceAlreadyRunningError,
createMultipleDoneError,
createNoFilesMatchPatternError,
createTimeoutError,
createUnsupportedError,
deprecate,
isMochaError,
warn
};

/**
* The error thrown when a Runnable times out
* @memberof module:lib/errors
* @typedef {Error} MochaTimeoutError
* @property {constants.TIMEOUT} code - Error code
* @property {number?} timeout Timeout in ms
* @property {string?} file Filepath, if given
*/
15 changes: 7 additions & 8 deletions lib/runnable.js
Expand Up @@ -5,9 +5,11 @@ var Pending = require('./pending');
var debug = require('debug')('mocha:runnable');
var milliseconds = require('ms');
var utils = require('./utils');
var errors = require('./errors');
var createInvalidExceptionError = errors.createInvalidExceptionError;
var createMultipleDoneError = errors.createMultipleDoneError;
const {
createInvalidExceptionError,
createMultipleDoneError,
createTimeoutError
} = require('./errors');

/**
* Save timer references to avoid Sinon interfering (see GH-237).
Expand Down Expand Up @@ -422,14 +424,11 @@ Runnable.prototype.run = function(fn) {
* @private
*/
Runnable.prototype._timeoutError = function(ms) {
var msg =
'Timeout of ' +
ms +
'ms exceeded. For async tests and hooks, ensure "done()" is called; if returning a Promise, ensure it resolves.';
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 new Error(msg);
return createTimeoutError(msg, ms, this.file);
};

var constants = utils.defineConstants(
Expand Down

0 comments on commit c6856ba

Please sign in to comment.