fake_timers.js 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467
  1. 'use strict';
  2. Object.defineProperty(exports, "__esModule", {
  3. value: true
  4. });
  5. var _jestMessageUtil;
  6. function _load_jestMessageUtil() {
  7. return _jestMessageUtil = require('jest-message-util');
  8. }
  9. var _set_global;
  10. function _load_set_global() {
  11. return _set_global = _interopRequireDefault(require('./set_global'));
  12. }
  13. function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
  14. /**
  15. * We don't know the type of arguments for a callback ahead of time which is why
  16. * we are disabling the flowtype/no-weak-types rule here.
  17. */
  18. /* eslint-disable flowtype/no-weak-types */
  19. /* eslint-enable flowtype/no-weak-types */
  20. const MS_IN_A_YEAR = 31536000000; /**
  21. * Copyright (c) 2014-present, Facebook, Inc. All rights reserved.
  22. *
  23. * This source code is licensed under the MIT license found in the
  24. * LICENSE file in the root directory of this source tree.
  25. *
  26. *
  27. */
  28. class FakeTimers {
  29. constructor(_ref) {
  30. let global = _ref.global,
  31. moduleMocker = _ref.moduleMocker,
  32. timerConfig = _ref.timerConfig,
  33. config = _ref.config,
  34. maxLoops = _ref.maxLoops;
  35. this._global = global;
  36. this._timerConfig = timerConfig;
  37. this._config = config;
  38. this._maxLoops = maxLoops || 100000;
  39. this._uuidCounter = 1;
  40. this._moduleMocker = moduleMocker;
  41. // Store original timer APIs for future reference
  42. this._timerAPIs = {
  43. clearImmediate: global.clearImmediate,
  44. clearInterval: global.clearInterval,
  45. clearTimeout: global.clearTimeout,
  46. nextTick: global.process && global.process.nextTick,
  47. setImmediate: global.setImmediate,
  48. setInterval: global.setInterval,
  49. setTimeout: global.setTimeout
  50. };
  51. this.reset();
  52. this._createMocks();
  53. // These globally-accessible function are now deprecated!
  54. // They will go away very soon, so do not use them!
  55. // Instead, use the versions available on the `jest` object
  56. global.mockRunTicksRepeatedly = this.runAllTicks.bind(this);
  57. global.mockRunTimersOnce = this.runOnlyPendingTimers.bind(this);
  58. global.mockAdvanceTimersByTime = this.advanceTimersByTime.bind(this);
  59. global.mockRunTimersRepeatedly = this.runAllTimers.bind(this);
  60. global.mockClearTimers = this.clearAllTimers.bind(this);
  61. global.mockGetTimersCount = () => Object.keys(this._timers).length;
  62. }
  63. clearAllTimers() {
  64. this._immediates.forEach(immediate => this._fakeClearImmediate(immediate.uuid));
  65. for (const uuid in this._timers) {
  66. delete this._timers[uuid];
  67. }
  68. }
  69. dispose() {
  70. this._disposed = true;
  71. this.clearAllTimers();
  72. }
  73. reset() {
  74. this._cancelledTicks = {};
  75. this._cancelledImmediates = {};
  76. this._now = 0;
  77. this._ticks = [];
  78. this._immediates = [];
  79. this._timers = {};
  80. }
  81. runAllTicks() {
  82. this._checkFakeTimers();
  83. // Only run a generous number of ticks and then bail.
  84. // This is just to help avoid recursive loops
  85. let i;
  86. for (i = 0; i < this._maxLoops; i++) {
  87. const tick = this._ticks.shift();
  88. if (tick === undefined) {
  89. break;
  90. }
  91. if (!this._cancelledTicks.hasOwnProperty(tick.uuid)) {
  92. // Callback may throw, so update the map prior calling.
  93. this._cancelledTicks[tick.uuid] = true;
  94. tick.callback();
  95. }
  96. }
  97. if (i === this._maxLoops) {
  98. throw new Error('Ran ' + this._maxLoops + ' ticks, and there are still more! ' + "Assuming we've hit an infinite recursion and bailing out...");
  99. }
  100. }
  101. runAllImmediates() {
  102. this._checkFakeTimers();
  103. // Only run a generous number of immediates and then bail.
  104. let i;
  105. for (i = 0; i < this._maxLoops; i++) {
  106. const immediate = this._immediates.shift();
  107. if (immediate === undefined) {
  108. break;
  109. }
  110. this._runImmediate(immediate);
  111. }
  112. if (i === this._maxLoops) {
  113. throw new Error('Ran ' + this._maxLoops + ' immediates, and there are still more! Assuming ' + "we've hit an infinite recursion and bailing out...");
  114. }
  115. }
  116. _runImmediate(immediate) {
  117. if (!this._cancelledImmediates.hasOwnProperty(immediate.uuid)) {
  118. // Callback may throw, so update the map prior calling.
  119. this._cancelledImmediates[immediate.uuid] = true;
  120. immediate.callback();
  121. }
  122. }
  123. runAllTimers() {
  124. this._checkFakeTimers();
  125. this.runAllTicks();
  126. this.runAllImmediates();
  127. // Only run a generous number of timers and then bail.
  128. // This is just to help avoid recursive loops
  129. let i;
  130. for (i = 0; i < this._maxLoops; i++) {
  131. const nextTimerHandle = this._getNextTimerHandle();
  132. // If there are no more timer handles, stop!
  133. if (nextTimerHandle === null) {
  134. break;
  135. }
  136. this._runTimerHandle(nextTimerHandle);
  137. // Some of the immediate calls could be enqueued
  138. // during the previous handling of the timers, we should
  139. // run them as well.
  140. if (this._immediates.length) {
  141. this.runAllImmediates();
  142. }
  143. if (this._ticks.length) {
  144. this.runAllTicks();
  145. }
  146. }
  147. if (i === this._maxLoops) {
  148. throw new Error('Ran ' + this._maxLoops + ' timers, and there are still more! ' + "Assuming we've hit an infinite recursion and bailing out...");
  149. }
  150. }
  151. runOnlyPendingTimers() {
  152. const timers = Object.assign({}, this._timers);
  153. this._checkFakeTimers();
  154. this._immediates.forEach(this._runImmediate, this);
  155. Object.keys(timers).sort((left, right) => timers[left].expiry - timers[right].expiry).forEach(this._runTimerHandle, this);
  156. }
  157. advanceTimersByTime(msToRun) {
  158. this._checkFakeTimers();
  159. // Only run a generous number of timers and then bail.
  160. // This is just to help avoid recursive loops
  161. let i;
  162. for (i = 0; i < this._maxLoops; i++) {
  163. const timerHandle = this._getNextTimerHandle();
  164. // If there are no more timer handles, stop!
  165. if (timerHandle === null) {
  166. break;
  167. }
  168. const nextTimerExpiry = this._timers[timerHandle].expiry;
  169. if (this._now + msToRun < nextTimerExpiry) {
  170. // There are no timers between now and the target we're running to, so
  171. // adjust our time cursor and quit
  172. this._now += msToRun;
  173. break;
  174. } else {
  175. msToRun -= nextTimerExpiry - this._now;
  176. this._now = nextTimerExpiry;
  177. this._runTimerHandle(timerHandle);
  178. }
  179. }
  180. if (i === this._maxLoops) {
  181. throw new Error('Ran ' + this._maxLoops + ' timers, and there are still more! ' + "Assuming we've hit an infinite recursion and bailing out...");
  182. }
  183. }
  184. runWithRealTimers(cb) {
  185. const prevClearImmediate = this._global.clearImmediate;
  186. const prevClearInterval = this._global.clearInterval;
  187. const prevClearTimeout = this._global.clearTimeout;
  188. const prevNextTick = this._global.process.nextTick;
  189. const prevSetImmediate = this._global.setImmediate;
  190. const prevSetInterval = this._global.setInterval;
  191. const prevSetTimeout = this._global.setTimeout;
  192. this.useRealTimers();
  193. let cbErr = null;
  194. let errThrown = false;
  195. try {
  196. cb();
  197. } catch (e) {
  198. errThrown = true;
  199. cbErr = e;
  200. }
  201. this._global.clearImmediate = prevClearImmediate;
  202. this._global.clearInterval = prevClearInterval;
  203. this._global.clearTimeout = prevClearTimeout;
  204. this._global.process.nextTick = prevNextTick;
  205. this._global.setImmediate = prevSetImmediate;
  206. this._global.setInterval = prevSetInterval;
  207. this._global.setTimeout = prevSetTimeout;
  208. if (errThrown) {
  209. throw cbErr;
  210. }
  211. }
  212. useRealTimers() {
  213. const global = this._global;
  214. (0, (_set_global || _load_set_global()).default)(global, 'clearImmediate', this._timerAPIs.clearImmediate);
  215. (0, (_set_global || _load_set_global()).default)(global, 'clearInterval', this._timerAPIs.clearInterval);
  216. (0, (_set_global || _load_set_global()).default)(global, 'clearTimeout', this._timerAPIs.clearTimeout);
  217. (0, (_set_global || _load_set_global()).default)(global, 'setImmediate', this._timerAPIs.setImmediate);
  218. (0, (_set_global || _load_set_global()).default)(global, 'setInterval', this._timerAPIs.setInterval);
  219. (0, (_set_global || _load_set_global()).default)(global, 'setTimeout', this._timerAPIs.setTimeout);
  220. global.process.nextTick = this._timerAPIs.nextTick;
  221. }
  222. useFakeTimers() {
  223. this._createMocks();
  224. const global = this._global;
  225. (0, (_set_global || _load_set_global()).default)(global, 'clearImmediate', this._fakeTimerAPIs.clearImmediate);
  226. (0, (_set_global || _load_set_global()).default)(global, 'clearInterval', this._fakeTimerAPIs.clearInterval);
  227. (0, (_set_global || _load_set_global()).default)(global, 'clearTimeout', this._fakeTimerAPIs.clearTimeout);
  228. (0, (_set_global || _load_set_global()).default)(global, 'setImmediate', this._fakeTimerAPIs.setImmediate);
  229. (0, (_set_global || _load_set_global()).default)(global, 'setInterval', this._fakeTimerAPIs.setInterval);
  230. (0, (_set_global || _load_set_global()).default)(global, 'setTimeout', this._fakeTimerAPIs.setTimeout);
  231. global.process.nextTick = this._fakeTimerAPIs.nextTick;
  232. }
  233. _checkFakeTimers() {
  234. if (this._global.setTimeout !== this._fakeTimerAPIs.setTimeout) {
  235. this._global.console.warn(`A function to advance timers was called but the timers API is not ` + `mocked with fake timers. Call \`jest.useFakeTimers()\` in this ` + `test or enable fake timers globally by setting ` + `\`"timers": "fake"\` in ` + `the configuration file. This warning is likely a result of a ` + `default configuration change in Jest 15.\n\n` + `Release Blog Post: https://facebook.github.io/jest/blog/2016/09/01/jest-15.html\n` + `Stack Trace:\n` + (0, (_jestMessageUtil || _load_jestMessageUtil()).formatStackTrace)(new Error().stack, this._config, {
  236. noStackTrace: false
  237. }));
  238. }
  239. }
  240. _createMocks() {
  241. const fn = impl => this._moduleMocker.fn().mockImplementation(impl);
  242. this._fakeTimerAPIs = {
  243. clearImmediate: fn(this._fakeClearImmediate.bind(this)),
  244. clearInterval: fn(this._fakeClearTimer.bind(this)),
  245. clearTimeout: fn(this._fakeClearTimer.bind(this)),
  246. nextTick: fn(this._fakeNextTick.bind(this)),
  247. setImmediate: fn(this._fakeSetImmediate.bind(this)),
  248. setInterval: fn(this._fakeSetInterval.bind(this)),
  249. setTimeout: fn(this._fakeSetTimeout.bind(this))
  250. };
  251. }
  252. _fakeClearTimer(timerRef) {
  253. const uuid = this._timerConfig.refToId(timerRef);
  254. if (uuid && this._timers.hasOwnProperty(uuid)) {
  255. delete this._timers[String(uuid)];
  256. }
  257. }
  258. _fakeClearImmediate(uuid) {
  259. this._cancelledImmediates[uuid] = true;
  260. }
  261. _fakeNextTick(callback) {
  262. if (this._disposed) {
  263. return;
  264. }
  265. const args = [];
  266. for (let ii = 1, ll = arguments.length; ii < ll; ii++) {
  267. args.push(arguments[ii]);
  268. }
  269. const uuid = String(this._uuidCounter++);
  270. this._ticks.push({
  271. callback: () => callback.apply(null, args),
  272. uuid
  273. });
  274. const cancelledTicks = this._cancelledTicks;
  275. this._timerAPIs.nextTick(() => {
  276. if (this._blocked) {
  277. return;
  278. }
  279. if (!cancelledTicks.hasOwnProperty(uuid)) {
  280. // Callback may throw, so update the map prior calling.
  281. cancelledTicks[uuid] = true;
  282. callback.apply(null, args);
  283. }
  284. });
  285. }
  286. _fakeSetImmediate(callback) {
  287. if (this._disposed) {
  288. return null;
  289. }
  290. const args = [];
  291. for (let ii = 1, ll = arguments.length; ii < ll; ii++) {
  292. args.push(arguments[ii]);
  293. }
  294. const uuid = this._uuidCounter++;
  295. this._immediates.push({
  296. callback: () => callback.apply(null, args),
  297. uuid: String(uuid)
  298. });
  299. const cancelledImmediates = this._cancelledImmediates;
  300. this._timerAPIs.setImmediate(() => {
  301. if (!cancelledImmediates.hasOwnProperty(uuid)) {
  302. // Callback may throw, so update the map prior calling.
  303. cancelledImmediates[String(uuid)] = true;
  304. callback.apply(null, args);
  305. }
  306. });
  307. return uuid;
  308. }
  309. _fakeSetInterval(callback, intervalDelay) {
  310. if (this._disposed) {
  311. return null;
  312. }
  313. if (intervalDelay == null) {
  314. intervalDelay = 0;
  315. }
  316. const args = [];
  317. for (let ii = 2, ll = arguments.length; ii < ll; ii++) {
  318. args.push(arguments[ii]);
  319. }
  320. const uuid = this._uuidCounter++;
  321. this._timers[String(uuid)] = {
  322. callback: () => callback.apply(null, args),
  323. expiry: this._now + intervalDelay,
  324. interval: intervalDelay,
  325. type: 'interval'
  326. };
  327. return this._timerConfig.idToRef(uuid);
  328. }
  329. _fakeSetTimeout(callback, delay) {
  330. if (this._disposed) {
  331. return null;
  332. }
  333. if (delay == null) {
  334. delay = 0;
  335. }
  336. const args = [];
  337. for (let ii = 2, ll = arguments.length; ii < ll; ii++) {
  338. args.push(arguments[ii]);
  339. }
  340. const uuid = this._uuidCounter++;
  341. this._timers[String(uuid)] = {
  342. callback: () => callback.apply(null, args),
  343. expiry: this._now + delay,
  344. interval: null,
  345. type: 'timeout'
  346. };
  347. return this._timerConfig.idToRef(uuid);
  348. }
  349. _getNextTimerHandle() {
  350. let nextTimerHandle = null;
  351. let uuid;
  352. let soonestTime = MS_IN_A_YEAR;
  353. let timer;
  354. for (uuid in this._timers) {
  355. timer = this._timers[uuid];
  356. if (timer.expiry < soonestTime) {
  357. soonestTime = timer.expiry;
  358. nextTimerHandle = uuid;
  359. }
  360. }
  361. return nextTimerHandle;
  362. }
  363. _runTimerHandle(timerHandle) {
  364. const timer = this._timers[timerHandle];
  365. if (!timer) {
  366. return;
  367. }
  368. switch (timer.type) {
  369. case 'timeout':
  370. const callback = timer.callback;
  371. delete this._timers[timerHandle];
  372. callback();
  373. break;
  374. case 'interval':
  375. timer.expiry = this._now + timer.interval;
  376. timer.callback();
  377. break;
  378. default:
  379. throw new Error('Unexpected timer type: ' + timer.type);
  380. }
  381. }
  382. }
  383. exports.default = FakeTimers;