Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add error code for test timeout errors #4506

Merged
merged 1 commit into from Nov 13, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
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