Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add clock.jump method #465

Merged
merged 2 commits into from May 13, 2023
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
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 @@ -39,13 +39,13 @@

/**
* @typedef RequestAnimationFrame
* @property {function(number):void} requestAnimationFrame

Check warning on line 42 in src/fake-timers-src.js

View workflow job for this annotation

GitHub Actions / lint

Missing JSDoc @Property "requestAnimationFrame" description
* @returns {number} - the id
*/

/**
* @typedef Performance
* @property {function(): number} now

Check warning on line 48 in src/fake-timers-src.js

View workflow job for this annotation

GitHub Actions / lint

Missing JSDoc @Property "now" description
*/

/* eslint-disable jsdoc/require-property-description */
Expand Down Expand Up @@ -79,6 +79,7 @@
* @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 @@ -316,7 +317,7 @@
return timer && timer.callAt >= from && timer.callAt <= to;
}

/**

Check warning on line 320 in src/fake-timers-src.js

View workflow job for this annotation

GitHub Actions / lint

Missing JSDoc @returns declaration
* @param {Clock} clock
* @param {Timer} job
*/
Expand Down Expand Up @@ -618,7 +619,7 @@
}

/* eslint consistent-return: "off" */
/**

Check warning on line 622 in src/fake-timers-src.js

View workflow job for this annotation

GitHub Actions / lint

JSDoc @returns declaration present but return expression not available in function
* Timer comparitor
*
* @param {Timer} a
Expand Down Expand Up @@ -750,7 +751,7 @@
}
}

/**

Check warning on line 754 in src/fake-timers-src.js

View workflow job for this annotation

GitHub Actions / lint

Missing JSDoc @returns declaration
* Gets clear handler name for a given timer type
*
* @param {string} ttype
Expand All @@ -762,7 +763,7 @@
return `clear${ttype}`;
}

/**

Check warning on line 766 in src/fake-timers-src.js

View workflow job for this annotation

GitHub Actions / lint

Missing JSDoc @returns declaration
* Gets schedule handler name for a given timer type
*
* @param {string} ttype
Expand All @@ -774,7 +775,7 @@
return `set${ttype}`;
}

/**

Check warning on line 778 in src/fake-timers-src.js

View workflow job for this annotation

GitHub Actions / lint

Missing JSDoc @returns declaration
* Creates an anonymous function to warn only once
*/
function createWarnOnce() {
Expand All @@ -786,7 +787,7 @@
}
const warnOnce = createWarnOnce();

/**

Check warning on line 790 in src/fake-timers-src.js

View workflow job for this annotation

GitHub Actions / lint

Missing JSDoc @returns declaration
* @param {Clock} clock
* @param {number} timerId
* @param {string} ttype
Expand Down Expand Up @@ -967,8 +968,8 @@

/**
* @typedef {object} Timers
* @property {setTimeout} setTimeout

Check warning on line 971 in src/fake-timers-src.js

View workflow job for this annotation

GitHub Actions / lint

Missing JSDoc @Property "setTimeout" description
* @property {clearTimeout} clearTimeout

Check warning on line 972 in src/fake-timers-src.js

View workflow job for this annotation

GitHub Actions / lint

Missing JSDoc @Property "clearTimeout" description
* @property {setInterval} setInterval
* @property {clearInterval} clearInterval
* @property {Date} Date
Expand Down Expand Up @@ -1605,6 +1606,25 @@
}
};

/**
* @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
56 changes: 56 additions & 0 deletions test/fake-timers-test.js
Expand Up @@ -4103,6 +4103,62 @@ 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, 1000);

this.clock.jump(2000);

assert(stub.calledOnce);
CreativeTechGuy marked this conversation as resolved.
Show resolved Hide resolved
});

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