Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add new option "--dry-run" #4640

Merged
merged 4 commits into from Jun 5, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
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