Chrono Mock

Faking the date in an Electron app

Search for this and every answer is about changing your own code - mock the Date object, use your test framework's fake timers. None of that helps when what you have is a built application. This runs the application you already have.

Why injection does not reach it

This mode exists because of a specific failure. A Pomodoro timer built on Electron was run under an x60 session and its countdown did not move at all. Taking the application's bundle apart showed two independent reasons, and neither had a fix on the native side:

  • The countdown was not reading a clock. It was a counter incremented by setInterval, running inside a Web Worker. There was no time call to intercept, because the application never asked what time it was.
  • That worker lives in a sandboxed renderer process, which is precisely the place a library injected from outside does not get into.

So the Windows-side mechanism cannot help here, and the answer is to speak to the application's own JavaScript engine instead.

What happens instead

The application is launched with a debugging port and a clean throwaway profile, and its own time APIs are put on the session clock through the DevTools protocol - Date.now, the new Date() constructor, performance.now, setTimeout and setInterval.

The override is installed into every context the application opens, including its Web Workers - which is where the timer usually turns out to live, and the one place native injection was never going to reach.

Nothing about how you drive it changes. The same command, the same window, the same session panel with the two clocks and the verdict.

# Same command as for a native application - the mode is detected
chrono run "%LOCALAPPDATA%\Programs\pomotroid\Pomotroid.exe" --at 2038-01-01T00:00:00 --mode x60

Two limits worth knowing before you start

The launch is invasive, and deliberately loud about it.

The application is started with a remote debugging port open and a fresh, isolated profile under %TEMP% - it does not run against your real profile, and it is not the copy already in your system tray. The profile is deleted when the session ends, and if it could not be, the session summary says so rather than leaving it quietly behind.

Passing your own --user-data-dir or --remote-debugging-port through is refused rather than overridden, because the obvious accident - pointing the session at your real browser profile - is not one worth allowing.

The session time zone follows the host.

In this mode the instant is faked, but the local-time getters are not, so an independent time zone is a native-mode feature only. That is a limitation of this version, not a decision.

One more thing that surprises people: an interval already scheduled keeps its old rate when you change the speed mid-session. Date.now, new Date() and performance.now react immediately, a running setInterval does not, and the session warns about it rather than letting you conclude the change did nothing.

How well tested is it

Experimental, and that word has a defined meaning here: it works on the targets it was built against, it has not been tested broadly, and the built-in audit is your source of truth rather than this page. It was verified by hand rather than by the automated suite, against a real Electron application on 64-bit Windows.

Coverage is reported per JavaScript context rather than per process, since that is the unit that exists here. An application sitting idle and never asking the time honestly produces undetermined - it did not read a clock, so nothing can be claimed about whether it would have seen the right one. What the verdicts mean →

Try it