run.js 8.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297
  1. 'use strict';
  2. /**
  3. * Definition for Mocha's default ("run tests") command
  4. *
  5. * @module
  6. * @private
  7. */
  8. const Mocha = require('../mocha');
  9. const {
  10. createUnsupportedError,
  11. createInvalidArgumentValueError,
  12. createMissingArgumentError
  13. } = require('../errors');
  14. const {
  15. list,
  16. handleFiles,
  17. handleRequires,
  18. validatePlugin,
  19. runMocha
  20. } = require('./run-helpers');
  21. const {ONE_AND_DONES, ONE_AND_DONE_ARGS} = require('./one-and-dones');
  22. const debug = require('debug')('mocha:cli:run');
  23. const defaults = require('../mocharc');
  24. const {types, aliases} = require('./run-option-metadata');
  25. /**
  26. * Logical option groups
  27. * @constant
  28. */
  29. const GROUPS = {
  30. FILES: 'File Handling',
  31. FILTERS: 'Test Filters',
  32. NODEJS: 'Node.js & V8',
  33. OUTPUT: 'Reporting & Output',
  34. RULES: 'Rules & Behavior',
  35. CONFIG: 'Configuration'
  36. };
  37. exports.command = ['$0 [spec..]', 'debug [spec..]'];
  38. exports.describe = 'Run tests with Mocha';
  39. exports.builder = yargs =>
  40. yargs
  41. .options({
  42. 'allow-uncaught': {
  43. description: 'Allow uncaught errors to propagate',
  44. group: GROUPS.RULES
  45. },
  46. 'async-only': {
  47. description:
  48. 'Require all tests to use a callback (async) or return a Promise',
  49. group: GROUPS.RULES
  50. },
  51. bail: {
  52. description: 'Abort ("bail") after first test failure',
  53. group: GROUPS.RULES
  54. },
  55. 'check-leaks': {
  56. description: 'Check for global variable leaks',
  57. group: GROUPS.RULES
  58. },
  59. color: {
  60. description: 'Force-enable color output',
  61. group: GROUPS.OUTPUT
  62. },
  63. config: {
  64. config: true,
  65. defaultDescription: '(nearest rc file)',
  66. description: 'Path to config file',
  67. group: GROUPS.CONFIG
  68. },
  69. delay: {
  70. description: 'Delay initial execution of root suite',
  71. group: GROUPS.RULES
  72. },
  73. diff: {
  74. default: true,
  75. description: 'Show diff on failure',
  76. group: GROUPS.OUTPUT
  77. },
  78. exclude: {
  79. defaultDescription: '(none)',
  80. description: 'Ignore file(s) or glob pattern(s)',
  81. group: GROUPS.FILES,
  82. requiresArg: true
  83. },
  84. exit: {
  85. description: 'Force Mocha to quit after tests complete',
  86. group: GROUPS.RULES
  87. },
  88. extension: {
  89. default: defaults.extension,
  90. defaultDescription: 'js',
  91. description: 'File extension(s) to load and/or watch',
  92. group: GROUPS.FILES,
  93. requiresArg: true,
  94. coerce: list
  95. },
  96. fgrep: {
  97. conflicts: 'grep',
  98. description: 'Only run tests containing this string',
  99. group: GROUPS.FILTERS,
  100. requiresArg: true
  101. },
  102. file: {
  103. defaultDescription: '(none)',
  104. description:
  105. 'Specify file(s) to be loaded prior to root suite execution',
  106. group: GROUPS.FILES,
  107. normalize: true,
  108. requiresArg: true
  109. },
  110. 'forbid-only': {
  111. description: 'Fail if exclusive test(s) encountered',
  112. group: GROUPS.RULES
  113. },
  114. 'forbid-pending': {
  115. description: 'Fail if pending test(s) encountered',
  116. group: GROUPS.RULES
  117. },
  118. 'full-trace': {
  119. description: 'Display full stack traces',
  120. group: GROUPS.OUTPUT
  121. },
  122. global: {
  123. coerce: list,
  124. description: 'List of allowed global variables',
  125. group: GROUPS.RULES,
  126. requiresArg: true
  127. },
  128. grep: {
  129. coerce: value => (!value ? null : value),
  130. conflicts: 'fgrep',
  131. description: 'Only run tests matching this string or regexp',
  132. group: GROUPS.FILTERS,
  133. requiresArg: true
  134. },
  135. growl: {
  136. description: 'Enable Growl notifications',
  137. group: GROUPS.OUTPUT
  138. },
  139. 'inline-diffs': {
  140. description:
  141. 'Display actual/expected differences inline within each string',
  142. group: GROUPS.OUTPUT
  143. },
  144. interfaces: {
  145. conflicts: Array.from(ONE_AND_DONE_ARGS),
  146. description: 'List built-in user interfaces & exit'
  147. },
  148. invert: {
  149. description: 'Inverts --grep and --fgrep matches',
  150. group: GROUPS.FILTERS
  151. },
  152. 'no-colors': {
  153. description: 'Force-disable color output',
  154. group: GROUPS.OUTPUT,
  155. hidden: true
  156. },
  157. opts: {
  158. default: defaults.opts,
  159. description: 'Path to `mocha.opts`',
  160. group: GROUPS.CONFIG,
  161. normalize: true,
  162. requiresArg: true
  163. },
  164. package: {
  165. description: 'Path to package.json for config',
  166. group: GROUPS.CONFIG,
  167. normalize: true,
  168. requiresArg: true
  169. },
  170. recursive: {
  171. description: 'Look for tests in subdirectories',
  172. group: GROUPS.FILES
  173. },
  174. reporter: {
  175. default: defaults.reporter,
  176. description: 'Specify reporter to use',
  177. group: GROUPS.OUTPUT,
  178. requiresArg: true
  179. },
  180. reporters: {
  181. conflicts: Array.from(ONE_AND_DONE_ARGS),
  182. description: 'List built-in reporters & exit'
  183. },
  184. 'reporter-option': {
  185. coerce: opts =>
  186. list(opts).reduce((acc, opt) => {
  187. const pair = opt.split('=');
  188. if (pair.length > 2 || !pair.length) {
  189. throw createInvalidArgumentValueError(
  190. `invalid reporter option '${opt}'`,
  191. '--reporter-option',
  192. opt,
  193. 'expected "key=value" format'
  194. );
  195. }
  196. acc[pair[0]] = pair.length === 2 ? pair[1] : true;
  197. return acc;
  198. }, {}),
  199. description: 'Reporter-specific options (<k=v,[k1=v1,..]>)',
  200. group: GROUPS.OUTPUT,
  201. requiresArg: true
  202. },
  203. require: {
  204. defaultDescription: '(none)',
  205. description: 'Require module',
  206. group: GROUPS.FILES,
  207. requiresArg: true
  208. },
  209. retries: {
  210. description: 'Retry failed tests this many times',
  211. group: GROUPS.RULES
  212. },
  213. slow: {
  214. default: defaults.slow,
  215. description: 'Specify "slow" test threshold (in milliseconds)',
  216. group: GROUPS.RULES
  217. },
  218. sort: {
  219. description: 'Sort test files',
  220. group: GROUPS.FILES
  221. },
  222. timeout: {
  223. default: defaults.timeout,
  224. description: 'Specify test timeout threshold (in milliseconds)',
  225. group: GROUPS.RULES
  226. },
  227. ui: {
  228. default: defaults.ui,
  229. description: 'Specify user interface',
  230. group: GROUPS.RULES,
  231. requiresArg: true
  232. },
  233. watch: {
  234. description: 'Watch files in the current working directory for changes',
  235. group: GROUPS.FILES
  236. }
  237. })
  238. .positional('spec', {
  239. default: ['test'],
  240. description: 'One or more files, directories, or globs to test',
  241. type: 'array'
  242. })
  243. .check(argv => {
  244. // "one-and-dones"; let yargs handle help and version
  245. Object.keys(ONE_AND_DONES).forEach(opt => {
  246. if (argv[opt]) {
  247. ONE_AND_DONES[opt].call(null, yargs);
  248. process.exit();
  249. }
  250. });
  251. // yargs.implies() isn't flexible enough to handle this
  252. if (argv.invert && !('fgrep' in argv || 'grep' in argv)) {
  253. throw createMissingArgumentError(
  254. '"--invert" requires one of "--fgrep <str>" or "--grep <regexp>"',
  255. '--fgrep|--grep',
  256. 'string|regexp'
  257. );
  258. }
  259. if (argv.compilers) {
  260. throw createUnsupportedError(
  261. `--compilers is DEPRECATED and no longer supported.
  262. See https://git.io/vdcSr for migration information.`
  263. );
  264. }
  265. // load requires first, because it can impact "plugin" validation
  266. handleRequires(argv.require);
  267. validatePlugin(argv, 'reporter', Mocha.reporters);
  268. validatePlugin(argv, 'ui', Mocha.interfaces);
  269. return true;
  270. })
  271. .array(types.array)
  272. .boolean(types.boolean)
  273. .string(types.string)
  274. .number(types.number)
  275. .alias(aliases);
  276. exports.handler = argv => {
  277. debug('post-yargs config', argv);
  278. const mocha = new Mocha(argv);
  279. const files = handleFiles(argv);
  280. debug('running tests with files', files);
  281. runMocha(mocha, argv, files);
  282. };