blob: 929f56399c5a5e144a17d0473675578d3ddd6370 [file] [log] [blame]
Yang Guo4fd355c2019-09-19 10:59:03 +02001'use strict';
Peter Marshall0b95ea12020-07-02 18:50:04 +02002
3var format = require('util').format;
4
Yang Guo4fd355c2019-09-19 10:59:03 +02005/**
6 * Factory functions to create throwable error objects
Peter Marshall0b95ea12020-07-02 18:50:04 +02007 * @module Errors
Yang Guo4fd355c2019-09-19 10:59:03 +02008 */
9
10/**
Peter Marshall0b95ea12020-07-02 18:50:04 +020011 * When Mocha throw exceptions (or otherwise errors), it attempts to assign a
12 * `code` property to the `Error` object, for easier handling. These are the
13 * potential values of `code`.
14 */
15var constants = {
16 /**
17 * An unrecoverable error.
18 */
19 FATAL: 'ERR_MOCHA_FATAL',
20
21 /**
22 * The type of an argument to a function call is invalid
23 */
24 INVALID_ARG_TYPE: 'ERR_MOCHA_INVALID_ARG_TYPE',
25
26 /**
27 * The value of an argument to a function call is invalid
28 */
29 INVALID_ARG_VALUE: 'ERR_MOCHA_INVALID_ARG_VALUE',
30
31 /**
32 * Something was thrown, but it wasn't an `Error`
33 */
34 INVALID_EXCEPTION: 'ERR_MOCHA_INVALID_EXCEPTION',
35
36 /**
37 * An interface (e.g., `Mocha.interfaces`) is unknown or invalid
38 */
39 INVALID_INTERFACE: 'ERR_MOCHA_INVALID_INTERFACE',
40
41 /**
42 * A reporter (.e.g, `Mocha.reporters`) is unknown or invalid
43 */
44 INVALID_REPORTER: 'ERR_MOCHA_INVALID_REPORTER',
45
46 /**
47 * `done()` was called twice in a `Test` or `Hook` callback
48 */
49 MULTIPLE_DONE: 'ERR_MOCHA_MULTIPLE_DONE',
50
51 /**
52 * No files matched the pattern provided by the user
53 */
54 NO_FILES_MATCH_PATTERN: 'ERR_MOCHA_NO_FILES_MATCH_PATTERN',
55
56 /**
57 * Known, but unsupported behavior of some kind
58 */
59 UNSUPPORTED: 'ERR_MOCHA_UNSUPPORTED',
60
61 /**
62 * Invalid state transition occuring in `Mocha` instance
63 */
64 INSTANCE_ALREADY_RUNNING: 'ERR_MOCHA_INSTANCE_ALREADY_RUNNING',
65
66 /**
67 * Invalid state transition occuring in `Mocha` instance
68 */
69 INSTANCE_ALREADY_DISPOSED: 'ERR_MOCHA_INSTANCE_ALREADY_DISPOSED',
70
71 /**
72 * Use of `only()` w/ `--forbid-only` results in this error.
73 */
74 FORBIDDEN_EXCLUSIVITY: 'ERR_MOCHA_FORBIDDEN_EXCLUSIVITY'
75};
76
77/**
Yang Guo4fd355c2019-09-19 10:59:03 +020078 * Creates an error object to be thrown when no files to be tested could be found using specified pattern.
79 *
80 * @public
81 * @param {string} message - Error message to be displayed.
82 * @param {string} pattern - User-specified argument value.
83 * @returns {Error} instance detailing the error condition
84 */
85function createNoFilesMatchPatternError(message, pattern) {
86 var err = new Error(message);
Peter Marshall0b95ea12020-07-02 18:50:04 +020087 err.code = constants.NO_FILES_MATCH_PATTERN;
Yang Guo4fd355c2019-09-19 10:59:03 +020088 err.pattern = pattern;
89 return err;
90}
91
92/**
93 * Creates an error object to be thrown when the reporter specified in the options was not found.
94 *
95 * @public
96 * @param {string} message - Error message to be displayed.
97 * @param {string} reporter - User-specified reporter value.
98 * @returns {Error} instance detailing the error condition
99 */
100function createInvalidReporterError(message, reporter) {
101 var err = new TypeError(message);
Peter Marshall0b95ea12020-07-02 18:50:04 +0200102 err.code = constants.INVALID_REPORTER;
Yang Guo4fd355c2019-09-19 10:59:03 +0200103 err.reporter = reporter;
104 return err;
105}
106
107/**
108 * Creates an error object to be thrown when the interface specified in the options was not found.
109 *
110 * @public
111 * @param {string} message - Error message to be displayed.
112 * @param {string} ui - User-specified interface value.
113 * @returns {Error} instance detailing the error condition
114 */
115function createInvalidInterfaceError(message, ui) {
116 var err = new Error(message);
Peter Marshall0b95ea12020-07-02 18:50:04 +0200117 err.code = constants.INVALID_INTERFACE;
Yang Guo4fd355c2019-09-19 10:59:03 +0200118 err.interface = ui;
119 return err;
120}
121
122/**
123 * Creates an error object to be thrown when a behavior, option, or parameter is unsupported.
124 *
125 * @public
126 * @param {string} message - Error message to be displayed.
127 * @returns {Error} instance detailing the error condition
128 */
129function createUnsupportedError(message) {
130 var err = new Error(message);
Peter Marshall0b95ea12020-07-02 18:50:04 +0200131 err.code = constants.UNSUPPORTED;
Yang Guo4fd355c2019-09-19 10:59:03 +0200132 return err;
133}
134
135/**
136 * Creates an error object to be thrown when an argument is missing.
137 *
138 * @public
139 * @param {string} message - Error message to be displayed.
140 * @param {string} argument - Argument name.
141 * @param {string} expected - Expected argument datatype.
142 * @returns {Error} instance detailing the error condition
143 */
144function createMissingArgumentError(message, argument, expected) {
145 return createInvalidArgumentTypeError(message, argument, expected);
146}
147
148/**
149 * Creates an error object to be thrown when an argument did not use the supported type
150 *
151 * @public
152 * @param {string} message - Error message to be displayed.
153 * @param {string} argument - Argument name.
154 * @param {string} expected - Expected argument datatype.
155 * @returns {Error} instance detailing the error condition
156 */
157function createInvalidArgumentTypeError(message, argument, expected) {
158 var err = new TypeError(message);
Peter Marshall0b95ea12020-07-02 18:50:04 +0200159 err.code = constants.INVALID_ARG_TYPE;
Yang Guo4fd355c2019-09-19 10:59:03 +0200160 err.argument = argument;
161 err.expected = expected;
162 err.actual = typeof argument;
163 return err;
164}
165
166/**
167 * Creates an error object to be thrown when an argument did not use the supported value
168 *
169 * @public
170 * @param {string} message - Error message to be displayed.
171 * @param {string} argument - Argument name.
172 * @param {string} value - Argument value.
173 * @param {string} [reason] - Why value is invalid.
174 * @returns {Error} instance detailing the error condition
175 */
176function createInvalidArgumentValueError(message, argument, value, reason) {
177 var err = new TypeError(message);
Peter Marshall0b95ea12020-07-02 18:50:04 +0200178 err.code = constants.INVALID_ARG_VALUE;
Yang Guo4fd355c2019-09-19 10:59:03 +0200179 err.argument = argument;
180 err.value = value;
181 err.reason = typeof reason !== 'undefined' ? reason : 'is invalid';
182 return err;
183}
184
185/**
186 * Creates an error object to be thrown when an exception was caught, but the `Error` is falsy or undefined.
187 *
188 * @public
189 * @param {string} message - Error message to be displayed.
190 * @returns {Error} instance detailing the error condition
191 */
192function createInvalidExceptionError(message, value) {
193 var err = new Error(message);
Peter Marshall0b95ea12020-07-02 18:50:04 +0200194 err.code = constants.INVALID_EXCEPTION;
Yang Guo4fd355c2019-09-19 10:59:03 +0200195 err.valueType = typeof value;
196 err.value = value;
197 return err;
198}
199
Peter Marshall0b95ea12020-07-02 18:50:04 +0200200/**
201 * Creates an error object to be thrown when an unrecoverable error occurs.
202 *
203 * @public
204 * @param {string} message - Error message to be displayed.
205 * @returns {Error} instance detailing the error condition
206 */
207function createFatalError(message, value) {
208 var err = new Error(message);
209 err.code = constants.FATAL;
210 err.valueType = typeof value;
211 err.value = value;
212 return err;
213}
214
215/**
216 * Dynamically creates a plugin-type-specific error based on plugin type
217 * @param {string} message - Error message
218 * @param {"reporter"|"interface"} pluginType - Plugin type. Future: expand as needed
219 * @param {string} [pluginId] - Name/path of plugin, if any
220 * @throws When `pluginType` is not known
221 * @public
222 * @returns {Error}
223 */
224function createInvalidPluginError(message, pluginType, pluginId) {
225 switch (pluginType) {
226 case 'reporter':
227 return createInvalidReporterError(message, pluginId);
228 case 'interface':
229 return createInvalidInterfaceError(message, pluginId);
230 default:
231 throw new Error('unknown pluginType "' + pluginType + '"');
232 }
233}
234
235/**
236 * Creates an error object to be thrown when a mocha object's `run` method is executed while it is already disposed.
237 * @param {string} message The error message to be displayed.
238 * @param {boolean} cleanReferencesAfterRun the value of `cleanReferencesAfterRun`
239 * @param {Mocha} instance the mocha instance that throw this error
240 */
241function createMochaInstanceAlreadyDisposedError(
242 message,
243 cleanReferencesAfterRun,
244 instance
245) {
246 var err = new Error(message);
247 err.code = constants.INSTANCE_ALREADY_DISPOSED;
248 err.cleanReferencesAfterRun = cleanReferencesAfterRun;
249 err.instance = instance;
250 return err;
251}
252
253/**
254 * Creates an error object to be thrown when a mocha object's `run` method is called while a test run is in progress.
255 * @param {string} message The error message to be displayed.
256 */
257function createMochaInstanceAlreadyRunningError(message, instance) {
258 var err = new Error(message);
259 err.code = constants.INSTANCE_ALREADY_RUNNING;
260 err.instance = instance;
261 return err;
262}
263
264/*
265 * Creates an error object to be thrown when done() is called multiple times in a test
266 *
267 * @public
268 * @param {Runnable} runnable - Original runnable
269 * @param {Error} [originalErr] - Original error, if any
270 * @returns {Error} instance detailing the error condition
271 */
272function createMultipleDoneError(runnable, originalErr) {
273 var title;
274 try {
275 title = format('<%s>', runnable.fullTitle());
276 if (runnable.parent.root) {
277 title += ' (of root suite)';
278 }
279 } catch (ignored) {
280 title = format('<%s> (of unknown suite)', runnable.title);
281 }
282 var message = format(
283 'done() called multiple times in %s %s',
284 runnable.type ? runnable.type : 'unknown runnable',
285 title
286 );
287 if (runnable.file) {
288 message += format(' of file %s', runnable.file);
289 }
290 if (originalErr) {
291 message += format('; in addition, done() received error: %s', originalErr);
292 }
293
294 var err = new Error(message);
295 err.code = constants.MULTIPLE_DONE;
296 err.valueType = typeof originalErr;
297 err.value = originalErr;
298 return err;
299}
300
301/**
302 * Creates an error object to be thrown when `.only()` is used with
303 * `--forbid-only`.
304 * @public
305 * @param {Mocha} mocha - Mocha instance
306 * @returns {Error} Error with code {@link constants.FORBIDDEN_EXCLUSIVITY}
307 */
308function createForbiddenExclusivityError(mocha) {
309 var err = new Error(
310 mocha.isWorker
311 ? '`.only` is not supported in parallel mode'
312 : '`.only` forbidden by --forbid-only'
313 );
314 err.code = constants.FORBIDDEN_EXCLUSIVITY;
315 return err;
316}
317
Yang Guo4fd355c2019-09-19 10:59:03 +0200318module.exports = {
319 createInvalidArgumentTypeError: createInvalidArgumentTypeError,
320 createInvalidArgumentValueError: createInvalidArgumentValueError,
321 createInvalidExceptionError: createInvalidExceptionError,
322 createInvalidInterfaceError: createInvalidInterfaceError,
323 createInvalidReporterError: createInvalidReporterError,
324 createMissingArgumentError: createMissingArgumentError,
325 createNoFilesMatchPatternError: createNoFilesMatchPatternError,
Peter Marshall0b95ea12020-07-02 18:50:04 +0200326 createUnsupportedError: createUnsupportedError,
327 createInvalidPluginError: createInvalidPluginError,
328 createMochaInstanceAlreadyDisposedError: createMochaInstanceAlreadyDisposedError,
329 createMochaInstanceAlreadyRunningError: createMochaInstanceAlreadyRunningError,
330 createFatalError: createFatalError,
331 createMultipleDoneError: createMultipleDoneError,
332 createForbiddenExclusivityError: createForbiddenExclusivityError,
333 constants: constants
Yang Guo4fd355c2019-09-19 10:59:03 +0200334};