@@ -30,6 +30,7 @@ var NOOP = function () { return undefined; };
3030var timeoutResult = setTimeout ( NOOP , 0 ) ;
3131var addTimerReturnsObject = typeof timeoutResult === "object" ;
3232var hrtimePresent = ( global . process && typeof global . process . hrtime === "function" ) ;
33+ var nextTickPresent = ( global . process && typeof global . process . nextTick === "function" ) ;
3334var performancePresent = ( global . performance && typeof global . performance . now === "function" ) ;
3435
3536clearTimeout ( timeoutResult ) ;
@@ -165,6 +166,27 @@ function createDate() {
165166 return mirrorDateProperties ( ClockDate , NativeDate ) ;
166167}
167168
169+
170+ function enqueueJob ( clock , job ) {
171+ // enqueues a microtick-deferred task - ecma262/#sec-enqueuejob
172+ if ( ! clock . jobs ) {
173+ clock . jobs = [ ] ;
174+ }
175+ clock . jobs . push ( job ) ;
176+ }
177+
178+ function runJobs ( clock ) {
179+ // runs all microtick-deferred tasks - ecma262/#sec-runjobs
180+ if ( ! clock . jobs ) {
181+ return ;
182+ }
183+ for ( var i = 0 ; i < clock . jobs . length ; i ++ ) {
184+ var job = clock . jobs [ i ] ;
185+ job . func . apply ( null , job . func . args ) ;
186+ }
187+ clock . jobs = [ ] ;
188+ }
189+
168190function addTimer ( clock , timer ) {
169191 if ( timer . func === undefined ) {
170192 throw new Error ( "Callback must be provided to timer calls" ) ;
@@ -288,26 +310,11 @@ function callTimer(clock, timer) {
288310 delete clock . timers [ timer . id ] ;
289311 }
290312
291- try {
292- if ( typeof timer . func === "function" ) {
293- timer . func . apply ( null , timer . args ) ;
294- } else {
295- /* eslint no-eval: "off" */
296- eval ( timer . func ) ;
297- }
298- } catch ( e ) {
299- exception = e ;
300- }
301-
302- if ( ! clock . timers [ timer . id ] ) {
303- if ( exception ) {
304- throw exception ;
305- }
306- return ;
307- }
308-
309- if ( exception ) {
310- throw exception ;
313+ if ( typeof timer . func === "function" ) {
314+ timer . func . apply ( null , timer . args ) ;
315+ } else {
316+ /* eslint no-eval: "off" */
317+ eval ( timer . func ) ;
311318 }
312319}
313320
@@ -350,19 +357,25 @@ function clearTimer(clock, timerId, ttype) {
350357 }
351358}
352359
353- function uninstall ( clock , target ) {
360+ function uninstall ( clock , target , config ) {
354361 var method ,
355362 i ,
356363 l ;
357364 var installedHrTime = "_hrtime" ;
365+ var installedNextTick = "_nextTick" ;
358366
359367 for ( i = 0 , l = clock . methods . length ; i < l ; i ++ ) {
360368 method = clock . methods [ i ] ;
361369 if ( method === "hrtime" && target . process ) {
362370 target . process . hrtime = clock [ installedHrTime ] ;
371+ } else if ( method === "nextTick" && target . process ) {
372+ target . process . nextTick = clock [ installedNextTick ] ;
363373 } else {
364374 if ( target [ method ] && target [ method ] . hadOwnProperty ) {
365375 target [ method ] = clock [ "_" + method ] ;
376+ if ( method === "clearInterval" && config . shouldAdvanceTime === true ) {
377+ target [ method ] ( clock . attachedInterval ) ;
378+ }
366379 } else {
367380 try {
368381 delete target [ method ] ;
@@ -377,7 +390,6 @@ function uninstall(clock, target) {
377390
378391function hijackMethod ( target , method , clock ) {
379392 var prop ;
380-
381393 clock [ method ] . hadOwnProperty = Object . prototype . hasOwnProperty . call ( target , method ) ;
382394 clock [ "_" + method ] = target [ method ] ;
383395
@@ -399,6 +411,10 @@ function hijackMethod(target, method, clock) {
399411 target [ method ] . clock = clock ;
400412}
401413
414+ function doIntervalTick ( clock , advanceTimeDelta ) {
415+ clock . tick ( advanceTimeDelta ) ;
416+ }
417+
402418var timers = {
403419 setTimeout : setTimeout ,
404420 clearTimeout : clearTimeout ,
@@ -413,6 +429,10 @@ if (hrtimePresent) {
413429 timers . hrtime = global . process . hrtime ;
414430}
415431
432+ if ( nextTickPresent ) {
433+ timers . nextTick = global . process . nextTick ;
434+ }
435+
416436if ( performancePresent ) {
417437 timers . performance = global . performance ;
418438}
@@ -460,7 +480,12 @@ function createClock(now, loopLimit) {
460480 clock . clearTimeout = function clearTimeout ( timerId ) {
461481 return clearTimer ( clock , timerId , "Timeout" ) ;
462482 } ;
463-
483+ clock . nextTick = function nextTick ( func ) {
484+ return enqueueJob ( clock , {
485+ func : func ,
486+ args : Array . prototype . slice . call ( 1 )
487+ } ) ;
488+ } ;
464489 clock . setInterval = function setInterval ( func , timeout ) {
465490 return addTimer ( clock , {
466491 func : func ,
@@ -486,6 +511,10 @@ function createClock(now, loopLimit) {
486511 return clearTimer ( clock , timerId , "Immediate" ) ;
487512 } ;
488513
514+ function updateHrTime ( newNow ) {
515+ clock . hrNow += ( newNow - clock . now ) ;
516+ }
517+
489518 clock . tick = function tick ( ms ) {
490519 ms = typeof ms === "number" ? ms : parseTime ( ms ) ;
491520 var tickFrom = clock . now ;
@@ -495,17 +524,15 @@ function createClock(now, loopLimit) {
495524 var oldNow , firstException ;
496525
497526 clock . duringTick = true ;
498-
499- function updateHrTime ( newNow ) {
500- clock . hrNow += ( newNow - clock . now ) ;
501- }
527+ runJobs ( clock ) ;
502528
503529 while ( timer && tickFrom <= tickTo ) {
504530 if ( clock . timers [ timer . id ] ) {
505531 updateHrTime ( timer . callAt ) ;
506532 tickFrom = timer . callAt ;
507533 clock . now = timer . callAt ;
508534 try {
535+ runJobs ( clock ) ;
509536 oldNow = clock . now ;
510537 callTimer ( clock , timer ) ;
511538 } catch ( e ) {
@@ -524,6 +551,7 @@ function createClock(now, loopLimit) {
524551 previous = tickFrom ;
525552 }
526553
554+ runJobs ( clock ) ;
527555 clock . duringTick = false ;
528556 updateHrTime ( tickTo ) ;
529557 clock . now = tickTo ;
@@ -536,15 +564,18 @@ function createClock(now, loopLimit) {
536564 } ;
537565
538566 clock . next = function next ( ) {
567+ runJobs ( clock ) ;
539568 var timer = firstTimer ( clock ) ;
540569 if ( ! timer ) {
541570 return clock . now ;
542571 }
543572
544573 clock . duringTick = true ;
545574 try {
575+ updateHrTime ( timer . callAt ) ;
546576 clock . now = timer . callAt ;
547577 callTimer ( clock , timer ) ;
578+ runJobs ( clock ) ;
548579 return clock . now ;
549580 } finally {
550581 clock . duringTick = false ;
@@ -553,12 +584,13 @@ function createClock(now, loopLimit) {
553584
554585 clock . runAll = function runAll ( ) {
555586 var numTimers , i ;
587+ runJobs ( clock ) ;
556588 for ( i = 0 ; i < clock . loopLimit ; i ++ ) {
557589 if ( ! clock . timers ) {
558590 return clock . now ;
559591 }
560592
561- numTimers = Object . keys ( clock . timers ) . length ;
593+ numTimers = keys ( clock . timers ) . length ;
562594 if ( numTimers === 0 ) {
563595 return clock . now ;
564596 }
@@ -572,6 +604,7 @@ function createClock(now, loopLimit) {
572604 clock . runToLast = function runToLast ( ) {
573605 var timer = lastTimer ( clock ) ;
574606 if ( ! timer ) {
607+ runJobs ( clock ) ;
575608 return clock . now ;
576609 }
577610
@@ -637,16 +670,24 @@ exports.createClock = createClock;
637670 * @param config.now {number|Date} a number (in milliseconds) or a Date object (default epoch)
638671 * @param config.toFake {string[]} names of the methods that should be faked.
639672 * @param config.loopLimit {number} the maximum number of timers that will be run when calling runAll()
673+ * @param config.shouldAdvanceTime {Boolean} tells lolex to increment mocked time automatically (default false)
674+ * @param config.advanceTimeDelta {Number} increment mocked time every <<advanceTimeDelta>> ms (default: 20ms)
640675 */
641676exports . install = function install ( config ) {
677+ if ( arguments . length > 1 || config instanceof Date || Array . isArray ( config ) || typeof config === "number" ) {
678+ throw new TypeError ( "lolex.install called with " + String ( config ) +
679+ " lolex 2.0+ requires an object parameter - see https://github.com/sinonjs/lolex" ) ;
680+ }
642681 config = typeof config !== "undefined" ? config : { } ;
682+ config . shouldAdvanceTime = config . shouldAdvanceTime || false ;
683+ config . advanceTimeDelta = config . advanceTimeDelta || 20 ;
643684
644685 var i , l ;
645686 var target = config . target || global ;
646687 var clock = createClock ( config . now , config . loopLimit ) ;
647688
648689 clock . uninstall = function ( ) {
649- uninstall ( clock , target ) ;
690+ uninstall ( clock , target , config ) ;
650691 } ;
651692
652693 clock . methods = config . toFake || [ ] ;
@@ -660,7 +701,18 @@ exports.install = function install(config) {
660701 if ( target . process && typeof target . process . hrtime === "function" ) {
661702 hijackMethod ( target . process , clock . methods [ i ] , clock ) ;
662703 }
704+ } else if ( clock . methods [ i ] === "nextTick" ) {
705+ if ( target . process && typeof target . process . nextTick === "function" ) {
706+ hijackMethod ( target . process , clock . methods [ i ] , clock ) ;
707+ }
663708 } else {
709+ if ( clock . methods [ i ] === "setInterval" && config . shouldAdvanceTime === true ) {
710+ var intervalTick = doIntervalTick . bind ( null , clock , config . advanceTimeDelta ) ;
711+ var intervalId = target [ clock . methods [ i ] ] (
712+ intervalTick ,
713+ config . advanceTimeDelta ) ;
714+ clock . attachedInterval = intervalId ;
715+ }
664716 hijackMethod ( target , clock . methods [ i ] , clock ) ;
665717 }
666718 }
0 commit comments