mocha.js 11 KB

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