Dieses Repository beinhaltet HTML- und Javascript Code zur einer NotizenWebApp auf Basis von Web Storage. Zudem sind Mocha/Chai Tests im Browser enthalten. https://meinenotizen.netlify.app/
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

suite.js 16KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668
  1. 'use strict';
  2. /**
  3. * Module dependencies.
  4. */
  5. var EventEmitter = require('events').EventEmitter;
  6. var Hook = require('./hook');
  7. var utils = require('./utils');
  8. var inherits = utils.inherits;
  9. var debug = require('debug')('mocha:suite');
  10. var milliseconds = require('ms');
  11. var errors = require('./errors');
  12. var createInvalidArgumentTypeError = errors.createInvalidArgumentTypeError;
  13. /**
  14. * Expose `Suite`.
  15. */
  16. exports = module.exports = Suite;
  17. /**
  18. * Create a new `Suite` with the given `title` and parent `Suite`.
  19. *
  20. * @public
  21. * @param {Suite} parent - Parent suite (required!)
  22. * @param {string} title - Title
  23. * @return {Suite}
  24. */
  25. Suite.create = function(parent, title) {
  26. var suite = new Suite(title, parent.ctx);
  27. suite.parent = parent;
  28. title = suite.fullTitle();
  29. parent.addSuite(suite);
  30. return suite;
  31. };
  32. /**
  33. * Constructs a new `Suite` instance with the given `title`, `ctx`, and `isRoot`.
  34. *
  35. * @public
  36. * @class
  37. * @extends EventEmitter
  38. * @see {@link https://nodejs.org/api/events.html#events_class_eventemitter|EventEmitter}
  39. * @param {string} title - Suite title.
  40. * @param {Context} parentContext - Parent context instance.
  41. * @param {boolean} [isRoot=false] - Whether this is the root suite.
  42. */
  43. function Suite(title, parentContext, isRoot) {
  44. if (!utils.isString(title)) {
  45. throw createInvalidArgumentTypeError(
  46. 'Suite argument "title" must be a string. Received type "' +
  47. typeof title +
  48. '"',
  49. 'title',
  50. 'string'
  51. );
  52. }
  53. this.title = title;
  54. function Context() {}
  55. Context.prototype = parentContext;
  56. this.ctx = new Context();
  57. this.suites = [];
  58. this.tests = [];
  59. this.root = isRoot === true;
  60. this.pending = false;
  61. this._retries = -1;
  62. this._beforeEach = [];
  63. this._beforeAll = [];
  64. this._afterEach = [];
  65. this._afterAll = [];
  66. this._timeout = 2000;
  67. this._enableTimeouts = true;
  68. this._slow = 75;
  69. this._bail = false;
  70. this._onlyTests = [];
  71. this._onlySuites = [];
  72. this.reset();
  73. this.on('newListener', function(event) {
  74. if (deprecatedEvents[event]) {
  75. utils.deprecate(
  76. 'Event "' +
  77. event +
  78. '" is deprecated. Please let the Mocha team know about your use case: https://git.io/v6Lwm'
  79. );
  80. }
  81. });
  82. }
  83. /**
  84. * Inherit from `EventEmitter.prototype`.
  85. */
  86. inherits(Suite, EventEmitter);
  87. /**
  88. * Resets the state initially or for a next run.
  89. */
  90. Suite.prototype.reset = function() {
  91. this.delayed = false;
  92. function doReset(thingToReset) {
  93. thingToReset.reset();
  94. }
  95. this.suites.forEach(doReset);
  96. this.tests.forEach(doReset);
  97. this._beforeEach.forEach(doReset);
  98. this._afterEach.forEach(doReset);
  99. this._beforeAll.forEach(doReset);
  100. this._afterAll.forEach(doReset);
  101. };
  102. /**
  103. * Return a clone of this `Suite`.
  104. *
  105. * @private
  106. * @return {Suite}
  107. */
  108. Suite.prototype.clone = function() {
  109. var suite = new Suite(this.title);
  110. debug('clone');
  111. suite.ctx = this.ctx;
  112. suite.root = this.root;
  113. suite.timeout(this.timeout());
  114. suite.retries(this.retries());
  115. suite.enableTimeouts(this.enableTimeouts());
  116. suite.slow(this.slow());
  117. suite.bail(this.bail());
  118. return suite;
  119. };
  120. /**
  121. * Set or get timeout `ms` or short-hand such as "2s".
  122. *
  123. * @private
  124. * @todo Do not attempt to set value if `ms` is undefined
  125. * @param {number|string} ms
  126. * @return {Suite|number} for chaining
  127. */
  128. Suite.prototype.timeout = function(ms) {
  129. if (!arguments.length) {
  130. return this._timeout;
  131. }
  132. if (ms.toString() === '0') {
  133. this._enableTimeouts = false;
  134. }
  135. if (typeof ms === 'string') {
  136. ms = milliseconds(ms);
  137. }
  138. debug('timeout %d', ms);
  139. this._timeout = parseInt(ms, 10);
  140. return this;
  141. };
  142. /**
  143. * Set or get number of times to retry a failed test.
  144. *
  145. * @private
  146. * @param {number|string} n
  147. * @return {Suite|number} for chaining
  148. */
  149. Suite.prototype.retries = function(n) {
  150. if (!arguments.length) {
  151. return this._retries;
  152. }
  153. debug('retries %d', n);
  154. this._retries = parseInt(n, 10) || 0;
  155. return this;
  156. };
  157. /**
  158. * Set or get timeout to `enabled`.
  159. *
  160. * @private
  161. * @param {boolean} enabled
  162. * @return {Suite|boolean} self or enabled
  163. */
  164. Suite.prototype.enableTimeouts = function(enabled) {
  165. if (!arguments.length) {
  166. return this._enableTimeouts;
  167. }
  168. debug('enableTimeouts %s', enabled);
  169. this._enableTimeouts = enabled;
  170. return this;
  171. };
  172. /**
  173. * Set or get slow `ms` or short-hand such as "2s".
  174. *
  175. * @private
  176. * @param {number|string} ms
  177. * @return {Suite|number} for chaining
  178. */
  179. Suite.prototype.slow = function(ms) {
  180. if (!arguments.length) {
  181. return this._slow;
  182. }
  183. if (typeof ms === 'string') {
  184. ms = milliseconds(ms);
  185. }
  186. debug('slow %d', ms);
  187. this._slow = ms;
  188. return this;
  189. };
  190. /**
  191. * Set or get whether to bail after first error.
  192. *
  193. * @private
  194. * @param {boolean} bail
  195. * @return {Suite|number} for chaining
  196. */
  197. Suite.prototype.bail = function(bail) {
  198. if (!arguments.length) {
  199. return this._bail;
  200. }
  201. debug('bail %s', bail);
  202. this._bail = bail;
  203. return this;
  204. };
  205. /**
  206. * Check if this suite or its parent suite is marked as pending.
  207. *
  208. * @private
  209. */
  210. Suite.prototype.isPending = function() {
  211. return this.pending || (this.parent && this.parent.isPending());
  212. };
  213. /**
  214. * Generic hook-creator.
  215. * @private
  216. * @param {string} title - Title of hook
  217. * @param {Function} fn - Hook callback
  218. * @returns {Hook} A new hook
  219. */
  220. Suite.prototype._createHook = function(title, fn) {
  221. var hook = new Hook(title, fn);
  222. hook.parent = this;
  223. hook.timeout(this.timeout());
  224. hook.retries(this.retries());
  225. hook.enableTimeouts(this.enableTimeouts());
  226. hook.slow(this.slow());
  227. hook.ctx = this.ctx;
  228. hook.file = this.file;
  229. return hook;
  230. };
  231. /**
  232. * Run `fn(test[, done])` before running tests.
  233. *
  234. * @private
  235. * @param {string} title
  236. * @param {Function} fn
  237. * @return {Suite} for chaining
  238. */
  239. Suite.prototype.beforeAll = function(title, fn) {
  240. if (this.isPending()) {
  241. return this;
  242. }
  243. if (typeof title === 'function') {
  244. fn = title;
  245. title = fn.name;
  246. }
  247. title = '"before all" hook' + (title ? ': ' + title : '');
  248. var hook = this._createHook(title, fn);
  249. this._beforeAll.push(hook);
  250. this.emit(constants.EVENT_SUITE_ADD_HOOK_BEFORE_ALL, hook);
  251. return this;
  252. };
  253. /**
  254. * Run `fn(test[, done])` after running tests.
  255. *
  256. * @private
  257. * @param {string} title
  258. * @param {Function} fn
  259. * @return {Suite} for chaining
  260. */
  261. Suite.prototype.afterAll = function(title, fn) {
  262. if (this.isPending()) {
  263. return this;
  264. }
  265. if (typeof title === 'function') {
  266. fn = title;
  267. title = fn.name;
  268. }
  269. title = '"after all" hook' + (title ? ': ' + title : '');
  270. var hook = this._createHook(title, fn);
  271. this._afterAll.push(hook);
  272. this.emit(constants.EVENT_SUITE_ADD_HOOK_AFTER_ALL, hook);
  273. return this;
  274. };
  275. /**
  276. * Run `fn(test[, done])` before each test case.
  277. *
  278. * @private
  279. * @param {string} title
  280. * @param {Function} fn
  281. * @return {Suite} for chaining
  282. */
  283. Suite.prototype.beforeEach = function(title, fn) {
  284. if (this.isPending()) {
  285. return this;
  286. }
  287. if (typeof title === 'function') {
  288. fn = title;
  289. title = fn.name;
  290. }
  291. title = '"before each" hook' + (title ? ': ' + title : '');
  292. var hook = this._createHook(title, fn);
  293. this._beforeEach.push(hook);
  294. this.emit(constants.EVENT_SUITE_ADD_HOOK_BEFORE_EACH, hook);
  295. return this;
  296. };
  297. /**
  298. * Run `fn(test[, done])` after each test case.
  299. *
  300. * @private
  301. * @param {string} title
  302. * @param {Function} fn
  303. * @return {Suite} for chaining
  304. */
  305. Suite.prototype.afterEach = function(title, fn) {
  306. if (this.isPending()) {
  307. return this;
  308. }
  309. if (typeof title === 'function') {
  310. fn = title;
  311. title = fn.name;
  312. }
  313. title = '"after each" hook' + (title ? ': ' + title : '');
  314. var hook = this._createHook(title, fn);
  315. this._afterEach.push(hook);
  316. this.emit(constants.EVENT_SUITE_ADD_HOOK_AFTER_EACH, hook);
  317. return this;
  318. };
  319. /**
  320. * Add a test `suite`.
  321. *
  322. * @private
  323. * @param {Suite} suite
  324. * @return {Suite} for chaining
  325. */
  326. Suite.prototype.addSuite = function(suite) {
  327. suite.parent = this;
  328. suite.root = false;
  329. suite.timeout(this.timeout());
  330. suite.retries(this.retries());
  331. suite.enableTimeouts(this.enableTimeouts());
  332. suite.slow(this.slow());
  333. suite.bail(this.bail());
  334. this.suites.push(suite);
  335. this.emit(constants.EVENT_SUITE_ADD_SUITE, suite);
  336. return this;
  337. };
  338. /**
  339. * Add a `test` to this suite.
  340. *
  341. * @private
  342. * @param {Test} test
  343. * @return {Suite} for chaining
  344. */
  345. Suite.prototype.addTest = function(test) {
  346. test.parent = this;
  347. test.timeout(this.timeout());
  348. test.retries(this.retries());
  349. test.enableTimeouts(this.enableTimeouts());
  350. test.slow(this.slow());
  351. test.ctx = this.ctx;
  352. this.tests.push(test);
  353. this.emit(constants.EVENT_SUITE_ADD_TEST, test);
  354. return this;
  355. };
  356. /**
  357. * Return the full title generated by recursively concatenating the parent's
  358. * full title.
  359. *
  360. * @memberof Suite
  361. * @public
  362. * @return {string}
  363. */
  364. Suite.prototype.fullTitle = function() {
  365. return this.titlePath().join(' ');
  366. };
  367. /**
  368. * Return the title path generated by recursively concatenating the parent's
  369. * title path.
  370. *
  371. * @memberof Suite
  372. * @public
  373. * @return {string}
  374. */
  375. Suite.prototype.titlePath = function() {
  376. var result = [];
  377. if (this.parent) {
  378. result = result.concat(this.parent.titlePath());
  379. }
  380. if (!this.root) {
  381. result.push(this.title);
  382. }
  383. return result;
  384. };
  385. /**
  386. * Return the total number of tests.
  387. *
  388. * @memberof Suite
  389. * @public
  390. * @return {number}
  391. */
  392. Suite.prototype.total = function() {
  393. return (
  394. this.suites.reduce(function(sum, suite) {
  395. return sum + suite.total();
  396. }, 0) + this.tests.length
  397. );
  398. };
  399. /**
  400. * Iterates through each suite recursively to find all tests. Applies a
  401. * function in the format `fn(test)`.
  402. *
  403. * @private
  404. * @param {Function} fn
  405. * @return {Suite}
  406. */
  407. Suite.prototype.eachTest = function(fn) {
  408. this.tests.forEach(fn);
  409. this.suites.forEach(function(suite) {
  410. suite.eachTest(fn);
  411. });
  412. return this;
  413. };
  414. /**
  415. * This will run the root suite if we happen to be running in delayed mode.
  416. * @private
  417. */
  418. Suite.prototype.run = function run() {
  419. if (this.root) {
  420. this.emit(constants.EVENT_ROOT_SUITE_RUN);
  421. }
  422. };
  423. /**
  424. * Determines whether a suite has an `only` test or suite as a descendant.
  425. *
  426. * @private
  427. * @returns {Boolean}
  428. */
  429. Suite.prototype.hasOnly = function hasOnly() {
  430. return (
  431. this._onlyTests.length > 0 ||
  432. this._onlySuites.length > 0 ||
  433. this.suites.some(function(suite) {
  434. return suite.hasOnly();
  435. })
  436. );
  437. };
  438. /**
  439. * Filter suites based on `isOnly` logic.
  440. *
  441. * @private
  442. * @returns {Boolean}
  443. */
  444. Suite.prototype.filterOnly = function filterOnly() {
  445. if (this._onlyTests.length) {
  446. // If the suite contains `only` tests, run those and ignore any nested suites.
  447. this.tests = this._onlyTests;
  448. this.suites = [];
  449. } else {
  450. // Otherwise, do not run any of the tests in this suite.
  451. this.tests = [];
  452. this._onlySuites.forEach(function(onlySuite) {
  453. // If there are other `only` tests/suites nested in the current `only` suite, then filter that `only` suite.
  454. // Otherwise, all of the tests on this `only` suite should be run, so don't filter it.
  455. if (onlySuite.hasOnly()) {
  456. onlySuite.filterOnly();
  457. }
  458. });
  459. // Run the `only` suites, as well as any other suites that have `only` tests/suites as descendants.
  460. var onlySuites = this._onlySuites;
  461. this.suites = this.suites.filter(function(childSuite) {
  462. return onlySuites.indexOf(childSuite) !== -1 || childSuite.filterOnly();
  463. });
  464. }
  465. // Keep the suite only if there is something to run
  466. return this.tests.length > 0 || this.suites.length > 0;
  467. };
  468. /**
  469. * Adds a suite to the list of subsuites marked `only`.
  470. *
  471. * @private
  472. * @param {Suite} suite
  473. */
  474. Suite.prototype.appendOnlySuite = function(suite) {
  475. this._onlySuites.push(suite);
  476. };
  477. /**
  478. * Adds a test to the list of tests marked `only`.
  479. *
  480. * @private
  481. * @param {Test} test
  482. */
  483. Suite.prototype.appendOnlyTest = function(test) {
  484. this._onlyTests.push(test);
  485. };
  486. /**
  487. * Returns the array of hooks by hook name; see `HOOK_TYPE_*` constants.
  488. * @private
  489. */
  490. Suite.prototype.getHooks = function getHooks(name) {
  491. return this['_' + name];
  492. };
  493. /**
  494. * cleans all references from this suite and all child suites.
  495. */
  496. Suite.prototype.dispose = function() {
  497. this.suites.forEach(function(suite) {
  498. suite.dispose();
  499. });
  500. this.cleanReferences();
  501. };
  502. /**
  503. * Cleans up the references to all the deferred functions
  504. * (before/after/beforeEach/afterEach) and tests of a Suite.
  505. * These must be deleted otherwise a memory leak can happen,
  506. * as those functions may reference variables from closures,
  507. * thus those variables can never be garbage collected as long
  508. * as the deferred functions exist.
  509. *
  510. * @private
  511. */
  512. Suite.prototype.cleanReferences = function cleanReferences() {
  513. function cleanArrReferences(arr) {
  514. for (var i = 0; i < arr.length; i++) {
  515. delete arr[i].fn;
  516. }
  517. }
  518. if (Array.isArray(this._beforeAll)) {
  519. cleanArrReferences(this._beforeAll);
  520. }
  521. if (Array.isArray(this._beforeEach)) {
  522. cleanArrReferences(this._beforeEach);
  523. }
  524. if (Array.isArray(this._afterAll)) {
  525. cleanArrReferences(this._afterAll);
  526. }
  527. if (Array.isArray(this._afterEach)) {
  528. cleanArrReferences(this._afterEach);
  529. }
  530. for (var i = 0; i < this.tests.length; i++) {
  531. delete this.tests[i].fn;
  532. }
  533. };
  534. var constants = utils.defineConstants(
  535. /**
  536. * {@link Suite}-related constants.
  537. * @public
  538. * @memberof Suite
  539. * @alias constants
  540. * @readonly
  541. * @static
  542. * @enum {string}
  543. */
  544. {
  545. /**
  546. * Event emitted after a test file has been loaded Not emitted in browser.
  547. */
  548. EVENT_FILE_POST_REQUIRE: 'post-require',
  549. /**
  550. * Event emitted before a test file has been loaded. In browser, this is emitted once an interface has been selected.
  551. */
  552. EVENT_FILE_PRE_REQUIRE: 'pre-require',
  553. /**
  554. * Event emitted immediately after a test file has been loaded. Not emitted in browser.
  555. */
  556. EVENT_FILE_REQUIRE: 'require',
  557. /**
  558. * Event emitted when `global.run()` is called (use with `delay` option)
  559. */
  560. EVENT_ROOT_SUITE_RUN: 'run',
  561. /**
  562. * Namespace for collection of a `Suite`'s "after all" hooks
  563. */
  564. HOOK_TYPE_AFTER_ALL: 'afterAll',
  565. /**
  566. * Namespace for collection of a `Suite`'s "after each" hooks
  567. */
  568. HOOK_TYPE_AFTER_EACH: 'afterEach',
  569. /**
  570. * Namespace for collection of a `Suite`'s "before all" hooks
  571. */
  572. HOOK_TYPE_BEFORE_ALL: 'beforeAll',
  573. /**
  574. * Namespace for collection of a `Suite`'s "before all" hooks
  575. */
  576. HOOK_TYPE_BEFORE_EACH: 'beforeEach',
  577. // the following events are all deprecated
  578. /**
  579. * Emitted after an "after all" `Hook` has been added to a `Suite`. Deprecated
  580. */
  581. EVENT_SUITE_ADD_HOOK_AFTER_ALL: 'afterAll',
  582. /**
  583. * Emitted after an "after each" `Hook` has been added to a `Suite` Deprecated
  584. */
  585. EVENT_SUITE_ADD_HOOK_AFTER_EACH: 'afterEach',
  586. /**
  587. * Emitted after an "before all" `Hook` has been added to a `Suite` Deprecated
  588. */
  589. EVENT_SUITE_ADD_HOOK_BEFORE_ALL: 'beforeAll',
  590. /**
  591. * Emitted after an "before each" `Hook` has been added to a `Suite` Deprecated
  592. */
  593. EVENT_SUITE_ADD_HOOK_BEFORE_EACH: 'beforeEach',
  594. /**
  595. * Emitted after a child `Suite` has been added to a `Suite`. Deprecated
  596. */
  597. EVENT_SUITE_ADD_SUITE: 'suite',
  598. /**
  599. * Emitted after a `Test` has been added to a `Suite`. Deprecated
  600. */
  601. EVENT_SUITE_ADD_TEST: 'test'
  602. }
  603. );
  604. /**
  605. * @summary There are no known use cases for these events.
  606. * @desc This is a `Set`-like object having all keys being the constant's string value and the value being `true`.
  607. * @todo Remove eventually
  608. * @type {Object<string,boolean>}
  609. * @ignore
  610. */
  611. var deprecatedEvents = Object.keys(constants)
  612. .filter(function(constant) {
  613. return constant.substring(0, 15) === 'EVENT_SUITE_ADD';
  614. })
  615. .reduce(function(acc, constant) {
  616. acc[constants[constant]] = true;
  617. return acc;
  618. }, utils.createMap());
  619. Suite.constants = constants;