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
804 stars 106 forks source link

Event timeStamp not using fake-timers #432

Closed mansona closed 1 year ago

mansona commented 2 years ago

We understand you have a problem and are in a hurry, but please provide us with some info to make it much more likely for your issue to be understood, worked on and resolved quickly.

What did you expect to happen?

I'm trying to create 2 events with a certain time between them:

    window.dispatchEvent(new KeyboardEvent('keypress', { key: 'A' }));
    this.clock.tick(70);
    window.dispatchEvent(new KeyboardEvent('keypress', { key: 'B' }));

and then if I checked the difference of those timestamps I was expecting that they be exactly 70ms apart:

const eventDiff = secondEvent.timeStamp - firstEvent.timeStamp;
assert.equals(eventDiff, 150);

What actually happens

The Events seem to just ignore the fake-timer implementation.

How to reproduce

See https://github.com/sinonjs/fake-timers/pull/433

fatso83 commented 2 years ago

Oooh, I wonder if we are able to fix that. If they maintain a reference to an internal clock, the answer is no.

mansona commented 2 years ago

well that's exactly why I asked here @fatso83 😂 I figured someone might be able to shed some light on it. I was trying to find some details on how that timeStamp is generated online but I kinda don't know what I'm looking for and didn't get very far 😞

benjamingr commented 2 years ago

We can (probably) mock KeyboardEvent.prototype.timeStamp to return the fake time?

fatso83 commented 2 years ago

I would say that faking UI event code in the browser is actually not a core feature of fake-timers, so not sure we should spend time implement it, but I think this is a pretty valid use case we could at least document how one could implement. I had never heard of it, so I researched this a bit on MDN and it seems that KeyboardEvent < UIEvent < Event and the timeStamp comes from the top of the hierarchy: the Event class.

This value is the number of milliseconds elapsed from the beginning of the time origin until the event was created. If the global object is Window, the time origin is the moment the user clicked on the link, or the script that initiated the loading of the document

That means we would just need to replace thetimeStamp field is with a something that returns something we want, like the current clock time in ms. When playing in DevTools right now on this page, I could see this working fine:

// Set up something to capture events for us to play with in DevTools console
document.getElementById('new_comment_field').addEventListener('keyup', function(args) { console.log(args); })

// replace the `timeStamp` on the prototype
// use `sinon.replace()` or similar if you want restore functionality
Object.defineProperty(window.Event.prototype, 'timeStamp', { get(){ return 101; } })

So if you then replace 101 in the code above with Date.now() for instance, your test should work just fine.

Skjermbilde 2022-06-17 kl  21 35 19 Skjermbilde 2022-06-17 kl  21 35 32
fatso83 commented 1 year ago

Closed as a workaround exists for a niche issue.