Skip to content

Commit

Permalink
rename lib/plugin => lib/plugin-loader
Browse files Browse the repository at this point in the history
- rename `createInvalidPluginError` => `createInvalidLegacyPluginError` and soft-deprecate
- add `createInvalidPluginDefinitionError` and `createInvalidPluginImplementationError` w/ constants; use them
- move some typedefs into `lib/mocha.js` as they are referenced via public aforementioned error factories
- remove TS docstrings
- better coverage
- move `plugin-loader` test back into `test/unit` (removed rewiremock from it)
  • Loading branch information
boneskull committed Jul 22, 2020
1 parent 3c2db2b commit 06e3d33
Show file tree
Hide file tree
Showing 7 changed files with 443 additions and 261 deletions.
26 changes: 16 additions & 10 deletions lib/cli/run-helpers.js
Expand Up @@ -13,9 +13,9 @@ const debug = require('debug')('mocha:cli:run:helpers');
const {watchRun, watchParallelRun} = require('./watch-run');
const collectFiles = require('./collect-files');
const {format} = require('util');
const {createInvalidPluginError} = require('../errors');
const {createInvalidLegacyPluginError} = require('../errors');
const {requireOrImport} = require('../esm-utils');
const {PluginLoader} = require('../plugin');
const PluginLoader = require('../plugin-loader');

/**
* Exits Mocha when tests + code under test has finished execution (default)
Expand Down Expand Up @@ -79,7 +79,7 @@ exports.list = str =>
*
* Returns array of `mochaHooks` exports, if any.
* @param {string[]} requires - Modules to require
* @returns {Promise<MochaRootHookObject|MochaRootHookFunction>} Any root hooks
* @returns {Promise<object>} Plugin implementations
* @private
*/
exports.handleRequires = async (requires = []) => {
Expand All @@ -93,11 +93,17 @@ exports.handleRequires = async (requires = []) => {
}
const requiredModule = await requireOrImport(modpath);
if (requiredModule && typeof requiredModule === 'object') {
pluginLoader.load(requiredModule);
if (pluginLoader.load(requiredModule)) {
debug('found one or more plugin implementations in %s', modpath);
}
}
debug('loaded required module "%s"', mod);
}
return pluginLoader.finalize();
const plugins = await pluginLoader.finalize();
if (Object.keys(plugins).length) {
debug('finalized plugin implementations: %O', plugins);
}
return plugins;
};

/**
Expand Down Expand Up @@ -208,14 +214,14 @@ exports.validateLegacyPlugin = (opts, pluginType, map = {}) => {
const pluginId = opts[pluginType];

if (Array.isArray(pluginId)) {
throw createInvalidPluginError(
throw createInvalidLegacyPluginError(
`"--${pluginType}" can only be specified once`,
pluginType
);
}

const unknownError = err =>
createInvalidPluginError(
const createUnknownError = err =>
createInvalidLegacyPluginError(
format('Could not load %s "%s":\n\n %O', pluginType, pluginId, err),
pluginType,
pluginId
Expand All @@ -231,10 +237,10 @@ exports.validateLegacyPlugin = (opts, pluginType, map = {}) => {
try {
opts[pluginType] = require(path.resolve(pluginId));
} catch (err) {
throw unknownError(err);
throw createUnknownError(err);
}
} else {
throw unknownError(err);
throw createUnknownError(err);
}
}
}
Expand Down
95 changes: 78 additions & 17 deletions lib/errors.js
@@ -1,6 +1,7 @@
'use strict';

var format = require('util').format;
const {deprecate} = require('./utils');

/**
* Factory functions to create throwable error objects
Expand Down Expand Up @@ -71,7 +72,17 @@ var constants = {
/**
* Use of `only()` w/ `--forbid-only` results in this error.
*/
FORBIDDEN_EXCLUSIVITY: 'ERR_MOCHA_FORBIDDEN_EXCLUSIVITY'
FORBIDDEN_EXCLUSIVITY: 'ERR_MOCHA_FORBIDDEN_EXCLUSIVITY',

/**
* To be thrown when a user-defined plugin implementation (e.g., `mochaHooks`) is invalid
*/
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
*/
INVALID_PLUGIN_DEFINITION: 'ERR_MOCHA_INVALID_PLUGIN_DEFINITION'
};

