mocha.js 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522
  1. 'use strict';
  2. /*!
  3. * mocha
  4. * Copyright(c) 2011 TJ Holowaychuk <tj@vision-media.ca>
  5. * MIT Licensed
  6. */
  7. /**
  8. * Module dependencies.
  9. */
  10. var escapeRe = require('escape-string-regexp');
  11. var path = require('path');
  12. var reporters = require('./reporters');
  13. var utils = require('./utils');
  14. /**
  15. * Expose `Mocha`.
  16. */
  17. exports = module.exports = Mocha;
  18. /**
  19. * To require local UIs and reporters when running in node.
  20. */
  21. if (!process.browser) {
  22. var cwd = process.cwd();
  23. module.paths.push(cwd, path.join(cwd, 'node_modules'));
  24. }
  25. /**
  26. * Expose internals.
  27. */
  28. exports.utils = utils;
  29. exports.interfaces = require('./interfaces');
  30. exports.reporters = reporters;
  31. exports.Runnable = require('./overrides/runnable');
  32. exports.Context = require('./context');
  33. exports.Runner = require('./overrides/runner');
  34. exports.Suite = require('./suite');
  35. exports.Hook = require('./hook');
  36. exports.Test = require('./test');
  37. /**
  38. * Return image `name` path.
  39. *
  40. * @api private
  41. * @param {string} name
  42. * @return {string}
  43. */
  44. function image (name) {
  45. return path.join(__dirname, '../images', name + '.png');
  46. }
  47. /**
  48. * Set up mocha with `options`.
  49. *
  50. * Options:
  51. *
  52. * - `ui` name "bdd", "tdd", "exports" etc
  53. * - `reporter` reporter instance, defaults to `mocha.reporters.spec`
  54. * - `globals` array of accepted globals
  55. * - `timeout` timeout in milliseconds
  56. * - `retries` number of times to retry failed tests
  57. * - `bail` bail on the first test failure
  58. * - `slow` milliseconds to wait before considering a test slow
  59. * - `ignoreLeaks` ignore global leaks
  60. * - `fullTrace` display the full stack-trace on failing
  61. * - `grep` string or regexp to filter tests with
  62. *
  63. * @param {Object} options
  64. * @api public
  65. */
  66. function Mocha (options) {
  67. options = options || {};
  68. this.files = [];
  69. this.options = options;
  70. if (options.grep) {
  71. this.grep(new RegExp(options.grep));
  72. }
  73. if (options.fgrep) {
  74. this.fgrep(options.fgrep);
  75. }
  76. this.suite = new exports.Suite('', new exports.Context());
  77. this.ui(options.ui);
  78. this.bail(options.bail);
  79. this.reporter(options.reporter, options.reporterOptions);
  80. if (typeof options.timeout !== 'undefined' && options.timeout !== null) {
  81. this.timeout(options.timeout);
  82. }
  83. if (typeof options.retries !== 'undefined' && options.retries !== null) {
  84. this.retries(options.retries);
  85. }
  86. this.useColors(options.useColors);
  87. if (options.enableTimeouts !== null) {
  88. this.enableTimeouts(options.enableTimeouts);
  89. }
  90. if (options.slow) {
  91. this.slow(options.slow);
  92. }
  93. }
  94. /**
  95. * Enable or disable bailing on the first failure.
  96. *
  97. * @api public
  98. * @param {boolean} [bail]
  99. */
  100. Mocha.prototype.bail = function (bail) {
  101. if (!arguments.length) {
  102. bail = true;
  103. }
  104. this.suite.bail(bail);
  105. return this;
  106. };
  107. /**
  108. * Add test `file`.
  109. *
  110. * @api public
  111. * @param {string} file
  112. */
  113. Mocha.prototype.addFile = function (file) {
  114. this.files.push(file);
  115. return this;
  116. };
  117. /**
  118. * Set reporter to `reporter`, defaults to "spec".
  119. *
  120. * @param {String|Function} reporter name or constructor
  121. * @param {Object} reporterOptions optional options
  122. * @api public
  123. * @param {string|Function} reporter name or constructor
  124. * @param {Object} reporterOptions optional options
  125. */
  126. Mocha.prototype.reporter = function (reporter, reporterOptions) {
  127. if (typeof reporter === 'function') {
  128. this._reporter = reporter;
  129. } else {
  130. reporter = reporter || 'spec';
  131. var _reporter;
  132. // Try to load a built-in reporter.
  133. if (reporters[reporter]) {
  134. _reporter = reporters[reporter];
  135. }
  136. // Try to load reporters from process.cwd() and node_modules
  137. if (!_reporter) {
  138. try {
  139. _reporter = require(reporter);
  140. } catch (err) {
  141. err.message.indexOf('Cannot find module') !== -1
  142. ? console.warn('"' + reporter + '" reporter not found')
  143. : console.warn('"' + reporter + '" reporter blew up with error:\n' + err.stack);
  144. }
  145. }
  146. if (!_reporter && reporter === 'teamcity') {
  147. console.warn('The Teamcity reporter was moved to a package named ' +
  148. 'mocha-teamcity-reporter ' +
  149. '(https://npmjs.org/package/mocha-teamcity-reporter).');
  150. }
  151. if (!_reporter) {
  152. throw new Error('invalid reporter "' + reporter + '"');
  153. }
  154. this._reporter = _reporter;
  155. }
  156. this.options.reporterOptions = reporterOptions;
  157. return this;
  158. };
  159. /**
  160. * Set test UI `name`, defaults to "bdd".
  161. *
  162. * @api public
  163. * @param {string} bdd
  164. */
  165. Mocha.prototype.ui = function (name) {
  166. name = name || 'bdd';
  167. this._ui = exports.interfaces[name];
  168. if (!this._ui) {
  169. try {
  170. this._ui = require(name);
  171. } catch (err) {
  172. throw new Error('invalid interface "' + name + '"');
  173. }
  174. }
  175. this._ui = this._ui(this.suite);
  176. this.suite.on('pre-require', function (context) {
  177. exports.afterEach = context.afterEach || context.teardown;
  178. exports.after = context.after || context.suiteTeardown;
  179. exports.beforeEach = context.beforeEach || context.setup;
  180. exports.before = context.before || context.suiteSetup;
  181. exports.describe = context.describe || context.suite;
  182. exports.it = context.it || context.test;
  183. exports.setup = context.setup || context.beforeEach;
  184. exports.suiteSetup = context.suiteSetup || context.before;
  185. exports.suiteTeardown = context.suiteTeardown || context.after;
  186. exports.suite = context.suite || context.describe;
  187. exports.teardown = context.teardown || context.afterEach;
  188. exports.test = context.test || context.it;
  189. exports.run = context.run;
  190. });
  191. return this;
  192. };
  193. /**
  194. * Load registered files.
  195. *
  196. * @api private
  197. */
  198. Mocha.prototype.loadFiles = function (fn) {
  199. var self = this;
  200. var suite = this.suite;
  201. this.files.forEach(function (file) {
  202. file = path.resolve(file);
  203. suite.emit('pre-require', global, file, self);
  204. suite.emit('require', require(file), file, self);
  205. suite.emit('post-require', global, file, self);
  206. });
  207. fn && fn();
  208. };
  209. /**
  210. * Enable growl support.
  211. *
  212. * @api private
  213. */
  214. Mocha.prototype._growl = function (runner, reporter) {
  215. var notify = require('growl');
  216. runner.on('end', function () {
  217. var stats = reporter.stats;
  218. if (stats.failures) {
  219. var msg = stats.failures + ' of ' + runner.total + ' tests failed';
  220. notify(msg, { name: 'mocha', title: 'Failed', image: image('error') });
  221. } else {
  222. notify(stats.passes + ' tests passed in ' + stats.duration + 'ms', {
  223. name: 'mocha',
  224. title: 'Passed',
  225. image: image('ok')
  226. });
  227. }
  228. });
  229. };
  230. /**
  231. * Escape string and add it to grep as a regexp.
  232. *
  233. * @api public
  234. * @param str
  235. * @returns {Mocha}
  236. */
  237. Mocha.prototype.fgrep = function (str) {
  238. return this.grep(new RegExp(escapeRe(str)));
  239. };
  240. /**
  241. * Add regexp to grep, if `re` is a string it is escaped.
  242. *
  243. * @param {RegExp|String} re
  244. * @return {Mocha}
  245. * @api public
  246. * @param {RegExp|string} re
  247. * @return {Mocha}
  248. */
  249. Mocha.prototype.grep = function (re) {
  250. if (utils.isString(re)) {
  251. // extract args if it's regex-like, i.e: [string, pattern, flag]
  252. var arg = re.match(/^\/(.*)\/(g|i|)$|.*/);
  253. this.options.grep = new RegExp(arg[1] || arg[0], arg[2]);
  254. } else {
  255. this.options.grep = re;
  256. }
  257. return this;
  258. };
  259. /**
  260. * Invert `.grep()` matches.
  261. *
  262. * @return {Mocha}
  263. * @api public
  264. */
  265. Mocha.prototype.invert = function () {
  266. this.options.invert = true;
  267. return this;
  268. };
  269. /**
  270. * Ignore global leaks.
  271. *
  272. * @param {Boolean} ignore
  273. * @return {Mocha}
  274. * @api public
  275. * @param {boolean} ignore
  276. * @return {Mocha}
  277. */
  278. Mocha.prototype.ignoreLeaks = function (ignore) {
  279. this.options.ignoreLeaks = Boolean(ignore);
  280. return this;
  281. };
  282. /**
  283. * Enable global leak checking.
  284. *
  285. * @return {Mocha}
  286. * @api public
  287. */
  288. Mocha.prototype.checkLeaks = function () {
  289. this.options.ignoreLeaks = false;
  290. return this;
  291. };
  292. /**
  293. * Display long stack-trace on failing
  294. *
  295. * @return {Mocha}
  296. * @api public
  297. */
  298. Mocha.prototype.fullTrace = function () {
  299. this.options.fullStackTrace = true;
  300. return this;
  301. };
  302. /**
  303. * Enable growl support.
  304. *
  305. * @return {Mocha}
  306. * @api public
  307. */
  308. Mocha.prototype.growl = function () {
  309. this.options.growl = true;
  310. return this;
  311. };
  312. /**
  313. * Ignore `globals` array or string.
  314. *
  315. * @param {Array|String} globals
  316. * @return {Mocha}
  317. * @api public
  318. * @param {Array|string} globals
  319. * @return {Mocha}
  320. */
  321. Mocha.prototype.globals = function (globals) {
  322. this.options.globals = (this.options.globals || []).concat(globals);
  323. return this;
  324. };
  325. /**
  326. * Emit color output.
  327. *
  328. * @param {Boolean} colors
  329. * @return {Mocha}
  330. * @api public
  331. * @param {boolean} colors
  332. * @return {Mocha}
  333. */
  334. Mocha.prototype.useColors = function (colors) {
  335. if (colors !== undefined) {
  336. this.options.useColors = colors;
  337. }
  338. return this;
  339. };
  340. /**
  341. * Use inline diffs rather than +/-.
  342. *
  343. * @param {Boolean} inlineDiffs
  344. * @return {Mocha}
  345. * @api public
  346. * @param {boolean} inlineDiffs
  347. * @return {Mocha}
  348. */
  349. Mocha.prototype.useInlineDiffs = function (inlineDiffs) {
  350. this.options.useInlineDiffs = inlineDiffs !== undefined && inlineDiffs;
  351. return this;
  352. };
  353. /**
  354. * Set the timeout in milliseconds.
  355. *
  356. * @param {Number} timeout
  357. * @return {Mocha}
  358. * @api public
  359. * @param {number} timeout
  360. * @return {Mocha}
  361. */
  362. Mocha.prototype.timeout = function (timeout) {
  363. this.suite.timeout(timeout);
  364. return this;
  365. };
  366. /**
  367. * Set the number of times to retry failed tests.
  368. *
  369. * @param {Number} retry times
  370. * @return {Mocha}
  371. * @api public
  372. */
  373. Mocha.prototype.retries = function (n) {
  374. this.suite.retries(n);
  375. return this;
  376. };
  377. /**
  378. * Set slowness threshold in milliseconds.
  379. *
  380. * @param {Number} slow
  381. * @return {Mocha}
  382. * @api public
  383. * @param {number} slow
  384. * @return {Mocha}
  385. */
  386. Mocha.prototype.slow = function (slow) {
  387. this.suite.slow(slow);
  388. return this;
  389. };
  390. /**
  391. * Enable timeouts.
  392. *
  393. * @param {Boolean} enabled
  394. * @return {Mocha}
  395. * @api public
  396. * @param {boolean} enabled
  397. * @return {Mocha}
  398. */
  399. Mocha.prototype.enableTimeouts = function (enabled) {
  400. this.suite.enableTimeouts(arguments.length && enabled !== undefined ? enabled : true);
  401. return this;
  402. };
  403. /**
  404. * Makes all tests async (accepting a callback)
  405. *
  406. * @return {Mocha}
  407. * @api public
  408. */
  409. Mocha.prototype.asyncOnly = function () {
  410. this.options.asyncOnly = true;
  411. return this;
  412. };
  413. /**
  414. * Disable syntax highlighting (in browser).
  415. *
  416. * @api public
  417. */
  418. Mocha.prototype.noHighlighting = function () {
  419. this.options.noHighlighting = true;
  420. return this;
  421. };
  422. /**
  423. * Enable uncaught errors to propagate (in browser).
  424. *
  425. * @return {Mocha}
  426. * @api public
  427. */
  428. Mocha.prototype.allowUncaught = function () {
  429. this.options.allowUncaught = true;
  430. return this;
  431. };
  432. /**
  433. * Delay root suite execution.
  434. * @returns {Mocha}
  435. */
  436. Mocha.prototype.delay = function delay () {
  437. this.options.delay = true;
  438. return this;
  439. };
  440. /**
  441. * Run tests and invoke `fn()` when complete.
  442. *
  443. * @api public
  444. * @param {Function} fn
  445. * @return {Runner}
  446. */
  447. Mocha.prototype.run = function(nightwatch, nwOpts, fn) {
  448. if (this.files.length) {
  449. this.loadFiles();
  450. }
  451. var suite = this.suite;
  452. var options = this.options;
  453. options.files = this.files;
  454. var runner = new exports.Runner(suite, options.delay);
  455. var reporter = new this._reporter(runner, options);
  456. runner.ignoreLeaks = options.ignoreLeaks !== false;
  457. runner.fullStackTrace = options.fullStackTrace;
  458. runner.hasOnly = options.hasOnly;
  459. runner.asyncOnly = options.asyncOnly;
  460. runner.allowUncaught = options.allowUncaught;
  461. if (options.grep) {
  462. runner.grep(options.grep, options.invert);
  463. }
  464. if (options.globals) {
  465. runner.globals(options.globals);
  466. }
  467. if (options.growl) {
  468. this._growl(runner, reporter);
  469. }
  470. if (options.useColors !== undefined) {
  471. exports.reporters.Base.useColors = options.useColors;
  472. }
  473. exports.reporters.Base.inlineDiffs = options.useInlineDiffs;
  474. function done (failures) {
  475. if (reporter.done) {
  476. reporter.done(failures, fn);
  477. } else {
  478. fn && fn(failures);
  479. }
  480. }
  481. return runner.run(nightwatch, nwOpts, done);
  482. };