Repository: sinonjs/fake-timers (js)

Issue:
Support `performance.timeOrigin` property
> 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.

-   FakeTimers version : 13.0.5
-   Environment : MacOS
-   Other libraries you are using: jest, @sentry/react

**What did you expect to happen?**

I expected that `performance.timeOrigin` (https://developer.mozilla.org/en-US/docs/Web/API/Performance/timeOrigin) would return a valid value with fake timers enabled in jest.

**What actually happens**

`performance.timeOrigin` is set to NOOP [here](https://github.com/sinonjs/fake-timers/blob/7861c930e8f9aaa9d227431623cc82347aff0291/src/fake-timers-src.js#L1843).

**How to reproduce**

```bash
git clone https://github.com/rriski/fake-timers-timeOrigin-repro.git
npm run test
# --> test fails
# Uncomment `doNotFake` in jest.config.js
npm run test
# --> test passes
```

**Additional information**

* Related to #493
* `@sentry/react` uses the performance API to set the session starting time, see [session.ts](https://github.com/getsentry/sentry-javascript/blob/6331d7ced7373e3f351ffacb71af54392c19acda/packages/core/src/session.ts#L14)



Pull request:
fix: return fake version for `performance.timeOrigin`
Fixes #514

#### Purpose (TL;DR) - mandatory

Return valid value for `performance.timeOrigin` instead of `NOOP`. This fixes an issue in tests that depend on `performance.timeOrigin` having a valid value.

#### Background (Problem in detail)  - optional

See #514

#### Solution  - optional

Set `performance.timeOrigin` to a dummy value. I'm not sure if 0 is better than setting the value to `clock.now`. When testing `performance.timeOrigin` in Chrome Console I can see that the value is set to the timestamp when the window was opened:

```
> globalThis.performance.timeOrigin
1733227796841
```

There is further information in MDS docs on how the value is supposed to be set: https://developer.mozilla.org/en-US/docs/Web/API/Performance/timeOrigin#value

Patch:
diff --git a/src/fake-timers-src.js b/src/fake-timers-src.js
index b9a8a8d..03ad8d5 100644
--- a/src/fake-timers-src.js
+++ b/src/fake-timers-src.js
@@ -23,7 +23,6 @@ if (typeof require === "function" && typeof module === "object") {
 
 /**
  * Queues a function to be called during a browser's idle periods
- *
  * @callback RequestIdleCallback
  * @param {function(IdleDeadline)} callback
  * @param {{timeout: number}} options - an options object
@@ -106,7 +105,6 @@ if (typeof require === "function" && typeof module === "object") {
 
 /**
  * Configuration object for the `install` method.
- *
  * @typedef {object} Config
  * @property {number|Date} [now] a number (in milliseconds) or a Date object (default epoch)
  * @property {string[]} [toFake] names of the methods that should be faked.
@@ -120,7 +118,6 @@ if (typeof require === "function" && typeof module === "object") {
 /* eslint-disable jsdoc/require-property-description */
 /**
  * The internal structure to describe a scheduled fake timer
- *
  * @typedef {object} Timer
  * @property {Function} func
  * @property {*[]} args
@@ -134,7 +131,6 @@ if (typeof require === "function" && typeof module === "object") {
 
 /**
  * A Node timer
- *
  * @typedef {object} NodeImmediate
  * @property {function(): boolean} hasRef
  * @property {function(): NodeImmediate} ref
@@ -146,7 +142,6 @@ if (typeof require === "function" && typeof module === "object") {
 
 /**
  * Mocks available features in the specified global namespace.
- *
  * @param {*} _global Namespace to mock (e.g. `window`)
  * @returns {FakeTimers}
  */
@@ -276,7 +271,6 @@ function withGlobal(_global) {
      * Parse strings like "01:10:00" (meaning 1 hour, 10 minutes, 0 seconds) into
      * number of milliseconds. This is used to support human-readable strings passed
      * to clock.tick()
-     *
      * @param {string} str
      * @returns {number}
      */
@@ -312,7 +306,6 @@ function withGlobal(_global) {
 
     /**
      * Get the decimal part of the millisecond value as nanoseconds
-     *
      * @param {number} msFloat the number of milliseconds
      * @returns {number} an integer number of nanoseconds in the range [0,1e6)
      *
@@ -329,7 +322,6 @@ function withGlobal(_global) {
 
     /**
      * Used to grok the `now` parameter to createClock.
-     *
      * @param {Date|number} epoch the system time
      * @returns {number}
      */
@@ -483,7 +475,6 @@ function withGlobal(_global) {
         /**
          * A normal Class constructor cannot be called without `new`, but Date can, so we need
          * to wrap it in a Proxy in order to ensure this functionality of Date is kept intact
-         *
          * @type {ClockDate}
          */
         const ClockDateProxy = new Proxy(ClockDate, {
@@ -510,7 +501,6 @@ function withGlobal(_global) {
      * Most of the properties are the original native ones,
      * but we need to take control of those that have a
      * dependency on the current clock.
-     *
      * @returns {object} the partly fake Intl implementation
      */
     function createIntl() {
@@ -683,7 +673,6 @@ function withGlobal(_global) {
     /* eslint consistent-return: "off" */
     /**
      * Timer comparitor
-     *
      * @param {Timer} a
      * @param {Timer} b
      * @returns {number}
@@ -815,7 +804,6 @@ function withGlobal(_global) {
 
     /**
      * Gets clear handler name for a given timer type
-     *
      * @param {string} ttype
      */
     function getClearHandler(ttype) {
@@ -827,7 +815,6 @@ function withGlobal(_global) {
 
     /**
      * Gets schedule handler name for a given timer type
-     *
      * @param {string} ttype
      */
     function getScheduleHandler(ttype) {
@@ -1183,13 +1170,11 @@ function withGlobal(_global) {
 
         /**
          * A high resolution timestamp in milliseconds.
-         *
          * @typedef {number} DOMHighResTimeStamp
          */
 
         /**
          * performance.now()
-         *
          * @returns {DOMHighResTimeStamp}
          */
         function fakePerformanceNow() {
@@ -1848,6 +1833,9 @@ function withGlobal(_global) {
                     new FakePerformanceEntry(name, "mark", 0, 0);
                 clock.performance.measure = (name) =>
                     new FakePerformanceEntry(name, "measure", 0, 100);
+                // `timeOrigin` should return the time of when the Window session started
+                // (or the Worker was installed)
+                clock.performance.timeOrigin = getEpoch(config.now);
             } else if ((config.toFake || []).includes("performance")) {
                 return handleMissingTimer("performance");
             }


Test patch:
diff --git a/test/fake-timers-test.js b/test/fake-timers-test.js
index 0a4b676..fcfd0ed 100644
--- a/test/fake-timers-test.js
+++ b/test/fake-timers-test.js
@@ -3684,6 +3684,16 @@ describe("FakeTimers", function () {
                 testEntry(performance.measure("bar", "s", "t"));
             });
 
+            it("should create fake version of `timeOrigin` that returns the installed time", function () {
+                if (typeof Performance === "undefined") {
+                    return this.skip();
+                }
+
+                this.clock = FakeTimers.install({ now: new Date(1234) });
+                assert.isNumber(performance.timeOrigin);
+                assert.equals(performance.timeOrigin, 1234);
+            });
+
             it("should replace the getEntries, getEntriesByX methods with noops that return []", function () {
                 if (typeof Performance === "undefined") {
                     return this.skip();
@@ -4788,7 +4798,6 @@ describe("FakeTimers", function () {
 
         /**
          * Returns elements that are present in both lists.
-         *
          * @function
          * @template E
          * @param {E[]} [list1]
@@ -4801,7 +4810,6 @@ describe("FakeTimers", function () {
 
         /**
          * Get property names and original values from timers module.
-         *
          * @function
          * @param {string[]} [toFake]
          * @returns {{propertyName: string, originalValue: any}[]}
@@ -5948,7 +5956,6 @@ describe("Node Timer: ref(), unref(),hasRef()", function () {
 describe("Intl API", function () {
     /**
      * Tester function to check if the globally hijacked Intl object is plugging into the faked Clock
-     *
      * @param {string} ianaTimeZone - IANA time zone name
      * @param {number} timestamp - UNIX timestamp
      * @returns {boolean}