Comments (17)
Conditional requires is not possible (AFAIK) when using ESM, as linking takes place before running the code, so I guess this would prevent us from easily going the ESM in the future? If we did, I think it would require clients to use something that captured calls to timers, which seems like a big nuisance. Unless you have some clever tricks to avoid this issue, @benjamingr?
A loader (not fun) or using top level await with dynamic import (easy and also works)
from fake-timers.
Ah, good catch. I'll create a new one, referencing this.
from fake-timers.
Thanks guys 🙏
As it turns out, this doesn't solve my initial problem, though. Jest is using a custom global object but I made it so that the timers module isn't touched unless the patched object is the "default" global object. So upon jest.useFakeTimers
the timers module stays the same.
Despite that, I still think it's not a good idea to have fake-timers change the timers module for every call to "install" as this might lead to unexpected behavior if multiple objects are being patched and reverted.
from fake-timers.
the referenced issue is # #469 btw
from fake-timers.
Sniffing out require
should work fine in Jest as that should work the same as Node, but that won't work for import
declarations or expressions. I think that would need to hook into the module mocking directly. I haven't looked into it, tho
from fake-timers.
PR welcome :)
from fake-timers.
@benjamingr You are bit more into the Node internals than me, so I just vaguely remember this or something related (HTTP Headers in http
using primordials?) coming up earlier. Are these timers (or the internal) easily stubbable somehow? Not totally sure where a volunteer would start. Jest would probably have an easier time doing this by just stubbing out calls to the timers
lib, but we would somehow need to interface with the library directly. I would assume that trying to modify the exports directly would not work (this being ESM, using a immutable namespace and all).
from fake-timers.
The timers should be pretty easily stubbable unlike HTTP headers - just require('timers')
and replace that object's setTimeout/setInterval properties
from fake-timers.
Thanks for the quick responses 🙏
PR welcome :)
I've had a quick glance at the fake-timers repo before opening this issue... but I wouldn't know where to put the code or how to make sure it's only done for NodeJS. Also I'm a senior Python developer, not so much JS/TS 😅
Jest would probably have an easier time doing this by just stubbing out calls to the timers lib
That's what I thought too and why I'm not sure whether a fix would belong here or there...
The timers should be pretty easily stubbable unlike HTTP headers - just require('timers') and replace that object's setTimeout/setInterval properties
Indeed they are and that's exactly my current workaround:
testUtils.ts
export function myUseFakeTimers() {
jest.useFakeTimers();
timers.setTimeout = setTimeout;
timers.clearTimeout = clearTimeout;
timers.setInterval = setInterval;
timers.clearInterval = clearInterval;
timers.setImmediate = setImmediate;
timers.clearImmediate = clearImmediate;
}
export function myUseRealTimers() {
jest.useRealTimers();
timers.setTimeout = setTimeout;
timers.clearTimeout = clearTimeout;
timers.setInterval = setInterval;
timers.clearInterval = clearInterval;
timers.setImmediate = setImmediate;
timers.clearImmediate = clearImmediate;
}
from fake-timers.
@swenzel-arc You already have code that works, so that is great. Now what about the interface ... We need to be able to selectively disable or enable the stubbing. I guess it makes sense to enable it as the default? And how should we deal with only stubbing selected timers? I think a reasonable assumption is that if you only want to stub out two specific timers from global
, you would not want another selection for Node's timers
module. If that assumption does not hold, we need a way of specifying it.
fakeTimers.install({ toFake: ['setTimeout', 'clearTimeout', 'nodeTimers']});
You would then basically add a section here to specifically deal with the Node timers lib.
I wouldn't know where to put the code or how to make sure it's only done for NodeJS
This is the real question. If using CommonJS one can simply do checks to see if we are running Node or if the library can be loaded successfully, and only try stubbing if that is successful. Something like
let nodeTimersModule;
try { nodeTimersModule = require('timers') }
catch(err) { // not recent Node version }
....
if (toFake.nodeTimers && nodeTimersModule) {
installNodeTimers()
}
Conditional requires is not possible (AFAIK) when using ESM, as linking takes place before running the code, so I guess this would prevent us from easily going the ESM in the future? If we did, I think it would require clients to use something that captured calls to timers
, which seems like a big nuisance. Unless you have some clever tricks to avoid this issue, @benjamingr?
Word of warning: instead of having an explicit section for dealing with Node timers, one could think (as I did) that we could just use the library's ability to target a specific object with its withGlobal(target)
export. The whole installNodeTimers
thing would then almost just be down to a single line: installNodeTimers(){ nodeTimersClock = withGlobal(nodeTimersModule); }
. We could then just restore that as nodeTimersClock.restore()
when restoring everything else. Unfortunately, you would then have to sync two sets of internal clocks (for instance when doing clock.tick()
), which I do not think is complexity we want :-)
from fake-timers.
AFAIK, we cannot start using promise based code without changing the API. If we were to do dynamic imports (great idea, btw), we would probably have to change all the exports to resolve to promises:
export install(config:Config|null): Promise<Clock>
export withGlobal(config:Config|null): Promise<Clock>
etc
Otherwise we would not know if the call to timers()
was resolved or not.
let asyncFunction = async () => {
async function main() {
var value = await Promise.resolve("Hey there");
console.log("inside: " + value);
return value;
}
var text = await main();
console.log("outside: " + text);
};
console.log("pre");
asyncFunction();
console.log("post");
will print
pre
post
inside: Hey there
outside: Hey there
Either that, or expose a promise one could wait for: await clock.nodeTimers
(or clock.asyncImportsResolved
or similar).
from fake-timers.
It appears to be a bit harder than I thought... especially the uninstall
part. Looks like clocks are supposed to hijack a single object and if there's more than one, then the mechanism for keeping track of hijacked methods has to be changed.
I guess I can do that, but seems to be a pretty invasive change.
from fake-timers.
Or actually, nevermind... I think I'll just handle the "timers" module separately. Similar to how I solved it in the workaround.
from fake-timers.
That's fine. We can still keep this issue around as we might want this still
from fake-timers.
Should this be closed or kept open for timers/promises?
from fake-timers.
@SimenB is there anything we can do better in this regard for jest?
from fake-timers.
In case anyone want's to check it out:
Jest creates the custom global object here which is then passed to their wrapper a few lines down here.
The wrapper implementation is here.
Is there another way to access the "currently active global object"? Maybe we could compare the _global
in withGlobal
to something other than globalObject
... which makes me wonder, with jest swapping out the global object, would the default install
even patch the globals correctly?
from fake-timers.
Related Issues (20)
- Test failing since Node 18.8.0 HOT 2
- The `shouldAdvanceTime` option seems to cause Jest Test environment to clear during the test HOT 4
- `runToLastAsync` doing infinite loop when `now` is specified to setup clock
- [Feature] Jump forward in time to simulate throttled timers HOT 22
- requestAnimationFrame is passed incorrect argument HOT 8
- [Feature]: Speed up all fake timers HOT 12
- [Feature]: In Node, hijack perf_hooks.performance in addition to global.performance HOT 5
- TypeError Since Node 19 HOT 4
- Calls to `tick()` do not cause all setTimeouts to fire in specific scenarios. HOT 6
- Mock "timers/promise" module HOT 2
- NodeJS `timers` module mock causing tests to timeout in JSDom HOT 3
- process.hrtime gives incorrect results HOT 4
- Leave global scope untouched HOT 5
- How should `shouldAdvanceTime` work together with `Date`? HOT 1
- runAllAsync and runToLastAsync do not run code in the microtask queue HOT 5
- Cleanly switch to real time and back again to fake time while testing HOT 2
- Skypack bundle cannot be loaded in the browser HOT 5
- `install({ toFake: ["setImmediate"] })` fails when `setImmediate` is not available in context (such as browser environment) HOT 5
- Support performance.measure method
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from fake-timers.