suite.js 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296
  1. /**
  2. * Module dependencies.
  3. */
  4. var EventEmitter = require('events').EventEmitter
  5. , debug = require('debug')('mocha:suite')
  6. , milliseconds = require('./ms')
  7. , utils = require('./utils')
  8. , Hook = require('./hook');
  9. /**
  10. * Expose `Suite`.
  11. */
  12. exports = module.exports = Suite;
  13. /**
  14. * Create a new `Suite` with the given `title`
  15. * and parent `Suite`. When a suite with the
  16. * same title is already present, that suite
  17. * is returned to provide nicer reporter
  18. * and more flexible meta-testing.
  19. *
  20. * @param {Suite} parent
  21. * @param {String} title
  22. * @return {Suite}
  23. * @api public
  24. */
  25. exports.create = function(parent, title){
  26. var suite = new Suite(title, parent.ctx);
  27. suite.parent = parent;
  28. if (parent.pending) suite.pending = true;
  29. title = suite.fullTitle();
  30. parent.addSuite(suite);
  31. return suite;
  32. };
  33. /**
  34. * Initialize a new `Suite` with the given
  35. * `title` and `ctx`.
  36. *
  37. * @param {String} title
  38. * @param {Context} ctx
  39. * @api private
  40. */
  41. function Suite(title, ctx) {
  42. this.title = title;
  43. this.ctx = ctx;
  44. this.suites = [];
  45. this.tests = [];
  46. this.pending = false;
  47. this._beforeEach = [];
  48. this._beforeAll = [];
  49. this._afterEach = [];
  50. this._afterAll = [];
  51. this.root = !title;
  52. this._timeout = 2000;
  53. this._slow = 75;
  54. this._bail = false;
  55. }
  56. /**
  57. * Inherit from `EventEmitter.prototype`.
  58. */
  59. Suite.prototype.__proto__ = EventEmitter.prototype;
  60. /**
  61. * Return a clone of this `Suite`.
  62. *
  63. * @return {Suite}
  64. * @api private
  65. */
  66. Suite.prototype.clone = function(){
  67. var suite = new Suite(this.title);
  68. debug('clone');
  69. suite.ctx = this.ctx;
  70. suite.timeout(this.timeout());
  71. suite.slow(this.slow());
  72. suite.bail(this.bail());
  73. return suite;
  74. };
  75. /**
  76. * Set timeout `ms` or short-hand such as "2s".
  77. *
  78. * @param {Number|String} ms
  79. * @return {Suite|Number} for chaining
  80. * @api private
  81. */
  82. Suite.prototype.timeout = function(ms){
  83. if (0 == arguments.length) return this._timeout;
  84. if ('string' == typeof ms) ms = milliseconds(ms);
  85. debug('timeout %d', ms);
  86. this._timeout = parseInt(ms, 10);
  87. return this;
  88. };
  89. /**
  90. * Set slow `ms` or short-hand such as "2s".
  91. *
  92. * @param {Number|String} ms
  93. * @return {Suite|Number} for chaining
  94. * @api private
  95. */
  96. Suite.prototype.slow = function(ms){
  97. if (0 === arguments.length) return this._slow;
  98. if ('string' == typeof ms) ms = milliseconds(ms);
  99. debug('slow %d', ms);
  100. this._slow = ms;
  101. return this;
  102. };
  103. /**
  104. * Sets whether to bail after first error.
  105. *
  106. * @parma {Boolean} bail
  107. * @return {Suite|Number} for chaining
  108. * @api private
  109. */
  110. Suite.prototype.bail = function(bail){
  111. if (0 == arguments.length) return this._bail;
  112. debug('bail %s', bail);
  113. this._bail = bail;
  114. return this;
  115. };
  116. /**
  117. * Run `fn(test[, done])` before running tests.
  118. *
  119. * @param {Function} fn
  120. * @return {Suite} for chaining
  121. * @api private
  122. */
  123. Suite.prototype.beforeAll = function(fn){
  124. if (this.pending) return this;
  125. var hook = new Hook('"before all" hook', fn);
  126. hook.parent = this;
  127. hook.timeout(this.timeout());
  128. hook.slow(this.slow());
  129. hook.ctx = this.ctx;
  130. this._beforeAll.push(hook);
  131. this.emit('beforeAll', hook);
  132. return this;
  133. };
  134. /**
  135. * Run `fn(test[, done])` after running tests.
  136. *
  137. * @param {Function} fn
  138. * @return {Suite} for chaining
  139. * @api private
  140. */
  141. Suite.prototype.afterAll = function(fn){
  142. if (this.pending) return this;
  143. var hook = new Hook('"after all" hook', fn);
  144. hook.parent = this;
  145. hook.timeout(this.timeout());
  146. hook.slow(this.slow());
  147. hook.ctx = this.ctx;
  148. this._afterAll.push(hook);
  149. this.emit('afterAll', hook);
  150. return this;
  151. };
  152. /**
  153. * Run `fn(test[, done])` before each test case.
  154. *
  155. * @param {Function} fn
  156. * @return {Suite} for chaining
  157. * @api private
  158. */
  159. Suite.prototype.beforeEach = function(fn){
  160. if (this.pending) return this;
  161. var hook = new Hook('"before each" hook', fn);
  162. hook.parent = this;
  163. hook.timeout(this.timeout());
  164. hook.slow(this.slow());
  165. hook.ctx = this.ctx;
  166. this._beforeEach.push(hook);
  167. this.emit('beforeEach', hook);
  168. return this;
  169. };
  170. /**
  171. * Run `fn(test[, done])` after each test case.
  172. *
  173. * @param {Function} fn
  174. * @return {Suite} for chaining
  175. * @api private
  176. */
  177. Suite.prototype.afterEach = function(fn){
  178. if (this.pending) return this;
  179. var hook = new Hook('"after each" hook', fn);
  180. hook.parent = this;
  181. hook.timeout(this.timeout());
  182. hook.slow(this.slow());
  183. hook.ctx = this.ctx;
  184. this._afterEach.push(hook);
  185. this.emit('afterEach', hook);
  186. return this;
  187. };
  188. /**
  189. * Add a test `suite`.
  190. *
  191. * @param {Suite} suite
  192. * @return {Suite} for chaining
  193. * @api private
  194. */
  195. Suite.prototype.addSuite = function(suite){
  196. suite.parent = this;
  197. suite.timeout(this.timeout());
  198. suite.slow(this.slow());
  199. suite.bail(this.bail());
  200. this.suites.push(suite);
  201. this.emit('suite', suite);
  202. return this;
  203. };
  204. /**
  205. * Add a `test` to this suite.
  206. *
  207. * @param {Test} test
  208. * @return {Suite} for chaining
  209. * @api private
  210. */
  211. Suite.prototype.addTest = function(test){
  212. test.parent = this;
  213. test.timeout(this.timeout());
  214. test.slow(this.slow());
  215. test.ctx = this.ctx;
  216. this.tests.push(test);
  217. this.emit('test', test);
  218. return this;
  219. };
  220. /**
  221. * Return the full title generated by recursively
  222. * concatenating the parent's full title.
  223. *
  224. * @return {String}
  225. * @api public
  226. */
  227. Suite.prototype.fullTitle = function(){
  228. if (this.parent) {
  229. var full = this.parent.fullTitle();
  230. if (full) return full + ' ' + this.title;
  231. }
  232. return this.title;
  233. };
  234. /**
  235. * Return the total number of tests.
  236. *
  237. * @return {Number}
  238. * @api public
  239. */
  240. Suite.prototype.total = function(){
  241. return utils.reduce(this.suites, function(sum, suite){
  242. return sum + suite.total();
  243. }, 0) + this.tests.length;
  244. };
  245. /**
  246. * Iterates through each suite recursively to find
  247. * all tests. Applies a function in the format
  248. * `fn(test)`.
  249. *
  250. * @param {Function} fn
  251. * @return {Suite}
  252. * @api private
  253. */
  254. Suite.prototype.eachTest = function(fn){
  255. utils.forEach(this.tests, fn);
  256. utils.forEach(this.suites, function(suite){
  257. suite.eachTest(fn);
  258. });
  259. return this;
  260. };