Skip to content

Commit

Permalink
Add clock.jump method (#465)
Browse files Browse the repository at this point in the history
* Add clock.jump method

* Test for timeout execution time
  • Loading branch information
CreativeTechGuy committed May 13, 2023
1 parent 947a7e7 commit 556f689
Show file tree
Hide file tree
Showing 3 changed files with 86 additions and 0 deletions.
7 changes: 7 additions & 0 deletions README.md
Expand Up @@ -255,6 +255,13 @@ Advances the clock to the the moment of the first scheduled timer, firing it.
The `nextAsync()` will also break the event loop, allowing any scheduled promise
callbacks to execute _before_ running the timers.

### `clock.jump(time)`

Advance the clock by jumping forward in time, firing callbacks at most once.
`time` takes the same formats as [`clock.tick`](#clockticktime--await-clocktickasynctime).

This can be used to simulate the JS engine (such as a browser) being put to sleep and resumed later, skipping intermediary timers.

### `clock.reset()`

Removes all timers and ticks without firing them, and sets `now` to `config.now`
Expand Down
20 changes: 20 additions & 0 deletions src/fake-timers-src.js
Expand Up @@ -79,6 +79,7 @@ const globalObject = require("@sinonjs/commons").global;
* @property {function(): Promise<number>} runToLastAsync
* @property {function(): void} reset
* @property {function(number | Date): void} setSystemTime
* @property {function(number): void} jump
* @property {Performance} performance
* @property {function(number[]): number[]} hrtime - process.hrtime (legacy)
* @property {function(): void} uninstall Uninstall the clock.
Expand Down Expand Up @@ -1605,6 +1606,25 @@ function withGlobal(_global) {
}
};

/**
* @param {string|number} tickValue number of milliseconds or a human-readable value like "01:11:15"
* @returns {number} will return the new `now` value
*/
clock.jump = function jump(tickValue) {
const msFloat =
typeof tickValue === "number"
? tickValue
: parseTime(tickValue);
const ms = Math.floor(msFloat);

for (const timer of Object.values(clock.timers)) {
if (clock.now + ms > timer.callAt) {
timer.callAt = clock.now + ms;
}
}
clock.tick(ms);
};

if (performancePresent) {
clock.performance = Object.create(null);
clock.performance.now = fakePerformanceNow;
Expand Down
59 changes: 59 additions & 0 deletions test/fake-timers-test.js
Expand Up @@ -4103,6 +4103,65 @@ describe("FakeTimers", function () {
});
});

describe("jump", function () {
beforeEach(function () {
this.clock = FakeTimers.install({ now: 0 });
});

afterEach(function () {
this.clock.uninstall();
});

it("ignores timers which wouldn't be run", function () {
const stub = sinon.stub();
this.clock.setTimeout(stub, 1000);

this.clock.jump(500);

assert(stub.notCalled);
});

it("pushes back execution time for skipped timers", function () {
const stub = sinon.stub();
this.clock.setTimeout(() => {
stub(this.clock.Date.now());
}, 1000);

this.clock.jump(2000);

assert(stub.calledOnce);
assert(stub.calledWith(2000));
});

it("handles multiple pending timers and types", function () {
const longTimers = [sinon.stub(), sinon.stub()];
const shortTimers = [sinon.stub(), sinon.stub(), sinon.stub()];
this.clock.setTimeout(longTimers[0], 2000);
this.clock.setInterval(longTimers[1], 2500);
this.clock.setTimeout(shortTimers[0], 250);
this.clock.setInterval(shortTimers[1], 100);
this.clock.requestAnimationFrame(shortTimers[2]);

this.clock.jump(1500);

for (const stub of longTimers) {
assert(stub.notCalled);
}
for (const stub of shortTimers) {
assert(stub.calledOnce);
}
});

it("supports string time arguments", function () {
const stub = sinon.stub();
this.clock.setTimeout(stub, 100000); // 100000 = 1:40

this.clock.jump("01:50");

assert(stub.calledOnce);
});
});

describe("performance.now()", function () {
before(function () {
if (!performanceNowPresent) {
Expand Down

0 comments on commit 556f689

Please sign in to comment.