Skip to content

Commit bbf0c11

Browse files
authored
feat: add new option 'fail-zero' (#4716)
1 parent 757b85d commit bbf0c11

File tree

9 files changed

+117
-59
lines changed

9 files changed

+117
-59
lines changed

docs/index.md

+14-9
Original file line numberDiff line numberDiff line change
@@ -876,7 +876,7 @@ Use this option to have Mocha check for global variables that are leaked while r
876876
877877
### `--dry-run`
878878

879-
> _New in v9.0.0._ Report tests without executing any of them, neither tests nor hooks.
879+
> _New in v9.0.0_ Report tests without executing any of them, neither tests nor hooks.
880880
881881
### `--exit`
882882

@@ -899,6 +899,10 @@ To ensure your tests aren't leaving messes around, here are some ideas to get st
899899
- Try something like [wtfnode][npm-wtfnode]
900900
- Use [`.only`](#exclusive-tests) until you find the test that causes Mocha to hang
901901

902+
### `--fail-zero`
903+
904+
> _New in v9.1.0_ Fail test run if no tests are encountered with `exit-code: 1`.
905+
902906
### `--forbid-only`
903907

904908
Enforce a rule that tests may not be exclusive (use of e.g., `describe.only()` or `it.only()` is disallowed).
@@ -1007,15 +1011,15 @@ Can be specified as a comma-delimited list.
10071011

10081012
### `--config <path>`
10091013

1010-
> _New in v6.0.0._
1014+
> _New in v6.0.0_
10111015
10121016
Specify an explicit path to a [configuration file](#configuring-mocha-nodejs).
10131017

10141018
By default, Mocha will search for a config file if `--config` is not specified; use `--no-config` to suppress this behavior.
10151019

10161020
### `--node-option <name>, -n <name>`
10171021

1018-
> _New in v9.1.0._
1022+
> _New in v9.1.0_
10191023
10201024
For Node.js and V8 options. Mocha forwards these options to Node.js by spawning a new child-process.<br>
10211025
The options are set without leading dashes `--`, e.g. `-n require=foo -n unhandled-rejections=strict`
@@ -1028,7 +1032,7 @@ Can also be specified as a comma-delimited list: `-n require=foo,unhandled-rejec
10281032
10291033
### `--package <path>`
10301034

1031-
> _New in v6.0.0._
1035+
> _New in v6.0.0_
10321036
10331037
Specify an explicit path to a [`package.json` file](#configuring-mocha-nodejs) (ostensibly containing configuration in a `mocha` property).
10341038

@@ -1042,7 +1046,7 @@ Specifying `--extension` will _remove_ `.js` as a test file extension; use `--ex
10421046

10431047
The option can be given multiple times. The option accepts a comma-delimited list: `--extension a,b` is equivalent to `--extension a --extension b`.
10441048

1045-
> _New in v8.2.0._
1049+
> _New in v8.2.0_
10461050
10471051
`--extension` now supports multipart extensions (e.g., `spec.js`), leading dots (`.js`) and combinations thereof (`.spec.js`);
10481052

@@ -1217,7 +1221,7 @@ These flags vary depending on your version of Node.js.
12171221

12181222
`node` flags can be defined in Mocha's [configuration](#configuring-mocha-nodejs).
12191223

1220-
> _New in v9.1.0._ You can also pass `node` flags to Node.js using [`--node-option`](#-node-option-name-n-name).
1224+
> _New in v9.1.0_ You can also pass `node` flags to Node.js using [`--node-option`](#-node-option-name-n-name).
12211225
12221226
### `--enable-source-maps`
12231227

@@ -1238,7 +1242,7 @@ Prepend `--v8-` to any flag listed in the output of `node --v8-options` (excludi
12381242

12391243
V8 flags can be defined in Mocha's [configuration](#configuring-mocha-nodejs).
12401244

1241-
> _New in v9.1.0._ You can also pass V8 flags (without `--v8-`) to Node.js using [`--node-option`](#-node-option-name-n-name).
1245+
> _New in v9.1.0_ You can also pass V8 flags (without `--v8-`) to Node.js using [`--node-option`](#-node-option-name-n-name).
12421246
12431247
## Parallel Tests
12441248

@@ -1369,7 +1373,7 @@ It's unlikely (but not impossible) to see a performance gain from a [job count](
13691373

13701374
## Root Hook Plugins
13711375

1372-
> _New in v8.0.0._
1376+
> _New in v8.0.0_
13731377
13741378
In some cases, you may want a [hook](#hooks) before (or after) every test in every file. These are called _root hooks_. Previous to v8.0.0, the way to accomplish this was to use `--file` combined with root hooks (see [example above](#root-hooks-are-not-global)). This still works in v8.0.0, but _not_ when running tests in parallel mode! For that reason, running root hooks using this method is _strongly discouraged_, and may be deprecated in the future.
13751379

@@ -1593,7 +1597,7 @@ If you're a library maintainer, and your library uses root hooks, you can migrat
15931597

15941598
## Global Fixtures
15951599

1596-
> New in v8.2.0
1600+
> _New in v8.2.0_
15971601
15981602
At first glance, _global fixtures_ seem similar to [root hooks](#root-hook-plugins). However, unlike root hooks, global fixtures:
15991603

@@ -2121,6 +2125,7 @@ mocha.setup({
21212125
bail: true,
21222126
checkLeaks: true,
21232127
dryRun: true,
2128+
failZero: true,
21242129
forbidOnly: true,
21252130
forbidPending: true,
21262131
global: ['MyLib'],

example/config/.mocharc.js

+1
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ module.exports = {
1515
diff: true,
1616
exit: false, // could be expressed as "'no-exit': true"
1717
extension: ['js', 'cjs', 'mjs'],
18+
'fail-zero': true,
1819
fgrep: 'something', // fgrep and grep are mutually exclusive
1920
file: ['/path/to/some/file', '/path/to/some/other/file'],
2021
'forbid-only': false,

example/config/.mocharc.yml

+1
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ delay: false
88
diff: true
99
exit: false # could be expressed as "no-exit: true"
1010
extension: ['js', 'cjs', 'mjs']
11+
fail-zero: true
1112
# fgrep and grep are mutually exclusive
1213
fgrep: 'something'
1314
file:

lib/cli/run-option-metadata.js

+1
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ const TYPES = (exports.types = {
3535
'diff',
3636
'dry-run',
3737
'exit',
38+
'fail-zero',
3839
'forbid-only',
3940
'forbid-pending',
4041
'full-trace',

lib/cli/run.js

+4
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,10 @@ exports.builder = yargs =>
9898
requiresArg: true,
9999
coerce: list
100100
},
101+
'fail-zero': {
102+
description: 'Fail test run if no test(s) encountered',
103+
group: GROUPS.RULES
104+
},
101105
fgrep: {
102106
conflicts: 'grep',
103107
description: 'Only run tests containing this string',

lib/mocha.js

+32-15
Original file line numberDiff line numberDiff line change
@@ -164,6 +164,7 @@ exports.run = function(...args) {
164164
* @param {boolean} [options.delay] - Delay root suite execution?
165165
* @param {boolean} [options.diff] - Show diff on failure?
166166
* @param {boolean} [options.dryRun] - Report tests without running them?
167+
* @param {boolean} [options.failZero] - Fail test run if zero tests?
167168
* @param {string} [options.fgrep] - Test filter given string.
168169
* @param {boolean} [options.forbidOnly] - Tests marked `only` fail the suite?
169170
* @param {boolean} [options.forbidPending] - Pending tests fail the suite?
@@ -223,6 +224,7 @@ function Mocha(options = {}) {
223224
'delay',
224225
'diff',
225226
'dryRun',
227+
'failZero',
226228
'forbidOnly',
227229
'forbidPending',
228230
'fullTrace',
@@ -778,20 +780,6 @@ Mocha.prototype.diff = function(diff) {
778780
return this;
779781
};
780782

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-
795783
/**
796784
* @summary
797785
* Sets timeout threshold value.
@@ -918,6 +906,34 @@ Mocha.prototype.delay = function delay() {
918906
return this;
919907
};
920908

909+
/**
910+
* Enables or disables running tests in dry-run mode.
911+
*
912+
* @public
913+
* @see [CLI option](../#-dry-run)
914+
* @param {boolean} [dryRun=true] - Whether to activate dry-run mode.
915+
* @return {Mocha} this
916+
* @chainable
917+
*/
918+
Mocha.prototype.dryRun = function(dryRun) {
919+
this.options.dryRun = dryRun !== false;
920+
return this;
921+
};
922+
923+
/**
924+
* Fails test run if no tests encountered with exit-code 1.
925+
*
926+
* @public
927+
* @see [CLI option](../#-fail-zero)
928+
* @param {boolean} [failZero=true] - Whether to fail test run.
929+
* @return {Mocha} this
930+
* @chainable
931+
*/
932+
Mocha.prototype.failZero = function(failZero) {
933+
this.options.failZero = failZero !== false;
934+
return this;
935+
};
936+
921937
/**
922938
* Causes tests marked `only` to fail the suite.
923939
*
@@ -1023,9 +1039,10 @@ Mocha.prototype.run = function(fn) {
10231039
var options = this.options;
10241040
options.files = this.files;
10251041
const runner = new this._runnerClass(suite, {
1042+
cleanReferencesAfterRun: this._cleanReferencesAfterRun,
10261043
delay: options.delay,
10271044
dryRun: options.dryRun,
1028-
cleanReferencesAfterRun: this._cleanReferencesAfterRun
1045+
failZero: options.failZero
10291046
});
10301047
createStatsCollector(runner);
10311048
var reporter = new this._reporter(runner, options);

lib/runner.js

+5-2
Original file line numberDiff line numberDiff line change
@@ -135,10 +135,11 @@ class Runner extends EventEmitter {
135135
* @public
136136
* @class
137137
* @param {Suite} suite - Root suite
138-
* @param {Object|boolean} [opts] - Options. If `boolean` (deprecated), whether or not to delay execution of root suite until ready.
138+
* @param {Object|boolean} [opts] - Options. If `boolean` (deprecated), whether to delay execution of root suite until ready.
139+
* @param {boolean} [opts.cleanReferencesAfterRun] - Whether to clean references to test fns and hooks when a suite is done.
139140
* @param {boolean} [opts.delay] - Whether to delay execution of root suite until ready.
140141
* @param {boolean} [opts.dryRun] - Whether to report tests without running them.
141-
* @param {boolean} [opts.cleanReferencesAfterRun] - Whether to clean references to test fns and hooks when a suite is done.
142+
* @param {boolean} [options.failZero] - Whether to fail test run if zero tests encountered.
142143
*/
143144
constructor(suite, opts) {
144145
super();
@@ -1044,6 +1045,8 @@ Runner.prototype.run = function(fn, opts = {}) {
10441045
fn = fn || function() {};
10451046

10461047
const end = () => {
1048+
if (!this.total && this._opts.failZero) this.failures = 1;
1049+
10471050
debug('run(): root suite completed; emitting %s', constants.EVENT_RUN_END);
10481051
this.emit(constants.EVENT_RUN_END);
10491052
};
+22
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
'use strict';
2+
3+
var helpers = require('../helpers');
4+
var runMochaJSON = helpers.runMochaJSON;
5+
6+
describe('--fail-zero', function() {
7+
var args = ['--fail-zero', '--grep', 'yyyyyy'];
8+
9+
it('should fail since no tests are encountered', function(done) {
10+
var fixture = '__default__.fixture.js';
11+
runMochaJSON(fixture, args, function(err, res) {
12+
if (err) {
13+
return done(err);
14+
}
15+
16+
expect(res, 'to have passed test count', 0)
17+
.and('to have test count', 0)
18+
.and('to have exit code', 1);
19+
done();
20+
});
21+
});
22+
});

test/unit/mocha.spec.js

+37-33
Original file line numberDiff line numberDiff line change
@@ -265,22 +265,18 @@ describe('Mocha', function() {
265265
});
266266

267267
describe('bail()', function() {
268-
describe('when provided no arguments', function() {
269-
it('should set the "bail" flag on the root suite', function() {
270-
mocha.bail();
271-
expect(suite.bail, 'to have a call satisfying', [true]).and(
272-
'was called once'
273-
);
274-
});
268+
it('should set the "bail" flag on the root suite', function() {
269+
mocha.bail();
270+
expect(suite.bail, 'to have a call satisfying', [true]).and(
271+
'was called once'
272+
);
275273
});
276274

277-
describe('when provided a falsy argument', function() {
278-
it('should unset the "bail" flag on the root suite', function() {
279-
mocha.bail(false);
280-
expect(suite.bail, 'to have a call satisfying', [false]).and(
281-
'was called once'
282-
);
283-
});
275+
it('should unset the "bail" flag on the root suite', function() {
276+
mocha.bail(false);
277+
expect(suite.bail, 'to have a call satisfying', [false]).and(
278+
'was called once'
279+
);
284280
});
285281

286282
it('should be chainable', function() {
@@ -344,25 +340,9 @@ describe('Mocha', function() {
344340
expect(mocha.options, 'to have property', 'diff', true);
345341
});
346342

347-
describe('when provided `false` argument', function() {
348-
it('should set the diff option to false', function() {
349-
mocha.diff(false);
350-
expect(mocha.options, 'to have property', 'diff', false);
351-
});
352-
});
353-
});
354-
355-
describe('dryRun()', function() {
356-
it('should set the dryRun option to true', function() {
357-
mocha.dryRun();
358-
expect(mocha.options, 'to have property', 'dryRun', true);
359-
});
360-
361-
describe('when provided `false` argument', function() {
362-
it('should set the dryRun option to false', function() {
363-
mocha.dryRun(false);
364-
expect(mocha.options, 'to have property', 'dryRun', false);
365-
});
343+
it('should set the diff option to false', function() {
344+
mocha.diff(false);
345+
expect(mocha.options, 'to have property', 'diff', false);
366346
});
367347
});
368348

@@ -385,6 +365,30 @@ describe('Mocha', function() {
385365
});
386366
});
387367

368+
describe('dryRun()', function() {
369+
it('should set the dryRun option to true', function() {
370+
mocha.dryRun();
371+
expect(mocha.options, 'to have property', 'dryRun', true);
372+
});
373+
374+
it('should set the dryRun option to false', function() {
375+
mocha.dryRun(false);
376+
expect(mocha.options, 'to have property', 'dryRun', false);
377+
});
378+
});
379+
380+
describe('failZero()', function() {
381+
it('should set the failZero option to true', function() {
382+
mocha.failZero();
383+
expect(mocha.options, 'to have property', 'failZero', true);
384+
});
385+
386+
it('should set the failZero option to false', function() {
387+
mocha.failZero(false);
388+
expect(mocha.options, 'to have property', 'failZero', false);
389+
});
390+
});
391+
388392
describe('forbidOnly()', function() {
389393
it('should set the forbidOnly option to true', function() {
390394
mocha.forbidOnly();

0 commit comments

Comments
 (0)