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.

runnable.js 11KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483
  1. 'use strict';
  2. var EventEmitter = require('events').EventEmitter;
  3. var Pending = require('./pending');
  4. var debug = require('debug')('mocha:runnable');
  5. var milliseconds = require('ms');
  6. var utils = require('./utils');
  7. var errors = require('./errors');
  8. var createInvalidExceptionError = errors.createInvalidExceptionError;
  9. var createMultipleDoneError = errors.createMultipleDoneError;
  10. /**
  11. * Save timer references to avoid Sinon interfering (see GH-237).
  12. */
  13. var Date = global.Date;
  14. var setTimeout = global.setTimeout;
  15. var clearTimeout = global.clearTimeout;
  16. var toString = Object.prototype.toString;
  17. module.exports = Runnable;
  18. /**
  19. * Initialize a new `Runnable` with the given `title` and callback `fn`.
  20. *
  21. * @class
  22. * @extends external:EventEmitter
  23. * @public
  24. * @param {String} title
  25. * @param {Function} fn
  26. */
  27. function Runnable(title, fn) {
  28. this.title = title;
  29. this.fn = fn;
  30. this.body = (fn || '').toString();
  31. this.async = fn && fn.length;
  32. this.sync = !this.async;
  33. this._timeout = 2000;
  34. this._slow = 75;
  35. this._enableTimeouts = true;
  36. this._retries = -1;
  37. this.reset();
  38. }
  39. /**
  40. * Inherit from `EventEmitter.prototype`.
  41. */
  42. utils.inherits(Runnable, EventEmitter);
  43. /**
  44. * Resets the state initially or for a next run.
  45. */
  46. Runnable.prototype.reset = function() {
  47. this.timedOut = false;
  48. this._currentRetry = 0;
  49. this.pending = false;
  50. delete this.state;
  51. delete this.err;
  52. };
  53. /**
  54. * Get current timeout value in msecs.
  55. *
  56. * @private
  57. * @returns {number} current timeout threshold value
  58. */
  59. /**
  60. * @summary
  61. * Set timeout threshold value (msecs).
  62. *
  63. * @description
  64. * A string argument can use shorthand (e.g., "2s") and will be converted.
  65. * The value will be clamped to range [<code>0</code>, <code>2^<sup>31</sup>-1</code>].
  66. * If clamped value matches either range endpoint, timeouts will be disabled.
  67. *
  68. * @private
  69. * @see {@link https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/setTimeout#Maximum_delay_value}
  70. * @param {number|string} ms - Timeout threshold value.
  71. * @returns {Runnable} this
  72. * @chainable
  73. */
  74. Runnable.prototype.timeout = function(ms) {
  75. if (!arguments.length) {
  76. return this._timeout;
  77. }
  78. if (typeof ms === 'string') {
  79. ms = milliseconds(ms);
  80. }
  81. // Clamp to range
  82. var INT_MAX = Math.pow(2, 31) - 1;
  83. var range = [0, INT_MAX];
  84. ms = utils.clamp(ms, range);
  85. // see #1652 for reasoning
  86. if (ms === range[0] || ms === range[1]) {
  87. this._enableTimeouts = false;
  88. }
  89. debug('timeout %d', ms);
  90. this._timeout = ms;
  91. if (this.timer) {
  92. this.resetTimeout();
  93. }
  94. return this;
  95. };
  96. /**
  97. * Set or get slow `ms`.
  98. *
  99. * @private
  100. * @param {number|string} ms
  101. * @return {Runnable|number} ms or Runnable instance.
  102. */
  103. Runnable.prototype.slow = function(ms) {
  104. if (!arguments.length || typeof ms === 'undefined') {
  105. return this._slow;
  106. }
  107. if (typeof ms === 'string') {
  108. ms = milliseconds(ms);
  109. }
  110. debug('slow %d', ms);
  111. this._slow = ms;
  112. return this;
  113. };
  114. /**
  115. * Set and get whether timeout is `enabled`.
  116. *
  117. * @private
  118. * @param {boolean} enabled
  119. * @return {Runnable|boolean} enabled or Runnable instance.
  120. */
  121. Runnable.prototype.enableTimeouts = function(enabled) {
  122. if (!arguments.length) {
  123. return this._enableTimeouts;
  124. }
  125. debug('enableTimeouts %s', enabled);
  126. this._enableTimeouts = enabled;
  127. return this;
  128. };
  129. /**
  130. * Halt and mark as pending.
  131. *
  132. * @memberof Mocha.Runnable
  133. * @public
  134. */
  135. Runnable.prototype.skip = function() {
  136. this.pending = true;
  137. throw new Pending('sync skip; aborting execution');
  138. };
  139. /**
  140. * Check if this runnable or its parent suite is marked as pending.
  141. *
  142. * @private
  143. */
  144. Runnable.prototype.isPending = function() {
  145. return this.pending || (this.parent && this.parent.isPending());
  146. };
  147. /**
  148. * Return `true` if this Runnable has failed.
  149. * @return {boolean}
  150. * @private
  151. */
  152. Runnable.prototype.isFailed = function() {
  153. return !this.isPending() && this.state === constants.STATE_FAILED;
  154. };
  155. /**
  156. * Return `true` if this Runnable has passed.
  157. * @return {boolean}
  158. * @private
  159. */
  160. Runnable.prototype.isPassed = function() {
  161. return !this.isPending() && this.state === constants.STATE_PASSED;
  162. };
  163. /**
  164. * Set or get number of retries.
  165. *
  166. * @private
  167. */
  168. Runnable.prototype.retries = function(n) {
  169. if (!arguments.length) {
  170. return this._retries;
  171. }
  172. this._retries = n;
  173. };
  174. /**
  175. * Set or get current retry
  176. *
  177. * @private
  178. */
  179. Runnable.prototype.currentRetry = function(n) {
  180. if (!arguments.length) {
  181. return this._currentRetry;
  182. }
  183. this._currentRetry = n;
  184. };
  185. /**
  186. * Return the full title generated by recursively concatenating the parent's
  187. * full title.
  188. *
  189. * @memberof Mocha.Runnable
  190. * @public
  191. * @return {string}
  192. */
  193. Runnable.prototype.fullTitle = function() {
  194. return this.titlePath().join(' ');
  195. };
  196. /**
  197. * Return the title path generated by concatenating the parent's title path with the title.
  198. *
  199. * @memberof Mocha.Runnable
  200. * @public
  201. * @return {string}
  202. */
  203. Runnable.prototype.titlePath = function() {
  204. return this.parent.titlePath().concat([this.title]);
  205. };
  206. /**
  207. * Clear the timeout.
  208. *
  209. * @private
  210. */
  211. Runnable.prototype.clearTimeout = function() {
  212. clearTimeout(this.timer);
  213. };
  214. /**
  215. * Reset the timeout.
  216. *
  217. * @private
  218. */
  219. Runnable.prototype.resetTimeout = function() {
  220. var self = this;
  221. var ms = this.timeout() || 1e9;
  222. if (!this._enableTimeouts) {
  223. return;
  224. }
  225. this.clearTimeout();
  226. this.timer = setTimeout(function() {
  227. if (!self._enableTimeouts) {
  228. return;
  229. }
  230. self.callback(self._timeoutError(ms));
  231. self.timedOut = true;
  232. }, ms);
  233. };
  234. /**
  235. * Set or get a list of whitelisted globals for this test run.
  236. *
  237. * @private
  238. * @param {string[]} globals
  239. */
  240. Runnable.prototype.globals = function(globals) {
  241. if (!arguments.length) {
  242. return this._allowedGlobals;
  243. }
  244. this._allowedGlobals = globals;
  245. };
  246. /**
  247. * Run the test and invoke `fn(err)`.
  248. *
  249. * @param {Function} fn
  250. * @private
  251. */
  252. Runnable.prototype.run = function(fn) {
  253. var self = this;
  254. var start = new Date();
  255. var ctx = this.ctx;
  256. var finished;
  257. var errorWasHandled = false;
  258. // Sometimes the ctx exists, but it is not runnable
  259. if (ctx && ctx.runnable) {
  260. ctx.runnable(this);
  261. }
  262. // called multiple times
  263. function multiple(err) {
  264. if (errorWasHandled) {
  265. return;
  266. }
  267. errorWasHandled = true;
  268. self.emit('error', createMultipleDoneError(self, err));
  269. }
  270. // finished
  271. function done(err) {
  272. var ms = self.timeout();
  273. if (self.timedOut) {
  274. return;
  275. }
  276. if (finished) {
  277. return multiple(err);
  278. }
  279. self.clearTimeout();
  280. self.duration = new Date() - start;
  281. finished = true;
  282. if (!err && self.duration > ms && self._enableTimeouts) {
  283. err = self._timeoutError(ms);
  284. }
  285. fn(err);
  286. }
  287. // for .resetTimeout() and Runner#uncaught()
  288. this.callback = done;
  289. if (this.fn && typeof this.fn.call !== 'function') {
  290. done(
  291. new TypeError(
  292. 'A runnable must be passed a function as its second argument.'
  293. )
  294. );
  295. return;
  296. }
  297. // explicit async with `done` argument
  298. if (this.async) {
  299. this.resetTimeout();
  300. // allows skip() to be used in an explicit async context
  301. this.skip = function asyncSkip() {
  302. this.pending = true;
  303. done();
  304. // halt execution, the uncaught handler will ignore the failure.
  305. throw new Pending('async skip; aborting execution');
  306. };
  307. try {
  308. callFnAsync(this.fn);
  309. } catch (err) {
  310. // handles async runnables which actually run synchronously
  311. errorWasHandled = true;
  312. if (err instanceof Pending) {
  313. return; // done() is already called in this.skip()
  314. } else if (this.allowUncaught) {
  315. throw err;
  316. }
  317. done(Runnable.toValueOrError(err));
  318. }
  319. return;
  320. }
  321. // sync or promise-returning
  322. try {
  323. if (this.isPending()) {
  324. done();
  325. } else {
  326. callFn(this.fn);
  327. }
  328. } catch (err) {
  329. errorWasHandled = true;
  330. if (err instanceof Pending) {
  331. return done();
  332. } else if (this.allowUncaught) {
  333. throw err;
  334. }
  335. done(Runnable.toValueOrError(err));
  336. }
  337. function callFn(fn) {
  338. var result = fn.call(ctx);
  339. if (result && typeof result.then === 'function') {
  340. self.resetTimeout();
  341. result.then(
  342. function() {
  343. done();
  344. // Return null so libraries like bluebird do not warn about
  345. // subsequently constructed Promises.
  346. return null;
  347. },
  348. function(reason) {
  349. done(reason || new Error('Promise rejected with no or falsy reason'));
  350. }
  351. );
  352. } else {
  353. if (self.asyncOnly) {
  354. return done(
  355. new Error(
  356. '--async-only option in use without declaring `done()` or returning a promise'
  357. )
  358. );
  359. }
  360. done();
  361. }
  362. }
  363. function callFnAsync(fn) {
  364. var result = fn.call(ctx, function(err) {
  365. if (err instanceof Error || toString.call(err) === '[object Error]') {
  366. return done(err);
  367. }
  368. if (err) {
  369. if (Object.prototype.toString.call(err) === '[object Object]') {
  370. return done(
  371. new Error('done() invoked with non-Error: ' + JSON.stringify(err))
  372. );
  373. }
  374. return done(new Error('done() invoked with non-Error: ' + err));
  375. }
  376. if (result && utils.isPromise(result)) {
  377. return done(
  378. new Error(
  379. 'Resolution method is overspecified. Specify a callback *or* return a Promise; not both.'
  380. )
  381. );
  382. }
  383. done();
  384. });
  385. }
  386. };
  387. /**
  388. * Instantiates a "timeout" error
  389. *
  390. * @param {number} ms - Timeout (in milliseconds)
  391. * @returns {Error} a "timeout" error
  392. * @private
  393. */
  394. Runnable.prototype._timeoutError = function(ms) {
  395. var msg =
  396. 'Timeout of ' +
  397. ms +
  398. 'ms exceeded. For async tests and hooks, ensure "done()" is called; if returning a Promise, ensure it resolves.';
  399. if (this.file) {
  400. msg += ' (' + this.file + ')';
  401. }
  402. return new Error(msg);
  403. };
  404. var constants = utils.defineConstants(
  405. /**
  406. * {@link Runnable}-related constants.
  407. * @public
  408. * @memberof Runnable
  409. * @readonly
  410. * @static
  411. * @alias constants
  412. * @enum {string}
  413. */
  414. {
  415. /**
  416. * Value of `state` prop when a `Runnable` has failed
  417. */
  418. STATE_FAILED: 'failed',
  419. /**
  420. * Value of `state` prop when a `Runnable` has passed
  421. */
  422. STATE_PASSED: 'passed'
  423. }
  424. );
  425. /**
  426. * Given `value`, return identity if truthy, otherwise create an "invalid exception" error and return that.
  427. * @param {*} [value] - Value to return, if present
  428. * @returns {*|Error} `value`, otherwise an `Error`
  429. * @private
  430. */
  431. Runnable.toValueOrError = function(value) {
  432. return (
  433. value ||
  434. createInvalidExceptionError(
  435. 'Runnable failed with falsy or undefined exception. Please throw an Error instead.',
  436. value
  437. )
  438. );
  439. };
  440. Runnable.constants = constants;