From 02514bc0e977c7888c8f5f44d04a8a6d6d9d14fd Mon Sep 17 00:00:00 2001 From: Craig Taub Date: Tue, 21 Aug 2018 12:18:48 +0100 Subject: [PATCH 01/17] add MochaError and use add missing E add codes to docs how to add new error E_ to ERR_ typo fix fix failing test typo fix --- docs/index.md | 30 ++++++++++++++++++++++++++++++ lib/error.js | 13 +++++++++++++ lib/interfaces/common.js | 6 ++++-- lib/mocha.js | 11 +++++++++-- lib/reporters/xunit.js | 6 +++++- lib/suite.js | 6 ++++-- lib/test.js | 6 ++++-- lib/utils.js | 16 +++++++++++----- test/reporters/helpers.js | 7 +++++-- 9 files changed, 85 insertions(+), 16 deletions(-) create mode 100644 lib/error.js diff --git a/docs/index.md b/docs/index.md index d8a76a8a8d..b162f14b9f 100644 --- a/docs/index.md +++ b/docs/index.md @@ -73,6 +73,7 @@ Mocha is a feature-rich JavaScript test framework running on [Node.js](https://n - [Configuring Mocha (Node.js)](#configuring-mocha-nodejs) - [`mocha.opts`](#mochaopts) - [The `test/` Directory](#the-test-directory) +- [Error Codes](#error-codes) - [Editor Plugins](#editor-plugins) - [Examples](#examples) - [Testing Mocha](#testing-mocha) @@ -1658,6 +1659,35 @@ $ mocha "./spec/**/*.js" *Note*: Double quotes around the glob are recommended for portability. +## Error Codes + +List of codes associated with Errors thrown inside Mocha. Following NodeJS practices. + +| Code | Meaning | +| ------------- | ------------- | +| ERR_CANNOT_RESOLVE_PATH | file/s of test not found | +| ERR_INVALID_REPORTER | reporter from options not found | +| ERR_INVALID_INTERFACE | interface from options not found | +| ERR_EXTENSIONS_REQUIRED | filepath given is a dir, extension parameter not found | +| ERR_OUTPUT_NOT_SUPPORTED | type of output not supported in the browser | +| ERR_RUNNER_STRING_NOT_SUPPORTED | runner specified is not supported or found | +| ERR_MISSING_CALLBACK | failure creating suite as no callback was found | +| ERR_TITLE_NOT_STRING | failure creating suite as title specified is not a string | +| ERR_UNDEFINED_ERROR | an error was thrown but no details were specified | + +To introduce a new Error please follow below. + +```javascript +// import custom error object +var MochaError = require('./error'); + +// throw inside block with catch +throw new MochaError( + 'Our error message' + 'ERR_ERROR_CODE' +); +``` + ## Editor Plugins The following editor-related packages are available: diff --git a/lib/error.js b/lib/error.js new file mode 100644 index 0000000000..a3d1a55e03 --- /dev/null +++ b/lib/error.js @@ -0,0 +1,13 @@ +'use strict'; + +function MochaError(message, code) { + this.name = 'MochaError'; + this.message = message; + this.code = code; + var error = new Error(this.message); + error.name = this.name; + this.stack = error.stack; +} +MochaError.prototype = Error.prototype; + +module.exports = MochaError; diff --git a/lib/interfaces/common.js b/lib/interfaces/common.js index 3c6dffa96f..5045abf8f2 100644 --- a/lib/interfaces/common.js +++ b/lib/interfaces/common.js @@ -2,6 +2,7 @@ var Suite = require('../suite'); var utils = require('../utils'); +var MochaError = require('../error'); /** * Functions common to more than one interface. @@ -147,11 +148,12 @@ module.exports = function(suites, context, mocha) { } suites.shift(); } else if (typeof opts.fn === 'undefined' && !suite.pending) { - throw new Error( + throw new MochaError( 'Suite "' + suite.fullTitle() + '" was defined but no callback was supplied. ' + - 'Supply a callback or explicitly skip the suite.' + 'Supply a callback or explicitly skip the suite.', + 'ERR_MISSING_CALLBACK' ); } else if (!opts.fn && suite.pending) { suites.shift(); diff --git a/lib/mocha.js b/lib/mocha.js index cce2bb9b2f..3f84f00abe 100644 --- a/lib/mocha.js +++ b/lib/mocha.js @@ -10,6 +10,7 @@ var escapeRe = require('escape-string-regexp'); var path = require('path'); var builtinReporters = require('./reporters'); var utils = require('./utils'); +var MochaError = require('./error'); var mocharc = require('./mocharc.json'); var assign = require('object.assign').getPolyfill(); @@ -249,7 +250,10 @@ Mocha.prototype.reporter = function(reporter, reporterOptions) { ); } if (!_reporter) { - throw new Error('invalid reporter "' + reporter + '"'); + throw new MochaError( + 'invalid reporter "' + reporter + '"', + 'ERR_INVALID_REPORTER' + ); } this._reporter = _reporter; } @@ -275,7 +279,10 @@ Mocha.prototype.ui = function(name) { try { this._ui = require(name); } catch (err) { - throw new Error('invalid interface "' + name + '"'); + throw new MochaError( + 'invalid interface "' + name + '"', + 'ERR_INVALID_INTERFACE' + ); } } this._ui = this._ui(this.suite); diff --git a/lib/reporters/xunit.js b/lib/reporters/xunit.js index f559207b18..2e55a068a2 100644 --- a/lib/reporters/xunit.js +++ b/lib/reporters/xunit.js @@ -13,6 +13,7 @@ var fs = require('fs'); var escape = utils.escape; var mkdirp = require('mkdirp'); var path = require('path'); +var MochaError = require('../error'); /** * Save timer references to avoid Sinon interfering (see GH-237). @@ -50,7 +51,10 @@ function XUnit(runner, options) { if (options && options.reporterOptions) { if (options.reporterOptions.output) { if (!fs.createWriteStream) { - throw new Error('file output not supported in browser'); + throw new MochaError( + 'file output not supported in browser', + 'ERR_OUTPUT_NOT_SUPPORTED' + ); } mkdirp.sync(path.dirname(options.reporterOptions.output)); diff --git a/lib/suite.js b/lib/suite.js index 482a47925b..60b3d36659 100644 --- a/lib/suite.js +++ b/lib/suite.js @@ -12,6 +12,7 @@ var utils = require('./utils'); var inherits = utils.inherits; var debug = require('debug')('mocha:suite'); var milliseconds = require('ms'); +var MochaError = require('./error'); /** * Expose `Suite`. @@ -49,10 +50,11 @@ exports.create = function(parent, title) { */ function Suite(title, parentContext) { if (!utils.isString(title)) { - throw new Error( + throw new MochaError( 'Suite `title` should be a "string" but "' + typeof title + - '" was given instead.' + '" was given instead.', + 'ERR_TITLE_NOT_STRING' ); } this.title = title; diff --git a/lib/test.js b/lib/test.js index d8233a3571..42e9b76a1f 100644 --- a/lib/test.js +++ b/lib/test.js @@ -1,6 +1,7 @@ 'use strict'; var Runnable = require('./runnable'); var utils = require('./utils'); +var MochaError = require('./error'); var isString = utils.isString; module.exports = Test; @@ -15,10 +16,11 @@ module.exports = Test; */ function Test(title, fn) { if (!isString(title)) { - throw new Error( + throw new MochaError( 'Test `title` should be a "string" but "' + typeof title + - '" was given instead.' + '" was given instead.', + 'ERR_TITLE_NOT_STRING' ); } Runnable.call(this, title, fn); diff --git a/lib/utils.js b/lib/utils.js index 71aa7804e8..0a978772d0 100644 --- a/lib/utils.js +++ b/lib/utils.js @@ -14,6 +14,7 @@ var glob = require('glob'); var path = require('path'); var join = path.join; var he = require('he'); +var MochaError = require('./error'); /** * Ignored directories. @@ -515,7 +516,10 @@ exports.lookupFiles = function lookupFiles(filepath, extensions, recursive) { } else { files = glob.sync(filepath); if (!files.length) { - throw new Error("cannot resolve path (or pattern) '" + filepath + "'"); + throw new MochaError( + "cannot resolve path (or pattern) '" + filepath + "'", + 'ERR_CANNOT_RESOLVE_PATH' + ); } return files; } @@ -546,8 +550,9 @@ exports.lookupFiles = function lookupFiles(filepath, extensions, recursive) { return; } if (!extensions) { - throw new Error( - 'extensions parameter required when filepath is a directory' + throw new MochaError( + 'extensions parameter required when filepath is a directory', + 'ERR_EXTENSIONS_REQUIRED' ); } var re = new RegExp('\\.(?:' + extensions.join('|') + ')$'); @@ -567,8 +572,9 @@ exports.lookupFiles = function lookupFiles(filepath, extensions, recursive) { */ exports.undefinedError = function() { - return new Error( - 'Caught undefined error, did you throw without specifying what?' + return new MochaError( + 'Caught undefined error, did you throw without specifying what?', + 'ERR_UNDEFINED_ERROR' ); }; diff --git a/test/reporters/helpers.js b/test/reporters/helpers.js index bd49392a40..9712194df0 100644 --- a/test/reporters/helpers.js +++ b/test/reporters/helpers.js @@ -1,5 +1,7 @@ 'use strict'; +var MochaError = require('../../lib/error'); + /* This function prevents the constant use of creating a runnerEvent. runStr is the argument that defines the runnerEvent. @@ -116,8 +118,9 @@ function createRunnerFunction(runStr, ifStr1, ifStr2, ifStr3, arg1, arg2) { } }; default: - throw new Error( - 'This function does not support the runner string specified.' + throw new MochaError( + 'This function does not support the runner string specified.', + 'ERR_RUNNER_STRING_NOT_SUPPORTED' ); } } From 346aeeeabda155155090bac8e422b7da6b7b74c4 Mon Sep 17 00:00:00 2001 From: Craig Taub Date: Sat, 8 Sep 2018 13:13:14 +0100 Subject: [PATCH 02/17] single custom object to multiple custom objects remove generic MochaError Use instance over code lint fix lint fix update references to suffix Error mocfix rebase --- docs/index.md | 28 ++--- lib/cli/run-helpers.js | 8 +- lib/error.js | 13 --- lib/errors.js | 162 ++++++++++++++++++++++++++++ lib/interfaces/common.js | 8 +- lib/mocha.js | 14 +-- lib/reporters/xunit.js | 9 +- lib/suite.js | 6 +- lib/test.js | 6 +- lib/utils.js | 22 ++-- test/integration/file-utils.spec.js | 10 +- test/integration/suite.spec.js | 3 +- test/reporters/helpers.js | 9 +- 13 files changed, 212 insertions(+), 86 deletions(-) delete mode 100644 lib/error.js create mode 100644 lib/errors.js diff --git a/docs/index.md b/docs/index.md index b162f14b9f..bdb4116184 100644 --- a/docs/index.md +++ b/docs/index.md @@ -1665,29 +1665,15 @@ List of codes associated with Errors thrown inside Mocha. Following NodeJS pract | Code | Meaning | | ------------- | ------------- | -| ERR_CANNOT_RESOLVE_PATH | file/s of test not found | -| ERR_INVALID_REPORTER | reporter from options not found | -| ERR_INVALID_INTERFACE | interface from options not found | -| ERR_EXTENSIONS_REQUIRED | filepath given is a dir, extension parameter not found | -| ERR_OUTPUT_NOT_SUPPORTED | type of output not supported in the browser | -| ERR_RUNNER_STRING_NOT_SUPPORTED | runner specified is not supported or found | -| ERR_MISSING_CALLBACK | failure creating suite as no callback was found | -| ERR_TITLE_NOT_STRING | failure creating suite as title specified is not a string | +| ERR_MOCHA_NO_FILES_MATCH_PATTERN | file/s of test could not be found | +| ERR_MOCHA_MISSING_CALLBACK | callback for a running suite was not found | +| ERR_MOCHA_INVALID_REPORTER | reporter specified in options not found | +| ERR_MOCHA_INVALID_INTERFACE | interface specified in options not found | +| ERR_MOCHA_NOT_SUPPORTED | type of output specified was not supported | +| ERR_MOCHA_INVALID_ARG_VALUE | an invalid argument value was found | +| ERR_MOCHA_MISSING_ARGUMENT | an argument was missing | | ERR_UNDEFINED_ERROR | an error was thrown but no details were specified | -To introduce a new Error please follow below. - -```javascript -// import custom error object -var MochaError = require('./error'); - -// throw inside block with catch -throw new MochaError( - 'Our error message' - 'ERR_ERROR_CODE' -); -``` - ## Editor Plugins The following editor-related packages are available: diff --git a/lib/cli/run-helpers.js b/lib/cli/run-helpers.js index 3afa7143d3..e1a71aa453 100644 --- a/lib/cli/run-helpers.js +++ b/lib/cli/run-helpers.js @@ -15,6 +15,8 @@ const utils = require('../utils'); const minimatch = require('minimatch'); const ansi = require('ansi-colors'); const symbols = require('log-symbols'); +const errors = require('../errors'); +const NoFilesMatchPatternError = errors.NoFilesMatchPatternError; const cwd = (exports.cwd = process.cwd()); @@ -143,10 +145,8 @@ exports.handleFiles = ({ try { newFiles = utils.lookupFiles(arg, extension, recursive); } catch (err) { - if (err.message.indexOf('cannot resolve path') === 0) { - console.error( - `Warning: Could not find any test files matching pattern: ${arg}` - ); + if (err instanceof NoFilesMatchPatternError) { + console.warn(`Warning: ${err.message}: ${err.pattern}`); return; } diff --git a/lib/error.js b/lib/error.js deleted file mode 100644 index a3d1a55e03..0000000000 --- a/lib/error.js +++ /dev/null @@ -1,13 +0,0 @@ -'use strict'; - -function MochaError(message, code) { - this.name = 'MochaError'; - this.message = message; - this.code = code; - var error = new Error(this.message); - error.name = this.name; - this.stack = error.stack; -} -MochaError.prototype = Error.prototype; - -module.exports = MochaError; diff --git a/lib/errors.js b/lib/errors.js new file mode 100644 index 0000000000..73e30aeb62 --- /dev/null +++ b/lib/errors.js @@ -0,0 +1,162 @@ +'use strict'; +/** + * @module Custom Errors + */ +/** + * Custom Errors + */ + +/** + * @summary + * File/s of test could not be found. + * + * @public + * @example + * // import custom errors + * const errors = require('./errors'); + * const ErrorInvalidArgValue = errors.ErrorInvalidArgValue; + * + * throw new ErrorInvalidArgValue('Our error message'); + * + * // to use it: + * const errors = require('./errors'); + * const NoFilesMatchPatternError = errors.NoFilesMatchPatternError + * + * if (someError instanceof NoFilesMatchPatternError) { + * // ... + * } + */ +function NoFilesMatchPatternError(message, pattern) { + this.name = 'NoFilesMatchPatternError'; + this.code = 'ERR_MOCHA_NO_FILES_MATCH_PATTERN'; + this.pattern = pattern; + this.message = message; + Error.captureStackTrace(this, NoFilesMatchPatternError); +} +NoFilesMatchPatternError.prototype = Object.create(Error.prototype); +NoFilesMatchPatternError.prototype.name = NoFilesMatchPatternError.name; +NoFilesMatchPatternError.prototype.constructor = NoFilesMatchPatternError; + +/** + * @summary + * Callback for a running suite was not found. + * + * @public + */ +function MissingCallbackError(message) { + this.name = 'MissingCallbackError'; + this.code = 'ERR_MOCHA_MISSING_CALLBACK'; + this.message = message; + Error.captureStackTrace(this, MissingCallbackError); +} +MissingCallbackError.prototype = Object.create(Error.prototype); +MissingCallbackError.prototype.name = MissingCallbackError.name; +MissingCallbackError.prototype.constructor = MissingCallbackError; + +/** + * @summary + * Reporter specified in options not found. + * + * @public + */ +function InvalidReporterError(message) { + this.name = 'InvalidReporterError'; + this.code = 'ERR_MOCHA_INVALID_REPORTER'; + this.message = message; + Error.captureStackTrace(this, InvalidReporterError); +} +InvalidReporterError.prototype = Object.create(Error.prototype); +InvalidReporterError.prototype.name = InvalidReporterError.name; +InvalidReporterError.prototype.constructor = InvalidReporterError; + +/** + * @summary + * Interface specified in options not found. + * + * @public + */ +function InvalidInterfaceError(message) { + this.name = 'InvalidInterfaceError'; + this.code = 'ERR_MOCHA_INVALID_INTERFACE'; + this.message = message; + Error.captureStackTrace(this, InvalidInterfaceError); +} +InvalidInterfaceError.prototype = Object.create(Error.prototype); +InvalidInterfaceError.prototype.name = InvalidInterfaceError.name; +InvalidInterfaceError.prototype.constructor = InvalidInterfaceError; + +/** + * @summary + * Type of output specified was not supported. + * + * @public + */ +function NotSupportedError(message) { + this.name = 'NotSupportedError'; + this.code = 'ERR_MOCHA_NOT_SUPPORTED'; + this.message = message; + Error.captureStackTrace(this, NotSupportedError); +} +NotSupportedError.prototype = Object.create(Error.prototype); +NotSupportedError.prototype.name = NotSupportedError.name; +NotSupportedError.prototype.constructor = NotSupportedError; + +/** + * @summary + * An invalid argument value was found. + * + * @public + */ +function InvalidArgumentValueError(message) { + this.name = 'InvalidArgumentValueError'; + this.code = 'ERR_MOCHA_INVALID_ARG_VALUE'; + this.message = message; + Error.captureStackTraceError(this, InvalidArgumentValueError); +} +InvalidArgumentValueError.prototype = Object.create(Error.prototype); +InvalidArgumentValueError.prototype.name = InvalidArgumentValueError.name; +InvalidArgumentValueError.prototype.constructor = InvalidArgumentValueError; + +/** + * @summary + * An argument was missing. + * + * @public + */ +function MissingArgumentError(message, argument) { + this.name = 'MissingArgumentError'; + this.code = 'ERR_MOCHA_MISSING_ARGUMENT'; + this.argument = argument; + this.message = message; + Error.captureStackTrace(this, MissingArgumentError); +} +MissingArgumentError.prototype = Object.create(Error.prototype); +MissingArgumentError.prototype.name = MissingArgumentError.name; +MissingArgumentError.prototype.constructor = MissingArgumentError; + +/** + * @summary + * An error was thrown but no details were specified. + * + * @public + */ +function UndefinedError(message) { + this.name = 'UndefinedError'; + this.code = 'ERR_MOCHA_UNDEFINED_ERROR'; + this.message = message; + Error.captureStackTrace(this, UndefinedError); +} +UndefinedError.prototype = Object.create(Error.prototype); +UndefinedError.prototype.name = UndefinedError.name; +UndefinedError.prototype.constructor = UndefinedError; + +module.exports = { + NoFilesMatchPatternError: NoFilesMatchPatternError, + MissingCallbackError: MissingCallbackError, + InvalidReporterError: InvalidReporterError, + InvalidInterfaceError: InvalidInterfaceError, + NotSupportedError: NotSupportedError, + InvalidArgumentValueError: InvalidArgumentValueError, + MissingArgumentError: MissingArgumentError, + UndefinedError: UndefinedError +}; diff --git a/lib/interfaces/common.js b/lib/interfaces/common.js index 5045abf8f2..a864da5d0c 100644 --- a/lib/interfaces/common.js +++ b/lib/interfaces/common.js @@ -2,7 +2,8 @@ var Suite = require('../suite'); var utils = require('../utils'); -var MochaError = require('../error'); +var errors = require('../errors'); +var MissingCallbackError = errors.MissingCallbackError; /** * Functions common to more than one interface. @@ -148,12 +149,11 @@ module.exports = function(suites, context, mocha) { } suites.shift(); } else if (typeof opts.fn === 'undefined' && !suite.pending) { - throw new MochaError( + throw new MissingCallbackError( 'Suite "' + suite.fullTitle() + '" was defined but no callback was supplied. ' + - 'Supply a callback or explicitly skip the suite.', - 'ERR_MISSING_CALLBACK' + 'Supply a callback or explicitly skip the suite.' ); } else if (!opts.fn && suite.pending) { suites.shift(); diff --git a/lib/mocha.js b/lib/mocha.js index 3f84f00abe..dd505cd26a 100644 --- a/lib/mocha.js +++ b/lib/mocha.js @@ -10,7 +10,9 @@ var escapeRe = require('escape-string-regexp'); var path = require('path'); var builtinReporters = require('./reporters'); var utils = require('./utils'); -var MochaError = require('./error'); +var errors = require('./errors'); +var InvalidReporterError = errors.InvalidReporterError; +var InvalidInterfaceError = errors.InvalidInterfaceError; var mocharc = require('./mocharc.json'); var assign = require('object.assign').getPolyfill(); @@ -250,10 +252,7 @@ Mocha.prototype.reporter = function(reporter, reporterOptions) { ); } if (!_reporter) { - throw new MochaError( - 'invalid reporter "' + reporter + '"', - 'ERR_INVALID_REPORTER' - ); + throw new InvalidReporterError('invalid reporter "' + reporter + '"'); } this._reporter = _reporter; } @@ -279,10 +278,7 @@ Mocha.prototype.ui = function(name) { try { this._ui = require(name); } catch (err) { - throw new MochaError( - 'invalid interface "' + name + '"', - 'ERR_INVALID_INTERFACE' - ); + throw new InvalidInterfaceError('invalid interface "' + name + '"'); } } this._ui = this._ui(this.suite); diff --git a/lib/reporters/xunit.js b/lib/reporters/xunit.js index 2e55a068a2..75483cdda1 100644 --- a/lib/reporters/xunit.js +++ b/lib/reporters/xunit.js @@ -13,8 +13,8 @@ var fs = require('fs'); var escape = utils.escape; var mkdirp = require('mkdirp'); var path = require('path'); -var MochaError = require('../error'); - +var errors = require('../errors'); +var NotSupportedError = errors.NotSupportedError; /** * Save timer references to avoid Sinon interfering (see GH-237). */ @@ -51,10 +51,7 @@ function XUnit(runner, options) { if (options && options.reporterOptions) { if (options.reporterOptions.output) { if (!fs.createWriteStream) { - throw new MochaError( - 'file output not supported in browser', - 'ERR_OUTPUT_NOT_SUPPORTED' - ); + throw new NotSupportedError('file output not supported in browser'); } mkdirp.sync(path.dirname(options.reporterOptions.output)); diff --git a/lib/suite.js b/lib/suite.js index 60b3d36659..bb7424cf20 100644 --- a/lib/suite.js +++ b/lib/suite.js @@ -12,7 +12,6 @@ var utils = require('./utils'); var inherits = utils.inherits; var debug = require('debug')('mocha:suite'); var milliseconds = require('ms'); -var MochaError = require('./error'); /** * Expose `Suite`. @@ -50,11 +49,10 @@ exports.create = function(parent, title) { */ function Suite(title, parentContext) { if (!utils.isString(title)) { - throw new MochaError( + throw new TypeError( 'Suite `title` should be a "string" but "' + typeof title + - '" was given instead.', - 'ERR_TITLE_NOT_STRING' + '" was given instead.' ); } this.title = title; diff --git a/lib/test.js b/lib/test.js index 42e9b76a1f..1d7e2545e9 100644 --- a/lib/test.js +++ b/lib/test.js @@ -1,7 +1,6 @@ 'use strict'; var Runnable = require('./runnable'); var utils = require('./utils'); -var MochaError = require('./error'); var isString = utils.isString; module.exports = Test; @@ -16,11 +15,10 @@ module.exports = Test; */ function Test(title, fn) { if (!isString(title)) { - throw new MochaError( + throw new TypeError( 'Test `title` should be a "string" but "' + typeof title + - '" was given instead.', - 'ERR_TITLE_NOT_STRING' + '" was given instead.' ); } Runnable.call(this, title, fn); diff --git a/lib/utils.js b/lib/utils.js index 0a978772d0..626d4866ed 100644 --- a/lib/utils.js +++ b/lib/utils.js @@ -14,7 +14,10 @@ var glob = require('glob'); var path = require('path'); var join = path.join; var he = require('he'); -var MochaError = require('./error'); +var errors = require('./errors'); +var NoFilesMatchPatternError = errors.NoFilesMatchPatternError; +var MissingArgumentError = errors.MissingArgumentError; +var UndefinedError = errors.UndefinedError; /** * Ignored directories. @@ -516,9 +519,9 @@ exports.lookupFiles = function lookupFiles(filepath, extensions, recursive) { } else { files = glob.sync(filepath); if (!files.length) { - throw new MochaError( - "cannot resolve path (or pattern) '" + filepath + "'", - 'ERR_CANNOT_RESOLVE_PATH' + throw new NoFilesMatchPatternError( + 'Could not find any test files matching pattern', + filepath ); } return files; @@ -550,9 +553,9 @@ exports.lookupFiles = function lookupFiles(filepath, extensions, recursive) { return; } if (!extensions) { - throw new MochaError( - 'extensions parameter required when filepath is a directory', - 'ERR_EXTENSIONS_REQUIRED' + throw new MissingArgumentError( + 'Parameter required when filepath is a directory', + 'extensions' ); } var re = new RegExp('\\.(?:' + extensions.join('|') + ')$'); @@ -572,9 +575,8 @@ exports.lookupFiles = function lookupFiles(filepath, extensions, recursive) { */ exports.undefinedError = function() { - return new MochaError( - 'Caught undefined error, did you throw without specifying what?', - 'ERR_UNDEFINED_ERROR' + return new UndefinedError( + 'Caught undefined error, did you throw without specifying what?' ); }; diff --git a/test/integration/file-utils.spec.js b/test/integration/file-utils.spec.js index 74c30be46e..b6dc96ad60 100644 --- a/test/integration/file-utils.spec.js +++ b/test/integration/file-utils.spec.js @@ -75,11 +75,11 @@ describe('file utils', function() { var dirLookup = function() { return utils.lookupFiles(tmpDir, undefined, false); }; - expect( - dirLookup, - 'to throw', - 'extensions parameter required when filepath is a directory' - ); + expect(dirLookup, 'to throw', { + message: 'Parameter required when filepath is a directory', + code: 'ERR_MOCHA_MISSING_ARGUMENT', + argument: 'extensions' + }); }); }); diff --git a/test/integration/suite.spec.js b/test/integration/suite.spec.js index 93450c9582..2e0c489914 100644 --- a/test/integration/suite.spec.js +++ b/test/integration/suite.spec.js @@ -13,7 +13,8 @@ describe('suite w/no callback', function() { if (err) { return done(err); } - var result = res.output.match(/no callback was supplied/) || []; + var pattern = /MissingCallbackError:/g; + var result = res.output.match(pattern) || []; assert.strictEqual(result.length, 1); done(); }, diff --git a/test/reporters/helpers.js b/test/reporters/helpers.js index 9712194df0..8dff1469cc 100644 --- a/test/reporters/helpers.js +++ b/test/reporters/helpers.js @@ -1,7 +1,7 @@ 'use strict'; -var MochaError = require('../../lib/error'); - +var errors = require('../../lib/errors'); +var NotSupportedError = errors.NotSupportedError; /* This function prevents the constant use of creating a runnerEvent. runStr is the argument that defines the runnerEvent. @@ -118,9 +118,8 @@ function createRunnerFunction(runStr, ifStr1, ifStr2, ifStr3, arg1, arg2) { } }; default: - throw new MochaError( - 'This function does not support the runner string specified.', - 'ERR_RUNNER_STRING_NOT_SUPPORTED' + throw new NotSupportedError( + 'This function does not support the runner string specified.' ); } } From 57d2e99c4891b225bcb76dec36067c5f633c40b1 Mon Sep 17 00:00:00 2001 From: Craig Taub Date: Wed, 12 Dec 2018 15:28:15 +0000 Subject: [PATCH 03/17] Remove custom errors, use factories Signed-off-by: Craig Taub module name Errors remove superfluous error type. --- docs/index.md | 3 +- lib/cli/run-helpers.js | 4 +- lib/cli/run.js | 4 +- lib/errors.js | 110 ++++++++------------------------- lib/interfaces/common.js | 2 +- lib/mocha.js | 8 +-- lib/reporters/xunit.js | 2 +- lib/utils.js | 6 +- test/integration/suite.spec.js | 2 +- test/reporters/helpers.js | 2 +- 10 files changed, 41 insertions(+), 102 deletions(-) diff --git a/docs/index.md b/docs/index.md index bdb4116184..6df7350c96 100644 --- a/docs/index.md +++ b/docs/index.md @@ -1670,9 +1670,8 @@ List of codes associated with Errors thrown inside Mocha. Following NodeJS pract | ERR_MOCHA_INVALID_REPORTER | reporter specified in options not found | | ERR_MOCHA_INVALID_INTERFACE | interface specified in options not found | | ERR_MOCHA_NOT_SUPPORTED | type of output specified was not supported | -| ERR_MOCHA_INVALID_ARG_VALUE | an invalid argument value was found | | ERR_MOCHA_MISSING_ARGUMENT | an argument was missing | -| ERR_UNDEFINED_ERROR | an error was thrown but no details were specified | +| ERR_MOCHA_UNDEFINED_ERROR | an error was thrown but no details were specified | ## Editor Plugins diff --git a/lib/cli/run-helpers.js b/lib/cli/run-helpers.js index e1a71aa453..4ff97e9ba8 100644 --- a/lib/cli/run-helpers.js +++ b/lib/cli/run-helpers.js @@ -15,8 +15,6 @@ const utils = require('../utils'); const minimatch = require('minimatch'); const ansi = require('ansi-colors'); const symbols = require('log-symbols'); -const errors = require('../errors'); -const NoFilesMatchPatternError = errors.NoFilesMatchPatternError; const cwd = (exports.cwd = process.cwd()); @@ -145,7 +143,7 @@ exports.handleFiles = ({ try { newFiles = utils.lookupFiles(arg, extension, recursive); } catch (err) { - if (err instanceof NoFilesMatchPatternError) { + if (err.code === 'ERR_MOCHA_NO_FILES_MATCH_PATTERN') { console.warn(`Warning: ${err.message}: ${err.pattern}`); return; } diff --git a/lib/cli/run.js b/lib/cli/run.js index 1d6bf1ac4c..b20d6026e8 100644 --- a/lib/cli/run.js +++ b/lib/cli/run.js @@ -9,6 +9,8 @@ const Mocha = require('../mocha'); const ansi = require('ansi-colors'); +var errors = require('./errors'); +var InvalidReporterError = errors.InvalidReporterError; const { list, @@ -190,7 +192,7 @@ exports.builder = yargs => const pair = opt.split('='); if (pair.length > 2 || !pair.length) { - throw new Error(`invalid reporter option '${opt}'`); + throw InvalidReporterError(`invalid reporter option '${opt}'`); } acc[pair[0]] = pair.length === 2 ? pair[1] : true; diff --git a/lib/errors.js b/lib/errors.js index 73e30aeb62..d5e597f407 100644 --- a/lib/errors.js +++ b/lib/errors.js @@ -1,9 +1,9 @@ 'use strict'; /** - * @module Custom Errors + * @module Errors */ /** - * Custom Errors + * Error object factories */ /** @@ -11,31 +11,13 @@ * File/s of test could not be found. * * @public - * @example - * // import custom errors - * const errors = require('./errors'); - * const ErrorInvalidArgValue = errors.ErrorInvalidArgValue; - * - * throw new ErrorInvalidArgValue('Our error message'); - * - * // to use it: - * const errors = require('./errors'); - * const NoFilesMatchPatternError = errors.NoFilesMatchPatternError - * - * if (someError instanceof NoFilesMatchPatternError) { - * // ... - * } */ function NoFilesMatchPatternError(message, pattern) { - this.name = 'NoFilesMatchPatternError'; - this.code = 'ERR_MOCHA_NO_FILES_MATCH_PATTERN'; - this.pattern = pattern; - this.message = message; - Error.captureStackTrace(this, NoFilesMatchPatternError); + var errorWithCode = new Error(message); + errorWithCode.code = 'ERR_MOCHA_NO_FILES_MATCH_PATTERN'; + errorWithCode.pattern = pattern; + return errorWithCode; } -NoFilesMatchPatternError.prototype = Object.create(Error.prototype); -NoFilesMatchPatternError.prototype.name = NoFilesMatchPatternError.name; -NoFilesMatchPatternError.prototype.constructor = NoFilesMatchPatternError; /** * @summary @@ -44,15 +26,10 @@ NoFilesMatchPatternError.prototype.constructor = NoFilesMatchPatternError; * @public */ function MissingCallbackError(message) { - this.name = 'MissingCallbackError'; - this.code = 'ERR_MOCHA_MISSING_CALLBACK'; - this.message = message; - Error.captureStackTrace(this, MissingCallbackError); + var errorWithCode = new Error(message); + errorWithCode.code = 'ERR_MOCHA_MISSING_CALLBACK'; + return errorWithCode; } -MissingCallbackError.prototype = Object.create(Error.prototype); -MissingCallbackError.prototype.name = MissingCallbackError.name; -MissingCallbackError.prototype.constructor = MissingCallbackError; - /** * @summary * Reporter specified in options not found. @@ -60,14 +37,10 @@ MissingCallbackError.prototype.constructor = MissingCallbackError; * @public */ function InvalidReporterError(message) { - this.name = 'InvalidReporterError'; - this.code = 'ERR_MOCHA_INVALID_REPORTER'; - this.message = message; - Error.captureStackTrace(this, InvalidReporterError); + var errorWithCode = new Error(message); + errorWithCode.code = 'ERR_MOCHA_INVALID_REPORTER'; + return errorWithCode; } -InvalidReporterError.prototype = Object.create(Error.prototype); -InvalidReporterError.prototype.name = InvalidReporterError.name; -InvalidReporterError.prototype.constructor = InvalidReporterError; /** * @summary @@ -76,14 +49,10 @@ InvalidReporterError.prototype.constructor = InvalidReporterError; * @public */ function InvalidInterfaceError(message) { - this.name = 'InvalidInterfaceError'; - this.code = 'ERR_MOCHA_INVALID_INTERFACE'; - this.message = message; - Error.captureStackTrace(this, InvalidInterfaceError); + var errorWithCode = new Error(message); + errorWithCode.code = 'ERR_MOCHA_INVALID_INTERFACE'; + return errorWithCode; } -InvalidInterfaceError.prototype = Object.create(Error.prototype); -InvalidInterfaceError.prototype.name = InvalidInterfaceError.name; -InvalidInterfaceError.prototype.constructor = InvalidInterfaceError; /** * @summary @@ -92,30 +61,10 @@ InvalidInterfaceError.prototype.constructor = InvalidInterfaceError; * @public */ function NotSupportedError(message) { - this.name = 'NotSupportedError'; - this.code = 'ERR_MOCHA_NOT_SUPPORTED'; - this.message = message; - Error.captureStackTrace(this, NotSupportedError); -} -NotSupportedError.prototype = Object.create(Error.prototype); -NotSupportedError.prototype.name = NotSupportedError.name; -NotSupportedError.prototype.constructor = NotSupportedError; - -/** - * @summary - * An invalid argument value was found. - * - * @public - */ -function InvalidArgumentValueError(message) { - this.name = 'InvalidArgumentValueError'; - this.code = 'ERR_MOCHA_INVALID_ARG_VALUE'; - this.message = message; - Error.captureStackTraceError(this, InvalidArgumentValueError); + var errorWithCode = new Error(message); + errorWithCode.code = 'ERR_MOCHA_NOT_SUPPORTED'; + return errorWithCode; } -InvalidArgumentValueError.prototype = Object.create(Error.prototype); -InvalidArgumentValueError.prototype.name = InvalidArgumentValueError.name; -InvalidArgumentValueError.prototype.constructor = InvalidArgumentValueError; /** * @summary @@ -124,15 +73,11 @@ InvalidArgumentValueError.prototype.constructor = InvalidArgumentValueError; * @public */ function MissingArgumentError(message, argument) { - this.name = 'MissingArgumentError'; - this.code = 'ERR_MOCHA_MISSING_ARGUMENT'; - this.argument = argument; - this.message = message; - Error.captureStackTrace(this, MissingArgumentError); + var errorWithCode = new Error(message); + errorWithCode.code = 'ERR_MOCHA_MISSING_ARGUMENT'; + errorWithCode.argument = argument; + return errorWithCode; } -MissingArgumentError.prototype = Object.create(Error.prototype); -MissingArgumentError.prototype.name = MissingArgumentError.name; -MissingArgumentError.prototype.constructor = MissingArgumentError; /** * @summary @@ -141,14 +86,10 @@ MissingArgumentError.prototype.constructor = MissingArgumentError; * @public */ function UndefinedError(message) { - this.name = 'UndefinedError'; - this.code = 'ERR_MOCHA_UNDEFINED_ERROR'; - this.message = message; - Error.captureStackTrace(this, UndefinedError); + var errorWithCode = new Error(message); + errorWithCode.code = 'ERR_MOCHA_UNDEFINED_ERROR'; + return errorWithCode; } -UndefinedError.prototype = Object.create(Error.prototype); -UndefinedError.prototype.name = UndefinedError.name; -UndefinedError.prototype.constructor = UndefinedError; module.exports = { NoFilesMatchPatternError: NoFilesMatchPatternError, @@ -156,7 +97,6 @@ module.exports = { InvalidReporterError: InvalidReporterError, InvalidInterfaceError: InvalidInterfaceError, NotSupportedError: NotSupportedError, - InvalidArgumentValueError: InvalidArgumentValueError, MissingArgumentError: MissingArgumentError, UndefinedError: UndefinedError }; diff --git a/lib/interfaces/common.js b/lib/interfaces/common.js index a864da5d0c..85d7d02f88 100644 --- a/lib/interfaces/common.js +++ b/lib/interfaces/common.js @@ -149,7 +149,7 @@ module.exports = function(suites, context, mocha) { } suites.shift(); } else if (typeof opts.fn === 'undefined' && !suite.pending) { - throw new MissingCallbackError( + throw MissingCallbackError( 'Suite "' + suite.fullTitle() + '" was defined but no callback was supplied. ' + diff --git a/lib/mocha.js b/lib/mocha.js index dd505cd26a..20b1682466 100644 --- a/lib/mocha.js +++ b/lib/mocha.js @@ -10,11 +10,11 @@ var escapeRe = require('escape-string-regexp'); var path = require('path'); var builtinReporters = require('./reporters'); var utils = require('./utils'); +var mocharc = require('./mocharc.json'); +var assign = require('object.assign').getPolyfill(); var errors = require('./errors'); var InvalidReporterError = errors.InvalidReporterError; var InvalidInterfaceError = errors.InvalidInterfaceError; -var mocharc = require('./mocharc.json'); -var assign = require('object.assign').getPolyfill(); exports = module.exports = Mocha; @@ -252,7 +252,7 @@ Mocha.prototype.reporter = function(reporter, reporterOptions) { ); } if (!_reporter) { - throw new InvalidReporterError('invalid reporter "' + reporter + '"'); + throw InvalidReporterError('invalid reporter "' + reporter + '"'); } this._reporter = _reporter; } @@ -278,7 +278,7 @@ Mocha.prototype.ui = function(name) { try { this._ui = require(name); } catch (err) { - throw new InvalidInterfaceError('invalid interface "' + name + '"'); + throw InvalidInterfaceError('invalid interface "' + name + '"'); } } this._ui = this._ui(this.suite); diff --git a/lib/reporters/xunit.js b/lib/reporters/xunit.js index 75483cdda1..d5e6395de2 100644 --- a/lib/reporters/xunit.js +++ b/lib/reporters/xunit.js @@ -51,7 +51,7 @@ function XUnit(runner, options) { if (options && options.reporterOptions) { if (options.reporterOptions.output) { if (!fs.createWriteStream) { - throw new NotSupportedError('file output not supported in browser'); + throw NotSupportedError('file output not supported in browser'); } mkdirp.sync(path.dirname(options.reporterOptions.output)); diff --git a/lib/utils.js b/lib/utils.js index 626d4866ed..1df3bc3639 100644 --- a/lib/utils.js +++ b/lib/utils.js @@ -519,7 +519,7 @@ exports.lookupFiles = function lookupFiles(filepath, extensions, recursive) { } else { files = glob.sync(filepath); if (!files.length) { - throw new NoFilesMatchPatternError( + throw NoFilesMatchPatternError( 'Could not find any test files matching pattern', filepath ); @@ -553,7 +553,7 @@ exports.lookupFiles = function lookupFiles(filepath, extensions, recursive) { return; } if (!extensions) { - throw new MissingArgumentError( + throw MissingArgumentError( 'Parameter required when filepath is a directory', 'extensions' ); @@ -575,7 +575,7 @@ exports.lookupFiles = function lookupFiles(filepath, extensions, recursive) { */ exports.undefinedError = function() { - return new UndefinedError( + return UndefinedError( 'Caught undefined error, did you throw without specifying what?' ); }; diff --git a/test/integration/suite.spec.js b/test/integration/suite.spec.js index 2e0c489914..e1fe34432f 100644 --- a/test/integration/suite.spec.js +++ b/test/integration/suite.spec.js @@ -13,7 +13,7 @@ describe('suite w/no callback', function() { if (err) { return done(err); } - var pattern = /MissingCallbackError:/g; + var pattern = /MissingCallbackError/g; var result = res.output.match(pattern) || []; assert.strictEqual(result.length, 1); done(); diff --git a/test/reporters/helpers.js b/test/reporters/helpers.js index 8dff1469cc..fc3a1fbda8 100644 --- a/test/reporters/helpers.js +++ b/test/reporters/helpers.js @@ -118,7 +118,7 @@ function createRunnerFunction(runStr, ifStr1, ifStr2, ifStr3, arg1, arg2) { } }; default: - throw new NotSupportedError( + throw NotSupportedError( 'This function does not support the runner string specified.' ); } From 1d3b15e83eb135138ffff70a24735f855bc47a0a Mon Sep 17 00:00:00 2001 From: Craig Taub Date: Wed, 12 Dec 2018 15:55:07 +0000 Subject: [PATCH 04/17] add another reporter error --- lib/cli/run.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/cli/run.js b/lib/cli/run.js index b20d6026e8..4e6b96bb2d 100644 --- a/lib/cli/run.js +++ b/lib/cli/run.js @@ -9,7 +9,7 @@ const Mocha = require('../mocha'); const ansi = require('ansi-colors'); -var errors = require('./errors'); +var errors = require('../errors'); var InvalidReporterError = errors.InvalidReporterError; const { From 59b736887961083f660ff21c925f9f3647677d89 Mon Sep 17 00:00:00 2001 From: Craig Taub Date: Wed, 12 Dec 2018 16:25:01 +0000 Subject: [PATCH 05/17] errors unit test --- test/unit/errors.spec.js | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) create mode 100644 test/unit/errors.spec.js diff --git a/test/unit/errors.spec.js b/test/unit/errors.spec.js new file mode 100644 index 0000000000..d78115983e --- /dev/null +++ b/test/unit/errors.spec.js @@ -0,0 +1,26 @@ +'use strict'; + +var errors = require('../../lib/errors'); + +describe('Errors', function() { + var expectedMessage = 'some message'; + it('should include expected code in throw reporter errors', function() { + var throwError = function() { + throw errors.InvalidReporterError(expectedMessage); + }; + expect(throwError, 'to throw', { + message: expectedMessage, + code: 'ERR_MOCHA_INVALID_REPORTER' + }); + }); + + it('should include expected code in throw interface errors', function() { + var throwError = function() { + throw errors.InvalidInterfaceError(expectedMessage); + }; + expect(throwError, 'to throw', { + message: expectedMessage, + code: 'ERR_MOCHA_INVALID_INTERFACE' + }); + }); +}); From aa02b83aef30f95c2e26c101d1c0e9aba665e3b2 Mon Sep 17 00:00:00 2001 From: Craig Taub Date: Wed, 12 Dec 2018 17:56:22 +0000 Subject: [PATCH 06/17] cleanup documentation --- lib/errors.js | 2 +- test/unit/errors.spec.js | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/errors.js b/lib/errors.js index d5e597f407..dc3eea0156 100644 --- a/lib/errors.js +++ b/lib/errors.js @@ -3,7 +3,7 @@ * @module Errors */ /** - * Error object factories + * Factory functions to create throwable error objects */ /** diff --git a/test/unit/errors.spec.js b/test/unit/errors.spec.js index d78115983e..247d61bb35 100644 --- a/test/unit/errors.spec.js +++ b/test/unit/errors.spec.js @@ -4,7 +4,7 @@ var errors = require('../../lib/errors'); describe('Errors', function() { var expectedMessage = 'some message'; - it('should include expected code in throw reporter errors', function() { + it('should include expected code in thrown reporter errors', function() { var throwError = function() { throw errors.InvalidReporterError(expectedMessage); }; @@ -14,7 +14,7 @@ describe('Errors', function() { }); }); - it('should include expected code in throw interface errors', function() { + it('should include expected code in thrown interface errors', function() { var throwError = function() { throw errors.InvalidInterfaceError(expectedMessage); }; From 462a9aec3b052701dd975339fd90decb3f7d7c7b Mon Sep 17 00:00:00 2001 From: Craig Taub Date: Mon, 24 Dec 2018 12:04:40 +0000 Subject: [PATCH 07/17] camelCase factories. Use props on errors. Use TypeError --- docs/index.md | 3 +- lib/cli/run-helpers.js | 2 +- lib/cli/run.js | 4 +- lib/errors.js | 107 +++++++++++++++------------- lib/mocha.js | 16 +++-- lib/reporters/xunit.js | 4 +- lib/suite.js | 10 +-- lib/test.js | 10 +-- lib/utils.js | 12 ++-- test/integration/file-utils.spec.js | 3 +- test/integration/suite.spec.js | 9 ++- test/reporters/helpers.js | 4 +- test/unit/errors.spec.js | 4 +- 13 files changed, 104 insertions(+), 84 deletions(-) diff --git a/docs/index.md b/docs/index.md index 6df7350c96..0a6d36148c 100644 --- a/docs/index.md +++ b/docs/index.md @@ -1670,7 +1670,8 @@ List of codes associated with Errors thrown inside Mocha. Following NodeJS pract | ERR_MOCHA_INVALID_REPORTER | reporter specified in options not found | | ERR_MOCHA_INVALID_INTERFACE | interface specified in options not found | | ERR_MOCHA_NOT_SUPPORTED | type of output specified was not supported | -| ERR_MOCHA_MISSING_ARGUMENT | an argument was missing | +| ERR_MOCHA_MISSING_ARG_TYPE | an argument was missing | +| ERR_MOCHA_INVALID_ARG_TYPE | an argument used did not use the supported type | | ERR_MOCHA_UNDEFINED_ERROR | an error was thrown but no details were specified | ## Editor Plugins diff --git a/lib/cli/run-helpers.js b/lib/cli/run-helpers.js index 4ff97e9ba8..5212d386db 100644 --- a/lib/cli/run-helpers.js +++ b/lib/cli/run-helpers.js @@ -144,7 +144,7 @@ exports.handleFiles = ({ newFiles = utils.lookupFiles(arg, extension, recursive); } catch (err) { if (err.code === 'ERR_MOCHA_NO_FILES_MATCH_PATTERN') { - console.warn(`Warning: ${err.message}: ${err.pattern}`); + console.warn('Warning: %s: %O', err.message, err.pattern); return; } diff --git a/lib/cli/run.js b/lib/cli/run.js index 4e6b96bb2d..845532f8ee 100644 --- a/lib/cli/run.js +++ b/lib/cli/run.js @@ -10,7 +10,7 @@ const Mocha = require('../mocha'); const ansi = require('ansi-colors'); var errors = require('../errors'); -var InvalidReporterError = errors.InvalidReporterError; +var createInvalidReporterError = errors.createInvalidReporterError; const { list, @@ -192,7 +192,7 @@ exports.builder = yargs => const pair = opt.split('='); if (pair.length > 2 || !pair.length) { - throw InvalidReporterError(`invalid reporter option '${opt}'`); + throw createInvalidReporterError('invalid reporter option', opt); } acc[pair[0]] = pair.length === 2 ? pair[1] : true; diff --git a/lib/errors.js b/lib/errors.js index dc3eea0156..271d24637f 100644 --- a/lib/errors.js +++ b/lib/errors.js @@ -7,96 +7,107 @@ */ /** - * @summary * File/s of test could not be found. * * @public */ -function NoFilesMatchPatternError(message, pattern) { - var errorWithCode = new Error(message); - errorWithCode.code = 'ERR_MOCHA_NO_FILES_MATCH_PATTERN'; - errorWithCode.pattern = pattern; - return errorWithCode; +function createNoFilesMatchPatternError(message, pattern) { + var err = new Error(message); + err.code = 'ERR_MOCHA_NO_FILES_MATCH_PATTERN'; + err.pattern = pattern; + return err; } /** - * @summary - * Callback for a running suite was not found. + * Reporter specified in options not found. * * @public */ -function MissingCallbackError(message) { - var errorWithCode = new Error(message); - errorWithCode.code = 'ERR_MOCHA_MISSING_CALLBACK'; - return errorWithCode; +function createInvalidReporterError(message, reporter) { + var err = new TypeError(message); + err.code = 'ERR_MOCHA_INVALID_REPORTER'; + err.reporter = reporter; + return err; } + /** - * @summary - * Reporter specified in options not found. + * Interface specified in options not found. * * @public */ -function InvalidReporterError(message) { - var errorWithCode = new Error(message); - errorWithCode.code = 'ERR_MOCHA_INVALID_REPORTER'; - return errorWithCode; +function createInvalidInterfaceError(message, badInterface) { + var err = new Error(message); + err.code = 'ERR_MOCHA_INVALID_INTERFACE'; + err.interface = badInterface; + return err; } /** - * @summary - * Interface specified in options not found. + * Type of output specified was not supported. * * @public */ -function InvalidInterfaceError(message) { - var errorWithCode = new Error(message); - errorWithCode.code = 'ERR_MOCHA_INVALID_INTERFACE'; - return errorWithCode; +function createNotSupportedError(message) { + var err = new Error(message); + err.code = 'ERR_MOCHA_NOT_SUPPORTED'; + return err; } /** - * @summary - * Type of output specified was not supported. + * Callback for a running suite was not found. * * @public */ -function NotSupportedError(message) { - var errorWithCode = new Error(message); - errorWithCode.code = 'ERR_MOCHA_NOT_SUPPORTED'; - return errorWithCode; +function createMissingCallbackError(message) { + var err = createMissingArgumentError(message, 'callback'); + err.code = 'ERR_MOCHA_MISSING_CALLBACK'; + return err; } /** - * @summary * An argument was missing. * * @public */ -function MissingArgumentError(message, argument) { - var errorWithCode = new Error(message); - errorWithCode.code = 'ERR_MOCHA_MISSING_ARGUMENT'; - errorWithCode.argument = argument; - return errorWithCode; +function createMissingArgumentError(message, argument) { + var err = new TypeError(message); + err.code = 'ERR_MOCHA_MISSING_ARG_TYPE'; + err.argument = argument; + return err; +} + +/** + * An argument used did not use the supported type + * + * @public + */ +function createArgumentTypeError(message, argument, expected) { + var err = new TypeError(message); + err.code = 'ERR_MOCHA_INVALID_ARG_TYPE'; + err.argument = argument; + err.expected = expected; + err.actual = typeof argument; + throw err; } /** - * @summary * An error was thrown but no details were specified. * * @public */ -function UndefinedError(message) { - var errorWithCode = new Error(message); - errorWithCode.code = 'ERR_MOCHA_UNDEFINED_ERROR'; - return errorWithCode; +function createUndefinedError(message) { + var err = new Error(message); + err.code = 'ERR_MOCHA_UNDEFINED_ERROR'; + return err; } module.exports = { - NoFilesMatchPatternError: NoFilesMatchPatternError, - MissingCallbackError: MissingCallbackError, - InvalidReporterError: InvalidReporterError, - InvalidInterfaceError: InvalidInterfaceError, - NotSupportedError: NotSupportedError, - MissingArgumentError: MissingArgumentError, - UndefinedError: UndefinedError + createNoFilesMatchPatternError: createNoFilesMatchPatternError, + createMissingCallbackError: createMissingCallbackError, + createInvalidReporterError: createInvalidReporterError, + createInvalidInterfaceError: createInvalidInterfaceError, + createNotSupportedError: createNotSupportedError, + createMissingArgumentError: createMissingArgumentError, + createUndefinedError: createUndefinedError, + createArgumentTypeError: createArgumentTypeError }; diff --git a/lib/mocha.js b/lib/mocha.js index 20b1682466..370d7a9237 100644 --- a/lib/mocha.js +++ b/lib/mocha.js @@ -13,8 +13,8 @@ var utils = require('./utils'); var mocharc = require('./mocharc.json'); var assign = require('object.assign').getPolyfill(); var errors = require('./errors'); -var InvalidReporterError = errors.InvalidReporterError; -var InvalidInterfaceError = errors.InvalidInterfaceError; +var createInvalidReporterError = errors.createInvalidReporterError; +var createInvalidInterfaceError = errors.createInvalidInterfaceError; exports = module.exports = Mocha; @@ -223,7 +223,10 @@ Mocha.prototype.reporter = function(reporter, reporterOptions) { try { _reporter = require(reporter); } catch (err) { - if (err.message.indexOf('Cannot find module') !== -1) { + if ( + err.code !== 'MODULE_NOT_FOUND' || + err.message.includes('Cannot find module') + ) { // Try to load reporters from a path (absolute or relative) try { _reporter = require(path.resolve(process.cwd(), reporter)); @@ -252,7 +255,7 @@ Mocha.prototype.reporter = function(reporter, reporterOptions) { ); } if (!_reporter) { - throw InvalidReporterError('invalid reporter "' + reporter + '"'); + throw createInvalidReporterError(`invalid reporter '${reporter}'`, reporter); } this._reporter = _reporter; } @@ -278,7 +281,10 @@ Mocha.prototype.ui = function(name) { try { this._ui = require(name); } catch (err) { - throw InvalidInterfaceError('invalid interface "' + name + '"'); + throw createInvalidInterfaceError( + 'invalid interface "' + name + '"', + name + ); } } this._ui = this._ui(this.suite); diff --git a/lib/reporters/xunit.js b/lib/reporters/xunit.js index d5e6395de2..643367aa9d 100644 --- a/lib/reporters/xunit.js +++ b/lib/reporters/xunit.js @@ -14,7 +14,7 @@ var escape = utils.escape; var mkdirp = require('mkdirp'); var path = require('path'); var errors = require('../errors'); -var NotSupportedError = errors.NotSupportedError; +var createNotSupportedError = errors.createNotSupportedError; /** * Save timer references to avoid Sinon interfering (see GH-237). */ @@ -51,7 +51,7 @@ function XUnit(runner, options) { if (options && options.reporterOptions) { if (options.reporterOptions.output) { if (!fs.createWriteStream) { - throw NotSupportedError('file output not supported in browser'); + throw createNotSupportedError('file output not supported in browser'); } mkdirp.sync(path.dirname(options.reporterOptions.output)); diff --git a/lib/suite.js b/lib/suite.js index bb7424cf20..aee7442e4b 100644 --- a/lib/suite.js +++ b/lib/suite.js @@ -12,6 +12,8 @@ var utils = require('./utils'); var inherits = utils.inherits; var debug = require('debug')('mocha:suite'); var milliseconds = require('ms'); +var errors = require('./errors'); +var createArgumentTypeError = errors.createArgumentTypeError; /** * Expose `Suite`. @@ -49,10 +51,10 @@ exports.create = function(parent, title) { */ function Suite(title, parentContext) { if (!utils.isString(title)) { - throw new TypeError( - 'Suite `title` should be a "string" but "' + - typeof title + - '" was given instead.' + throw createArgumentTypeError( + `Suite argument 'title' must be a string. Received type ${typeof title}.`, + 'title', + 'string' ); } this.title = title; diff --git a/lib/test.js b/lib/test.js index 1d7e2545e9..8a3525222e 100644 --- a/lib/test.js +++ b/lib/test.js @@ -1,6 +1,8 @@ 'use strict'; var Runnable = require('./runnable'); var utils = require('./utils'); +var errors = require('./errors'); +var createArgumentTypeError = errors.createArgumentTypeError; var isString = utils.isString; module.exports = Test; @@ -15,10 +17,10 @@ module.exports = Test; */ function Test(title, fn) { if (!isString(title)) { - throw new TypeError( - 'Test `title` should be a "string" but "' + - typeof title + - '" was given instead.' + throw createArgumentTypeError( + `Test argument 'title' should be a string. Received type ${typeof title}.`, + 'title', + 'string' ); } Runnable.call(this, title, fn); diff --git a/lib/utils.js b/lib/utils.js index 1df3bc3639..f14b20ecfc 100644 --- a/lib/utils.js +++ b/lib/utils.js @@ -15,9 +15,9 @@ var path = require('path'); var join = path.join; var he = require('he'); var errors = require('./errors'); -var NoFilesMatchPatternError = errors.NoFilesMatchPatternError; -var MissingArgumentError = errors.MissingArgumentError; -var UndefinedError = errors.UndefinedError; +var createNoFilesMatchPatternError = errors.createNoFilesMatchPatternError; +var createMissingArgumentError = errors.createMissingArgumentError; +var createUndefinedError = errors.createUndefinedError; /** * Ignored directories. @@ -519,7 +519,7 @@ exports.lookupFiles = function lookupFiles(filepath, extensions, recursive) { } else { files = glob.sync(filepath); if (!files.length) { - throw NoFilesMatchPatternError( + throw createNoFilesMatchPatternError( 'Could not find any test files matching pattern', filepath ); @@ -553,7 +553,7 @@ exports.lookupFiles = function lookupFiles(filepath, extensions, recursive) { return; } if (!extensions) { - throw MissingArgumentError( + throw createMissingArgumentError( 'Parameter required when filepath is a directory', 'extensions' ); @@ -575,7 +575,7 @@ exports.lookupFiles = function lookupFiles(filepath, extensions, recursive) { */ exports.undefinedError = function() { - return UndefinedError( + return createUndefinedError( 'Caught undefined error, did you throw without specifying what?' ); }; diff --git a/test/integration/file-utils.spec.js b/test/integration/file-utils.spec.js index b6dc96ad60..1a625c9de1 100644 --- a/test/integration/file-utils.spec.js +++ b/test/integration/file-utils.spec.js @@ -76,8 +76,7 @@ describe('file utils', function() { return utils.lookupFiles(tmpDir, undefined, false); }; expect(dirLookup, 'to throw', { - message: 'Parameter required when filepath is a directory', - code: 'ERR_MOCHA_MISSING_ARGUMENT', + code: 'ERR_MOCHA_MISSING_ARG_TYPE', argument: 'extensions' }); }); diff --git a/test/integration/suite.spec.js b/test/integration/suite.spec.js index e1fe34432f..ecd585836e 100644 --- a/test/integration/suite.spec.js +++ b/test/integration/suite.spec.js @@ -1,6 +1,5 @@ 'use strict'; -var assert = require('assert'); var run = require('./helpers').runMocha; var args = []; @@ -15,7 +14,7 @@ describe('suite w/no callback', function() { } var pattern = /MissingCallbackError/g; var result = res.output.match(pattern) || []; - assert.strictEqual(result.length, 1); + expect(result, 'to have length', 1); done(); }, {stdio: 'pipe'} @@ -31,7 +30,7 @@ describe('skipped suite w/no callback', function() { } var pattern = new RegExp('Error', 'g'); var result = res.output.match(pattern) || []; - assert.strictEqual(result.length, 0); + expect(result, 'to have length', 0); done(); }); }); @@ -45,7 +44,7 @@ describe('skipped suite w/ callback', function() { } var pattern = new RegExp('Error', 'g'); var result = res.output.match(pattern) || []; - assert.strictEqual(result.length, 0); + expect(result, 'to have length', 0); done(); }); }); @@ -62,7 +61,7 @@ describe('suite returning a value', function() { } var pattern = new RegExp('Deprecation Warning', 'g'); var result = res.output.match(pattern) || []; - assert.strictEqual(result.length, 1); + expect(result, 'to have length', 1); done(); }, {stdio: 'pipe'} diff --git a/test/reporters/helpers.js b/test/reporters/helpers.js index fc3a1fbda8..a507f25c81 100644 --- a/test/reporters/helpers.js +++ b/test/reporters/helpers.js @@ -1,7 +1,7 @@ 'use strict'; var errors = require('../../lib/errors'); -var NotSupportedError = errors.NotSupportedError; +var createNotSupportedError = errors.createNotSupportedError; /* This function prevents the constant use of creating a runnerEvent. runStr is the argument that defines the runnerEvent. @@ -118,7 +118,7 @@ function createRunnerFunction(runStr, ifStr1, ifStr2, ifStr3, arg1, arg2) { } }; default: - throw NotSupportedError( + throw createNotSupportedError( 'This function does not support the runner string specified.' ); } diff --git a/test/unit/errors.spec.js b/test/unit/errors.spec.js index 247d61bb35..0d3d8e467a 100644 --- a/test/unit/errors.spec.js +++ b/test/unit/errors.spec.js @@ -6,7 +6,7 @@ describe('Errors', function() { var expectedMessage = 'some message'; it('should include expected code in thrown reporter errors', function() { var throwError = function() { - throw errors.InvalidReporterError(expectedMessage); + throw errors.createInvalidReporterError(expectedMessage); }; expect(throwError, 'to throw', { message: expectedMessage, @@ -16,7 +16,7 @@ describe('Errors', function() { it('should include expected code in thrown interface errors', function() { var throwError = function() { - throw errors.InvalidInterfaceError(expectedMessage); + throw errors.createInvalidInterfaceError(expectedMessage); }; expect(throwError, 'to throw', { message: expectedMessage, From 356c8b1163ee13341315efcb3498dd1340adf0c4 Mon Sep 17 00:00:00 2001 From: Craig Taub Date: Mon, 24 Dec 2018 12:24:22 +0000 Subject: [PATCH 08/17] test case use code --- .eslintrc.yml | 5 +++-- lib/mocha.js | 10 +++++----- lib/suite.js | 2 +- lib/test.js | 4 ++-- 4 files changed, 11 insertions(+), 10 deletions(-) diff --git a/.eslintrc.yml b/.eslintrc.yml index 2d4721b8ed..e703fcbace 100644 --- a/.eslintrc.yml +++ b/.eslintrc.yml @@ -22,6 +22,7 @@ overrides: - karma.conf.js - .wallaby.js - bin/* + - lib/* - lib/cli/**/*.js - test/node-unit/**/*.js parserOptions: @@ -36,8 +37,8 @@ overrides: globals: expect: no - files: - - bin/* - - lib/**/*.js + - bin/* + - lib/**/*.js rules: no-restricted-globals: - error diff --git a/lib/mocha.js b/lib/mocha.js index 370d7a9237..ceac8d9c28 100644 --- a/lib/mocha.js +++ b/lib/mocha.js @@ -255,7 +255,10 @@ Mocha.prototype.reporter = function(reporter, reporterOptions) { ); } if (!_reporter) { - throw createInvalidReporterError(`invalid reporter '${reporter}'`, reporter); + throw createInvalidReporterError( + `invalid reporter '${reporter}'`, + reporter + ); } this._reporter = _reporter; } @@ -281,10 +284,7 @@ Mocha.prototype.ui = function(name) { try { this._ui = require(name); } catch (err) { - throw createInvalidInterfaceError( - 'invalid interface "' + name + '"', - name - ); + throw createInvalidInterfaceError(`invalid interface '${name}'`, name); } } this._ui = this._ui(this.suite); diff --git a/lib/suite.js b/lib/suite.js index aee7442e4b..7be16ca8c3 100644 --- a/lib/suite.js +++ b/lib/suite.js @@ -53,7 +53,7 @@ function Suite(title, parentContext) { if (!utils.isString(title)) { throw createArgumentTypeError( `Suite argument 'title' must be a string. Received type ${typeof title}.`, - 'title', + 'title', 'string' ); } diff --git a/lib/test.js b/lib/test.js index 8a3525222e..989f55dd65 100644 --- a/lib/test.js +++ b/lib/test.js @@ -18,8 +18,8 @@ module.exports = Test; function Test(title, fn) { if (!isString(title)) { throw createArgumentTypeError( - `Test argument 'title' should be a string. Received type ${typeof title}.`, - 'title', + `Test argument 'title' should be a string. Received type ${typeof title}.`, + 'title', 'string' ); } From 716e75fa017d6d30bd173814920a382289533830 Mon Sep 17 00:00:00 2001 From: Craig Taub Date: Mon, 24 Dec 2018 19:31:10 +0000 Subject: [PATCH 09/17] use TypeError for type issues From ddbb30efeaf55ff1f031c71bac0bc55d2e29de69 Mon Sep 17 00:00:00 2001 From: Craig Taub Date: Sat, 29 Dec 2018 12:31:44 +0000 Subject: [PATCH 10/17] add full documentation. update error names --- .eslintrc.yml | 5 +- docs/index.md | 9 ++-- lib/errors.js | 75 +++++++++++++++++++---------- lib/mocha.js | 7 ++- lib/suite.js | 8 +-- lib/test.js | 8 +-- lib/utils.js | 4 +- test/integration/file-utils.spec.js | 3 +- test/integration/glob.spec.js | 12 ++--- test/integration/suite.spec.js | 4 +- test/unit/errors.spec.js | 10 ++-- 11 files changed, 89 insertions(+), 56 deletions(-) diff --git a/.eslintrc.yml b/.eslintrc.yml index e703fcbace..2d4721b8ed 100644 --- a/.eslintrc.yml +++ b/.eslintrc.yml @@ -22,7 +22,6 @@ overrides: - karma.conf.js - .wallaby.js - bin/* - - lib/* - lib/cli/**/*.js - test/node-unit/**/*.js parserOptions: @@ -37,8 +36,8 @@ overrides: globals: expect: no - files: - - bin/* - - lib/**/*.js + - bin/* + - lib/**/*.js rules: no-restricted-globals: - error diff --git a/docs/index.md b/docs/index.md index 0a6d36148c..1f351ad90a 100644 --- a/docs/index.md +++ b/docs/index.md @@ -1665,13 +1665,12 @@ List of codes associated with Errors thrown inside Mocha. Following NodeJS pract | Code | Meaning | | ------------- | ------------- | -| ERR_MOCHA_NO_FILES_MATCH_PATTERN | file/s of test could not be found | -| ERR_MOCHA_MISSING_CALLBACK | callback for a running suite was not found | -| ERR_MOCHA_INVALID_REPORTER | reporter specified in options not found | +| ERR_MOCHA_INVALID_ARG_VALUE | an argument used did not use the supported value | +| ERR_MOCHA_INVALID_ARG_TYPE | an argument used did not use the supported type | | ERR_MOCHA_INVALID_INTERFACE | interface specified in options not found | +| ERR_MOCHA_INVALID_REPORTER | reporter specified in options not found | +| ERR_MOCHA_NO_FILES_MATCH_PATTERN | file/s of test could not be found | | ERR_MOCHA_NOT_SUPPORTED | type of output specified was not supported | -| ERR_MOCHA_MISSING_ARG_TYPE | an argument was missing | -| ERR_MOCHA_INVALID_ARG_TYPE | an argument used did not use the supported type | | ERR_MOCHA_UNDEFINED_ERROR | an error was thrown but no details were specified | ## Editor Plugins diff --git a/lib/errors.js b/lib/errors.js index 271d24637f..db2c681e92 100644 --- a/lib/errors.js +++ b/lib/errors.js @@ -7,9 +7,12 @@ */ /** - * File/s of test could not be found. + * Creates an error object used when no files to be tested could be found using specified pattern. * * @public + * @param {string} message - Error message to be displayed. + * @param {string} pattern - User-specified argument value. + * @returns {Error} instance detailing the error condition */ function createNoFilesMatchPatternError(message, pattern) { var err = new Error(message); @@ -19,9 +22,12 @@ function createNoFilesMatchPatternError(message, pattern) { } /** - * Reporter specified in options not found. + * Creates an error object used when the reporter specified in the options was not found. * * @public + * @param {string} message - Error message to be displayed. + * @param {string} reporter - User-specified reporter value. + * @returns {Error} instance detailing the error condition */ function createInvalidReporterError(message, reporter) { var err = new TypeError(message); @@ -31,21 +37,26 @@ function createInvalidReporterError(message, reporter) { } /** - * Interface specified in options not found. + * Creates an error object used when the interface specified in the options was not found. * * @public + * @param {string} message - Error message to be displayed. + * @param {string} ui - User-specified interface value. + * @returns {Error} instance detailing the error condition */ -function createInvalidInterfaceError(message, badInterface) { +function createInvalidInterfaceError(message, ui) { var err = new Error(message); err.code = 'ERR_MOCHA_INVALID_INTERFACE'; - err.interface = badInterface; + err.interface = ui; return err; } /** - * Type of output specified was not supported. + * Creates an error object used when the type of output specified was not supported. * * @public + * @param {string} message - Error message to be displayed. + * @returns {Error} instance detailing the error condition */ function createNotSupportedError(message) { var err = new Error(message); @@ -54,46 +65,60 @@ function createNotSupportedError(message) { } /** - * Callback for a running suite was not found. + * Creates an error object used when an argument is missing. * * @public + * @param {string} message - Error message to be displayed. + * @param {string} argument - User-specified missing argument name. + * @param {string} expected - User-specified expected argument name. + * @returns {Error} instance detailing the error condition */ -function createMissingCallbackError(message) { - var err = createMissingArgumentError(message, 'callback'); - err.code = 'ERR_MOCHA_MISSING_CALLBACK'; - return err; +function createMissingArgumentError(message, argument, expected) { + return createInvalidArgumentTypeError(message, argument, expected); } /** - * An argument was missing. + * Creates an error object used when an argument did not use the supported type * * @public + * @param {string} message - Error message to be displayed. + * @param {string} argument - User-specified actual argument type. + * @param {string} expected - User-specified expected argument type. + * @returns {Error} instance detailing the error condition */ -function createMissingArgumentError(message, argument) { +function createInvalidArgumentTypeError(message, argument, expected) { var err = new TypeError(message); - err.code = 'ERR_MOCHA_MISSING_ARG_TYPE'; + err.code = 'ERR_MOCHA_INVALID_ARG_TYPE'; err.argument = argument; + err.expected = expected; + err.actual = typeof argument; return err; } /** - * An argument used did not use the supported type + * Creates an error object used when an argument did not use the supported value * * @public + * @param {string} message - Error message to be displayed. + * @param {string} argument - User-specified actual argument value. + * @param {string} expected - User-specified expected argument value. + * @returns {Error} instance detailing the error condition */ -function createArgumentTypeError(message, argument, expected) { +function createInvalidArgumentValueError(message, argument, expected) { var err = new TypeError(message); - err.code = 'ERR_MOCHA_INVALID_ARG_TYPE'; + err.code = 'ERR_MOCHA_INVALID_ARG_VALUE'; err.argument = argument; err.expected = expected; err.actual = typeof argument; - throw err; + return err; } /** - * An error was thrown but no details were specified. + * Creates an error object used when an error was thrown but no details were specified. * * @public + * @param {string} message - Error message to be displayed. + * @returns {Error} instance detailing the error condition */ function createUndefinedError(message) { var err = new Error(message); @@ -102,12 +127,12 @@ function createUndefinedError(message) { } module.exports = { - createNoFilesMatchPatternError: createNoFilesMatchPatternError, - createMissingCallbackError: createMissingCallbackError, - createInvalidReporterError: createInvalidReporterError, + createInvalidArgumentValueError: createInvalidArgumentValueError, + createInvalidArgumentTypeError: createInvalidArgumentTypeError, createInvalidInterfaceError: createInvalidInterfaceError, - createNotSupportedError: createNotSupportedError, + createInvalidReporterError: createInvalidReporterError, createMissingArgumentError: createMissingArgumentError, - createUndefinedError: createUndefinedError, - createArgumentTypeError: createArgumentTypeError + createNoFilesMatchPatternError: createNoFilesMatchPatternError, + createNotSupportedError: createNotSupportedError, + createUndefinedError: createUndefinedError }; diff --git a/lib/mocha.js b/lib/mocha.js index ceac8d9c28..e31c474ab4 100644 --- a/lib/mocha.js +++ b/lib/mocha.js @@ -256,7 +256,7 @@ Mocha.prototype.reporter = function(reporter, reporterOptions) { } if (!_reporter) { throw createInvalidReporterError( - `invalid reporter '${reporter}'`, + 'invalid reporter "' + reporter + '"', reporter ); } @@ -284,7 +284,10 @@ Mocha.prototype.ui = function(name) { try { this._ui = require(name); } catch (err) { - throw createInvalidInterfaceError(`invalid interface '${name}'`, name); + throw createInvalidInterfaceError( + 'invalid interface "' + name + '"', + name + ); } } this._ui = this._ui(this.suite); diff --git a/lib/suite.js b/lib/suite.js index 7be16ca8c3..64e7db4b24 100644 --- a/lib/suite.js +++ b/lib/suite.js @@ -13,7 +13,7 @@ var inherits = utils.inherits; var debug = require('debug')('mocha:suite'); var milliseconds = require('ms'); var errors = require('./errors'); -var createArgumentTypeError = errors.createArgumentTypeError; +var createInvalidArgumentTypeError = errors.createInvalidArgumentTypeError; /** * Expose `Suite`. @@ -51,8 +51,10 @@ exports.create = function(parent, title) { */ function Suite(title, parentContext) { if (!utils.isString(title)) { - throw createArgumentTypeError( - `Suite argument 'title' must be a string. Received type ${typeof title}.`, + throw createInvalidArgumentTypeError( + 'Suite argument "title" must be a string. Received type "' + + typeof title + + '"', 'title', 'string' ); diff --git a/lib/test.js b/lib/test.js index 989f55dd65..6c6aeb6b7a 100644 --- a/lib/test.js +++ b/lib/test.js @@ -2,7 +2,7 @@ var Runnable = require('./runnable'); var utils = require('./utils'); var errors = require('./errors'); -var createArgumentTypeError = errors.createArgumentTypeError; +var createInvalidArgumentTypeError = errors.createInvalidArgumentTypeError; var isString = utils.isString; module.exports = Test; @@ -17,8 +17,10 @@ module.exports = Test; */ function Test(title, fn) { if (!isString(title)) { - throw createArgumentTypeError( - `Test argument 'title' should be a string. Received type ${typeof title}.`, + throw createInvalidArgumentTypeError( + 'Test argument "title" should be a string. Received type "' + + typeof title + + '"', 'title', 'string' ); diff --git a/lib/utils.js b/lib/utils.js index f14b20ecfc..6b23451f74 100644 --- a/lib/utils.js +++ b/lib/utils.js @@ -520,7 +520,7 @@ exports.lookupFiles = function lookupFiles(filepath, extensions, recursive) { files = glob.sync(filepath); if (!files.length) { throw createNoFilesMatchPatternError( - 'Could not find any test files matching pattern', + 'cannot find any files matching pattern "' + filepath + '"', filepath ); } @@ -554,7 +554,7 @@ exports.lookupFiles = function lookupFiles(filepath, extensions, recursive) { } if (!extensions) { throw createMissingArgumentError( - 'Parameter required when filepath is a directory', + 'Argument "extensions" required when argument "filepath" is a directory', 'extensions' ); } diff --git a/test/integration/file-utils.spec.js b/test/integration/file-utils.spec.js index 1a625c9de1..3fe030ee46 100644 --- a/test/integration/file-utils.spec.js +++ b/test/integration/file-utils.spec.js @@ -76,7 +76,8 @@ describe('file utils', function() { return utils.lookupFiles(tmpDir, undefined, false); }; expect(dirLookup, 'to throw', { - code: 'ERR_MOCHA_MISSING_ARG_TYPE', + name: 'TypeError', + code: 'ERR_MOCHA_INVALID_ARG_TYPE', argument: 'extensions' }); }); diff --git a/test/integration/glob.spec.js b/test/integration/glob.spec.js index 29d7ec64aa..6aa7563bf5 100644 --- a/test/integration/glob.spec.js +++ b/test/integration/glob.spec.js @@ -28,7 +28,7 @@ describe('globbing', function() { expect( results.stderr, 'to contain', - 'Could not find any test files matching pattern' + 'cannot find any files matching pattern "./*-none.js"' ); }, done @@ -47,7 +47,7 @@ describe('globbing', function() { expect( results.stderr, 'to contain', - 'Could not find any test files matching pattern' + 'cannot find any files matching pattern' ); }, done @@ -77,7 +77,7 @@ describe('globbing', function() { expect( results.stderr, 'to contain', - 'Could not find any test files matching pattern' + 'cannot find any files matching pattern' ); }, done @@ -96,7 +96,7 @@ describe('globbing', function() { expect( results.stderr, 'to contain', - 'Could not find any test files matching pattern' + 'cannot find any files matching pattern' ); }, done @@ -125,7 +125,7 @@ describe('globbing', function() { expect( results.stderr, 'to contain', - 'Could not find any test files matching pattern' + 'cannot find any files matching pattern' ); }, done @@ -144,7 +144,7 @@ describe('globbing', function() { expect( results.stderr, 'to contain', - 'Could not find any test files matching pattern' + 'cannot find any files matching pattern' ); }, done diff --git a/test/integration/suite.spec.js b/test/integration/suite.spec.js index ecd585836e..75b774962f 100644 --- a/test/integration/suite.spec.js +++ b/test/integration/suite.spec.js @@ -28,7 +28,7 @@ describe('skipped suite w/no callback', function() { if (err) { return done(err); } - var pattern = new RegExp('Error', 'g'); + var pattern = new RegExp('TypeError', 'g'); var result = res.output.match(pattern) || []; expect(result, 'to have length', 0); done(); @@ -42,7 +42,7 @@ describe('skipped suite w/ callback', function() { if (err) { return done(err); } - var pattern = new RegExp('Error', 'g'); + var pattern = new RegExp('TypeError', 'g'); var result = res.output.match(pattern) || []; expect(result, 'to have length', 0); done(); diff --git a/test/unit/errors.spec.js b/test/unit/errors.spec.js index 0d3d8e467a..51d066ef98 100644 --- a/test/unit/errors.spec.js +++ b/test/unit/errors.spec.js @@ -6,21 +6,23 @@ describe('Errors', function() { var expectedMessage = 'some message'; it('should include expected code in thrown reporter errors', function() { var throwError = function() { - throw errors.createInvalidReporterError(expectedMessage); + throw errors.createInvalidReporterError(expectedMessage, 'badReporter'); }; expect(throwError, 'to throw', { message: expectedMessage, - code: 'ERR_MOCHA_INVALID_REPORTER' + code: 'ERR_MOCHA_INVALID_REPORTER', + reporter: 'badReporter' }); }); it('should include expected code in thrown interface errors', function() { var throwError = function() { - throw errors.createInvalidInterfaceError(expectedMessage); + throw errors.createInvalidInterfaceError(expectedMessage, 'badUi'); }; expect(throwError, 'to throw', { message: expectedMessage, - code: 'ERR_MOCHA_INVALID_INTERFACE' + code: 'ERR_MOCHA_INVALID_INTERFACE', + interface: 'badUi' }); }); }); From 5e69794d9b9e97f36073bdaa3708169970ca85ea Mon Sep 17 00:00:00 2001 From: Craig Taub Date: Sat, 29 Dec 2018 12:40:30 +0000 Subject: [PATCH 11/17] use code and message for reporter check --- lib/mocha.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/mocha.js b/lib/mocha.js index e31c474ab4..c3d9f99b96 100644 --- a/lib/mocha.js +++ b/lib/mocha.js @@ -231,7 +231,8 @@ Mocha.prototype.reporter = function(reporter, reporterOptions) { try { _reporter = require(path.resolve(process.cwd(), reporter)); } catch (_err) { - err.message.indexOf('Cannot find module') !== -1 + _err.code !== 'MODULE_NOT_FOUND' || + _err.message.includes('Cannot find module') ? console.warn('"' + reporter + '" reporter not found') : console.warn( '"' + From 4ff280728dd84155243c8b3002d891c85371c353 Mon Sep 17 00:00:00 2001 From: Craig Taub Date: Sun, 30 Dec 2018 19:16:47 +0000 Subject: [PATCH 12/17] add error handling unit test for mocha --- test/unit/mocha.spec.js | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/test/unit/mocha.spec.js b/test/unit/mocha.spec.js index 2511b1204d..46c20513f8 100644 --- a/test/unit/mocha.spec.js +++ b/test/unit/mocha.spec.js @@ -197,4 +197,19 @@ describe('Mocha', function() { expect(mocha.suite._bail, 'to be', true); }); }); + + describe('error handling', function() { + it('should throw reporter error if an invalid reporter is given', function() { + var updatedOpts = {reporter: 'invalidReporter', reporterOptions: {}}; + var throwError = function() { + // eslint-disable-next-line no-new + new Mocha(updatedOpts); + }; + expect(throwError, 'to throw', { + message: 'invalid reporter "invalidReporter"', + code: 'ERR_MOCHA_INVALID_REPORTER', + reporter: 'invalidReporter' + }); + }); + }); }); From b754a535a8bcb9670a8733f85619d962a45375e1 Mon Sep 17 00:00:00 2001 From: Craig Taub Date: Mon, 31 Dec 2018 11:03:01 +0000 Subject: [PATCH 13/17] CLI to throw invalidArgumentValue error --- docs/index.md | 4 ++-- lib/cli/run.js | 9 +++++++-- lib/errors.js | 21 +++++++++++---------- 3 files changed, 20 insertions(+), 14 deletions(-) diff --git a/docs/index.md b/docs/index.md index 1f351ad90a..0c8bb08e6f 100644 --- a/docs/index.md +++ b/docs/index.md @@ -1665,8 +1665,8 @@ List of codes associated with Errors thrown inside Mocha. Following NodeJS pract | Code | Meaning | | ------------- | ------------- | -| ERR_MOCHA_INVALID_ARG_VALUE | an argument used did not use the supported value | -| ERR_MOCHA_INVALID_ARG_TYPE | an argument used did not use the supported type | +| ERR_MOCHA_INVALID_ARG_TYPE | argument of the wrong type was passed to Mocha's API | +| ERR_MOCHA_INVALID_ARG_VALUE | invalid or unsupported value was passed for a given argument | | ERR_MOCHA_INVALID_INTERFACE | interface specified in options not found | | ERR_MOCHA_INVALID_REPORTER | reporter specified in options not found | | ERR_MOCHA_NO_FILES_MATCH_PATTERN | file/s of test could not be found | diff --git a/lib/cli/run.js b/lib/cli/run.js index 845532f8ee..4670b93a63 100644 --- a/lib/cli/run.js +++ b/lib/cli/run.js @@ -10,7 +10,7 @@ const Mocha = require('../mocha'); const ansi = require('ansi-colors'); var errors = require('../errors'); -var createInvalidReporterError = errors.createInvalidReporterError; +var createInvalidArgumentValueError = errors.createInvalidArgumentValueError; const { list, @@ -192,7 +192,12 @@ exports.builder = yargs => const pair = opt.split('='); if (pair.length > 2 || !pair.length) { - throw createInvalidReporterError('invalid reporter option', opt); + throw createInvalidArgumentValueError( + `invalid reporter option '${opt}'`, + '--reporter-option', + opt, + 'expected "key=value" format' + ); } acc[pair[0]] = pair.length === 2 ? pair[1] : true; diff --git a/lib/errors.js b/lib/errors.js index db2c681e92..7756251e8f 100644 --- a/lib/errors.js +++ b/lib/errors.js @@ -69,8 +69,8 @@ function createNotSupportedError(message) { * * @public * @param {string} message - Error message to be displayed. - * @param {string} argument - User-specified missing argument name. - * @param {string} expected - User-specified expected argument name. + * @param {string} argument - Argument name. + * @param {string} expected - Expected argument datatype. * @returns {Error} instance detailing the error condition */ function createMissingArgumentError(message, argument, expected) { @@ -82,8 +82,8 @@ function createMissingArgumentError(message, argument, expected) { * * @public * @param {string} message - Error message to be displayed. - * @param {string} argument - User-specified actual argument type. - * @param {string} expected - User-specified expected argument type. + * @param {string} argument - Argument name. + * @param {string} expected - Expected argument datatype. * @returns {Error} instance detailing the error condition */ function createInvalidArgumentTypeError(message, argument, expected) { @@ -100,16 +100,17 @@ function createInvalidArgumentTypeError(message, argument, expected) { * * @public * @param {string} message - Error message to be displayed. - * @param {string} argument - User-specified actual argument value. - * @param {string} expected - User-specified expected argument value. + * @param {string} argument - Argument name. + * @param {string} value - Argument value. + * @param {string} reason - Why value is invalid. * @returns {Error} instance detailing the error condition */ -function createInvalidArgumentValueError(message, argument, expected) { +function createInvalidArgumentValueError(message, argument, value, reason) { var err = new TypeError(message); err.code = 'ERR_MOCHA_INVALID_ARG_VALUE'; err.argument = argument; - err.expected = expected; - err.actual = typeof argument; + err.value = value; + err.reason = typeof reason !== 'undefined' ? reason : 'is invalid'; return err; } @@ -127,8 +128,8 @@ function createUndefinedError(message) { } module.exports = { - createInvalidArgumentValueError: createInvalidArgumentValueError, createInvalidArgumentTypeError: createInvalidArgumentTypeError, + createInvalidArgumentValueError: createInvalidArgumentValueError, createInvalidInterfaceError: createInvalidInterfaceError, createInvalidReporterError: createInvalidReporterError, createMissingArgumentError: createMissingArgumentError, From b478d6f05245a2dd7cf483bc82f76dd1c08d5ae1 Mon Sep 17 00:00:00 2001 From: Craig Taub Date: Mon, 31 Dec 2018 11:14:29 +0000 Subject: [PATCH 14/17] add data type for MissingArgument error --- lib/interfaces/common.js | 8 +++++--- lib/utils.js | 3 ++- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/lib/interfaces/common.js b/lib/interfaces/common.js index 85d7d02f88..1656ce2aaa 100644 --- a/lib/interfaces/common.js +++ b/lib/interfaces/common.js @@ -3,7 +3,7 @@ var Suite = require('../suite'); var utils = require('../utils'); var errors = require('../errors'); -var MissingCallbackError = errors.MissingCallbackError; +var createMissingArgumentError = errors.createMissingArgumentError; /** * Functions common to more than one interface. @@ -149,11 +149,13 @@ module.exports = function(suites, context, mocha) { } suites.shift(); } else if (typeof opts.fn === 'undefined' && !suite.pending) { - throw MissingCallbackError( + throw createMissingArgumentError( 'Suite "' + suite.fullTitle() + '" was defined but no callback was supplied. ' + - 'Supply a callback or explicitly skip the suite.' + 'Supply a callback or explicitly skip the suite.', + 'callback', + 'function' ); } else if (!opts.fn && suite.pending) { suites.shift(); diff --git a/lib/utils.js b/lib/utils.js index 6b23451f74..d89b35ccdb 100644 --- a/lib/utils.js +++ b/lib/utils.js @@ -555,7 +555,8 @@ exports.lookupFiles = function lookupFiles(filepath, extensions, recursive) { if (!extensions) { throw createMissingArgumentError( 'Argument "extensions" required when argument "filepath" is a directory', - 'extensions' + 'extensions', + 'array' ); } var re = new RegExp('\\.(?:' + extensions.join('|') + ')$'); From 2088133c4c5f6e2fe69b9d4c57ba6622b5f7d89f Mon Sep 17 00:00:00 2001 From: Craig Taub Date: Mon, 31 Dec 2018 11:21:57 +0000 Subject: [PATCH 15/17] updated suite test --- test/integration/suite.spec.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/integration/suite.spec.js b/test/integration/suite.spec.js index 75b774962f..cd379dc329 100644 --- a/test/integration/suite.spec.js +++ b/test/integration/suite.spec.js @@ -12,9 +12,9 @@ describe('suite w/no callback', function() { if (err) { return done(err); } - var pattern = /MissingCallbackError/g; + var pattern = new RegExp('TypeError', 'g'); var result = res.output.match(pattern) || []; - expect(result, 'to have length', 1); + expect(result, 'to have length', 2); done(); }, {stdio: 'pipe'} From 3c2f7252a218e127bbdacbc310b971c3a4d02208 Mon Sep 17 00:00:00 2001 From: Craig Taub Date: Mon, 31 Dec 2018 11:35:46 +0000 Subject: [PATCH 16/17] use const for cli's errors --- lib/cli/run.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/cli/run.js b/lib/cli/run.js index 4670b93a63..e6c14f67ad 100644 --- a/lib/cli/run.js +++ b/lib/cli/run.js @@ -9,8 +9,8 @@ const Mocha = require('../mocha'); const ansi = require('ansi-colors'); -var errors = require('../errors'); -var createInvalidArgumentValueError = errors.createInvalidArgumentValueError; +const errors = require('../errors'); +const createInvalidArgumentValueError = errors.createInvalidArgumentValueError; const { list, From 713533d93539074737f67ff4e8b9eb5610f11879 Mon Sep 17 00:00:00 2001 From: Craig Taub Date: Tue, 1 Jan 2019 17:21:45 +0000 Subject: [PATCH 17/17] follow jsdoc for optional params --- lib/errors.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/errors.js b/lib/errors.js index 7756251e8f..90d0b92d05 100644 --- a/lib/errors.js +++ b/lib/errors.js @@ -102,7 +102,7 @@ function createInvalidArgumentTypeError(message, argument, expected) { * @param {string} message - Error message to be displayed. * @param {string} argument - Argument name. * @param {string} value - Argument value. - * @param {string} reason - Why value is invalid. + * @param {string} [reason] - Why value is invalid. * @returns {Error} instance detailing the error condition */ function createInvalidArgumentValueError(message, argument, value, reason) {