Skip to content

Commit d99c698

Browse files
committed
Merge pull request #50 from karl/feature-run-to-last
Run to last existing timer
2 parents 27bca97 + a776def commit d99c698

File tree

3 files changed

+142
-2
lines changed

3 files changed

+142
-2
lines changed

Readme.md

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -51,8 +51,8 @@ Upon executing the last line, an interesting fact about the
5151
the screen. If you want to simulate asynchronous behavior, you have to use your
5252
imagination when calling the various functions.
5353

54-
The `next` method is available to advance to the next scheduled timer. See the
55-
API Reference for more.
54+
The `next`, `runAll`, and `runToLast` methods are available to advance the clock. See the
55+
API Reference for more details.
5656

5757
### Faking the native timers
5858

@@ -191,6 +191,17 @@ This makes it easier to run asynchronous tests to completion without worrying ab
191191

192192
It runs a maximum of `loopLimit` times after which it assumes there is an infinite loop of timers and throws an error.
193193

194+
### `clock.runToLast()`
195+
196+
This takes note of the last scheduled timer when it is run, and advances the
197+
clock to that time firing callbacks as necessary.
198+
199+
If new timers are added while it is executing they will be run only if they
200+
would occur before this time.
201+
202+
This is useful when you want to run a test to completion, but the test recursively
203+
sets timers that would cause `runAll` to trigger an infinite loop warning.
204+
194205
### `clock.setSystemTime([now])`
195206

196207
This simulates a user changing the system clock while your program is running.

src/lolex-src.js

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -263,6 +263,22 @@
263263
return timer;
264264
}
265265

266+
function lastTimer(clock) {
267+
var timers = clock.timers,
268+
timer = null,
269+
id;
270+
271+
for (id in timers) {
272+
if (timers.hasOwnProperty(id)) {
273+
if (!timer || compareTimers(timer, timers[id]) === -1) {
274+
timer = timers[id];
275+
}
276+
}
277+
}
278+
279+
return timer;
280+
}
281+
266282
function callTimer(clock, timer) {
267283
var exception;
268284

@@ -534,6 +550,15 @@
534550
throw new Error('Aborting after running ' + clock.loopLimit + 'timers, assuming an infinite loop!');
535551
};
536552

553+
clock.runToLast = function runToLast() {
554+
var timer = lastTimer(clock);
555+
if (!timer) {
556+
return clock.now;
557+
}
558+
559+
return clock.tick(timer.callAt);
560+
};
561+
537562
clock.reset = function reset() {
538563
clock.timers = {};
539564
};

test/lolex-test.js

Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -873,6 +873,110 @@ describe("lolex", function () {
873873

874874
});
875875

876+
describe('runToLast', function() {
877+
878+
it('returns current time when there are no timers', function() {
879+
this.clock = lolex.createClock();
880+
881+
var time = this.clock.runToLast();
882+
883+
assert.equals(time, 0);
884+
});
885+
886+
it('runs all existing timers', function() {
887+
this.clock = lolex.createClock();
888+
var spies = [sinon.spy(), sinon.spy()];
889+
this.clock.setTimeout(spies[0], 10);
890+
this.clock.setTimeout(spies[1], 50);
891+
892+
this.clock.runToLast();
893+
894+
assert(spies[0].called);
895+
assert(spies[1].called);
896+
});
897+
898+
it('returns time of the last timer', function() {
899+
this.clock = lolex.createClock();
900+
var spies = [sinon.spy(), sinon.spy()];
901+
this.clock.setTimeout(spies[0], 10);
902+
this.clock.setTimeout(spies[1], 50);
903+
904+
var time = this.clock.runToLast();
905+
906+
assert.equals(time, 50);
907+
});
908+
909+
it('runs all existing timers when two timers are matched for being last', function() {
910+
this.clock = lolex.createClock();
911+
var spies = [sinon.spy(), sinon.spy()];
912+
this.clock.setTimeout(spies[0], 10);
913+
this.clock.setTimeout(spies[1], 10);
914+
915+
this.clock.runToLast();
916+
917+
assert(spies[0].called);
918+
assert(spies[1].called);
919+
});
920+
921+
it('new timers added with a call time later than the last existing timer are NOT run', function() {
922+
this.clock = lolex.createClock();
923+
var test = this;
924+
var spies = [
925+
sinon.spy(function() {
926+
test.clock.setTimeout(spies[1], 50);
927+
}),
928+
sinon.spy()
929+
];
930+
931+
// Spy calls another setTimeout
932+
this.clock.setTimeout(spies[0], 10);
933+
934+
this.clock.runToLast();
935+
936+
assert.isTrue(spies[0].called);
937+
assert.isFalse(spies[1].called);
938+
});
939+
940+
it('new timers added with a call time ealier than the last existing timer are run', function() {
941+
this.clock = lolex.createClock();
942+
var test = this;
943+
var spies = [
944+
sinon.spy(),
945+
sinon.spy(function() {
946+
test.clock.setTimeout(spies[2], 50);
947+
}),
948+
sinon.spy()
949+
];
950+
951+
this.clock.setTimeout(spies[0], 100);
952+
// Spy calls another setTimeout
953+
this.clock.setTimeout(spies[1], 10);
954+
955+
this.clock.runToLast();
956+
957+
assert.isTrue(spies[0].called);
958+
assert.isTrue(spies[1].called);
959+
assert.isTrue(spies[2].called);
960+
});
961+
962+
it('new timers cannot cause an infinite loop', function() {
963+
this.clock = lolex.createClock();
964+
var test = this;
965+
var spy = sinon.spy();
966+
var recursiveCallback = function() {
967+
test.clock.setTimeout(recursiveCallback, 0);
968+
};
969+
970+
this.clock.setTimeout(recursiveCallback, 0);
971+
this.clock.setTimeout(spy, 100);
972+
973+
this.clock.runToLast();
974+
975+
assert.isTrue(spy.called);
976+
});
977+
978+
});
979+
876980
describe("clearTimeout", function () {
877981

878982
beforeEach(function () {

0 commit comments

Comments
 (0)