Skip to content

Commit 8212306

Browse files
authored
Add new option "--dry-run" (#4640)
1 parent 9872410 commit 8212306

File tree

12 files changed

+141
-104
lines changed

12 files changed

+141
-104
lines changed

docs/index.md

+7
Original file line numberDiff line numberDiff line change
@@ -874,6 +874,12 @@ Use this option to have Mocha check for global variables that are leaked while r
874874

875875
> _`--compilers` was removed in v6.0.0. See [further explanation and workarounds][mocha-wiki-compilers]._
876876
877+
### `--dry-run`
878+
879+
> _New in v9.0.0._
880+
881+
Report tests without executing any of them, neither tests nor hooks.
882+
877883
### `--exit`
878884

879885
> _Updated in v4.0.0._
@@ -2102,6 +2108,7 @@ mocha.setup({
21022108
asyncOnly: true,
21032109
bail: true,
21042110
checkLeaks: true,
2111+
dryRun: true,
21052112
forbidOnly: true,
21062113
forbidPending: true,
21072114
global: ['MyLib'],

lib/cli/run-option-metadata.js

+1
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ const TYPES = (exports.types = {
3232
'color',
3333
'delay',
3434
'diff',
35+
'dry-run',
3536
'exit',
3637
'forbid-only',
3738
'forbid-pending',

lib/cli/run.js

+4
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,10 @@ exports.builder = yargs =>
8383
description: 'Show diff on failure',
8484
group: GROUPS.OUTPUT
8585
},
86+
'dry-run': {
87+
description: 'Report tests without executing them',
88+
group: GROUPS.RULES
89+
},
8690
exit: {
8791
description: 'Force Mocha to quit after tests complete',
8892
group: GROUPS.RULES

lib/mocha.js

+23-14
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,6 @@ const {
3030
EVENT_FILE_POST_REQUIRE,
3131
EVENT_FILE_REQUIRE
3232
} = Suite.constants;
33-
var sQuote = utils.sQuote;
3433
var debug = require('debug')('mocha:mocha');
3534

3635
exports = module.exports = Mocha;
@@ -164,6 +163,7 @@ exports.run = function(...args) {
164163
* @param {boolean} [options.color] - Color TTY output from reporter?
165164
* @param {boolean} [options.delay] - Delay root suite execution?
166165
* @param {boolean} [options.diff] - Show diff on failure?
166+
* @param {boolean} [options.dryRun] - Report tests without running them?
167167
* @param {string} [options.fgrep] - Test filter given string.
168168
* @param {boolean} [options.forbidOnly] - Tests marked `only` fail the suite?
169169
* @param {boolean} [options.forbidPending] - Pending tests fail the suite?
@@ -200,7 +200,7 @@ function Mocha(options = {}) {
200200
.ui(options.ui)
201201
.reporter(
202202
options.reporter,
203-
options.reporterOption || options.reporterOptions // reporterOptions was previously the only way to specify options to reporter
203+
options.reporterOption || options.reporterOptions // for backwards compability
204204
)
205205
.slow(options.slow)
206206
.global(options.global);
@@ -222,6 +222,7 @@ function Mocha(options = {}) {
222222
'color',
223223
'delay',
224224
'diff',
225+
'dryRun',
225226
'forbidOnly',
226227
'forbidPending',
227228
'fullTrace',
@@ -346,23 +347,19 @@ Mocha.prototype.reporter = function(reporterName, reporterOptions) {
346347
reporter = require(path.resolve(utils.cwd(), reporterName));
347348
} catch (_err) {
348349
_err.code === 'MODULE_NOT_FOUND'
349-
? warn(sQuote(reporterName) + ' reporter not found')
350+
? warn(`'${reporterName}' reporter not found`)
350351
: warn(
351-
sQuote(reporterName) +
352-
' reporter blew up with error:\n' +
353-
err.stack
352+
`'${reporterName}' reporter blew up with error:\n ${err.stack}`
354353
);
355354
}
356355
} else {
357-
warn(
358-
sQuote(reporterName) + ' reporter blew up with error:\n' + err.stack
359-
);
356+
warn(`'${reporterName}' reporter blew up with error:\n ${err.stack}`);
360357
}
361358
}
362359
}
363360
if (!reporter) {
364361
throw createInvalidReporterError(
365-
'invalid reporter ' + sQuote(reporterName),
362+
`invalid reporter '${reporterName}'`,
366363
reporterName
367364
);
368365
}
@@ -396,10 +393,7 @@ Mocha.prototype.ui = function(ui) {
396393
try {
397394
bindInterface = require(ui);
398395
} catch (err) {
399-
throw createInvalidInterfaceError(
400-
'invalid interface ' + sQuote(ui),
401-
ui
402-
);
396+
throw createInvalidInterfaceError(`invalid interface '${ui}'`, ui);
403397
}
404398
}
405399
}
@@ -784,6 +778,20 @@ Mocha.prototype.diff = function(diff) {
784778
return this;
785779
};
786780

781+
/**
782+
* Enables or disables running tests in dry-run mode.
783+
*
784+
* @public
785+
* @see [CLI option](../#-dry-run)
786+
* @param {boolean} [dryRun=true] - Whether to activate dry-run mode.
787+
* @return {Mocha} this
788+
* @chainable
789+
*/
790+
Mocha.prototype.dryRun = function(dryRun) {
791+
this.options.dryRun = dryRun !== false;
792+
return this;
793+
};
794+
787795
/**
788796
* @summary
789797
* Sets timeout threshold value.
@@ -1016,6 +1024,7 @@ Mocha.prototype.run = function(fn) {
10161024
options.files = this.files;
10171025
const runner = new this._runnerClass(suite, {
10181026
delay: options.delay,
1027+
dryRun: options.dryRun,
10191028
cleanReferencesAfterRun: this._cleanReferencesAfterRun
10201029
});
10211030
createStatsCollector(runner);

lib/runner.js

+11-12
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44
* Module dependencies.
55
* @private
66
*/
7-
var util = require('util');
87
var EventEmitter = require('events').EventEmitter;
98
var Pending = require('./pending');
109
var utils = require('./utils');
@@ -19,8 +18,6 @@ var EVENT_ROOT_SUITE_RUN = Suite.constants.EVENT_ROOT_SUITE_RUN;
1918
var STATE_FAILED = Runnable.constants.STATE_FAILED;
2019
var STATE_PASSED = Runnable.constants.STATE_PASSED;
2120
var STATE_PENDING = Runnable.constants.STATE_PENDING;
22-
var dQuote = utils.dQuote;
23-
var sQuote = utils.sQuote;
2421
var stackFilter = utils.stackTraceFilter();
2522
var stringify = utils.stringify;
2623

@@ -140,6 +137,7 @@ class Runner extends EventEmitter {
140137
* @param {Suite} suite - Root suite
141138
* @param {Object|boolean} [opts] - Options. If `boolean` (deprecated), whether or not to delay execution of root suite until ready.
142139
* @param {boolean} [opts.delay] - Whether to delay execution of root suite until ready.
140+
* @param {boolean} [opts.dryRun] - Whether to report tests without running them.
143141
* @param {boolean} [opts.cleanReferencesAfterRun] - Whether to clean references to test fns and hooks when a suite is done.
144142
*/
145143
constructor(suite, opts) {
@@ -410,9 +408,8 @@ Runner.prototype.checkGlobals = function(test) {
410408
this._globals = this._globals.concat(leaks);
411409

412410
if (leaks.length) {
413-
var msg = 'global leak(s) detected: %s';
414-
var error = new Error(util.format(msg, leaks.map(sQuote).join(', ')));
415-
this.fail(test, error);
411+
var msg = `global leak(s) detected: ${leaks.map(e => `'${e}'`).join(', ')}`;
412+
this.fail(test, new Error(msg));
416413
}
417414
};
418415

@@ -479,6 +476,8 @@ Runner.prototype.fail = function(test, err, force) {
479476
*/
480477

481478
Runner.prototype.hook = function(name, fn) {
479+
if (this._opts.dryRun) return fn();
480+
482481
var suite = this.suite;
483482
var hooks = suite.getHooks(name);
484483
var self = this;
@@ -557,16 +556,15 @@ Runner.prototype.hook = function(name, fn) {
557556
function setHookTitle(hook) {
558557
hook.originalTitle = hook.originalTitle || hook.title;
559558
if (hook.ctx && hook.ctx.currentTest) {
560-
hook.title =
561-
hook.originalTitle + ' for ' + dQuote(hook.ctx.currentTest.title);
559+
hook.title = `${hook.originalTitle} for "${hook.ctx.currentTest.title}"`;
562560
} else {
563561
var parentTitle;
564562
if (hook.parent.title) {
565563
parentTitle = hook.parent.title;
566564
} else {
567565
parentTitle = hook.parent.root ? '{root}' : '';
568566
}
569-
hook.title = hook.originalTitle + ' in ' + dQuote(parentTitle);
567+
hook.title = `${hook.originalTitle} in "${parentTitle}"`;
570568
}
571569
}
572570
}
@@ -612,7 +610,7 @@ Runner.prototype.hooks = function(name, suites, fn) {
612610
};
613611

614612
/**
615-
* Run hooks from the top level down.
613+
* Run 'afterEach' hooks from bottom up.
616614
*
617615
* @param {String} name
618616
* @param {Function} fn
@@ -624,7 +622,7 @@ Runner.prototype.hookUp = function(name, fn) {
624622
};
625623

626624
/**
627-
* Run hooks from the bottom up.
625+
* Run 'beforeEach' hooks from top level down.
628626
*
629627
* @param {String} name
630628
* @param {Function} fn
@@ -659,6 +657,8 @@ Runner.prototype.parents = function() {
659657
* @private
660658
*/
661659
Runner.prototype.runTest = function(fn) {
660+
if (this._opts.dryRun) return fn();
661+
662662
var self = this;
663663
var test = this.test;
664664

@@ -704,7 +704,6 @@ Runner.prototype.runTests = function(suite, fn) {
704704
self.suite = after ? errSuite.parent : errSuite;
705705

706706
if (self.suite) {
707-
// call hookUp afterEach
708707
self.hookUp(HOOK_TYPE_AFTER_EACH, function(err2, errSuite2) {
709708
self.suite = orig;
710709
// some hooks may fail even now

lib/utils.js

-38
Original file line numberDiff line numberDiff line change
@@ -518,44 +518,6 @@ exports.clamp = function clamp(value, range) {
518518
return Math.min(Math.max(value, range[0]), range[1]);
519519
};
520520

521-
/**
522-
* Single quote text by combining with undirectional ASCII quotation marks.
523-
*
524-
* @description
525-
* Provides a simple means of markup for quoting text to be used in output.
526-
* Use this to quote names of variables, methods, and packages.
527-
*
528-
* <samp>package 'foo' cannot be found</samp>
529-
*
530-
* @private
531-
* @param {string} str - Value to be quoted.
532-
* @returns {string} quoted value
533-
* @example
534-
* sQuote('n') // => 'n'
535-
*/
536-
exports.sQuote = function(str) {
537-
return "'" + str + "'";
538-
};
539-
540-
/**
541-
* Double quote text by combining with undirectional ASCII quotation marks.
542-
*
543-
* @description
544-
* Provides a simple means of markup for quoting text to be used in output.
545-
* Use this to quote names of datatypes, classes, pathnames, and strings.
546-
*
547-
* <samp>argument 'value' must be "string" or "number"</samp>
548-
*
549-
* @private
550-
* @param {string} str - Value to be quoted.
551-
* @returns {string} quoted value
552-
* @example
553-
* dQuote('number') // => "number"
554-
*/
555-
exports.dQuote = function(str) {
556-
return '"' + str + '"';
557-
};
558-
559521
/**
560522
* It's a noop.
561523
* @public
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
'use strict';
2+
3+
describe.only('suite1', function() {
4+
it.skip('test1 - report as skipped', function() { });
5+
6+
it('test2 - report as passed', function() { });
7+
8+
it('test3 - report as passed', function() {
9+
throw new Error('this test should not run');
10+
});
11+
});
12+
13+
describe('suite2', function () {
14+
before(function() {
15+
throw new Error('this hook should not run');
16+
});
17+
beforeEach(function() {
18+
throw new Error('this hook should not run');
19+
});
20+
21+
it.only('test4 - report as passed', function () {
22+
throw new Error('this test should not run');
23+
});
24+
25+
it('test5 - should be ignored', function () {
26+
throw new Error('this test should not run');
27+
});
28+
29+
afterEach(function() {
30+
throw new Error('this hook should not run');
31+
});
32+
after(function() {
33+
throw new Error('this hook should not run');
34+
});
35+
});
+30
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
'use strict';
2+
3+
var path = require('path').posix;
4+
var helpers = require('../helpers');
5+
var runMochaJSON = helpers.runMochaJSON;
6+
7+
describe('--dry-run', function() {
8+
var args = ['--dry-run'];
9+
10+
it('should only report, but not execute any test', function(done) {
11+
var fixture = path.join('options/dry-run', 'dry-run');
12+
runMochaJSON(fixture, args, function(err, res) {
13+
if (err) {
14+
return done(err);
15+
}
16+
17+
expect(res, 'to have passed')
18+
.and(
19+
'to have passed tests',
20+
'test2 - report as passed',
21+
'test3 - report as passed',
22+
'test4 - report as passed'
23+
)
24+
.and('to have passed test count', 3)
25+
.and('to have pending test count', 1)
26+
.and('to have failed test count', 0);
27+
done();
28+
});
29+
});
30+
});

test/integration/reporters.spec.js

+1-5
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,6 @@ var fs = require('fs');
55
var crypto = require('crypto');
66
var path = require('path');
77
var run = require('./helpers').runMocha;
8-
var utils = require('../../lib/utils');
9-
var dQuote = utils.dQuote;
108

119
describe('reporters', function() {
1210
describe('markdown', function() {
@@ -215,9 +213,7 @@ describe('reporters', function() {
215213
return;
216214
}
217215

218-
var pattern =
219-
'^Error: invalid or unsupported TAP version: ' +
220-
dQuote(invalidTapVersion);
216+
var pattern = `^Error: invalid or unsupported TAP version: "${invalidTapVersion}"`;
221217
expect(res, 'to satisfy', {
222218
code: 1,
223219
output: new RegExp(pattern, 'm')

0 commit comments

Comments
 (0)