diff --git a/.eslintignore b/.eslintignore
index 5b8791b988..d9a508ec53 100644
--- a/.eslintignore
+++ b/.eslintignore
@@ -4,4 +4,5 @@ mocha.js
docs/
out/
!lib/mocha.js
+test/integration/fixtures
!.*.js
diff --git a/.wallaby.js b/.wallaby.js
index fa6063fa55..a54f3576fc 100644
--- a/.wallaby.js
+++ b/.wallaby.js
@@ -38,8 +38,14 @@ module.exports = () => {
const runningMocha = wallaby.testFramework;
runningMocha.timeout(200);
// to expose it/describe etc. on the mocha under test
- const mochaUnderTest = new (require('./'))();
- mochaUnderTest.suite.emit('pre-require', global, '', mochaUnderTest);
+ const MochaUnderTest = require('./');
+ const mochaUnderTest = new MochaUnderTest();
+ mochaUnderTest.suite.emit(
+ MochaUnderTest.Suite.constants.EVENT_FILE_PRE_REQUIRE,
+ global,
+ '',
+ mochaUnderTest
+ );
// to make test/node-unit/color.spec.js pass, we need to run mocha in the project's folder context
const childProcess = require('child_process');
const execFile = childProcess.execFile;
@@ -53,6 +59,7 @@ module.exports = () => {
return execFile.apply(this, arguments);
};
require('./test/setup');
- }
+ },
+ debug: true
};
};
diff --git a/docs/api-tutorials/custom-reporter.md b/docs/api-tutorials/custom-reporter.md
new file mode 100644
index 0000000000..3f0313aac8
--- /dev/null
+++ b/docs/api-tutorials/custom-reporter.md
@@ -0,0 +1,120 @@
+Mocha allows you to define and use custom reporters install via `npm`.
+
+For example, if `mocha-foo-reporter` was published to the npm registry, you could install it via `npm install mocha-foo-reporter --save-dev`, then use it via `mocha --reporter mocha-foo-reporter`.
+
+## Example Custom Reporter
+
+If you're looking to get started quickly, here's an example of a custom reporter:
+
+
+
+```js
+// my-reporter.js
+'use strict';
+
+const Mocha = require('mocha');
+const {
+ EVENT_RUN_BEGIN,
+ EVENT_RUN_END,
+ EVENT_TEST_FAIL,
+ EVENT_TEST_PASS,
+ EVENT_SUITE_BEGIN,
+ EVENT_SUITE_END
+} = Mocha.Runner.constants;
+
+// this reporter outputs test results, indenting two spaces per suite
+class MyReporter {
+ constructor(runner) {
+ this._indents = 0;
+ const stats = runner.stats;
+
+ runner
+ .once(EVENT_RUN_BEGIN, () => {
+ console.log('start');
+ })
+ .on(EVENT_SUITE_BEGIN, () => {
+ this.increaseIndent();
+ })
+ .on(EVENT_SUITE_END, () => {
+ this.decreaseIndent();
+ })
+ .on(EVENT_TEST_PASS, test => {
+ // Test#fullTitle() returns the suite name(s)
+ // prepended to the test title
+ console.log(`${this.indent()}pass: ${test.fullTitle()}`);
+ })
+ .on(EVENT_TEST_FAIL, (test, err) => {
+ console.log(
+ `${this.indent()}fail: ${test.fullTitle()} - error: ${err.message}`
+ );
+ })
+ .once(EVENT_RUN_END, () => {
+ console.log(`end: ${stats.passes}/${stats.passes + stats.failures} ok`);
+ });
+ }
+
+ indent() {
+ return Array(this._indents).join(' ');
+ }
+
+ increaseIndent() {
+ this._indents++;
+ }
+
+ decreaseIndent() {
+ this._indents--;
+ }
+}
+
+module.exports = MyReporter;
+```
+
+
+
+To use this reporter, execute `mocha --reporter /path/to/my-reporter.js`.
+
+For further examples, the built-in reporter implementations are the [best place to look](https://github.com/mochajs/mocha/tree/master/lib/reporters). The [integration tests](https://github.com/mochajs/mocha/tree/master/test/reporters) may also be helpful.
+
+## The `Base` Reporter Class
+
+[Base] is an "abstract" class. It contains console-specific settings and utilities, commonly used by built-in reporters. Browsing the built-in reporter implementations, you may often see static properties of [Base] referenced.
+
+It's often useful--but not necessary--for a custom reporter to extend [Base].
+
+## Statistics
+
+Mocha adds a `stats` property of type [StatsCollector](/api/module-lib_stats-collector.html) to the reporter's `Runner` instance (passed as first argument to constructor).
+
+## Events
+
+A reporter should listen for events emitted from the `runner` (a singleton instance of [Runner]).
+
+The event names are exported from the `constants` property of `Mocha.Runner`:
+
+| Constant | Event Name | Event Arguments | Description |
+| -------------------- | ----------- | --------------- | ------------------------------------------------------------------------------------------- |
+| `EVENT_RUN_BEGIN` | `start` | _(n/a)_ | Execution will begin. |
+| `EVENT_RUN_END` | `end` | _(n/a)_ | All [Suite]s, [Test]s and [Hook]s have completed execution. |
+| `EVENT_DELAY_BEGIN` | `waiting` | _(n/a)_ | Waiting for `global.run()` to be called; only emitted when [delay] option is `true`. |
+| `EVENT_DELAY_END` | `ready` | _(n/a)_ | User called `global.run()` and the root suite is ready to execute. |
+| `EVENT_SUITE_BEGIN` | `suite` | `Suite` | The [Hook]s and [Test]s within a [Suite] will be executed, including any nested [Suite]s. |
+| `EVENT_SUITE_END` | `suite end` | `Suite` | The [Hook]s, [Test]s, and nested [Suite]s within a [Suite] have completed execution. |
+| `EVENT_HOOK_BEGIN` | `hook` | `Hook` | A [Hook] will be executed. |
+| `EVENT_HOOK_END` | `hook end` | `Hook` | A [Hook] has completed execution. |
+| `EVENT_TEST_BEGIN` | `test` | `Test` | A [Test] will be executed. |
+| `EVENT_TEST_END` | `test end` | `Test` | A [Test] has completed execution. |
+| `EVENT_TEST_FAIL` | `fail` | `Test`, `Error` | A [Test] has failed or thrown an exception. |
+| `EVENT_TEST_PASS` | `pass` | `Test` | A [Test] has passed. |
+| `EVENT_TEST_PENDING` | `pending` | `Test` | A [Test] was skipped. |
+| `EVENT_TEST_RETRY` | `retry` | `Test`, `Error` | A [Test] failed, but is about to be retried; only emitted if the `retry` option is nonzero. |
+
+**Please use these constants** instead of the event names in your own reporter! This will ensure compatibility with future versions of Mocha.
+
+> It's important to understand that all `Suite` callbacks will be run _before_ the [Runner] emits `EVENT_RUN_BEGIN`. Hooks and tests, however, won't run until _after_ the [Runner] emits `EVENT_RUN_BEGIN`.
+
+[runner]: /api/mocha.runner
+[test]: /api/mocha.test
+[hook]: /api/mocha.hook
+[suite]: /api/mocha.suite
+[base]: /api/mocha.reporters.base
+[delay]: /#delayed-root-suite
diff --git a/docs/api-tutorials/jsdoc.tutorials.json b/docs/api-tutorials/jsdoc.tutorials.json
new file mode 100644
index 0000000000..4880bf552f
--- /dev/null
+++ b/docs/api-tutorials/jsdoc.tutorials.json
@@ -0,0 +1,5 @@
+{
+ "custom-reporter": {
+ "title": "Create a Custom Reporter"
+ }
+}
diff --git a/jsdoc.conf.json b/jsdoc.conf.json
index f696d2c683..1407bf170e 100644
--- a/jsdoc.conf.json
+++ b/jsdoc.conf.json
@@ -1,32 +1,33 @@
{
- "tags": {
- "allowUnknownTags": true
+ "markdown": {
+ "hardwrap": true,
+ "parser": "gfm"
},
- "source": {
- "include": ["lib/", "./docs/API.md", "bin"]
+ "mocha-docdash": {
+ "sort": true,
+ "static": false
},
- "plugins": ["plugins/markdown"],
"opts": {
- "encoding": "utf8",
- "template": "node_modules/@mocha/docdash",
"destination": "docs/_dist/api",
+ "encoding": "utf8",
"recurse": true,
+ "template": "node_modules/@mocha/docdash",
+ "tutorials": "docs/api-tutorials",
"verbose": true
},
- "markdown": {
- "parser": "gfm",
- "hardwrap": true
+ "plugins": ["plugins/markdown"],
+ "source": {
+ "include": ["lib/", "./docs/API.md", "bin"]
+ },
+ "tags": {
+ "allowUnknownTags": true
},
"templates": {
"cleverLinks": false,
- "monospaceLinks": false,
"default": {
- "outputSourceFiles": true,
- "includeDate": false
- }
+ "includeDate": false,
+ "outputSourceFiles": true
},
- "mocha-docdash": {
- "static": false,
- "sort": true
+ "monospaceLinks": false
}
}
diff --git a/lib/browser/growl.js b/lib/browser/growl.js
index 8472cebb2b..016798501a 100644
--- a/lib/browser/growl.js
+++ b/lib/browser/growl.js
@@ -10,6 +10,7 @@
*/
var Date = global.Date;
var setTimeout = global.setTimeout;
+var EVENT_RUN_END = require('../runner').constants.EVENT_RUN_END;
/**
* Checks if browser notification support exists.
@@ -53,7 +54,7 @@ exports.notify = function(runner) {
.catch(notPermitted);
};
- runner.once('end', sendNotification);
+ runner.once(EVENT_RUN_END, sendNotification);
};
/**
diff --git a/lib/growl.js b/lib/growl.js
index d9ee37cf50..53164563bb 100644
--- a/lib/growl.js
+++ b/lib/growl.js
@@ -8,6 +8,7 @@
const os = require('os');
const path = require('path');
const {sync: which} = require('which');
+const {EVENT_RUN_END} = require('./runner').constants;
/**
* @summary
@@ -41,7 +42,7 @@ exports.isCapable = () => {
* @param {Runner} runner - Runner instance.
*/
exports.notify = runner => {
- runner.once('end', () => {
+ runner.once(EVENT_RUN_END, () => {
display(runner);
});
};
diff --git a/lib/interfaces/bdd.js b/lib/interfaces/bdd.js
index 87408f8a8d..ef8d281e17 100644
--- a/lib/interfaces/bdd.js
+++ b/lib/interfaces/bdd.js
@@ -1,6 +1,8 @@
'use strict';
var Test = require('../test');
+var EVENT_FILE_PRE_REQUIRE = require('../suite').constants
+ .EVENT_FILE_PRE_REQUIRE;
/**
* BDD-style interface:
@@ -22,7 +24,7 @@ var Test = require('../test');
module.exports = function bddInterface(suite) {
var suites = [suite];
- suite.on('pre-require', function(context, file, mocha) {
+ suite.on(EVENT_FILE_PRE_REQUIRE, function(context, file, mocha) {
var common = require('./common')(suites, context, mocha);
context.before = common.before;
diff --git a/lib/interfaces/exports.js b/lib/interfaces/exports.js
index da5e5d84d8..5b4c7bd6f4 100644
--- a/lib/interfaces/exports.js
+++ b/lib/interfaces/exports.js
@@ -22,7 +22,7 @@ var Test = require('../test');
module.exports = function(suite) {
var suites = [suite];
- suite.on('require', visit);
+ suite.on(Suite.constants.EVENT_FILE_REQUIRE, visit);
function visit(obj, file) {
var suite;
diff --git a/lib/interfaces/qunit.js b/lib/interfaces/qunit.js
index becc110859..a2dc332030 100644
--- a/lib/interfaces/qunit.js
+++ b/lib/interfaces/qunit.js
@@ -1,6 +1,8 @@
'use strict';
var Test = require('../test');
+var EVENT_FILE_PRE_REQUIRE = require('../suite').constants
+ .EVENT_FILE_PRE_REQUIRE;
/**
* QUnit-style interface:
@@ -30,7 +32,7 @@ var Test = require('../test');
module.exports = function qUnitInterface(suite) {
var suites = [suite];
- suite.on('pre-require', function(context, file, mocha) {
+ suite.on(EVENT_FILE_PRE_REQUIRE, function(context, file, mocha) {
var common = require('./common')(suites, context, mocha);
context.before = common.before;
diff --git a/lib/interfaces/tdd.js b/lib/interfaces/tdd.js
index c36c7afc44..a44c7fe27d 100644
--- a/lib/interfaces/tdd.js
+++ b/lib/interfaces/tdd.js
@@ -1,6 +1,8 @@
'use strict';
var Test = require('../test');
+var EVENT_FILE_PRE_REQUIRE = require('../suite').constants
+ .EVENT_FILE_PRE_REQUIRE;
/**
* TDD-style interface:
@@ -30,7 +32,7 @@ var Test = require('../test');
module.exports = function(suite) {
var suites = [suite];
- suite.on('pre-require', function(context, file, mocha) {
+ suite.on(EVENT_FILE_PRE_REQUIRE, function(context, file, mocha) {
var common = require('./common')(suites, context, mocha);
context.setup = common.beforeEach;
diff --git a/lib/mocha.js b/lib/mocha.js
index 687f5c77a3..9886ded56d 100644
--- a/lib/mocha.js
+++ b/lib/mocha.js
@@ -12,10 +12,14 @@ var builtinReporters = require('./reporters');
var growl = require('./growl');
var utils = require('./utils');
var mocharc = require('./mocharc.json');
-var assign = require('object.assign').getPolyfill();
var errors = require('./errors');
+var Suite = require('./suite');
+var createStatsCollector = require('./stats-collector');
var createInvalidReporterError = errors.createInvalidReporterError;
var createInvalidInterfaceError = errors.createInvalidInterfaceError;
+var EVENT_FILE_PRE_REQUIRE = Suite.constants.EVENT_FILE_PRE_REQUIRE;
+var EVENT_FILE_POST_REQUIRE = Suite.constants.EVENT_FILE_POST_REQUIRE;
+var EVENT_FILE_REQUIRE = Suite.constants.EVENT_FILE_REQUIRE;
exports = module.exports = Mocha;
@@ -51,7 +55,7 @@ exports.Context = require('./context');
* @memberof Mocha
*/
exports.Runner = require('./runner');
-exports.Suite = require('./suite');
+exports.Suite = Suite;
exports.Hook = require('./hook');
exports.Test = require('./test');
@@ -88,7 +92,7 @@ exports.Test = require('./test');
* @param {boolean} [options.useInlineDiffs] - Use inline diffs?
*/
function Mocha(options) {
- options = assign({}, mocharc, options || {});
+ options = utils.assign({}, mocharc, options || {});
this.files = [];
this.options = options;
// root suite
@@ -276,7 +280,7 @@ Mocha.prototype.ui = function(name) {
}
this._ui = this._ui(this.suite);
- this.suite.on('pre-require', function(context) {
+ this.suite.on(EVENT_FILE_PRE_REQUIRE, function(context) {
exports.afterEach = context.afterEach || context.teardown;
exports.after = context.after || context.suiteTeardown;
exports.beforeEach = context.beforeEach || context.setup;
@@ -313,9 +317,9 @@ Mocha.prototype.loadFiles = function(fn) {
var suite = this.suite;
this.files.forEach(function(file) {
file = path.resolve(file);
- suite.emit('pre-require', global, file, self);
- suite.emit('require', require(file), file, self);
- suite.emit('post-require', global, file, self);
+ suite.emit(EVENT_FILE_PRE_REQUIRE, global, file, self);
+ suite.emit(EVENT_FILE_REQUIRE, require(file), file, self);
+ suite.emit(EVENT_FILE_POST_REQUIRE, global, file, self);
});
fn && fn();
};
@@ -759,6 +763,7 @@ Mocha.prototype.run = function(fn) {
var options = this.options;
options.files = this.files;
var runner = new exports.Runner(suite, options.delay);
+ createStatsCollector(runner);
var reporter = new this._reporter(runner, options);
runner.ignoreLeaks = options.ignoreLeaks !== false;
runner.fullStackTrace = options.fullStackTrace;
diff --git a/lib/reporters/base.js b/lib/reporters/base.js
index 16d4ca586e..343b94fb9e 100644
--- a/lib/reporters/base.js
+++ b/lib/reporters/base.js
@@ -11,6 +11,9 @@ var diff = require('diff');
var milliseconds = require('ms');
var utils = require('../utils');
var supportsColor = process.browser ? null : require('supports-color');
+var constants = require('../runner').constants;
+var EVENT_TEST_PASS = constants.EVENT_TEST_PASS;
+var EVENT_TEST_FAIL = constants.EVENT_TEST_FAIL;
/**
* Expose `Base`.
@@ -274,7 +277,7 @@ function Base(runner) {
this.stats = runner.stats; // assigned so Reporters keep a closer reference
this.runner = runner;
- runner.on('pass', function(test) {
+ runner.on(EVENT_TEST_PASS, function(test) {
if (test.duration > test.slow()) {
test.speed = 'slow';
} else if (test.duration > test.slow() / 2) {
@@ -284,7 +287,7 @@ function Base(runner) {
}
});
- runner.on('fail', function(test, err) {
+ runner.on(EVENT_TEST_FAIL, function(test, err) {
if (showDiff(err)) {
stringifyDiffObjs(err);
}
diff --git a/lib/reporters/doc.js b/lib/reporters/doc.js
index ce183d6766..67246a9931 100644
--- a/lib/reporters/doc.js
+++ b/lib/reporters/doc.js
@@ -8,6 +8,11 @@
var Base = require('./base');
var utils = require('../utils');
+var constants = require('../runner').constants;
+var EVENT_TEST_PASS = constants.EVENT_TEST_PASS;
+var EVENT_TEST_FAIL = constants.EVENT_TEST_FAIL;
+var EVENT_SUITE_BEGIN = constants.EVENT_SUITE_BEGIN;
+var EVENT_SUITE_END = constants.EVENT_SUITE_END;
/**
* Expose `Doc`.
@@ -33,7 +38,7 @@ function Doc(runner) {
return Array(indents).join(' ');
}
- runner.on('suite', function(suite) {
+ runner.on(EVENT_SUITE_BEGIN, function(suite) {
if (suite.root) {
return;
}
@@ -44,7 +49,7 @@ function Doc(runner) {
console.log('%s
', indent());
});
- runner.on('suite end', function(suite) {
+ runner.on(EVENT_SUITE_END, function(suite) {
if (suite.root) {
return;
}
@@ -54,13 +59,13 @@ function Doc(runner) {
--indents;
});
- runner.on('pass', function(test) {
+ runner.on(EVENT_TEST_PASS, function(test) {
console.log('%s - %s
', indent(), utils.escape(test.title));
var code = utils.escape(utils.clean(test.body));
console.log('%s %s
', indent(), code);
});
- runner.on('fail', function(test, err) {
+ runner.on(EVENT_TEST_FAIL, function(test, err) {
console.log(
'%s - %s
',
indent(),
diff --git a/lib/reporters/dot.js b/lib/reporters/dot.js
index 8479c31e6d..26d4ff953d 100644
--- a/lib/reporters/dot.js
+++ b/lib/reporters/dot.js
@@ -8,6 +8,12 @@
var Base = require('./base');
var inherits = require('../utils').inherits;
+var constants = require('../runner').constants;
+var EVENT_TEST_PASS = constants.EVENT_TEST_PASS;
+var EVENT_TEST_FAIL = constants.EVENT_TEST_FAIL;
+var EVENT_RUN_BEGIN = constants.EVENT_RUN_BEGIN;
+var EVENT_TEST_PENDING = constants.EVENT_TEST_PENDING;
+var EVENT_RUN_END = constants.EVENT_RUN_END;
/**
* Expose `Dot`.
@@ -31,18 +37,18 @@ function Dot(runner) {
var width = (Base.window.width * 0.75) | 0;
var n = -1;
- runner.on('start', function() {
+ runner.on(EVENT_RUN_BEGIN, function() {
process.stdout.write('\n');
});
- runner.on('pending', function() {
+ runner.on(EVENT_TEST_PENDING, function() {
if (++n % width === 0) {
process.stdout.write('\n ');
}
process.stdout.write(Base.color('pending', Base.symbols.comma));
});
- runner.on('pass', function(test) {
+ runner.on(EVENT_TEST_PASS, function(test) {
if (++n % width === 0) {
process.stdout.write('\n ');
}
@@ -53,14 +59,14 @@ function Dot(runner) {
}
});
- runner.on('fail', function() {
+ runner.on(EVENT_TEST_FAIL, function() {
if (++n % width === 0) {
process.stdout.write('\n ');
}
process.stdout.write(Base.color('fail', Base.symbols.bang));
});
- runner.once('end', function() {
+ runner.once(EVENT_RUN_END, function() {
console.log();
self.epilogue();
});
diff --git a/lib/reporters/html.js b/lib/reporters/html.js
index 71bc21e113..3c854eb529 100644
--- a/lib/reporters/html.js
+++ b/lib/reporters/html.js
@@ -12,6 +12,12 @@ var Base = require('./base');
var utils = require('../utils');
var Progress = require('../browser/progress');
var escapeRe = require('escape-string-regexp');
+var constants = require('../runner').constants;
+var EVENT_TEST_PASS = constants.EVENT_TEST_PASS;
+var EVENT_TEST_FAIL = constants.EVENT_TEST_FAIL;
+var EVENT_SUITE_BEGIN = constants.EVENT_SUITE_BEGIN;
+var EVENT_SUITE_END = constants.EVENT_SUITE_END;
+var EVENT_TEST_PENDING = constants.EVENT_TEST_PENDING;
var escape = utils.escape;
/**
@@ -112,7 +118,7 @@ function HTML(runner) {
progress.size(40);
}
- runner.on('suite', function(suite) {
+ runner.on(EVENT_SUITE_BEGIN, function(suite) {
if (suite.root) {
return;
}
@@ -131,7 +137,7 @@ function HTML(runner) {
el.appendChild(stack[0]);
});
- runner.on('suite end', function(suite) {
+ runner.on(EVENT_SUITE_END, function(suite) {
if (suite.root) {
updateStats();
return;
@@ -139,7 +145,7 @@ function HTML(runner) {
stack.shift();
});
- runner.on('pass', function(test) {
+ runner.on(EVENT_TEST_PASS, function(test) {
var url = self.testURL(test);
var markup =
'
%e%ems ' +
@@ -152,7 +158,7 @@ function HTML(runner) {
updateStats();
});
- runner.on('fail', function(test) {
+ runner.on(EVENT_TEST_FAIL, function(test) {
var el = fragment(
'
%e ' +
playIcon +
@@ -208,7 +214,7 @@ function HTML(runner) {
updateStats();
});
- runner.on('pending', function(test) {
+ runner.on(EVENT_TEST_PENDING, function(test) {
var el = fragment(
'%e
',
test.title
diff --git a/lib/reporters/json-stream.js b/lib/reporters/json-stream.js
index c05050227b..5ca0a42051 100644
--- a/lib/reporters/json-stream.js
+++ b/lib/reporters/json-stream.js
@@ -7,6 +7,11 @@
*/
var Base = require('./base');
+var constants = require('../runner').constants;
+var EVENT_TEST_PASS = constants.EVENT_TEST_PASS;
+var EVENT_TEST_FAIL = constants.EVENT_TEST_FAIL;
+var EVENT_RUN_BEGIN = constants.EVENT_RUN_BEGIN;
+var EVENT_RUN_END = constants.EVENT_RUN_END;
/**
* Expose `JSONStream`.
@@ -29,22 +34,22 @@ function JSONStream(runner) {
var self = this;
var total = runner.total;
- runner.once('start', function() {
+ runner.once(EVENT_RUN_BEGIN, function() {
writeEvent(['start', {total: total}]);
});
- runner.on('pass', function(test) {
+ runner.on(EVENT_TEST_PASS, function(test) {
writeEvent(['pass', clean(test)]);
});
- runner.on('fail', function(test, err) {
+ runner.on(EVENT_TEST_FAIL, function(test, err) {
test = clean(test);
test.err = err.message;
test.stack = err.stack || null;
writeEvent(['fail', test]);
});
- runner.once('end', function() {
+ runner.once(EVENT_RUN_END, function() {
writeEvent(['end', self.stats]);
});
}
diff --git a/lib/reporters/json.js b/lib/reporters/json.js
index c5106d3a65..8678b3a18d 100644
--- a/lib/reporters/json.js
+++ b/lib/reporters/json.js
@@ -7,6 +7,12 @@
*/
var Base = require('./base');
+var constants = require('../runner').constants;
+var EVENT_TEST_PASS = constants.EVENT_TEST_PASS;
+var EVENT_TEST_FAIL = constants.EVENT_TEST_FAIL;
+var EVENT_TEST_END = constants.EVENT_TEST_END;
+var EVENT_RUN_END = constants.EVENT_RUN_END;
+var EVENT_TEST_PENDING = constants.EVENT_TEST_PENDING;
/**
* Expose `JSON`.
@@ -32,23 +38,23 @@ function JSONReporter(runner) {
var failures = [];
var passes = [];
- runner.on('test end', function(test) {
+ runner.on(EVENT_TEST_END, function(test) {
tests.push(test);
});
- runner.on('pass', function(test) {
+ runner.on(EVENT_TEST_PASS, function(test) {
passes.push(test);
});
- runner.on('fail', function(test) {
+ runner.on(EVENT_TEST_FAIL, function(test) {
failures.push(test);
});
- runner.on('pending', function(test) {
+ runner.on(EVENT_TEST_PENDING, function(test) {
pending.push(test);
});
- runner.once('end', function() {
+ runner.once(EVENT_RUN_END, function() {
var obj = {
stats: self.stats,
tests: tests.map(clean),
diff --git a/lib/reporters/landing.js b/lib/reporters/landing.js
index a33267adeb..6c16cda7e5 100644
--- a/lib/reporters/landing.js
+++ b/lib/reporters/landing.js
@@ -8,6 +8,12 @@
var Base = require('./base');
var inherits = require('../utils').inherits;
+var constants = require('../runner').constants;
+var EVENT_RUN_BEGIN = constants.EVENT_RUN_BEGIN;
+var EVENT_RUN_END = constants.EVENT_RUN_END;
+var EVENT_TEST_END = constants.EVENT_TEST_END;
+var STATE_FAILED = require('../runnable').constants.STATE_FAILED;
+
var cursor = Base.cursor;
var color = Base.color;
@@ -60,17 +66,17 @@ function Landing(runner) {
return ' ' + color('runway', buf);
}
- runner.on('start', function() {
+ runner.on(EVENT_RUN_BEGIN, function() {
stream.write('\n\n\n ');
cursor.hide();
});
- runner.on('test end', function(test) {
+ runner.on(EVENT_TEST_END, function(test) {
// check if the plane crashed
var col = crashed === -1 ? ((width * ++n) / total) | 0 : crashed;
// show the crash
- if (test.state === 'failed') {
+ if (test.state === STATE_FAILED) {
plane = color('plane crash', '✈');
crashed = col;
}
@@ -86,7 +92,7 @@ function Landing(runner) {
stream.write('\u001b[0m');
});
- runner.once('end', function() {
+ runner.once(EVENT_RUN_END, function() {
cursor.show();
console.log();
self.epilogue();
diff --git a/lib/reporters/list.js b/lib/reporters/list.js
index 4e324b61c7..db4bd860e7 100644
--- a/lib/reporters/list.js
+++ b/lib/reporters/list.js
@@ -8,6 +8,13 @@
var Base = require('./base');
var inherits = require('../utils').inherits;
+var constants = require('../runner').constants;
+var EVENT_RUN_BEGIN = constants.EVENT_RUN_BEGIN;
+var EVENT_RUN_END = constants.EVENT_RUN_END;
+var EVENT_TEST_BEGIN = constants.EVENT_TEST_BEGIN;
+var EVENT_TEST_FAIL = constants.EVENT_TEST_FAIL;
+var EVENT_TEST_PASS = constants.EVENT_TEST_PASS;
+var EVENT_TEST_PENDING = constants.EVENT_TEST_PENDING;
var color = Base.color;
var cursor = Base.cursor;
@@ -32,20 +39,20 @@ function List(runner) {
var self = this;
var n = 0;
- runner.on('start', function() {
+ runner.on(EVENT_RUN_BEGIN, function() {
console.log();
});
- runner.on('test', function(test) {
+ runner.on(EVENT_TEST_BEGIN, function(test) {
process.stdout.write(color('pass', ' ' + test.fullTitle() + ': '));
});
- runner.on('pending', function(test) {
+ runner.on(EVENT_TEST_PENDING, function(test) {
var fmt = color('checkmark', ' -') + color('pending', ' %s');
console.log(fmt, test.fullTitle());
});
- runner.on('pass', function(test) {
+ runner.on(EVENT_TEST_PASS, function(test) {
var fmt =
color('checkmark', ' ' + Base.symbols.ok) +
color('pass', ' %s: ') +
@@ -54,12 +61,12 @@ function List(runner) {
console.log(fmt, test.fullTitle(), test.duration);
});
- runner.on('fail', function(test) {
+ runner.on(EVENT_TEST_FAIL, function(test) {
cursor.CR();
console.log(color('fail', ' %d) %s'), ++n, test.fullTitle());
});
- runner.once('end', self.epilogue.bind(self));
+ runner.once(EVENT_RUN_END, self.epilogue.bind(self));
}
/**
diff --git a/lib/reporters/markdown.js b/lib/reporters/markdown.js
index 70926ac617..f202c8f951 100644
--- a/lib/reporters/markdown.js
+++ b/lib/reporters/markdown.js
@@ -8,6 +8,11 @@
var Base = require('./base');
var utils = require('../utils');
+var constants = require('../runner').constants;
+var EVENT_RUN_END = constants.EVENT_RUN_END;
+var EVENT_SUITE_BEGIN = constants.EVENT_SUITE_BEGIN;
+var EVENT_SUITE_END = constants.EVENT_SUITE_END;
+var EVENT_TEST_PASS = constants.EVENT_TEST_PASS;
/**
* Constants
@@ -77,18 +82,18 @@ function Markdown(runner) {
generateTOC(runner.suite);
- runner.on('suite', function(suite) {
+ runner.on(EVENT_SUITE_BEGIN, function(suite) {
++level;
var slug = utils.slug(suite.fullTitle());
buf += '' + '\n';
buf += title(suite.title) + '\n';
});
- runner.on('suite end', function() {
+ runner.on(EVENT_SUITE_END, function() {
--level;
});
- runner.on('pass', function(test) {
+ runner.on(EVENT_TEST_PASS, function(test) {
var code = utils.clean(test.body);
buf += test.title + '.\n';
buf += '\n```js\n';
@@ -96,7 +101,7 @@ function Markdown(runner) {
buf += '```\n\n';
});
- runner.once('end', function() {
+ runner.once(EVENT_RUN_END, function() {
process.stdout.write('# TOC\n');
process.stdout.write(generateTOC(runner.suite));
process.stdout.write(buf);
diff --git a/lib/reporters/min.js b/lib/reporters/min.js
index 3277ed3442..930c97edbc 100644
--- a/lib/reporters/min.js
+++ b/lib/reporters/min.js
@@ -8,6 +8,9 @@
var Base = require('./base');
var inherits = require('../utils').inherits;
+var constants = require('../runner').constants;
+var EVENT_RUN_END = constants.EVENT_RUN_END;
+var EVENT_RUN_BEGIN = constants.EVENT_RUN_BEGIN;
/**
* Expose `Min`.
@@ -27,14 +30,14 @@ exports = module.exports = Min;
function Min(runner) {
Base.call(this, runner);
- runner.on('start', function() {
+ runner.on(EVENT_RUN_BEGIN, function() {
// clear screen
process.stdout.write('\u001b[2J');
// set cursor position
process.stdout.write('\u001b[1;3H');
});
- runner.once('end', this.epilogue.bind(this));
+ runner.once(EVENT_RUN_END, this.epilogue.bind(this));
}
/**
diff --git a/lib/reporters/nyan.js b/lib/reporters/nyan.js
index c91cd5ddf3..9c0a44a653 100644
--- a/lib/reporters/nyan.js
+++ b/lib/reporters/nyan.js
@@ -7,7 +7,13 @@
*/
var Base = require('./base');
+var constants = require('../runner').constants;
var inherits = require('../utils').inherits;
+var EVENT_RUN_BEGIN = constants.EVENT_RUN_BEGIN;
+var EVENT_TEST_PENDING = constants.EVENT_TEST_PENDING;
+var EVENT_TEST_PASS = constants.EVENT_TEST_PASS;
+var EVENT_RUN_END = constants.EVENT_RUN_END;
+var EVENT_TEST_FAIL = constants.EVENT_TEST_FAIL;
/**
* Expose `Dot`.
@@ -40,24 +46,24 @@ function NyanCat(runner) {
this.trajectories = [[], [], [], []];
this.trajectoryWidthMax = width - nyanCatWidth;
- runner.on('start', function() {
+ runner.on(EVENT_RUN_BEGIN, function() {
Base.cursor.hide();
self.draw();
});
- runner.on('pending', function() {
+ runner.on(EVENT_TEST_PENDING, function() {
self.draw();
});
- runner.on('pass', function() {
+ runner.on(EVENT_TEST_PASS, function() {
self.draw();
});
- runner.on('fail', function() {
+ runner.on(EVENT_TEST_FAIL, function() {
self.draw();
});
- runner.once('end', function() {
+ runner.once(EVENT_RUN_END, function() {
Base.cursor.show();
for (var i = 0; i < self.numberOfLines; i++) {
write('\n');
diff --git a/lib/reporters/progress.js b/lib/reporters/progress.js
index 5b174f0d5b..470feb982c 100644
--- a/lib/reporters/progress.js
+++ b/lib/reporters/progress.js
@@ -7,6 +7,10 @@
*/
var Base = require('./base');
+var constants = require('../runner').constants;
+var EVENT_RUN_BEGIN = constants.EVENT_RUN_BEGIN;
+var EVENT_TEST_END = constants.EVENT_TEST_END;
+var EVENT_RUN_END = constants.EVENT_RUN_END;
var inherits = require('../utils').inherits;
var color = Base.color;
var cursor = Base.cursor;
@@ -53,13 +57,13 @@ function Progress(runner, options) {
options.verbose = reporterOptions.verbose || false;
// tests started
- runner.on('start', function() {
+ runner.on(EVENT_RUN_BEGIN, function() {
console.log();
cursor.hide();
});
// tests complete
- runner.on('test end', function() {
+ runner.on(EVENT_TEST_END, function() {
complete++;
var percent = complete / total;
@@ -85,7 +89,7 @@ function Progress(runner, options) {
// tests are complete, output some stats
// and the failures if any
- runner.once('end', function() {
+ runner.once(EVENT_RUN_END, function() {
cursor.show();
console.log();
self.epilogue();
diff --git a/lib/reporters/spec.js b/lib/reporters/spec.js
index 82a438fba7..49522096ae 100644
--- a/lib/reporters/spec.js
+++ b/lib/reporters/spec.js
@@ -7,6 +7,14 @@
*/
var Base = require('./base');
+var constants = require('../runner').constants;
+var EVENT_RUN_BEGIN = constants.EVENT_RUN_BEGIN;
+var EVENT_RUN_END = constants.EVENT_RUN_END;
+var EVENT_SUITE_BEGIN = constants.EVENT_SUITE_BEGIN;
+var EVENT_SUITE_END = constants.EVENT_SUITE_END;
+var EVENT_TEST_FAIL = constants.EVENT_TEST_FAIL;
+var EVENT_TEST_PASS = constants.EVENT_TEST_PASS;
+var EVENT_TEST_PENDING = constants.EVENT_TEST_PENDING;
var inherits = require('../utils').inherits;
var color = Base.color;
@@ -36,28 +44,28 @@ function Spec(runner) {
return Array(indents).join(' ');
}
- runner.on('start', function() {
+ runner.on(EVENT_RUN_BEGIN, function() {
console.log();
});
- runner.on('suite', function(suite) {
+ runner.on(EVENT_SUITE_BEGIN, function(suite) {
++indents;
console.log(color('suite', '%s%s'), indent(), suite.title);
});
- runner.on('suite end', function() {
+ runner.on(EVENT_SUITE_END, function() {
--indents;
if (indents === 1) {
console.log();
}
});
- runner.on('pending', function(test) {
+ runner.on(EVENT_TEST_PENDING, function(test) {
var fmt = indent() + color('pending', ' - %s');
console.log(fmt, test.title);
});
- runner.on('pass', function(test) {
+ runner.on(EVENT_TEST_PASS, function(test) {
var fmt;
if (test.speed === 'fast') {
fmt =
@@ -75,11 +83,11 @@ function Spec(runner) {
}
});
- runner.on('fail', function(test) {
+ runner.on(EVENT_TEST_FAIL, function(test) {
console.log(indent() + color('fail', ' %d) %s'), ++n, test.title);
});
- runner.once('end', self.epilogue.bind(self));
+ runner.once(EVENT_RUN_END, self.epilogue.bind(self));
}
/**
diff --git a/lib/reporters/tap.js b/lib/reporters/tap.js
index 4c7c15a864..f71269bebb 100644
--- a/lib/reporters/tap.js
+++ b/lib/reporters/tap.js
@@ -8,6 +8,13 @@
var util = require('util');
var Base = require('./base');
+var constants = require('../runner').constants;
+var EVENT_TEST_PASS = constants.EVENT_TEST_PASS;
+var EVENT_TEST_FAIL = constants.EVENT_TEST_FAIL;
+var EVENT_RUN_BEGIN = constants.EVENT_RUN_BEGIN;
+var EVENT_RUN_END = constants.EVENT_RUN_END;
+var EVENT_TEST_PENDING = constants.EVENT_TEST_PENDING;
+var EVENT_TEST_END = constants.EVENT_TEST_END;
var inherits = require('../utils').inherits;
var sprintf = util.format;
@@ -42,29 +49,29 @@ function TAP(runner, options) {
this._producer = createProducer(tapVersion);
- runner.once('start', function() {
+ runner.once(EVENT_RUN_BEGIN, function() {
var ntests = runner.grepTotal(runner.suite);
self._producer.writeVersion();
self._producer.writePlan(ntests);
});
- runner.on('test end', function() {
+ runner.on(EVENT_TEST_END, function() {
++n;
});
- runner.on('pending', function(test) {
+ runner.on(EVENT_TEST_PENDING, function(test) {
self._producer.writePending(n, test);
});
- runner.on('pass', function(test) {
+ runner.on(EVENT_TEST_PASS, function(test) {
self._producer.writePass(n, test);
});
- runner.on('fail', function(test, err) {
+ runner.on(EVENT_TEST_FAIL, function(test, err) {
self._producer.writeFail(n, test, err);
});
- runner.once('end', function() {
+ runner.once(EVENT_RUN_END, function() {
self._producer.writeEpilogue(runner.stats);
});
}
diff --git a/lib/reporters/xunit.js b/lib/reporters/xunit.js
index 643367aa9d..faccdaf7e9 100644
--- a/lib/reporters/xunit.js
+++ b/lib/reporters/xunit.js
@@ -8,13 +8,20 @@
var Base = require('./base');
var utils = require('../utils');
-var inherits = utils.inherits;
var fs = require('fs');
-var escape = utils.escape;
var mkdirp = require('mkdirp');
var path = require('path');
var errors = require('../errors');
+var constants = require('../runner').constants;
+var EVENT_TEST_PASS = constants.EVENT_TEST_PASS;
+var EVENT_TEST_FAIL = constants.EVENT_TEST_FAIL;
+var EVENT_RUN_END = constants.EVENT_RUN_END;
+var EVENT_TEST_PENDING = constants.EVENT_TEST_PENDING;
+var STATE_FAILED = require('../runnable').constants.STATE_FAILED;
+var inherits = utils.inherits;
+var escape = utils.escape;
var createNotSupportedError = errors.createNotSupportedError;
+
/**
* Save timer references to avoid Sinon interfering (see GH-237).
*/
@@ -65,19 +72,19 @@ function XUnit(runner, options) {
// fall back to the default suite name
suiteName = suiteName || DEFAULT_SUITE_NAME;
- runner.on('pending', function(test) {
+ runner.on(EVENT_TEST_PENDING, function(test) {
tests.push(test);
});
- runner.on('pass', function(test) {
+ runner.on(EVENT_TEST_PASS, function(test) {
tests.push(test);
});
- runner.on('fail', function(test) {
+ runner.on(EVENT_TEST_FAIL, function(test) {
tests.push(test);
});
- runner.once('end', function() {
+ runner.once(EVENT_RUN_END, function() {
self.write(
tag(
'testsuite',
@@ -152,7 +159,7 @@ XUnit.prototype.test = function(test) {
time: test.duration / 1000 || 0
};
- if (test.state === 'failed') {
+ if (test.state === STATE_FAILED) {
var err = test.err;
var diff =
Base.hideDiff || !err.actual || !err.expected
diff --git a/lib/runnable.js b/lib/runnable.js
index be06cdf318..2509d19996 100644
--- a/lib/runnable.js
+++ b/lib/runnable.js
@@ -1,4 +1,5 @@
'use strict';
+
var EventEmitter = require('events').EventEmitter;
var Pending = require('./pending');
var debug = require('debug')('mocha:runnable');
@@ -16,10 +17,11 @@ var toString = Object.prototype.toString;
module.exports = Runnable;
/**
- * Initialize a new `Runnable` with the given `title` and callback `fn`. Derived from [EventEmitter](https://nodejs.org/api/events.html#events_class_eventemitter)
+ * Initialize a new `Runnable` with the given `title` and callback `fn`.
*
* @class
- * @extends EventEmitter
+ * @extends external:EventEmitter
+ * @public
* @param {String} title
* @param {Function} fn
*/
@@ -149,7 +151,7 @@ Runnable.prototype.isPending = function() {
* @private
*/
Runnable.prototype.isFailed = function() {
- return !this.isPending() && this.state === 'failed';
+ return !this.isPending() && this.state === constants.STATE_FAILED;
};
/**
@@ -158,7 +160,7 @@ Runnable.prototype.isFailed = function() {
* @private
*/
Runnable.prototype.isPassed = function() {
- return !this.isPending() && this.state === 'passed';
+ return !this.isPending() && this.state === constants.STATE_PASSED;
};
/**
@@ -450,3 +452,26 @@ Runnable.prototype._timeoutError = function(ms) {
}
return new Error(msg);
};
+
+var constants = utils.defineConstants(
+ /**
+ * {@link Runnable}-related constants.
+ * @public
+ * @memberof Runnable
+ * @readonly
+ * @static
+ * @alias constants
+ * @enum {string}
+ */ {
+ /**
+ * Value of `state` prop when a `Runnable` has failed
+ */
+ STATE_FAILED: 'failed',
+ /**
+ * Value of `state` prop when a `Runnable` has passed
+ */
+ STATE_PASSED: 'passed'
+ }
+);
+
+Runnable.constants = constants;
diff --git a/lib/runner.js b/lib/runner.js
index 7c4435219e..6952efa92c 100644
--- a/lib/runner.js
+++ b/lib/runner.js
@@ -1,18 +1,19 @@
'use strict';
-/**
- * @module Runner
- */
-/**
- * Module dependencies.
- */
var EventEmitter = require('events').EventEmitter;
var Pending = require('./pending');
var utils = require('./utils');
var inherits = utils.inherits;
var debug = require('debug')('mocha:runner');
var Runnable = require('./runnable');
-var createStatsCollector = require('./stats-collector');
+var Suite = require('./suite');
+var HOOK_TYPE_BEFORE_EACH = Suite.constants.HOOK_TYPE_BEFORE_EACH;
+var HOOK_TYPE_AFTER_EACH = Suite.constants.HOOK_TYPE_AFTER_EACH;
+var HOOK_TYPE_AFTER_ALL = Suite.constants.HOOK_TYPE_AFTER_ALL;
+var HOOK_TYPE_BEFORE_ALL = Suite.constants.HOOK_TYPE_BEFORE_ALL;
+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 stackFilter = utils.stackTraceFilter();
var stringify = utils.stringify;
var type = utils.type;
@@ -20,8 +21,8 @@ var undefinedError = utils.undefinedError;
/**
* Non-enumerable globals.
+ * @readonly
*/
-
var globals = [
'setTimeout',
'clearTimeout',
@@ -33,33 +34,85 @@ var globals = [
'clearImmediate'
];
-/**
- * Expose `Runner`.
- */
+var constants = utils.defineConstants(
+ /**
+ * {@link Runner}-related constants.
+ * @public
+ * @memberof Runner
+ * @readonly
+ * @alias constants
+ * @static
+ * @enum {string}
+ */
+ {
+ /**
+ * Emitted when {@link Hook} execution begins
+ */
+ EVENT_HOOK_BEGIN: 'hook',
+ /**
+ * Emitted when {@link Hook} execution ends
+ */
+ EVENT_HOOK_END: 'hook end',
+ /**
+ * Emitted when Root {@link Suite} execution begins (all files have been parsed and hooks/tests are ready for execution)
+ */
+ EVENT_RUN_BEGIN: 'start',
+ /**
+ * Emitted when Root {@link Suite} execution has been delayed via `delay` option
+ */
+ EVENT_DELAY_BEGIN: 'waiting',
+ /**
+ * Emitted when delayed Root {@link Suite} execution is triggered by user via `global.run()`
+ */
+ EVENT_DELAY_END: 'ready',
+ /**
+ * Emitted when Root {@link Suite} execution ends
+ */
+ EVENT_RUN_END: 'end',
+ /**
+ * Emitted when {@link Suite} execution begins
+ */
+ EVENT_SUITE_BEGIN: 'suite',
+ /**
+ * Emitted when {@link Suite} execution ends
+ */
+ EVENT_SUITE_END: 'suite end',
+ /**
+ * Emitted when {@link Test} execution begins
+ */
+ EVENT_TEST_BEGIN: 'test',
+ /**
+ * Emitted when {@link Test} execution ends
+ */
+ EVENT_TEST_END: 'test end',
+ /**
+ * Emitted when {@link Test} execution fails
+ */
+ EVENT_TEST_FAIL: 'fail',
+ /**
+ * Emitted when {@link Test} execution succeeds
+ */
+ EVENT_TEST_PASS: 'pass',
+ /**
+ * Emitted when {@link Test} becomes pending
+ */
+ EVENT_TEST_PENDING: 'pending',
+ /**
+ * Emitted when {@link Test} execution has failed, but will retry
+ */
+ EVENT_TEST_RETRY: 'retry'
+ }
+);
module.exports = Runner;
/**
- * Initialize a `Runner` for the given `suite`. Derived from [EventEmitter](https://nodejs.org/api/events.html#events_class_eventemitter)
- *
- * Events:
+ * Initialize a `Runner` at the Root {@link Suite}, which represents a hierarchy of {@link Suite|Suites} and {@link Test|Tests}.
*
- * - `start` execution started
- * - `end` execution complete
- * - `suite` (suite) test suite execution started
- * - `suite end` (suite) all tests (and sub-suites) have finished
- * - `test` (test) test execution started
- * - `test end` (test) test completed
- * - `hook` (hook) hook execution started
- * - `hook end` (hook) hook complete
- * - `pass` (test) test passed
- * - `fail` (test, err) test failed
- * - `pending` (test) test pending
- *
- * @memberof Mocha
+ * @extends external:EventEmitter
* @public
* @class
- * @param {Suite} [suite] Root suite
+ * @param {Suite} suite Root suite
* @param {boolean} [delay] Whether or not to delay execution of root suite
* until ready.
*/
@@ -72,16 +125,15 @@ function Runner(suite, delay) {
this.started = false;
this.total = suite.total();
this.failures = 0;
- this.on('test end', function(test) {
+ this.on(constants.EVENT_TEST_END, function(test) {
self.checkGlobals(test);
});
- this.on('hook end', function(hook) {
+ this.on(constants.EVENT_HOOK_END, function(hook) {
self.checkGlobals(hook);
});
this._defaultGrep = /.*/;
this.grep(this._defaultGrep);
this.globals(this.globalProps().concat(extraGlobals()));
- createStatsCollector(this);
}
/**
@@ -102,7 +154,7 @@ inherits(Runner, EventEmitter);
* with number of tests matched.
*
* @public
- * @memberof Mocha.Runner
+ * @memberof Runner
* @param {RegExp} re
* @param {boolean} invert
* @return {Runner} Runner instance.
@@ -119,7 +171,7 @@ Runner.prototype.grep = function(re, invert) {
* Returns the number of tests matching the grep search for the
* given suite.
*
- * @memberof Mocha.Runner
+ * @memberof Runner
* @public
* @param {Suite} suite
* @return {number}
@@ -165,7 +217,7 @@ Runner.prototype.globalProps = function() {
* Allow the given `arr` of globals.
*
* @public
- * @memberof Mocha.Runner
+ * @memberof Runner
* @param {Array} arr
* @return {Runner} Runner instance.
*/
@@ -227,7 +279,7 @@ Runner.prototype.fail = function(test, err) {
}
++this.failures;
- test.state = 'failed';
+ test.state = STATE_FAILED;
if (!isError(err)) {
err = thrown2Error(err);
@@ -240,7 +292,7 @@ Runner.prototype.fail = function(test, err) {
// some environments do not take kindly to monkeying with the stack
}
- this.emit('fail', test, err);
+ this.emit(constants.EVENT_TEST_FAIL, test, err);
};
/**
@@ -292,7 +344,7 @@ Runner.prototype.failHook = function(hook, err) {
Runner.prototype.hook = function(name, fn) {
var suite = this.suite;
- var hooks = suite['_' + name];
+ var hooks = suite.getHooks(name);
var self = this;
function next(i) {
@@ -310,7 +362,7 @@ Runner.prototype.hook = function(name, fn) {
hook.ctx.currentTest = self.test;
}
- self.emit('hook', hook);
+ self.emit(constants.EVENT_HOOK_BEGIN, hook);
if (!hook.listeners('error').length) {
hook.on('error', function(err) {
@@ -325,7 +377,7 @@ Runner.prototype.hook = function(name, fn) {
}
if (err) {
if (err instanceof Pending) {
- if (name === 'beforeEach' || name === 'afterEach') {
+ if (name === HOOK_TYPE_BEFORE_EACH || name === HOOK_TYPE_AFTER_EACH) {
self.test.pending = true;
} else {
suite.tests.forEach(function(test) {
@@ -344,7 +396,7 @@ Runner.prototype.hook = function(name, fn) {
return fn(err);
}
}
- self.emit('hook end', hook);
+ self.emit(constants.EVENT_HOOK_END, hook);
delete hook.ctx.currentTest;
next(++i);
});
@@ -487,7 +539,7 @@ Runner.prototype.runTests = function(suite, fn) {
if (self.suite) {
// call hookUp afterEach
- self.hookUp('afterEach', function(err2, errSuite2) {
+ self.hookUp(HOOK_TYPE_AFTER_EACH, function(err2, errSuite2) {
self.suite = orig;
// some hooks may fail even now
if (err2) {
@@ -553,24 +605,24 @@ Runner.prototype.runTests = function(suite, fn) {
self.fail(test, new Error('Pending test forbidden'));
delete test.isPending;
} else {
- self.emit('pending', test);
+ self.emit(constants.EVENT_TEST_PENDING, test);
}
- self.emit('test end', test);
+ self.emit(constants.EVENT_TEST_END, test);
return next();
}
// execute test and hook(s)
- self.emit('test', (self.test = test));
- self.hookDown('beforeEach', function(err, errSuite) {
+ self.emit(constants.EVENT_TEST_BEGIN, (self.test = test));
+ self.hookDown(HOOK_TYPE_BEFORE_EACH, function(err, errSuite) {
if (test.isPending()) {
if (self.forbidPending) {
test.isPending = alwaysFalse;
self.fail(test, new Error('Pending test forbidden'));
delete test.isPending;
} else {
- self.emit('pending', test);
+ self.emit(constants.EVENT_TEST_PENDING, test);
}
- self.emit('test end', test);
+ self.emit(constants.EVENT_TEST_END, test);
return next();
}
if (err) {
@@ -585,33 +637,33 @@ Runner.prototype.runTests = function(suite, fn) {
self.fail(test, new Error('Pending test forbidden'));
} else if (err instanceof Pending) {
test.pending = true;
- self.emit('pending', test);
+ self.emit(constants.EVENT_TEST_PENDING, test);
} else if (retry < test.retries()) {
var clonedTest = test.clone();
clonedTest.currentRetry(retry + 1);
tests.unshift(clonedTest);
- self.emit('retry', test, err);
+ self.emit(constants.EVENT_TEST_RETRY, test, err);
// Early return + hook trigger so that it doesn't
// increment the count wrong
- return self.hookUp('afterEach', next);
+ return self.hookUp(HOOK_TYPE_AFTER_EACH, next);
} else {
self.fail(test, err);
}
- self.emit('test end', test);
+ self.emit(constants.EVENT_TEST_END, test);
if (err instanceof Pending) {
return next();
}
- return self.hookUp('afterEach', next);
+ return self.hookUp(HOOK_TYPE_AFTER_EACH, next);
}
- test.state = 'passed';
- self.emit('pass', test);
- self.emit('test end', test);
- self.hookUp('afterEach', next);
+ test.state = STATE_PASSED;
+ self.emit(constants.EVENT_TEST_PASS, test);
+ self.emit(constants.EVENT_TEST_END, test);
+ self.hookUp(HOOK_TYPE_AFTER_EACH, next);
});
});
}
@@ -644,7 +696,7 @@ Runner.prototype.runSuite = function(suite, fn) {
return fn();
}
- this.emit('suite', (this.suite = suite));
+ this.emit(constants.EVENT_SUITE_BEGIN, (this.suite = suite));
function next(errSuite) {
if (errSuite) {
@@ -694,8 +746,8 @@ Runner.prototype.runSuite = function(suite, fn) {
// remove reference to test
delete self.test;
- self.hook('afterAll', function() {
- self.emit('suite end', suite);
+ self.hook(HOOK_TYPE_AFTER_ALL, function() {
+ self.emit(constants.EVENT_SUITE_END, suite);
fn(errSuite);
});
}
@@ -703,7 +755,7 @@ Runner.prototype.runSuite = function(suite, fn) {
this.nextSuite = next;
- this.hook('beforeAll', function(err) {
+ this.hook(HOOK_TYPE_BEFORE_ALL, function(err) {
if (err) {
return done();
}
@@ -748,9 +800,9 @@ Runner.prototype.uncaught = function(err) {
this.fail(runnable, err);
} else {
// Can't recover from this failure
- this.emit('start');
+ this.emit(constants.EVENT_RUN_BEGIN);
this.fail(runnable, err);
- this.emit('end');
+ this.emit(constants.EVENT_RUN_END);
}
return;
@@ -770,14 +822,16 @@ Runner.prototype.uncaught = function(err) {
this.fail(runnable, err);
if (!alreadyPassed) {
// recover from test
- if (runnable.type === 'test') {
- this.emit('test end', runnable);
- this.hookUp('afterEach', this.next);
+ if (runnable.type === constants.EVENT_TEST_BEGIN) {
+ this.emit(constants.EVENT_TEST_END, runnable);
+ this.hookUp(HOOK_TYPE_AFTER_EACH, this.next);
return;
}
// recover from hooks
var errSuite = this.suite;
+
+ // XXX how about a less awful way to determine this?
// if hook failure is in afterEach block
if (runnable.fullTitle().indexOf('after each') > -1) {
return this.hookErr(err, errSuite, true);
@@ -791,53 +845,15 @@ Runner.prototype.uncaught = function(err) {
}
// bail
- this.emit('end');
+ this.emit(constants.EVENT_RUN_END);
};
-/**
- * Cleans up the references to all the deferred functions
- * (before/after/beforeEach/afterEach) and tests of a Suite.
- * These must be deleted otherwise a memory leak can happen,
- * as those functions may reference variables from closures,
- * thus those variables can never be garbage collected as long
- * as the deferred functions exist.
- *
- * @param {Suite} suite
- */
-function cleanSuiteReferences(suite) {
- function cleanArrReferences(arr) {
- for (var i = 0; i < arr.length; i++) {
- delete arr[i].fn;
- }
- }
-
- if (Array.isArray(suite._beforeAll)) {
- cleanArrReferences(suite._beforeAll);
- }
-
- if (Array.isArray(suite._beforeEach)) {
- cleanArrReferences(suite._beforeEach);
- }
-
- if (Array.isArray(suite._afterAll)) {
- cleanArrReferences(suite._afterAll);
- }
-
- if (Array.isArray(suite._afterEach)) {
- cleanArrReferences(suite._afterEach);
- }
-
- for (var i = 0; i < suite.tests.length; i++) {
- delete suite.tests[i].fn;
- }
-}
-
/**
* Run the root suite and invoke `fn(failures)`
* on completion.
*
* @public
- * @memberof Mocha.Runner
+ * @memberof Runner
* @param {Function} fn
* @return {Runner} Runner instance.
*/
@@ -857,26 +873,31 @@ Runner.prototype.run = function(fn) {
filterOnly(rootSuite);
}
self.started = true;
+ if (self._delay) {
+ self.emit(constants.EVENT_DELAY_END);
+ }
Runner.immediately(function() {
- self.emit('start');
+ self.emit(constants.EVENT_RUN_BEGIN);
});
self.runSuite(rootSuite, function() {
debug('finished running');
Runner.immediately(function() {
- self.emit('end');
+ self.emit(constants.EVENT_RUN_END);
});
});
}
- debug('start');
+ debug(constants.EVENT_RUN_BEGIN);
// references cleanup to avoid memory leaks
- this.on('suite end', cleanSuiteReferences);
+ this.on(constants.EVENT_SUITE_END, function(suite) {
+ suite.cleanReferences();
+ });
// callback
- this.on('end', function() {
- debug('end');
+ this.on(constants.EVENT_RUN_END, function() {
+ debug(constants.EVENT_RUN_END);
process.removeListener('uncaughtException', uncaught);
fn(self.failures);
});
@@ -887,8 +908,8 @@ Runner.prototype.run = function(fn) {
if (this._delay) {
// for reporters, I guess.
// might be nice to debounce some dots while we wait.
- this.emit('waiting', rootSuite);
- rootSuite.once('run', start);
+ this.emit(constants.EVENT_DELAY_BEGIN, rootSuite);
+ rootSuite.once(EVENT_ROOT_SUITE_RUN, start);
} else {
start();
}
@@ -899,7 +920,7 @@ Runner.prototype.run = function(fn) {
/**
* Cleanly abort execution.
*
- * @memberof Mocha.Runner
+ * @memberof Runner
* @public
* @return {Runner} Runner instance.
*/
@@ -1031,6 +1052,8 @@ function thrown2Error(err) {
* Array of globals dependent on the environment.
*
* @return {Array}
+ * @deprecated
+ * @todo remove; long since unsupported
* @private
*/
function extraGlobals() {
@@ -1041,7 +1064,6 @@ function extraGlobals() {
});
// 'errno' was renamed to process._errno in v0.9.11.
-
if (nodeVersion < 0x00090b) {
return ['errno'];
}
@@ -1049,3 +1071,11 @@ function extraGlobals() {
return [];
}
+
+Runner.constants = constants;
+
+/**
+ * Node.js' `EventEmitter`
+ * @external EventEmitter
+ * @see {@link https://nodejs.org/api/events.html#events_class_eventemitter}
+ */
diff --git a/lib/stats-collector.js b/lib/stats-collector.js
index 7a5b3fc61f..938778fb0e 100644
--- a/lib/stats-collector.js
+++ b/lib/stats-collector.js
@@ -2,14 +2,22 @@
/**
* Provides a factory function for a {@link StatsCollector} object.
- * @private
* @module
*/
+var constants = require('./runner').constants;
+var EVENT_TEST_PASS = constants.EVENT_TEST_PASS;
+var EVENT_TEST_FAIL = constants.EVENT_TEST_FAIL;
+var EVENT_SUITE_BEGIN = constants.EVENT_SUITE_BEGIN;
+var EVENT_RUN_BEGIN = constants.EVENT_RUN_BEGIN;
+var EVENT_TEST_PENDING = constants.EVENT_TEST_PENDING;
+var EVENT_RUN_END = constants.EVENT_RUN_END;
+var EVENT_TEST_END = constants.EVENT_TEST_END;
+
/**
* Test statistics collector.
*
- * @private
+ * @public
* @typedef {Object} StatsCollector
* @property {number} suites - integer count of suites run.
* @property {number} tests - integer count of tests run.
@@ -48,31 +56,25 @@ function createStatsCollector(runner) {
runner.stats = stats;
- runner.once('start', function() {
+ runner.once(EVENT_RUN_BEGIN, function() {
stats.start = new Date();
});
-
- runner.on('suite', function(suite) {
+ runner.on(EVENT_SUITE_BEGIN, function(suite) {
suite.root || stats.suites++;
});
-
- runner.on('pass', function() {
+ runner.on(EVENT_TEST_PASS, function() {
stats.passes++;
});
-
- runner.on('fail', function() {
+ runner.on(EVENT_TEST_FAIL, function() {
stats.failures++;
});
-
- runner.on('pending', function() {
+ runner.on(EVENT_TEST_PENDING, function() {
stats.pending++;
});
-
- runner.on('test end', function() {
+ runner.on(EVENT_TEST_END, function() {
stats.tests++;
});
-
- runner.once('end', function() {
+ runner.once(EVENT_RUN_END, function() {
stats.end = new Date();
stats.duration = stats.end - stats.start;
});
diff --git a/lib/suite.js b/lib/suite.js
index 2cb3a39de9..e2fb75bd41 100644
--- a/lib/suite.js
+++ b/lib/suite.js
@@ -1,7 +1,4 @@
'use strict';
-/**
- * @module Suite
- */
/**
* Module dependencies.
@@ -24,13 +21,12 @@ exports = module.exports = Suite;
/**
* Create a new `Suite` with the given `title` and parent `Suite`.
*
- * @memberof Mocha
* @public
- * @param {Suite} parent
- * @param {string} title
+ * @param {Suite} parent - Parent suite (required!)
+ * @param {string} title - Title
* @return {Suite}
*/
-exports.create = function(parent, title) {
+Suite.create = function(parent, title) {
var suite = new Suite(title, parent.ctx);
suite.parent = parent;
title = suite.fullTitle();
@@ -44,7 +40,6 @@ exports.create = function(parent, title) {
* @public
* @class
* @extends EventEmitter
- * @memberof Mocha
* @see {@link https://nodejs.org/api/events.html#events_class_eventemitter|EventEmitter}
* @param {string} title - Suite title.
* @param {Context} parentContext - Parent context instance.
@@ -80,6 +75,16 @@ function Suite(title, parentContext, isRoot) {
this._onlyTests = [];
this._onlySuites = [];
this.delayed = false;
+
+ this.on('newListener', function(event) {
+ if (deprecatedEvents[event]) {
+ utils.deprecate(
+ 'Event "' +
+ event +
+ '" is deprecated. Please let the Mocha team know about your use case: https://git.io/v6Lwm'
+ );
+ }
+ });
}
/**
@@ -242,7 +247,7 @@ Suite.prototype.beforeAll = function(title, fn) {
var hook = this._createHook(title, fn);
this._beforeAll.push(hook);
- this.emit('beforeAll', hook);
+ this.emit(constants.EVENT_SUITE_ADD_HOOK_BEFORE_ALL, hook);
return this;
};
@@ -266,7 +271,7 @@ Suite.prototype.afterAll = function(title, fn) {
var hook = this._createHook(title, fn);
this._afterAll.push(hook);
- this.emit('afterAll', hook);
+ this.emit(constants.EVENT_SUITE_ADD_HOOK_AFTER_ALL, hook);
return this;
};
@@ -290,7 +295,7 @@ Suite.prototype.beforeEach = function(title, fn) {
var hook = this._createHook(title, fn);
this._beforeEach.push(hook);
- this.emit('beforeEach', hook);
+ this.emit(constants.EVENT_SUITE_ADD_HOOK_BEFORE_EACH, hook);
return this;
};
@@ -314,7 +319,7 @@ Suite.prototype.afterEach = function(title, fn) {
var hook = this._createHook(title, fn);
this._afterEach.push(hook);
- this.emit('afterEach', hook);
+ this.emit(constants.EVENT_SUITE_ADD_HOOK_AFTER_EACH, hook);
return this;
};
@@ -334,7 +339,7 @@ Suite.prototype.addSuite = function(suite) {
suite.slow(this.slow());
suite.bail(this.bail());
this.suites.push(suite);
- this.emit('suite', suite);
+ this.emit(constants.EVENT_SUITE_ADD_SUITE, suite);
return this;
};
@@ -353,7 +358,7 @@ Suite.prototype.addTest = function(test) {
test.slow(this.slow());
test.ctx = this.ctx;
this.tests.push(test);
- this.emit('test', test);
+ this.emit(constants.EVENT_SUITE_ADD_TEST, test);
return this;
};
@@ -361,7 +366,7 @@ Suite.prototype.addTest = function(test) {
* Return the full title generated by recursively concatenating the parent's
* full title.
*
- * @memberof Mocha.Suite
+ * @memberof Suite
* @public
* @return {string}
*/
@@ -373,7 +378,7 @@ Suite.prototype.fullTitle = function() {
* Return the title path generated by recursively concatenating the parent's
* title path.
*
- * @memberof Mocha.Suite
+ * @memberof Suite
* @public
* @return {string}
*/
@@ -391,7 +396,7 @@ Suite.prototype.titlePath = function() {
/**
* Return the total number of tests.
*
- * @memberof Mocha.Suite
+ * @memberof Suite
* @public
* @return {number}
*/
@@ -421,9 +426,148 @@ Suite.prototype.eachTest = function(fn) {
/**
* This will run the root suite if we happen to be running in delayed mode.
+ * @private
*/
Suite.prototype.run = function run() {
if (this.root) {
- this.emit('run');
+ this.emit(constants.EVENT_ROOT_SUITE_RUN);
}
};
+
+/**
+ * Returns the array of hooks by hook name; see `HOOK_TYPE_*` constants.
+ * @private
+ */
+Suite.prototype.getHooks = function getHooks(name) {
+ return this['_' + name];
+};
+
+/**
+ * Cleans up the references to all the deferred functions
+ * (before/after/beforeEach/afterEach) and tests of a Suite.
+ * These must be deleted otherwise a memory leak can happen,
+ * as those functions may reference variables from closures,
+ * thus those variables can never be garbage collected as long
+ * as the deferred functions exist.
+ *
+ * @private
+ */
+Suite.prototype.cleanReferences = function cleanReferences() {
+ function cleanArrReferences(arr) {
+ for (var i = 0; i < arr.length; i++) {
+ delete arr[i].fn;
+ }
+ }
+
+ if (Array.isArray(this._beforeAll)) {
+ cleanArrReferences(this._beforeAll);
+ }
+
+ if (Array.isArray(this._beforeEach)) {
+ cleanArrReferences(this._beforeEach);
+ }
+
+ if (Array.isArray(this._afterAll)) {
+ cleanArrReferences(this._afterAll);
+ }
+
+ if (Array.isArray(this._afterEach)) {
+ cleanArrReferences(this._afterEach);
+ }
+
+ for (var i = 0; i < this.tests.length; i++) {
+ delete this.tests[i].fn;
+ }
+};
+
+var constants = utils.defineConstants(
+ /**
+ * {@link Suite}-related constants.
+ * @public
+ * @memberof Suite
+ * @alias constants
+ * @readonly
+ * @static
+ * @enum {string}
+ */
+ {
+ /**
+ * Event emitted after a test file has been loaded Not emitted in browser.
+ */
+ EVENT_FILE_POST_REQUIRE: 'post-require',
+ /**
+ * Event emitted before a test file has been loaded. In browser, this is emitted once an interface has been selected.
+ */
+ EVENT_FILE_PRE_REQUIRE: 'pre-require',
+ /**
+ * Event emitted immediately after a test file has been loaded. Not emitted in browser.
+ */
+ EVENT_FILE_REQUIRE: 'require',
+ /**
+ * Event emitted when `global.run()` is called (use with `delay` option)
+ */
+ EVENT_ROOT_SUITE_RUN: 'run',
+
+ /**
+ * Namespace for collection of a `Suite`'s "after all" hooks
+ */
+ HOOK_TYPE_AFTER_ALL: 'afterAll',
+ /**
+ * Namespace for collection of a `Suite`'s "after each" hooks
+ */
+ HOOK_TYPE_AFTER_EACH: 'afterEach',
+ /**
+ * Namespace for collection of a `Suite`'s "before all" hooks
+ */
+ HOOK_TYPE_BEFORE_ALL: 'beforeAll',
+ /**
+ * Namespace for collection of a `Suite`'s "before all" hooks
+ */
+ HOOK_TYPE_BEFORE_EACH: 'beforeEach',
+
+ // the following events are all deprecated
+
+ /**
+ * Emitted after an "after all" `Hook` has been added to a `Suite`. Deprecated
+ */
+ EVENT_SUITE_ADD_HOOK_AFTER_ALL: 'afterAll',
+ /**
+ * Emitted after an "after each" `Hook` has been added to a `Suite` Deprecated
+ */
+ EVENT_SUITE_ADD_HOOK_AFTER_EACH: 'afterEach',
+ /**
+ * Emitted after an "before all" `Hook` has been added to a `Suite` Deprecated
+ */
+ EVENT_SUITE_ADD_HOOK_BEFORE_ALL: 'beforeAll',
+ /**
+ * Emitted after an "before each" `Hook` has been added to a `Suite` Deprecated
+ */
+ EVENT_SUITE_ADD_HOOK_BEFORE_EACH: 'beforeEach',
+ /**
+ * Emitted after a child `Suite` has been added to a `Suite`. Deprecated
+ */
+ EVENT_SUITE_ADD_SUITE: 'suite',
+ /**
+ * Emitted after a `Test` has been added to a `Suite`. Deprecated
+ */
+ EVENT_SUITE_ADD_TEST: 'test'
+ }
+);
+
+/**
+ * @summary There are no known use cases for these events.
+ * @desc This is a `Set`-like object having all keys being the constant's string value and the value being `true`.
+ * @todo Remove eventually
+ * @type {Object}
+ * @ignore
+ */
+var deprecatedEvents = Object.keys(constants)
+ .filter(function(constant) {
+ return constant.substring(0, 15) === 'EVENT_SUITE_ADD';
+ })
+ .reduce(function(acc, constant) {
+ acc[constants[constant]] = true;
+ return acc;
+ }, utils.createMap());
+
+Suite.constants = constants;
diff --git a/lib/test.js b/lib/test.js
index 6c6aeb6b7a..f32008a85b 100644
--- a/lib/test.js
+++ b/lib/test.js
@@ -10,10 +10,11 @@ module.exports = Test;
/**
* Initialize a new `Test` with the given `title` and callback `fn`.
*
+ * @public
* @class
* @extends Runnable
- * @param {String} title
- * @param {Function} fn
+ * @param {String} title - Test title (required)
+ * @param {Function} [fn] - Test callback. If omitted, the Test is considered "pending"
*/
function Test(title, fn) {
if (!isString(title)) {
diff --git a/lib/utils.js b/lib/utils.js
index bf50ee8ee9..c6e7245bc2 100644
--- a/lib/utils.js
+++ b/lib/utils.js
@@ -1,7 +1,8 @@
'use strict';
/**
- * @module
+ * Various utility functions used throughout Mocha's codebase.
+ * @module utils
*/
/**
@@ -27,6 +28,8 @@ var ignore = ['node_modules', '.git'];
exports.inherits = require('util').inherits;
+var assign = (exports.assign = require('object.assign').getPolyfill());
+
/**
* Escape special characters in the given string of html.
*
@@ -741,3 +744,33 @@ exports.clamp = function clamp(value, range) {
* @public
*/
exports.noop = function() {};
+
+/**
+ * @summary Creates a map-like object.
+ * @desc A "map" is an object with no prototype, for our purposes. In some cases this would be more appropriate than a `Map`, especially if your environment doesn't support it. Recommended for use in Mocha's public APIs.
+ * @param {...*} [obj] - Arguments to `Object.assign()`
+ * @returns {Object} An object with no prototype, having `...obj` properties
+ * @public
+ * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map
+ * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/create#Custom_and_Null_objects
+ * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/assign
+ */
+exports.createMap = function(obj) {
+ return assign.apply(
+ null,
+ [Object.create(null)].concat(Array.prototype.slice.call(arguments))
+ );
+};
+
+/**
+ * @summary Create a read-only map-like object.
+ * This differs from {@link module:utils.createMap createMap} only in that the argument must be non-empty, because the result is frozen.
+ * @see {@link module:utils.createMap createMap}
+ * @returns {Object} A frozen object with no prototype, having `...obj` properties
+ */
+exports.defineConstants = function(obj) {
+ if (type(obj) !== 'object' || !Object.keys(obj).length) {
+ throw new TypeError('Invalid argument; expected a non-empty object');
+ }
+ return Object.freeze(exports.createMap(obj));
+};
diff --git a/package-scripts.js b/package-scripts.js
index 561737dbd7..391b28be92 100644
--- a/package-scripts.js
+++ b/package-scripts.js
@@ -281,18 +281,27 @@ module.exports = {
hiddenFromHelp: true
},
preprocess: {
- script:
- 'md-magic --config ./scripts/markdown-magic.config.js --path docs/index.md',
- description: 'Preprocess documenation',
- hiddenFromHelp: true
+ default: {
+ script:
+ 'md-magic --config ./scripts/markdown-magic.config.js --path docs/index.md',
+ description: 'Preprocess documentation',
+ hiddenFromHelp: true
+ },
+ api: {
+ script:
+ 'md-magic --config ./scripts/markdown-magic.config.js --path "docs/api-tutorials/*.md"',
+ description: 'Preprocess API documentation',
+ hiddenFromHelp: true
+ }
},
watch: {
script: 'nps docs.preprocess && eleventy --serve',
description: 'Watch docs for changes & build'
},
api: {
- script: 'jsdoc -c jsdoc.conf.json && cp LICENSE docs/_dist/api',
- description: 'build api docs'
+ script:
+ 'nps docs.preprocess.api && jsdoc -c jsdoc.conf.json && cp LICENSE docs/_dist/api',
+ description: 'Build API docs'
}
},
updateContributors: {
diff --git a/scripts/markdown-magic.config.js b/scripts/markdown-magic.config.js
index 40f90cd87a..ad05067979 100644
--- a/scripts/markdown-magic.config.js
+++ b/scripts/markdown-magic.config.js
@@ -11,8 +11,12 @@ const {execSync} = require('child_process');
const stripAnsi = require('strip-ansi');
const markdownToc = require('markdown-toc');
const path = require('path');
+const fs = require('fs');
exports.transforms = {
+ /**
+ * Takes STDOUT of some command and injects it into the markdown
+ */
usage: (content, options) => {
const {executable} = options;
const flag = options.flag || '--help';
@@ -27,11 +31,13 @@ exports.transforms = {
);
return [header, output, footer].join('\n\n');
},
- // we can't use the builtin `TOC` plugin in markdown-magic
- // because it's simply not flexible enough; we can't pad with newlines,
- // nor can we provide a custom filter. the custom filter would be required
- // since the `TOC` plugin supplies its own which means we can't use the
- // `maxdepth` option, which we need!
+ /**
+ * We can't use the builtin `TOC` plugin in markdown-magic
+ * because it's simply not flexible enough; we can't pad with newlines,
+ * nor can we provide a custom filter. the custom filter would be required
+ * since the `TOC` plugin supplies its own which means we can't use the
+ * `maxdepth` option, which we need!
+ */
toc: (content, options, config) => {
const IGNORED_HEADINGS_REGEXP = /Features|Table of Contents/i;
return (
@@ -47,5 +53,56 @@ exports.transforms = {
'\n'
);
},
- manifest: require('markdown-magic-package-json')
+ manifest: require('markdown-magic-package-json'),
+ /**
+ * Inserts the contents of a file; takes same options as builtin CODE plugin,
+ * but does not fetch remote URLs, tries to replace relative paths, and
+ * formats in a way our markdown linter likes.
+ */
+ file: (content, options, config) => {
+ let output;
+ if (!options.src) {
+ return false;
+ }
+ const fileDir = path.dirname(config.originalPath);
+ const filePath = path.join(fileDir, options.src);
+ const rootDir = path.join(__dirname, '..');
+ const relativeDir = path.relative(path.dirname(filePath), rootDir);
+
+ const syntax = options.syntax || path.extname(filePath).replace(/^./, '');
+ try {
+ output = fs.readFileSync(filePath, 'utf8', (err, contents) => {
+ if (err) {
+ console.log(`FILE NOT FOUND: ${filePath}`);
+ throw err;
+ }
+ return contents;
+ });
+ } catch (err) {
+ console.log(`FILE NOT FOUND: ${filePath}`);
+ throw err;
+ }
+
+ // replace relative paths in `require()` to root with "mocha".
+ // might not work in the general case. not gonna parse an AST for this
+ // e.g. `require('../../lib/foo')` => `require('mocha/lib/foo')`
+ // also trim any trailing whitespace
+ output = output
+ .replace(
+ new RegExp(`require\\(['"]${relativeDir}(.*?)['"]\\)`, 'g'),
+ "require('mocha$1')"
+ )
+ .trim();
+
+ let header = '';
+ if (options.header) {
+ header = `\n${options.header}`;
+ }
+
+ return `
+\`\`\`${syntax}${header}
+${output}
+\`\`\`
+`;
+ }
};
diff --git a/test/assertions.js b/test/assertions.js
index 08f7885074..fac66ce94a 100644
--- a/test/assertions.js
+++ b/test/assertions.js
@@ -10,7 +10,7 @@ exports.mixinMochaAssertions = function(expect) {
Object.prototype.toString.call(v) === '[object Object]' &&
typeof v.output === 'string' &&
typeof v.code === 'number' &&
- Object.keys(v).length === 2
+ Array.isArray(v.args)
);
}
})
diff --git a/test/integration/events.spec.js b/test/integration/events.spec.js
index dc08a57381..ef3aea7931 100644
--- a/test/integration/events.spec.js
+++ b/test/integration/events.spec.js
@@ -56,4 +56,23 @@ describe('event order', function() {
);
});
});
+
+ describe('--delay test case', function() {
+ it('should assert --delay event order', function(done) {
+ runMochaJSON('runner/events-delay.fixture.js', ['--delay'], function(
+ err,
+ res
+ ) {
+ if (err) {
+ done(err);
+ return;
+ }
+ expect(res, 'to have passed')
+ .and('to have passed test count', 2)
+ .and('to have passed test order', 'test A', 'test B')
+ .and('to have failed test count', 0);
+ done();
+ });
+ });
+ });
});
diff --git a/test/integration/fixtures/regression/1794/simple-ui.js b/test/integration/fixtures/regression/1794/simple-ui.fixture.js
similarity index 60%
rename from test/integration/fixtures/regression/1794/simple-ui.js
rename to test/integration/fixtures/regression/1794/simple-ui.fixture.js
index b27c623003..0e8c666b83 100644
--- a/test/integration/fixtures/regression/1794/simple-ui.js
+++ b/test/integration/fixtures/regression/1794/simple-ui.fixture.js
@@ -1,15 +1,22 @@
'use strict';
-var path = '../../../../../lib/';
-var Mocha = require(path + 'mocha');
-var Test = require(path + 'test');
+var Mocha = require('../../../../../lib/mocha');
+var Test = Mocha.Test;
+var EVENT_FILE_PRE_REQUIRE = Mocha.Suite.constants.EVENT_FILE_PRE_REQUIRE;
/**
* A simple UI that only exposes a single function: test
*/
module.exports = Mocha.interfaces['simple-ui'] = function(suite) {
- suite.on('pre-require', function(context, file, mocha) {
- var common = require(path + 'interfaces/common')([suite], context);
+ suite.on(EVENT_FILE_PRE_REQUIRE, function(
+ context,
+ file,
+ mocha
+ ) {
+ var common = require('../../../../../lib/interfaces/common')(
+ [suite],
+ context
+ );
context.run = mocha.options.delay && common.runWithSuite(suite);
diff --git a/test/integration/fixtures/runner/events-bail.fixture.js b/test/integration/fixtures/runner/events-bail.fixture.js
index 3a345f8169..7b060bd43c 100644
--- a/test/integration/fixtures/runner/events-bail.fixture.js
+++ b/test/integration/fixtures/runner/events-bail.fixture.js
@@ -1,16 +1,39 @@
'use strict';
var Runner = require('../../../../lib/runner.js');
var assert = require('assert');
+var constants = Runner.constants;
+var EVENT_HOOK_BEGIN = constants.EVENT_HOOK_BEGIN;
+var EVENT_HOOK_END = constants.EVENT_HOOK_END;
+var EVENT_RUN_BEGIN = constants.EVENT_RUN_BEGIN;
+var EVENT_RUN_END = constants.EVENT_RUN_END;
+var EVENT_SUITE_BEGIN = constants.EVENT_SUITE_BEGIN;
+var EVENT_SUITE_END = constants.EVENT_SUITE_END;
+var EVENT_TEST_BEGIN = constants.EVENT_TEST_BEGIN;
+var EVENT_TEST_END = constants.EVENT_TEST_END;
+var EVENT_TEST_FAIL = constants.EVENT_TEST_FAIL;
var emitOrder = [
- 'suite'/* incorrect order*/, 'start', 'suite',
- 'hook', 'hook end', 'test', 'hook', 'hook end', 'fail', 'test end', 'hook', 'hook end',
- 'hook', 'hook end', 'suite end', 'suite end', 'end'
+ EVENT_SUITE_BEGIN, // incorrect order
+ EVENT_RUN_BEGIN,
+ EVENT_SUITE_BEGIN,
+ EVENT_HOOK_BEGIN,
+ EVENT_HOOK_END,
+ EVENT_TEST_BEGIN,
+ EVENT_HOOK_BEGIN,
+ EVENT_HOOK_END,
+ EVENT_TEST_FAIL,
+ EVENT_TEST_END,
+ EVENT_HOOK_BEGIN,
+ EVENT_HOOK_END,
+ EVENT_HOOK_BEGIN,
+ EVENT_HOOK_END,
+ EVENT_SUITE_END,
+ EVENT_SUITE_END,
+ EVENT_RUN_END
];
var realEmit = Runner.prototype.emit;
Runner.prototype.emit = function(event, ...args) {
- // console.log(`emit: ${event}`);
assert.strictEqual(event, emitOrder.shift());
return realEmit.call(this, event, ...args);
};
diff --git a/test/integration/fixtures/runner/events-basic.fixture.js b/test/integration/fixtures/runner/events-basic.fixture.js
index 81c55a8bb4..7a352379ec 100644
--- a/test/integration/fixtures/runner/events-basic.fixture.js
+++ b/test/integration/fixtures/runner/events-basic.fixture.js
@@ -1,17 +1,53 @@
'use strict';
var Runner = require('../../../../lib/runner.js');
var assert = require('assert');
+var constants = Runner.constants;
+var EVENT_DELAY_BEGIN = constants.EVENT_DELAY_BEGIN;
+var EVENT_DELAY_END = constants.EVENT_DELAY_END;
+var EVENT_HOOK_BEGIN = constants.EVENT_HOOK_BEGIN;
+var EVENT_HOOK_END = constants.EVENT_HOOK_END;
+var EVENT_RUN_BEGIN = constants.EVENT_RUN_BEGIN;
+var EVENT_RUN_END = constants.EVENT_RUN_END;
+var EVENT_SUITE_BEGIN = constants.EVENT_SUITE_BEGIN;
+var EVENT_SUITE_END = constants.EVENT_SUITE_END;
+var EVENT_TEST_BEGIN = constants.EVENT_TEST_BEGIN;
+var EVENT_TEST_END = constants.EVENT_TEST_END;
+var EVENT_TEST_FAIL = constants.EVENT_TEST_FAIL;
+var EVENT_TEST_PASS = constants.EVENT_TEST_PASS;
+var EVENT_TEST_PENDING = constants.EVENT_TEST_PENDING;
+var EVENT_TEST_RETRY = constants.EVENT_TEST_RETRY;
var emitOrder = [
- 'suite'/* incorrect order*/, 'start', 'suite',
- 'hook', 'hook end', 'test', 'hook', 'hook end', 'pass', 'test end', 'hook', 'hook end',
- 'suite', 'test', 'hook', 'hook end', 'pass', 'test end', 'hook', 'hook end',
- 'suite end', 'hook', 'hook end', 'suite end', 'suite end', 'end'
+ EVENT_SUITE_BEGIN, // incorrect order
+ EVENT_RUN_BEGIN,
+ EVENT_SUITE_BEGIN,
+ EVENT_HOOK_BEGIN,
+ EVENT_HOOK_END,
+ EVENT_TEST_BEGIN,
+ EVENT_HOOK_BEGIN,
+ EVENT_HOOK_END,
+ EVENT_TEST_PASS,
+ EVENT_TEST_END,
+ EVENT_HOOK_BEGIN,
+ EVENT_HOOK_END,
+ EVENT_SUITE_BEGIN,
+ EVENT_TEST_BEGIN,
+ EVENT_HOOK_BEGIN,
+ EVENT_HOOK_END,
+ EVENT_TEST_PASS,
+ EVENT_TEST_END,
+ EVENT_HOOK_BEGIN,
+ EVENT_HOOK_END,
+ EVENT_SUITE_END,
+ EVENT_HOOK_BEGIN,
+ EVENT_HOOK_END,
+ EVENT_SUITE_END,
+ EVENT_SUITE_END,
+ EVENT_RUN_END
];
var realEmit = Runner.prototype.emit;
Runner.prototype.emit = function(event, ...args) {
- // console.log(`emit: ${event}`);
assert.strictEqual(event, emitOrder.shift());
return realEmit.call(this, event, ...args);
};
diff --git a/test/integration/fixtures/runner/events-delay.fixture.js b/test/integration/fixtures/runner/events-delay.fixture.js
new file mode 100644
index 0000000000..9a50760884
--- /dev/null
+++ b/test/integration/fixtures/runner/events-delay.fixture.js
@@ -0,0 +1,71 @@
+'use strict';
+var Runner = require('../../../../lib/runner.js');
+var assert = require('assert');
+var constants = Runner.constants;
+var EVENT_DELAY_BEGIN = constants.EVENT_DELAY_BEGIN;
+var EVENT_DELAY_END = constants.EVENT_DELAY_END;
+var EVENT_HOOK_BEGIN = constants.EVENT_HOOK_BEGIN;
+var EVENT_HOOK_END = constants.EVENT_HOOK_END;
+var EVENT_RUN_BEGIN = constants.EVENT_RUN_BEGIN;
+var EVENT_RUN_END = constants.EVENT_RUN_END;
+var EVENT_SUITE_BEGIN = constants.EVENT_SUITE_BEGIN;
+var EVENT_SUITE_END = constants.EVENT_SUITE_END;
+var EVENT_TEST_BEGIN = constants.EVENT_TEST_BEGIN;
+var EVENT_TEST_END = constants.EVENT_TEST_END;
+var EVENT_TEST_FAIL = constants.EVENT_TEST_FAIL;
+var EVENT_TEST_PASS = constants.EVENT_TEST_PASS;
+var EVENT_TEST_PENDING = constants.EVENT_TEST_PENDING;
+var EVENT_TEST_RETRY = constants.EVENT_TEST_RETRY;
+
+var emitOrder = [
+ EVENT_DELAY_BEGIN,
+ EVENT_DELAY_END,
+ EVENT_SUITE_BEGIN, // incorrect order
+ EVENT_RUN_BEGIN,
+ EVENT_SUITE_BEGIN,
+ EVENT_HOOK_BEGIN,
+ EVENT_HOOK_END,
+ EVENT_TEST_BEGIN,
+ EVENT_HOOK_BEGIN,
+ EVENT_HOOK_END,
+ EVENT_TEST_PASS,
+ EVENT_TEST_END,
+ EVENT_HOOK_BEGIN,
+ EVENT_HOOK_END,
+ EVENT_SUITE_BEGIN,
+ EVENT_TEST_BEGIN,
+ EVENT_HOOK_BEGIN,
+ EVENT_HOOK_END,
+ EVENT_TEST_PASS,
+ EVENT_TEST_END,
+ EVENT_HOOK_BEGIN,
+ EVENT_HOOK_END,
+ EVENT_SUITE_END,
+ EVENT_HOOK_BEGIN,
+ EVENT_HOOK_END,
+ EVENT_SUITE_END,
+ EVENT_SUITE_END,
+ EVENT_RUN_END
+];
+
+var realEmit = Runner.prototype.emit;
+Runner.prototype.emit = function(event, ...args) {
+ assert.strictEqual(event, emitOrder.shift());
+ return realEmit.call(this, event, ...args);
+};
+
+setTimeout(function() {
+
+ describe('suite A', function() {
+ before('before', function() {});
+ beforeEach('beforeEach', function() {});
+ it('test A', function() {});
+ describe('suite B', function() {
+ it('test B', function() {});
+ });
+ afterEach('afterEach', function() {});
+ after('after', function() {});
+ });
+
+ run();
+}, 100);
diff --git a/test/integration/fixtures/runner/events-retries.fixture.js b/test/integration/fixtures/runner/events-retries.fixture.js
index a4547c8fa8..f8936aebfa 100644
--- a/test/integration/fixtures/runner/events-retries.fixture.js
+++ b/test/integration/fixtures/runner/events-retries.fixture.js
@@ -1,17 +1,46 @@
'use strict';
var Runner = require('../../../../lib/runner.js');
var assert = require('assert');
+var constants = Runner.constants;
+var EVENT_HOOK_BEGIN = constants.EVENT_HOOK_BEGIN;
+var EVENT_HOOK_END = constants.EVENT_HOOK_END;
+var EVENT_RUN_BEGIN = constants.EVENT_RUN_BEGIN;
+var EVENT_RUN_END = constants.EVENT_RUN_END;
+var EVENT_SUITE_BEGIN = constants.EVENT_SUITE_BEGIN;
+var EVENT_SUITE_END = constants.EVENT_SUITE_END;
+var EVENT_TEST_BEGIN = constants.EVENT_TEST_BEGIN;
+var EVENT_TEST_END = constants.EVENT_TEST_END;
+var EVENT_TEST_FAIL = constants.EVENT_TEST_FAIL;
+var EVENT_TEST_RETRY = constants.EVENT_TEST_RETRY;
var emitOrder = [
- 'suite'/* incorrect order*/, 'start', 'suite',
- 'hook', 'hook end', 'test', 'hook', 'hook end', 'retry', 'hook', 'hook end',
- 'test', 'hook', 'hook end', 'fail', 'test end', 'hook', 'hook end', 'hook', 'hook end',
- 'suite end', 'suite end', 'end'
+ EVENT_SUITE_BEGIN, // incorrect order
+ EVENT_RUN_BEGIN,
+ EVENT_SUITE_BEGIN,
+ EVENT_HOOK_BEGIN,
+ EVENT_HOOK_END,
+ EVENT_TEST_BEGIN,
+ EVENT_HOOK_BEGIN,
+ EVENT_HOOK_END,
+ EVENT_TEST_RETRY,
+ EVENT_HOOK_BEGIN,
+ EVENT_HOOK_END,
+ EVENT_TEST_BEGIN,
+ EVENT_HOOK_BEGIN,
+ EVENT_HOOK_END,
+ EVENT_TEST_FAIL,
+ EVENT_TEST_END,
+ EVENT_HOOK_BEGIN,
+ EVENT_HOOK_END,
+ EVENT_HOOK_BEGIN,
+ EVENT_HOOK_END,
+ EVENT_SUITE_END,
+ EVENT_SUITE_END,
+ EVENT_RUN_END
];
var realEmit = Runner.prototype.emit;
Runner.prototype.emit = function(event, ...args) {
- // console.log(`emit: ${event}`);
assert.strictEqual(event, emitOrder.shift());
return realEmit.call(this, event, ...args);
};
diff --git a/test/integration/fixtures/simple-reporter.js b/test/integration/fixtures/simple-reporter.js
index d066a84304..a80cf66ba9 100644
--- a/test/integration/fixtures/simple-reporter.js
+++ b/test/integration/fixtures/simple-reporter.js
@@ -1,28 +1,57 @@
'use strict';
-var baseReporter = require('../../../lib/reporters/base');
-module.exports = simplereporter;
+const Mocha = require('../../..');
+const {
+ EVENT_RUN_BEGIN,
+ EVENT_RUN_END,
+ EVENT_TEST_FAIL,
+ EVENT_TEST_PASS,
+ EVENT_SUITE_BEGIN,
+ EVENT_SUITE_END
+} = Mocha.Runner.constants;
-function simplereporter(runner) {
- baseReporter.call(this, runner);
+// this reporter outputs test results, indenting two spaces per suite
+class MyReporter {
+ constructor(runner) {
+ this._indents = 0;
+ const stats = runner.stats;
- runner.on('suite', function(suite) {
- console.log("on('suite') called");
- });
+ runner
+ .once(EVENT_RUN_BEGIN, () => {
+ console.log('start');
+ })
+ .on(EVENT_SUITE_BEGIN, () => {
+ this.increaseIndent();
+ })
+ .on(EVENT_SUITE_END, () => {
+ this.decreaseIndent();
+ })
+ .on(EVENT_TEST_PASS, test => {
+ // Test#fullTitle() returns the suite name(s)
+ // prepended to the test title
+ console.log(`${this.indent()}pass: ${test.fullTitle()}`);
+ })
+ .on(EVENT_TEST_FAIL, (test, err) => {
+ console.log(
+ `${this.indent()}fail: ${test.fullTitle()} - error: ${err.message}`
+ );
+ })
+ .once(EVENT_RUN_END, () => {
+ console.log(`end: ${stats.passes}/${stats.passes + stats.failures} ok`);
+ });
+ }
- runner.on('fail', function(test, err) {
- console.log("on('fail') called");
- });
+ indent() {
+ return Array(this._indents).join(' ');
+ }
- runner.on('pass', function(test) {
- console.log("on('pass') called");
- });
+ increaseIndent() {
+ this._indents++;
+ }
- runner.on('test end', function(test, err) {
- console.log("on('test end') called");
- });
-
- runner.on('end', function() {
- console.log("on('end') called");
- });
+ decreaseIndent() {
+ this._indents--;
+ }
}
+
+module.exports = MyReporter;
diff --git a/test/integration/helpers.js b/test/integration/helpers.js
index 760ae41b90..b2e98434c7 100644
--- a/test/integration/helpers.js
+++ b/test/integration/helpers.js
@@ -3,7 +3,7 @@
var format = require('util').format;
var spawn = require('cross-spawn').spawn;
var path = require('path');
-var baseReporter = require('../../lib/reporters/base');
+var Base = require('../../lib/reporters/base');
var DEFAULT_FIXTURE = resolveFixturePath('__default__');
var MOCHA_EXECUTABLE = require.resolve('../../bin/mocha');
@@ -95,7 +95,8 @@ module.exports = {
fn(
new Error(
format(
- 'Failed to parse JSON reporter output from result:\n\n%O',
+ 'Failed to parse JSON reporter output.\nArgs: %O\nResult:\n\n%O',
+ args,
res
)
)
@@ -137,7 +138,7 @@ module.exports = {
/**
* regular expression used for splitting lines based on new line / dot symbol.
*/
- splitRegExp: new RegExp('[\\n' + baseReporter.symbols.dot + ']+'),
+ splitRegExp: new RegExp('[\\n' + Base.symbols.dot + ']+'),
/**
* Invokes the mocha binary. Accepts an array of additional command line args
@@ -253,7 +254,8 @@ function _spawnMochaWithListeners(args, fn, opts) {
mocha.on('close', function(code) {
fn(null, {
output: output.split('\n').join('\n'),
- code: code
+ code: code,
+ args: args
});
});
diff --git a/test/integration/regression.spec.js b/test/integration/regression.spec.js
index 566ccc7cd8..b122311383 100644
--- a/test/integration/regression.spec.js
+++ b/test/integration/regression.spec.js
@@ -31,7 +31,7 @@ describe('regressions', function() {
'fixtures',
'regression',
'1794',
- 'simple-ui.js'
+ 'simple-ui.fixture.js'
);
var args = ['--require', simpleUiPath, '--ui', 'simple-ui'];
run('regression/1794/issue-1794.fixture.js', args, function(err, res) {
diff --git a/test/jsapi/index.js b/test/jsapi/index.js
index beac56864b..231c52fedf 100644
--- a/test/jsapi/index.js
+++ b/test/jsapi/index.js
@@ -21,10 +21,6 @@ mocha.addFile('test/unit/duration.spec.js');
mocha.addFile('test/unit/globals.spec.js');
mocha.addFile('test/unit/timeout.spec.js');
-mocha
- .run(function() {
- console.log('done');
- })
- .on('pass', function(test) {
- // console.log('... %s', test.title);
- });
+mocha.run(function() {
+ console.log('done');
+});
diff --git a/test/reporters/landing.spec.js b/test/reporters/landing.spec.js
index 1fee291e41..e561c23d55 100644
--- a/test/reporters/landing.spec.js
+++ b/test/reporters/landing.spec.js
@@ -1,7 +1,11 @@
'use strict';
-var reporters = require('../../').reporters;
+var Mocha = require('../..');
+var reporters = Mocha.reporters;
var Landing = reporters.Landing;
+var constants = Mocha.Runnable.constants;
+var STATE_FAILED = constants.STATE_FAILED;
+var STATE_PASSED = constants.STATE_PASSED;
var Base = reporters.Base;
var createMockRunner = require('./helpers').createMockRunner;
@@ -67,7 +71,7 @@ describe('Landing reporter', function() {
describe('if test has failed', function() {
it('should write expected landing strip', function() {
var test = {
- state: 'failed'
+ state: STATE_FAILED
};
runner = createMockRunner('test end', 'test end', null, null, test);
runner.total = 12;
@@ -79,7 +83,7 @@ describe('Landing reporter', function() {
describe('if test has not failed', function() {
it('should write expected landing strip', function() {
var test = {
- state: 'success'
+ state: STATE_PASSED
};
runner = createMockRunner('test end', 'test end', null, null, test);
diff --git a/test/reporters/xunit.spec.js b/test/reporters/xunit.spec.js
index b3e463c88c..35555968a1 100644
--- a/test/reporters/xunit.spec.js
+++ b/test/reporters/xunit.spec.js
@@ -7,6 +7,9 @@ var assert = require('assert');
var createStatsCollector = require('../../lib/stats-collector');
var EventEmitter = require('events').EventEmitter;
var reporters = require('../../').reporters;
+var constants = require('../../lib/runnable').constants;
+var STATE_FAILED = constants.STATE_FAILED;
+var STATE_PASSED = constants.STATE_PASSED;
var XUnit = reporters.XUnit;
describe('XUnit reporter', function() {
@@ -211,7 +214,7 @@ describe('XUnit reporter', function() {
it('should write expected tag with error details', function() {
var xunit = new XUnit({on: function() {}, once: function() {}});
var expectedTest = {
- state: 'failed',
+ state: STATE_FAILED,
title: expectedTitle,
parent: {
fullTitle: function() {
@@ -332,7 +335,7 @@ describe('XUnit reporter', function() {
var generateTest = function(passed) {
var t = {
title: expectedTitle + count,
- state: passed ? 'passed' : 'failed',
+ state: passed ? STATE_PASSED : STATE_FAILED,
isPending: function() {
return false;
},
diff --git a/test/unit/runnable.spec.js b/test/unit/runnable.spec.js
index f0c02ec0b8..94df5f56b4 100644
--- a/test/unit/runnable.spec.js
+++ b/test/unit/runnable.spec.js
@@ -1,11 +1,12 @@
'use strict';
-var mocha = require('../../lib/mocha');
-var utils = mocha.utils;
-var Runnable = mocha.Runnable;
-var Suite = mocha.Suite;
+var Mocha = require('../../lib/mocha');
+var utils = Mocha.utils;
+var Runnable = Mocha.Runnable;
+var Suite = Mocha.Suite;
var sinon = require('sinon');
var Pending = require('../../lib/pending');
+var STATE_FAILED = Runnable.constants.STATE_FAILED;
describe('Runnable(title, fn)', function() {
describe('#timeout(ms)', function() {
@@ -682,7 +683,7 @@ describe('Runnable(title, fn)', function() {
it('should return `true` if test has failed', function() {
var runnable = new Runnable('foo', function() {});
// runner sets the state
- runnable.state = 'failed';
+ runnable.state = STATE_FAILED;
runnable.run(function() {
expect(runnable.isFailed(), 'to be false');
});
diff --git a/test/unit/runner.spec.js b/test/unit/runner.spec.js
index 71a89b6af0..6c04f93f31 100644
--- a/test/unit/runner.spec.js
+++ b/test/unit/runner.spec.js
@@ -1,12 +1,17 @@
'use strict';
+var Mocha = require('../../lib/mocha');
+var Suite = Mocha.Suite;
+var Runner = Mocha.Runner;
+var Test = Mocha.Test;
+var Runnable = Mocha.Runnable;
+var Hook = Mocha.Hook;
var path = require('path');
-var mocha = require('../../lib/mocha');
-var Suite = mocha.Suite;
-var Runner = mocha.Runner;
-var Test = mocha.Test;
-var Hook = mocha.Hook;
-var noop = mocha.utils.noop;
+var noop = Mocha.utils.noop;
+var EVENT_TEST_FAIL = Runner.constants.EVENT_TEST_FAIL;
+var EVENT_TEST_RETRY = Runner.constants.EVENT_TEST_RETRY;
+var EVENT_RUN_END = Runner.constants.EVENT_RUN_END;
+var STATE_FAILED = Runnable.constants.STATE_FAILED;
describe('Runner', function() {
var suite;
@@ -103,7 +108,7 @@ describe('Runner', function() {
var test = new Test('im a test', noop);
runner.checkGlobals();
global.foo = 'bar';
- runner.on('fail', function(_test, _err) {
+ runner.on(EVENT_TEST_FAIL, function(_test, _err) {
expect(_test, 'to be', test);
expect(_err, 'to have message', 'global leak detected: foo');
delete global.foo;
@@ -116,7 +121,7 @@ describe('Runner', function() {
var doneCalled = false;
runner.globals('good');
global.bad = 1;
- runner.on('fail', function() {
+ runner.on(EVENT_TEST_FAIL, function() {
delete global.bad;
done();
doneCalled = true;
@@ -157,7 +162,7 @@ describe('Runner', function() {
runner.checkGlobals();
global.foo = 'bar';
global.bar = 'baz';
- runner.on('fail', function(_test, _err) {
+ runner.on(EVENT_TEST_FAIL, function(_test, _err) {
expect(_test, 'to be', test);
expect(_err, 'to have message', 'global leaks detected: foo, bar');
delete global.foo;
@@ -191,7 +196,7 @@ describe('Runner', function() {
global.foo = 'bar';
global.bar = 'baz';
- runner.on('fail', function(_test, _err) {
+ runner.on(EVENT_TEST_FAIL, function(_test, _err) {
expect(_test.title, 'to be', 'im a test about lions');
expect(_err, 'to have message', 'global leak detected: bar');
delete global.foo;
@@ -202,7 +207,9 @@ describe('Runner', function() {
it('should emit "fail" when a global beginning with d is introduced', function(done) {
global.derp = 'bar';
- runner.on('fail', function() {
+ runner.on(EVENT_TEST_FAIL, function(test, err) {
+ expect(test.title, 'to be', 'herp');
+ expect(err.message, 'to be', 'global leak detected: derp');
delete global.derp;
done();
});
@@ -236,13 +243,13 @@ describe('Runner', function() {
it('should set test.state to "failed"', function() {
var test = new Test('some test', noop);
runner.fail(test, 'some error');
- expect(test.state, 'to be', 'failed');
+ expect(test.state, 'to be', STATE_FAILED);
});
it('should emit "fail"', function(done) {
var test = new Test('some other test', noop);
var err = {};
- runner.on('fail', function(_test, _err) {
+ runner.on(EVENT_TEST_FAIL, function(_test, _err) {
expect(_test, 'to be', test);
expect(_err, 'to be an', Error);
expect(_err, 'not to be', {});
@@ -254,7 +261,7 @@ describe('Runner', function() {
it('should emit a helpful message when failed with a string', function(done) {
var test = new Test('helpful test', noop);
var err = 'string';
- runner.on('fail', function(_test, _err) {
+ runner.on(EVENT_TEST_FAIL, function(_test, _err) {
expect(_err, 'to be an', Error);
expect(
_err,
@@ -269,7 +276,7 @@ describe('Runner', function() {
it('should emit a the error when failed with an Error instance', function(done) {
var test = new Test('a test', noop);
var err = new Error('an error message');
- runner.on('fail', function(_test, _err) {
+ runner.on(EVENT_TEST_FAIL, function(_test, _err) {
expect(_err, 'to be an', Error);
expect(_err, 'to have message', 'an error message');
done();
@@ -280,7 +287,7 @@ describe('Runner', function() {
it('should emit the error when failed with an Error-like object', function(done) {
var test = new Test('a test', noop);
var err = {message: 'an error message'};
- runner.on('fail', function(_test, _err) {
+ runner.on(EVENT_TEST_FAIL, function(_test, _err) {
expect(_err, 'not to be an', Error);
expect(_err.message, 'to be', 'an error message');
done();
@@ -291,7 +298,7 @@ describe('Runner', function() {
it('should emit a helpful message when failed with an Object', function(done) {
var test = new Test('a test', noop);
var err = {x: 1};
- runner.on('fail', function(_test, _err) {
+ runner.on(EVENT_TEST_FAIL, function(_test, _err) {
expect(_err, 'to be an', Error);
expect(
_err,
@@ -306,7 +313,7 @@ describe('Runner', function() {
it('should emit a helpful message when failed with an Array', function(done) {
var test = new Test('a test', noop);
var err = [1, 2];
- runner.on('fail', function(_test, _err) {
+ runner.on(EVENT_TEST_FAIL, function(_test, _err) {
expect(_err, 'to be an', Error);
expect(
_err,
@@ -330,7 +337,7 @@ describe('Runner', function() {
});
var test = new Test('a test', noop);
- runner.on('fail', function(_test, _err) {
+ runner.on(EVENT_TEST_FAIL, function(_test, _err) {
expect(_err, 'to have message', 'not evil');
done();
});
@@ -376,7 +383,7 @@ describe('Runner', function() {
var hook = new Hook();
hook.parent = suite;
var err = new Error('error');
- runner.on('fail', function(_hook, _err) {
+ runner.on(EVENT_TEST_FAIL, function(_hook, _err) {
expect(_hook, 'to be', hook);
expect(_err, 'to be', err);
done();
@@ -389,7 +396,7 @@ describe('Runner', function() {
hook.parent = suite;
var err = new Error('error');
suite.bail(false);
- runner.on('end', function() {
+ runner.on(EVENT_RUN_END, function() {
throw new Error('"end" was emit, but the bail is false');
});
runner.failHook(hook, err);
@@ -412,7 +419,7 @@ describe('Runner', function() {
suite.retries(retries);
suite.addTest(test);
- runner.on('retry', function(testClone, testErr) {
+ runner.on(EVENT_TEST_RETRY, function(testClone, testErr) {
retryableFails += 1;
expect(testClone.title, 'to be', test.title);
expect(testErr, 'to be', err);
@@ -491,7 +498,7 @@ describe('Runner', function() {
// Fake stack-trace
err.stack = stack.join('\n');
- runner.on('fail', function(_hook, _err) {
+ runner.on(EVENT_TEST_FAIL, function(_hook, _err) {
expect(_err.stack, 'to be', stack.slice(0, 3).join('\n'));
done();
});
@@ -509,7 +516,7 @@ describe('Runner', function() {
// Add --stack-trace option
runner.fullStackTrace = true;
- runner.on('fail', function(_hook, _err) {
+ runner.on(EVENT_TEST_FAIL, function(_hook, _err) {
expect(_err.stack, 'to be', stack.join('\n'));
done();
});
diff --git a/test/unit/suite.spec.js b/test/unit/suite.spec.js
index fa57898a51..971208b9b9 100644
--- a/test/unit/suite.spec.js
+++ b/test/unit/suite.spec.js
@@ -1,8 +1,10 @@
'use strict';
-var mocha = require('../../lib/mocha');
-var Suite = mocha.Suite;
-var Test = mocha.Test;
+var Mocha = require('../../lib/mocha');
+var Suite = Mocha.Suite;
+var Test = Mocha.Test;
+var sinon = require('sinon');
+var utils = Mocha.utils;
function supportsFunctionNames() {
// eslint-disable-next-line no-extra-parens
@@ -10,6 +12,15 @@ function supportsFunctionNames() {
}
describe('Suite', function() {
+ var sandbox;
+ beforeEach(function() {
+ sandbox = sinon.createSandbox();
+ });
+
+ afterEach(function() {
+ sandbox.restore();
+ });
+
describe('.clone()', function() {
beforeEach(function() {
this.suite = new Suite('To be cloned');
@@ -497,7 +508,11 @@ describe('Suite', function() {
});
});
- describe('initialization', function() {
+ describe('constructor', function() {
+ beforeEach(function() {
+ sandbox.stub(utils, 'deprecate');
+ });
+
/* eslint no-new: off */
it("should throw an error if the title isn't a string", function() {
expect(function() {
@@ -514,6 +529,13 @@ describe('Suite', function() {
new Suite('Bdd suite', 'root');
}, 'not to throw');
});
+
+ it('should report listened-for deprecated events as deprecated', function() {
+ new Suite('foo').on(Suite.constants.EVENT_SUITE_ADD_TEST, function() {});
+ expect(utils.deprecate, 'to have all calls satisfying', [
+ /Event "[^"]+" is deprecated/i
+ ]);
+ });
});
describe('timeout()', function() {
diff --git a/test/unit/throw.spec.js b/test/unit/throw.spec.js
index 66021d3d38..2dc3c8a759 100644
--- a/test/unit/throw.spec.js
+++ b/test/unit/throw.spec.js
@@ -2,9 +2,13 @@
/* eslint no-throw-literal: off */
-var Suite = require('../../lib/suite');
-var Test = require('../../lib/test');
-var Runner = require('../../lib/runner');
+var Mocha = require('../../lib/mocha');
+var Suite = Mocha.Suite;
+var Test = Mocha.Test;
+var Runnable = Mocha.Runnable;
+var Runner = Mocha.Runner;
+var EVENT_RUN_END = Runner.constants.EVENT_RUN_END;
+var STATE_FAILED = Runnable.constants.STATE_FAILED;
describe('a test that throws', function() {
var suite;
@@ -33,9 +37,9 @@ describe('a test that throws', function() {
});
suite.addTest(test);
runner = new Runner(suite);
- runner.on('end', function() {
+ runner.on(EVENT_RUN_END, function() {
expect(runner.failures, 'to be', 1);
- expect(test.state, 'to be', 'failed');
+ expect(test.state, 'to be', STATE_FAILED);
done();
});
runner.run();
@@ -47,9 +51,9 @@ describe('a test that throws', function() {
});
suite.addTest(test);
runner = new Runner(suite);
- runner.on('end', function() {
+ runner.on(EVENT_RUN_END, function() {
expect(runner.failures, 'to be', 1);
- expect(test.state, 'to be', 'failed');
+ expect(test.state, 'to be', STATE_FAILED);
done();
});
runner.run();
@@ -63,9 +67,9 @@ describe('a test that throws', function() {
});
suite.addTest(test);
runner = new Runner(suite);
- runner.on('end', function() {
+ runner.on(EVENT_RUN_END, function() {
expect(runner.failures, 'to be', 1);
- expect(test.state, 'to be', 'failed');
+ expect(test.state, 'to be', STATE_FAILED);
done();
});
runner.run();
@@ -79,9 +83,9 @@ describe('a test that throws', function() {
});
suite.addTest(test);
runner = new Runner(suite);
- runner.on('end', function() {
+ runner.on(EVENT_RUN_END, function() {
expect(runner.failures, 'to be', 1);
- expect(test.state, 'to be', 'failed');
+ expect(test.state, 'to be', STATE_FAILED);
done();
});
runner.run();
@@ -93,9 +97,9 @@ describe('a test that throws', function() {
});
suite.addTest(test);
runner = new Runner(suite);
- runner.on('end', function() {
+ runner.on(EVENT_RUN_END, function() {
expect(runner.failures, 'to be', 1);
- expect(test.state, 'to be', 'failed');
+ expect(test.state, 'to be', STATE_FAILED);
done();
});
runner.run();
@@ -111,9 +115,9 @@ describe('a test that throws', function() {
});
suite.addTest(test);
runner = new Runner(suite);
- runner.on('end', function() {
+ runner.on(EVENT_RUN_END, function() {
expect(runner.failures, 'to be', 1);
- expect(test.state, 'to be', 'failed');
+ expect(test.state, 'to be', STATE_FAILED);
done();
});
runner.run();
@@ -127,9 +131,9 @@ describe('a test that throws', function() {
});
suite.addTest(test);
runner = new Runner(suite);
- runner.on('end', function() {
+ runner.on(EVENT_RUN_END, function() {
expect(runner.failures, 'to be', 1);
- expect(test.state, 'to be', 'failed');
+ expect(test.state, 'to be', STATE_FAILED);
done();
});
runner.run();
@@ -141,9 +145,9 @@ describe('a test that throws', function() {
});
suite.addTest(test);
runner = new Runner(suite);
- runner.on('end', function() {
+ runner.on(EVENT_RUN_END, function() {
expect(runner.failures, 'to be', 1);
- expect(test.state, 'to be', 'failed');
+ expect(test.state, 'to be', STATE_FAILED);
done();
});
runner.run();
@@ -157,9 +161,9 @@ describe('a test that throws', function() {
});
suite.addTest(test);
runner = new Runner(suite);
- runner.on('end', function() {
+ runner.on(EVENT_RUN_END, function() {
expect(runner.failures, 'to be', 1);
- expect(test.state, 'to be', 'failed');
+ expect(test.state, 'to be', STATE_FAILED);
done();
});
runner.run();
diff --git a/test/unit/utils.spec.js b/test/unit/utils.spec.js
index fc52f4ef85..bd0360362d 100644
--- a/test/unit/utils.spec.js
+++ b/test/unit/utils.spec.js
@@ -705,4 +705,24 @@ describe('lib/utils', function() {
expect(process.emitWarning, 'was not called');
});
});
+
+ describe('createMap', function() {
+ it('should return an object with a null prototype', function() {
+ expect(Object.getPrototypeOf(utils.createMap()), 'to be', null);
+ });
+
+ it('should add props to the object', function() {
+ expect(utils.createMap({foo: 'bar'}), 'to exhaustively satisfy', {
+ foo: 'bar'
+ });
+ });
+
+ it('should add props from all object parameters to the object', function() {
+ expect(
+ utils.createMap({foo: 'bar'}, {bar: 'baz'}),
+ 'to exhaustively satisfy',
+ {foo: 'bar', bar: 'baz'}
+ );
+ });
+ });
});