/**
Expand Down Expand Up @@ -221,7 +232,7 @@ function createFatalError(message, value) {
* @public
* @returns {Error}
*/
function createInvalidPluginError(message, pluginType, pluginId) {
function createInvalidLegacyPluginError(message, pluginType, pluginId) {
switch (pluginType) {
case 'reporter':
return createInvalidReporterError(message, pluginId);
Expand All @@ -232,6 +243,21 @@ function createInvalidPluginError(message, pluginType, pluginId) {
}
}

/**
* **DEPRECATED**. Use {@link createInvalidLegacyPluginError} instead Dynamically creates a plugin-type-specific error based on plugin type
* @deprecated
* @param {string} message - Error message
* @param {"reporter"|"interface"} pluginType - Plugin type. Future: expand as needed
* @param {string} [pluginId] - Name/path of plugin, if any
* @throws When `pluginType` is not known
* @public
* @returns {Error}
*/
function createInvalidPluginError(...args) {
deprecate('Use createInvalidLegacyPluginError() instead');
return createInvalidLegacyPluginError(...args);
}

/**
* Creates an error object to be thrown when a mocha object's `run` method is executed while it is already disposed.
* @param {string} message The error message to be displayed.
Expand Down Expand Up @@ -315,20 +341,55 @@ function createForbiddenExclusivityError(mocha) {
return err;
}

/**
* Creates an error object to be thrown when a plugin definition is invalid
* @param {string} msg - Error message
* @param {PluginDefinition} [pluginDef] - Problematic plugin definition
* @public
* @returns {Error} Error with code {@link constants.INVALID_PLUGIN_DEFINITION}
*/
function createInvalidPluginDefinitionError(msg, pluginDef) {
const err = new Error(msg);
err.code = constants.INVALID_PLUGIN_DEFINITION;
err.pluginDef = pluginDef;
return err;
}

/**
* Creates an error object to be thrown when a plugin implementation (user code) is invalid
* @param {string} msg - Error message
* @param {{pluginDef?: PluginDefinition, pluginImpl?: *}} [opts] - Plugin definition and user-supplied implementation
* @public
* @returns {Error} Error with code {@link constants.INVALID_PLUGIN_DEFINITION}
*/
function createInvalidPluginImplementationError(
msg,
{pluginDef, pluginImpl} = {}
) {
const err = new Error(msg);
err.code = constants.INVALID_PLUGIN_IMPLEMENTATION;
err.pluginDef = pluginDef;
err.pluginImpl = pluginImpl;
return err;
}

module.exports = {
createInvalidArgumentTypeError: createInvalidArgumentTypeError,
createInvalidArgumentValueError: createInvalidArgumentValueError,
createInvalidExceptionError: createInvalidExceptionError,
createInvalidInterfaceError: createInvalidInterfaceError,
createInvalidReporterError: createInvalidReporterError,
createMissingArgumentError: createMissingArgumentError,
createNoFilesMatchPatternError: createNoFilesMatchPatternError,
createUnsupportedError: createUnsupportedError,
createInvalidPluginError: createInvalidPluginError,
createMochaInstanceAlreadyDisposedError: createMochaInstanceAlreadyDisposedError,
createMochaInstanceAlreadyRunningError: createMochaInstanceAlreadyRunningError,
createFatalError: createFatalError,
createMultipleDoneError: createMultipleDoneError,
createForbiddenExclusivityError: createForbiddenExclusivityError,
constants: constants
constants,
createFatalError,
createForbiddenExclusivityError,
createInvalidArgumentTypeError,
createInvalidArgumentValueError,
createInvalidExceptionError,
createInvalidInterfaceError,
createInvalidPluginDefinitionError,
createInvalidPluginImplementationError,
createInvalidPluginError,
createInvalidLegacyPluginError,
createInvalidReporterError,
createMissingArgumentError,
createMochaInstanceAlreadyDisposedError,
createMochaInstanceAlreadyRunningError,
createMultipleDoneError,
createNoFilesMatchPatternError,
createUnsupportedError
};
27 changes: 27 additions & 0 deletions lib/mocha.js
Expand Up @@ -1280,3 +1280,30 @@ Mocha.prototype.hasGlobalTeardownFixtures = function hasGlobalTeardownFixtures()
* @callback MochaGlobalFixture
* @returns {void|Promise<void>}
*/

/**
* An object making up all necessary parts of a plugin loader and aggregator
* @typedef {Object} PluginDefinition
* @property {string} exportName - Named export to use
* @property {string} [optionName] - Option name for Mocha constructor (use `exportName` if omitted)
* @property {PluginValidator} [validate] - Validator function
* @property {PluginFinalizer} [finalize] - Finalizer/aggregator function
*/

/**
* A (sync) function to assert a user-supplied plugin implementation is valid.
*
* Defined in a {@link PluginDefinition}.
* @callback PluginValidator
* @param {*} value - Value to check
* @this {PluginDefinition}
* @returns {void}
*/

/**
* A function to finalize plugins impls of a particular ilk
* @callback PluginFinalizer
* @param {*[]} impls - User-supplied implementations
* @returns {Promise<*>|*}
*/

0 comments on commit 06e3d33

Please sign in to comment.