Skip to content

Commit

Permalink
Add new option "--dry-run" (#4640)
Browse files Browse the repository at this point in the history
  • Loading branch information
juergba committed Jun 5, 2021
1 parent 9872410 commit 8212306
Show file tree
Hide file tree
Showing 12 changed files with 141 additions and 104 deletions.
7 changes: 7 additions & 0 deletions docs/index.md
Expand Up @@ -874,6 +874,12 @@ Use this option to have Mocha check for global variables that are leaked while r

> _`--compilers` was removed in v6.0.0. See [further explanation and workarounds][mocha-wiki-compilers]._
### `--dry-run`

> _New in v9.0.0._
Report tests without executing any of them, neither tests nor hooks.

### `--exit`

> _Updated in v4.0.0._
Expand Down Expand Up @@ -2102,6 +2108,7 @@ mocha.setup({
asyncOnly: true,
bail: true,
checkLeaks: true,
dryRun: true,
forbidOnly: true,
forbidPending: true,
global: ['MyLib'],
Expand Down
1 change: 1 addition & 0 deletions lib/cli/run-option-metadata.js
Expand Up @@ -32,6 +32,7 @@ const TYPES = (exports.types = {
'color',
'delay',
'diff',
'dry-run',
'exit',
'forbid-only',
'forbid-pending',
Expand Down
4 changes: 4 additions & 0 deletions lib/cli/run.js
Expand Up @@ -83,6 +83,10 @@ exports.builder = yargs =>
description: 'Show diff on failure',
group: GROUPS.OUTPUT
},
'dry-run': {
description: 'Report tests without executing them',
group: GROUPS.RULES
},
exit: {
description: 'Force Mocha to quit after tests complete',
group: GROUPS.RULES
Expand Down
37 changes: 23 additions & 14 deletions lib/mocha.js
Expand Up @@ -30,7 +30,6 @@ const {
EVENT_FILE_POST_REQUIRE,
EVENT_FILE_REQUIRE
} = Suite.constants;
var sQuote = utils.sQuote;
var debug = require('debug')('mocha:mocha');

exports = module.exports = Mocha;
Expand Down Expand Up @@ -164,6 +163,7 @@ exports.run = function(...args) {
* @param {boolean} [options.color] - Color TTY output from reporter?
* @param {boolean} [options.delay] - Delay root suite execution?
* @param {boolean} [options.diff] - Show diff on failure?
* @param {boolean} [options.dryRun] - Report tests without running them?
* @param {string} [options.fgrep] - Test filter given string.
* @param {boolean} [options.forbidOnly] - Tests marked `only` fail the suite?
* @param {boolean} [options.forbidPending] - Pending tests fail the suite?
Expand Down Expand Up @@ -200,7 +200,7 @@ function Mocha(options = {}) {
.ui(options.ui)
.reporter(
options.reporter,
options.reporterOption || options.reporterOptions // reporterOptions was previously the only way to specify options to reporter
options.reporterOption || options.reporterOptions // for backwards compability
)
.slow(options.slow)
.global(options.global);
Expand All @@ -222,6 +222,7 @@ function Mocha(options = {}) {
'color',
'delay',
'diff',
'dryRun',
'forbidOnly',
'forbidPending',
'fullTrace',
Expand Down Expand Up @@ -346,23 +347,19 @@ Mocha.prototype.reporter = function(reporterName, reporterOptions) {
reporter = require(path.resolve(utils.cwd(), reporterName));
} catch (_err) {
_err.code === 'MODULE_NOT_FOUND'
? warn(sQuote(reporterName) + ' reporter not found')
? warn(`'${reporterName}' reporter not found`)
: warn(
sQuote(reporterName) +
' reporter blew up with error:\n' +
err.stack
`'${reporterName}' reporter blew up with error:\n ${err.stack}`
);
}
} else {
warn(
sQuote(reporterName) + ' reporter blew up with error:\n' + err.stack
);
warn(`'${reporterName}' reporter blew up with error:\n ${err.stack}`);
}
}
}
if (!reporter) {
throw createInvalidReporterError(
'invalid reporter ' + sQuote(reporterName),
`invalid reporter '${reporterName}'`,
reporterName
);
}
Expand Down Expand Up @@ -396,10 +393,7 @@ Mocha.prototype.ui = function(ui) {
try {
bindInterface = require(ui);
} catch (err) {
throw createInvalidInterfaceError(
'invalid interface ' + sQuote(ui),
ui
);
throw createInvalidInterfaceError(`invalid interface '${ui}'`, ui);
}
}
}
Expand Down Expand Up @@ -784,6 +778,20 @@ Mocha.prototype.diff = function(diff) {
return this;
};

/**
* Enables or disables running tests in dry-run mode.
*
* @public
* @see [CLI option](../#-dry-run)
* @param {boolean} [dryRun=true] - Whether to activate dry-run mode.
* @return {Mocha} this
* @chainable
*/
Mocha.prototype.dryRun = function(dryRun) {
this.options.dryRun = dryRun !== false;
return this;
};

/**
* @summary
* Sets timeout threshold value.
Expand Down Expand Up @@ -1016,6 +1024,7 @@ Mocha.prototype.run = function(fn) {
options.files = this.files;
const runner = new this._runnerClass(suite, {
delay: options.delay,
dryRun: options.dryRun,
cleanReferencesAfterRun: this._cleanReferencesAfterRun
});
createStatsCollector(runner);
Expand Down
23 changes: 11 additions & 12 deletions lib/runner.js
Expand Up @@ -4,7 +4,6 @@
* Module dependencies.
* @private
*/
var util = require('util');
var EventEmitter = require('events').EventEmitter;
var Pending = require('./pending');
var utils = require('./utils');
Expand All @@ -19,8 +18,6 @@ var EVENT_ROOT_SUITE_RUN = Suite.constants.EVENT_ROOT_SUITE_RUN;
var STATE_FAILED = Runnable.constants.STATE_FAILED;
var STATE_PASSED = Runnable.constants.STATE_PASSED;
var STATE_PENDING = Runnable.constants.STATE_PENDING;
var dQuote = utils.dQuote;
var sQuote = utils.sQuote;
var stackFilter = utils.stackTraceFilter();
var stringify = utils.stringify;

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

if (leaks.length) {
var msg = 'global leak(s) detected: %s';
var error = new Error(util.format(msg, leaks.map(sQuote).join(', ')));
this.fail(test, error);
var msg = `global leak(s) detected: ${leaks.map(e => `'${e}'`).join(', ')}`;
this.fail(test, new Error(msg));
}
};

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

Runner.prototype.hook = function(name, fn) {
if (this._opts.dryRun) return fn();

var suite = this.suite;
var hooks = suite.getHooks(name);
var self = this;
Expand Down Expand Up @@ -557,16 +556,15 @@ Runner.prototype.hook = function(name, fn) {
function setHookTitle(hook) {
hook.originalTitle = hook.originalTitle || hook.title;
if (hook.ctx && hook.ctx.currentTest) {
hook.title =
hook.originalTitle + ' for ' + dQuote(hook.ctx.currentTest.title);
hook.title = `${hook.originalTitle} for "${hook.ctx.currentTest.title}"`;
} else {
var parentTitle;
if (hook.parent.title) {
parentTitle = hook.parent.title;
} else {
parentTitle = hook.parent.root ? '{root}' : '';
}
hook.title = hook.originalTitle + ' in ' + dQuote(parentTitle);
hook.title = `${hook.originalTitle} in "${parentTitle}"`;
}
}
}
Expand Down Expand Up @@ -612,7 +610,7 @@ Runner.prototype.hooks = function(name, suites, fn) {
};

/**
* Run hooks from the top level down.
* Run 'afterEach' hooks from bottom up.
*
* @param {String} name
* @param {Function} fn
Expand All @@ -624,7 +622,7 @@ Runner.prototype.hookUp = function(name, fn) {
};

/**
* Run hooks from the bottom up.
* Run 'beforeEach' hooks from top level down.
*
* @param {String} name
* @param {Function} fn
Expand Down Expand Up @@ -659,6 +657,8 @@ Runner.prototype.parents = function() {
* @private
*/
Runner.prototype.runTest = function(fn) {
if (this._opts.dryRun) return fn();

var self = this;
var test = this.test;

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

if (self.suite) {
// call hookUp afterEach
self.hookUp(HOOK_TYPE_AFTER_EACH, function(err2, errSuite2) {
self.suite = orig;
// some hooks may fail even now
Expand Down
38 changes: 0 additions & 38 deletions lib/utils.js
Expand Up @@ -518,44 +518,6 @@ exports.clamp = function clamp(value, range) {
return Math.min(Math.max(value, range[0]), range[1]);
};

/**
* Single quote text by combining with undirectional ASCII quotation marks.
*
* @description
* Provides a simple means of markup for quoting text to be used in output.
* Use this to quote names of variables, methods, and packages.
*
* <samp>package 'foo' cannot be found</samp>
*
* @private
* @param {string} str - Value to be quoted.
* @returns {string} quoted value
* @example
* sQuote('n') // => 'n'
*/
exports.sQuote = function(str) {
return "'" + str + "'";
};

/**
* Double quote text by combining with undirectional ASCII quotation marks.
*
* @description
* Provides a simple means of markup for quoting text to be used in output.
* Use this to quote names of datatypes, classes, pathnames, and strings.
*
* <samp>argument 'value' must be "string" or "number"</samp>
*
* @private
* @param {string} str - Value to be quoted.
* @returns {string} quoted value
* @example
* dQuote('number') // => "number"
*/
exports.dQuote = function(str) {
return '"' + str + '"';
};

/**
* It's a noop.
* @public
Expand Down
35 changes: 35 additions & 0 deletions test/integration/fixtures/options/dry-run/dry-run.fixture.js
@@ -0,0 +1,35 @@
'use strict';

describe.only('suite1', function() {
it.skip('test1 - report as skipped', function() { });

it('test2 - report as passed', function() { });

it('test3 - report as passed', function() {
throw new Error('this test should not run');
});
});

describe('suite2', function () {
before(function() {
throw new Error('this hook should not run');
});
beforeEach(function() {
throw new Error('this hook should not run');
});

it.only('test4 - report as passed', function () {
throw new Error('this test should not run');
});

it('test5 - should be ignored', function () {
throw new Error('this test should not run');
});

afterEach(function() {
throw new Error('this hook should not run');
});
after(function() {
throw new Error('this hook should not run');
});
});
30 changes: 30 additions & 0 deletions test/integration/options/dryRun.spec.js
@@ -0,0 +1,30 @@
'use strict';

var path = require('path').posix;
var helpers = require('../helpers');
var runMochaJSON = helpers.runMochaJSON;

describe('--dry-run', function() {
var args = ['--dry-run'];

it('should only report, but not execute any test', function(done) {
var fixture = path.join('options/dry-run', 'dry-run');
runMochaJSON(fixture, args, function(err, res) {
if (err) {
return done(err);
}

expect(res, 'to have passed')
.and(
'to have passed tests',
'test2 - report as passed',
'test3 - report as passed',
'test4 - report as passed'
)
.and('to have passed test count', 3)
.and('to have pending test count', 1)
.and('to have failed test count', 0);
done();
});
});
});
6 changes: 1 addition & 5 deletions test/integration/reporters.spec.js
Expand Up @@ -5,8 +5,6 @@ var fs = require('fs');
var crypto = require('crypto');
var path = require('path');
var run = require('./helpers').runMocha;
var utils = require('../../lib/utils');
var dQuote = utils.dQuote;

describe('reporters', function() {
describe('markdown', function() {
Expand Down Expand Up @@ -215,9 +213,7 @@ describe('reporters', function() {
return;
}

var pattern =
'^Error: invalid or unsupported TAP version: ' +
dQuote(invalidTapVersion);
var pattern = `^Error: invalid or unsupported TAP version: "${invalidTapVersion}"`;
expect(res, 'to satisfy', {
code: 1,
output: new RegExp(pattern, 'm')
Expand Down

0 comments on commit 8212306

Please sign in to comment.