_mocha 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545
  1. #!/usr/bin/env node
  2. 'use strict';
  3. /* eslint no-unused-vars: off */
  4. /**
  5. * Module dependencies.
  6. */
  7. var program = require('commander');
  8. var path = require('path');
  9. var fs = require('fs');
  10. var resolve = path.resolve;
  11. var exists = fs.existsSync || path.existsSync;
  12. var Mocha = require('../');
  13. var utils = Mocha.utils;
  14. var interfaceNames = Object.keys(Mocha.interfaces);
  15. var join = path.join;
  16. var cwd = process.cwd();
  17. var getOptions = require('./options');
  18. var mocha = new Mocha();
  19. /**
  20. * Save timer references to avoid Sinon interfering (see GH-237).
  21. */
  22. var Date = global.Date;
  23. var setTimeout = global.setTimeout;
  24. var setInterval = global.setInterval;
  25. var clearTimeout = global.clearTimeout;
  26. var clearInterval = global.clearInterval;
  27. /**
  28. * Files.
  29. */
  30. var files = [];
  31. /**
  32. * Globals.
  33. */
  34. var globals = [];
  35. /**
  36. * Requires.
  37. */
  38. var requires = [];
  39. /**
  40. * Images.
  41. */
  42. var images = {
  43. fail: path.join(__dirname, '..', 'images', 'error.png'),
  44. pass: path.join(__dirname, '..', 'images', 'ok.png')
  45. };
  46. // options
  47. program
  48. .version(JSON.parse(fs.readFileSync(path.join(__dirname, '..', 'package.json'), 'utf8')).version)
  49. .usage('[debug] [options] [files]')
  50. .option('-A, --async-only', 'force all tests to take a callback (async) or return a promise')
  51. .option('-c, --colors', 'force enabling of colors')
  52. .option('-C, --no-colors', 'force disabling of colors')
  53. .option('-G, --growl', 'enable growl notification support')
  54. .option('-O, --reporter-options <k=v,k2=v2,...>', 'reporter-specific options')
  55. .option('-R, --reporter <name>', 'specify the reporter to use', 'spec')
  56. .option('-S, --sort', 'sort test files')
  57. .option('-b, --bail', 'bail after first test failure')
  58. .option('-d, --debug', "enable node's debugger, synonym for node --debug")
  59. .option('-g, --grep <pattern>', 'only run tests matching <pattern>')
  60. .option('-f, --fgrep <string>', 'only run tests containing <string>')
  61. .option('-gc, --expose-gc', 'expose gc extension')
  62. .option('-i, --invert', 'inverts --grep and --fgrep matches')
  63. .option('-r, --require <name>', 'require the given module')
  64. .option('-s, --slow <ms>', '"slow" test threshold in milliseconds [75]')
  65. .option('-t, --timeout <ms>', 'set test-case timeout in milliseconds [2000]')
  66. .option('-u, --ui <name>', 'specify user-interface (' + interfaceNames.join('|') + ')', 'bdd')
  67. .option('-w, --watch', 'watch files for changes')
  68. .option('--check-leaks', 'check for global variable leaks')
  69. .option('--full-trace', 'display the full stack trace')
  70. .option('--compilers <ext>:<module>,...', 'use the given module(s) to compile files', list, [])
  71. .option('--debug-brk', "enable node's debugger breaking on the first line")
  72. .option('--globals <names>', 'allow the given comma-delimited global [names]', list, [])
  73. .option('--es_staging', 'enable all staged features')
  74. .option('--harmony<_classes,_generators,...>', 'all node --harmony* flags are available')
  75. .option('--preserve-symlinks', 'Instructs the module loader to preserve symbolic links when resolving and caching modules')
  76. .option('--icu-data-dir', 'include ICU data')
  77. .option('--inline-diffs', 'display actual/expected differences inline within each string')
  78. .option('--inspect', 'activate devtools in chrome')
  79. .option('--interfaces', 'display available interfaces')
  80. .option('--no-deprecation', 'silence deprecation warnings')
  81. .option('--no-exit', 'require a clean shutdown of the event loop: mocha will not call process.exit')
  82. .option('--no-timeouts', 'disables timeouts, given implicitly with --debug')
  83. .option('--opts <path>', 'specify opts path', 'test/mocha.opts')
  84. .option('--perf-basic-prof', 'enable perf linux profiler (basic support)')
  85. .option('--prof', 'log statistical profiling information')
  86. .option('--log-timer-events', 'Time events including external callbacks')
  87. .option('--recursive', 'include sub directories')
  88. .option('--reporters', 'display available reporters')
  89. .option('--retries <times>', 'set numbers of time to retry a failed test case')
  90. .option('--throw-deprecation', 'throw an exception anytime a deprecated function is used')
  91. .option('--trace', 'trace function calls')
  92. .option('--trace-deprecation', 'show stack traces on deprecations')
  93. .option('--use_strict', 'enforce strict mode')
  94. .option('--watch-extensions <ext>,...', 'additional extensions to monitor with --watch', list, [])
  95. .option('--delay', 'wait for async suite definition');
  96. program._name = 'mocha';
  97. // init command
  98. program
  99. .command('init <path>')
  100. .description('initialize a client-side mocha setup at <path>')
  101. .action(function (path) {
  102. var mkdir = require('mkdirp');
  103. mkdir.sync(path);
  104. var css = fs.readFileSync(join(__dirname, '..', 'mocha.css'));
  105. var js = fs.readFileSync(join(__dirname, '..', 'mocha.js'));
  106. var tmpl = fs.readFileSync(join(__dirname, '..', 'lib/template.html'));
  107. fs.writeFileSync(join(path, 'mocha.css'), css);
  108. fs.writeFileSync(join(path, 'mocha.js'), js);
  109. fs.writeFileSync(join(path, 'tests.js'), '');
  110. fs.writeFileSync(join(path, 'index.html'), tmpl);
  111. process.exit(0);
  112. });
  113. // --globals
  114. program.on('globals', function (val) {
  115. globals = globals.concat(list(val));
  116. });
  117. // --reporters
  118. program.on('reporters', function () {
  119. console.log();
  120. console.log(' dot - dot matrix');
  121. console.log(' doc - html documentation');
  122. console.log(' spec - hierarchical spec list');
  123. console.log(' json - single json object');
  124. console.log(' progress - progress bar');
  125. console.log(' list - spec-style listing');
  126. console.log(' tap - test-anything-protocol');
  127. console.log(' landing - unicode landing strip');
  128. console.log(' xunit - xunit reporter');
  129. console.log(' min - minimal reporter (great with --watch)');
  130. console.log(' json-stream - newline delimited json events');
  131. console.log(' markdown - markdown documentation (github flavour)');
  132. console.log(' nyan - nyan cat!');
  133. console.log();
  134. process.exit();
  135. });
  136. // --interfaces
  137. program.on('interfaces', function () {
  138. console.log('');
  139. interfaceNames.forEach(function (interfaceName) {
  140. console.log(' ' + interfaceName);
  141. });
  142. console.log('');
  143. process.exit();
  144. });
  145. // -r, --require
  146. module.paths.push(cwd, join(cwd, 'node_modules'));
  147. program.on('require', function (mod) {
  148. var abs = exists(mod) || exists(mod + '.js');
  149. if (abs) {
  150. mod = resolve(mod);
  151. }
  152. requires.push(mod);
  153. });
  154. // If not already done, load mocha.opts
  155. if (!process.env.LOADED_MOCHA_OPTS) {
  156. getOptions();
  157. }
  158. // parse args
  159. program.parse(process.argv);
  160. // infinite stack traces
  161. Error.stackTraceLimit = Infinity; // TODO: config
  162. // reporter options
  163. var reporterOptions = {};
  164. if (program.reporterOptions !== undefined) {
  165. program.reporterOptions.split(',').forEach(function (opt) {
  166. var L = opt.split('=');
  167. if (L.length > 2 || L.length === 0) {
  168. throw new Error("invalid reporter option '" + opt + "'");
  169. } else if (L.length === 2) {
  170. reporterOptions[L[0]] = L[1];
  171. } else {
  172. reporterOptions[L[0]] = true;
  173. }
  174. });
  175. }
  176. // reporter
  177. mocha.reporter(program.reporter, reporterOptions);
  178. // load reporter
  179. var Reporter = null;
  180. try {
  181. Reporter = require('../lib/reporters/' + program.reporter);
  182. } catch (err) {
  183. try {
  184. Reporter = require(program.reporter);
  185. } catch (err2) {
  186. throw new Error('reporter "' + program.reporter + '" does not exist');
  187. }
  188. }
  189. // --no-colors
  190. if (!program.colors) {
  191. mocha.useColors(false);
  192. }
  193. // --colors
  194. if (~process.argv.indexOf('--colors') || ~process.argv.indexOf('-c')) {
  195. mocha.useColors(true);
  196. }
  197. // --inline-diffs
  198. if (program.inlineDiffs) {
  199. mocha.useInlineDiffs(true);
  200. }
  201. // --slow <ms>
  202. if (program.slow) {
  203. mocha.suite.slow(program.slow);
  204. }
  205. // --no-timeouts
  206. if (!program.timeouts) {
  207. mocha.enableTimeouts(false);
  208. }
  209. // --timeout
  210. if (program.timeout) {
  211. mocha.suite.timeout(program.timeout);
  212. }
  213. // --bail
  214. mocha.suite.bail(program.bail);
  215. // --grep
  216. if (program.grep) {
  217. mocha.grep(program.grep);
  218. }
  219. // --fgrep
  220. if (program.fgrep) {
  221. mocha.fgrep(program.fgrep);
  222. }
  223. // --invert
  224. if (program.invert) {
  225. mocha.invert();
  226. }
  227. // --check-leaks
  228. if (program.checkLeaks) {
  229. mocha.checkLeaks();
  230. }
  231. // --stack-trace
  232. if (program.fullTrace) {
  233. mocha.fullTrace();
  234. }
  235. // --growl
  236. if (program.growl) {
  237. mocha.growl();
  238. }
  239. // --async-only
  240. if (program.asyncOnly) {
  241. mocha.asyncOnly();
  242. }
  243. // --delay
  244. if (program.delay) {
  245. mocha.delay();
  246. }
  247. // --globals
  248. mocha.globals(globals);
  249. // --retries
  250. if (program.retries) {
  251. mocha.suite.retries(program.retries);
  252. }
  253. // custom compiler support
  254. var extensions = ['js'];
  255. program.compilers.forEach(function (c) {
  256. var idx = c.indexOf(':');
  257. var ext = c.slice(0, idx);
  258. var mod = c.slice(idx + 1);
  259. if (mod[0] === '.') {
  260. mod = join(process.cwd(), mod);
  261. }
  262. require(mod);
  263. extensions.push(ext);
  264. program.watchExtensions.push(ext);
  265. });
  266. // requires
  267. requires.forEach(function (mod) {
  268. require(mod);
  269. });
  270. // interface
  271. mocha.ui(program.ui);
  272. // args
  273. var args = program.args;
  274. // default files to test/*.{js,coffee}
  275. if (!args.length) {
  276. args.push('test');
  277. }
  278. args.forEach(function (arg) {
  279. var newFiles;
  280. try {
  281. newFiles = utils.lookupFiles(arg, extensions, program.recursive);
  282. } catch (err) {
  283. if (err.message.indexOf('cannot resolve path') === 0) {
  284. console.error('Warning: Could not find any test files matching pattern: ' + arg);
  285. return;
  286. }
  287. throw err;
  288. }
  289. files = files.concat(newFiles);
  290. });
  291. if (!files.length) {
  292. console.error('No test files found');
  293. process.exit(1);
  294. }
  295. // resolve
  296. files = files.map(function (path) {
  297. return resolve(path);
  298. });
  299. if (program.sort) {
  300. files.sort();
  301. }
  302. // --watch
  303. var runner;
  304. var loadAndRun;
  305. var purge;
  306. var rerun;
  307. if (program.watch) {
  308. console.log();
  309. hideCursor();
  310. process.on('SIGINT', function () {
  311. showCursor();
  312. console.log('\n');
  313. process.exit(130);
  314. });
  315. var watchFiles = utils.files(cwd, [ 'js' ].concat(program.watchExtensions));
  316. var runAgain = false;
  317. loadAndRun = function loadAndRun () {
  318. try {
  319. mocha.files = files;
  320. runAgain = false;
  321. runner = mocha.run(function () {
  322. runner = null;
  323. if (runAgain) {
  324. rerun();
  325. }
  326. });
  327. } catch (e) {
  328. console.log(e.stack);
  329. }
  330. };
  331. purge = function purge () {
  332. watchFiles.forEach(function (file) {
  333. delete require.cache[file];
  334. });
  335. };
  336. loadAndRun();
  337. rerun = function rerun () {
  338. purge();
  339. stop();
  340. if (!program.grep) {
  341. mocha.grep(null);
  342. }
  343. mocha.suite = mocha.suite.clone();
  344. mocha.suite.ctx = new Mocha.Context();
  345. mocha.ui(program.ui);
  346. loadAndRun();
  347. };
  348. utils.watch(watchFiles, function () {
  349. runAgain = true;
  350. if (runner) {
  351. runner.abort();
  352. } else {
  353. rerun();
  354. }
  355. });
  356. } else {
  357. // load
  358. mocha.files = files;
  359. runner = mocha.run(program.exit ? exit : exitLater);
  360. }
  361. function exitLater (code) {
  362. process.on('exit', function () {
  363. process.exit(Math.min(code, 255));
  364. });
  365. }
  366. function exit (code) {
  367. // flush output for Node.js Windows pipe bug
  368. // https://github.com/joyent/node/issues/6247 is just one bug example
  369. // https://github.com/visionmedia/mocha/issues/333 has a good discussion
  370. function done () {
  371. if (!(draining--)) {
  372. process.exit(Math.min(code, 255));
  373. }
  374. }
  375. var draining = 0;
  376. var streams = [process.stdout, process.stderr];
  377. streams.forEach(function (stream) {
  378. // submit empty write request and wait for completion
  379. draining += 1;
  380. stream.write('', done);
  381. });
  382. done();
  383. }
  384. process.on('SIGINT', function () {
  385. runner.abort();
  386. // This is a hack:
  387. // Instead of `process.exit(130)`, set runner.failures to 130 (exit code for SIGINT)
  388. // The amount of failures will be emitted as error code later
  389. runner.failures = 130;
  390. });
  391. /**
  392. * Parse list.
  393. */
  394. function list (str) {
  395. return str.split(/ *, */);
  396. }
  397. /**
  398. * Hide the cursor.
  399. */
  400. function hideCursor () {
  401. process.stdout.write('\u001b[?25l');
  402. }
  403. /**
  404. * Show the cursor.
  405. */
  406. function showCursor () {
  407. process.stdout.write('\u001b[?25h');
  408. }
  409. /**
  410. * Stop play()ing.
  411. */
  412. function stop () {
  413. process.stdout.write('\u001b[2K');
  414. clearInterval(play.timer);
  415. }
  416. /**
  417. * Play the given array of strings.
  418. */
  419. function play (arr, interval) {
  420. var len = arr.length;
  421. interval = interval || 100;
  422. var i = 0;
  423. play.timer = setInterval(function () {
  424. var str = arr[i++ % len];
  425. process.stdout.write('\u001b[0G' + str);
  426. }, interval);
  427. }