sinonjs / fake-timers

Fake setTimeout and friends (collectively known as "timers"). Useful in your JavaScript tests. Extracted from Sinon.JS
BSD 3-Clause "New" or "Revised" License
802 stars 105 forks source link

Date.now() cannot be faked when using @babel/runtime-corejs* #321

Closed todofixthis closed 4 years ago

todofixthis commented 4 years ago

What did you expect to happen?

Date.now() returns 0

What actually happens

Date.now() returns current timestamp.

How to reproduce

  1. Configure project to use @babel/runtime-corejs3:

    package.json ```json { "name": "test", "version": "1.0.0", "description": "", "main": "index.js", "scripts": { "test": "echo \"Error: no test specified\" && exit 1" }, "author": "", "license": "ISC", "devDependencies": { "@babel/core": "^7.9.0", "@babel/plugin-transform-runtime": "^7.9.0", "@babel/preset-env": "^7.9.0", "@babel/register": "^7.9.0" }, "dependencies": { "@babel/runtime": "^7.9.2", "@babel/runtime-corejs3": "^7.9.2", "@sinonjs/fake-timers": "^6.0.1" } } ```
    babel.config.js ```js module.exports = { "presets": ["@babel/preset-env"], "plugins": [ ["@babel/plugin-transform-runtime", { "corejs": 3 }] ] } ```
  2. Create index.js as follows:

    import FakeTimers from '@sinonjs/fake-timers'
    
    FakeTimers.install()
    
    console.log(new Date().getTime())
    console.log(Date.now())
  3. Execute the script:

    > node -r @babel/register index.js
    0
    1585877900883

The last line of output should be 0, but it is the current timestamp instead.

Note: can also be reproduced using (deprecated) @babel/runtime-corejs2.

fatso83 commented 4 years ago

There is nothing per se that is saying fake-timers is to blame here, given that transpilers can do all sorts of things. I am guessing this has something to do with what global is detected to be. Is there, for instance, a window global from somewhere? If this is what is causing it, then you can use the target prop to work around it. This is an example from the readme:

var FakeTimers = require("@sinonjs/fake-timers");
var context = {
    setTimeout: setTimeout // By default context.setTimeout uses the global setTimeout
}
var clock = FakeTimers.install({target: context});

context.setTimeout(fn, 15); // Schedules with clock.setTimeout

clock.uninstall();
// context.setTimeout is restored to the original implementation

Here, context would be global.window or something else that might be messing up the state of things. Could you interactively inspect the environment for weird stuff?

fatso83 commented 4 years ago

Screw that. I decided to just run your test case (very easy to do, so great job, btw!) and I had misunderstood what was going on: fake timers ARE being installed, it seems, it's just that it does not seem to be working for that one call. The other is fine. I'll inspect.

fatso83 commented 4 years ago

OK, this was easy. Just run babel to inspect the output and it is immediatly clear:

$ npm i @babel/cli 
...

$ $(npm bin)/babel index.js 
"use strict";

var _interopRequireDefault = require("@babel/runtime-corejs3/helpers/interopRequireDefault");

var _now = _interopRequireDefault(require("@babel/runtime-corejs3/core-js-stable/date/now"));

var _fakeTimers = _interopRequireDefault(require("@sinonjs/fake-timers"));

_fakeTimers["default"].install();

console.log(new Date().getTime());
console.log((0, _now["default"])());

You corejs3 is replacing Date.now by a strictly bound reference to @babel/runtime-corejs3/core-js-stable/date/now. We cannot do anything about that, basically, but maybe you can find a way to configure core-js to not do this.

fatso83 commented 4 years ago

You can see others being bit by this exact issue: https://github.com/zeit/next.js/issues/7050.

I spent ten minutes looking into how core-js can be configured via babel's own configs for transform-runtime and babel-preset-env, but could not really find anything useful. core-js itself says it can be configured to be less aggressive, so you might need to dig deeper.

todofixthis commented 4 years ago

Thanks for looking into this @fatso83 ! I'll dive a bit deeper and see if I can come up with something interesting 😺

todofixthis commented 4 years ago

Just now coming up for air, and I'm as stumped as you are. I suspect that @babel/plugin-transform-runtime simply doesn't provide enough configurability, and the only option (aside from not using Date.now() 😁 ) would be to include core-js directly.

But, I posted on Stackoverflow, just in case 😸