Repository: sinonjs/fake-timers
Pull request #400 (2021-09-16 14:26:54)

Faking performance when not present should throw an error (and minor changes) - fix for #374 
#### Original purpose: enabling adding performance to toFake array - apparently this was already possible so a few minor changes were made.

Issue #374 refers to the fact that `fake-timers` overrides `performance` object (as in other properties/methods in addition to overriding `performance.now()`).  The first suggested solution/notion was to enable users to add `performance` to the `toFake` array - after realizing this was already possible a test was added to make sure an error is thrown when a user puts `performance` in the `toFake` array but the `performance` object isn't present.

I don't know if this PR makes a good addition/improvement - I'm waiting for your notes and comments and I'll fix whatever's needed.

codecov[bot]: # [Codecov](https://codecov.io/gh/sinonjs/fake-timers/pull/400?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=sinonjs) Report
> Merging [#400](https://codecov.io/gh/sinonjs/fake-timers/pull/400?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=sinonjs) (fa323c8) into [master](https://codecov.io/gh/sinonjs/fake-timers/commit/a13a0540e6a2f8ea641b3613167fc98a71137a6f?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=sinonjs) (a13a054) will **increase** coverage by `0.18%`.
> The diff coverage is `100.00%`.

[![Impacted file tree graph](https://codecov.io/gh/sinonjs/fake-timers/pull/400/graphs/tree.svg?width=650&height=150&src=pr&token=YubuW6TySz&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=sinonjs)](https://codecov.io/gh/sinonjs/fake-timers/pull/400?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=sinonjs)

```diff
@@            Coverage Diff             @@
##           master     #400      +/-   ##
==========================================
+ Coverage   94.10%   94.29%   +0.18%     
==========================================
  Files           1        1              
  Lines         611      613       +2     
==========================================
+ Hits          575      578       +3     
+ Misses         36       35       -1     
```

| Flag | Coverage Δ | |
|---|---|---|
| unit | `94.29% <100.00%> (+0.18%)` | :arrow_up: |

Flags with carried forward coverage won't be shown. [Click here](https://docs.codecov.io/docs/carryforward-flags?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=sinonjs#carryforward-flags-in-the-pull-request-comment) to find out more.

| [Impacted Files](https://codecov.io/gh/sinonjs/fake-timers/pull/400?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=sinonjs) | Coverage Δ | |
|---|---|---|
| [src/fake-timers-src.js](https://codecov.io/gh/sinonjs/fake-timers/pull/400/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=sinonjs#diff-c3JjL2Zha2UtdGltZXJzLXNyYy5qcw==) | `94.29% <100.00%> (+0.18%)` | :arrow_up: |

------

[Continue to review full report at Codecov](https://codecov.io/gh/sinonjs/fake-timers/pull/400?src=pr&el=continue&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=sinonjs).
> **Legend** - [Click here to learn more](https://docs.codecov.io/docs/codecov-delta?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=sinonjs)
> `Δ = absolute <relative> (impact)`, `ø = not affected`, `? = missing data`
> Powered by [Codecov](https://codecov.io/gh/sinonjs/fake-timers/pull/400?src=pr&el=footer&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=sinonjs). Last update [a13a054...fa323c8](https://codecov.io/gh/sinonjs/fake-timers/pull/400?src=pr&el=lastupdated&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=sinonjs). Read the [comment docs](https://docs.codecov.io/docs/pull-request-comments?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=sinonjs).

itayperry: Hi @fatso83 :) I didn't edit the README file, but I still get a `prettier` error because of it - should I fix that and just make another commit?

>  [warn] README.md
fatso83: Yup, that was me editing the README in the web UI.

Patch:
diff --git a/README.md b/README.md
index 26e5f8e..c0ab900 100644
--- a/README.md
+++ b/README.md
@@ -150,14 +150,14 @@ The `loopLimit` argument sets the maximum number of timers that will be run when
 
 Installs FakeTimers using the specified config (otherwise with epoch `0` on the global scope). The following configuration options are available
 
-| Parameter                        | Type        | Default                                                                                                                                                                                                         | Description                                                                                                                                                                                                                                       |
-| -------------------------------- | ----------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
-| `config.now`                     | Number/Date | 0                                                                                                                                                                                                               | installs FakeTimers with the specified unix epoch                                                                                                                                                                                                 |
+| Parameter                        | Type        | Default                                                                                                                                                                                                                        | Description                                                                                                                                                                                                                                                                                  |
+| -------------------------------- | ----------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
+| `config.now`                     | Number/Date | 0                                                                                                                                                                                                                              | installs FakeTimers with the specified unix epoch                                                                                                                                                                                                                                            |
 | `config.toFake`                  | String[]    | ["setTimeout", "clearTimeout", "setImmediate", "clearImmediate","setInterval", "clearInterval", "Date", "requestAnimationFrame", "cancelAnimationFrame", "requestIdleCallback", "cancelIdleCallback", "hrtime", "performance"] | an array with explicit function names (or objects, in the case of "performance") to hijack. _When not set, FakeTimers will automatically fake all methods **except** `nextTick`_ e.g., `FakeTimers.install({ toFake: ["setTimeout","nextTick"]})` will fake only `setTimeout` and `nextTick` |
-| `config.loopLimit`               | Number      | 1000                                                                                                                                                                                                            | the maximum number of timers that will be run when calling runAll()                                                                                                                                                                               |
-| `config.shouldAdvanceTime`       | Boolean     | false                                                                                                                                                                                                           | tells FakeTimers to increment mocked time automatically based on the real system time shift (e.g. the mocked time will be incremented by 20ms for every 20ms change in the real system time)                                                      |
-| `config.advanceTimeDelta`        | Number      | 20                                                                                                                                                                                                              | relevant only when using with `shouldAdvanceTime: true`. increment mocked time by `advanceTimeDelta` ms every `advanceTimeDelta` ms change in the real system time.                                                                               |
-| `config.shouldClearNativeTimers` | Boolean     | false                                                                                                                                                                                                           | tells FakeTimers to clear 'native' (i.e. not fake) timers by delegating to their respective handlers. These are not cleared by default, leading to potentially unexpected behavior if timers existed prior to installing FakeTimers.              |
+| `config.loopLimit`               | Number      | 1000                                                                                                                                                                                                                           | the maximum number of timers that will be run when calling runAll()                                                                                                                                                                                                                          |
+| `config.shouldAdvanceTime`       | Boolean     | false                                                                                                                                                                                                                          | tells FakeTimers to increment mocked time automatically based on the real system time shift (e.g. the mocked time will be incremented by 20ms for every 20ms change in the real system time)                                                                                                 |
+| `config.advanceTimeDelta`        | Number      | 20                                                                                                                                                                                                                             | relevant only when using with `shouldAdvanceTime: true`. increment mocked time by `advanceTimeDelta` ms every `advanceTimeDelta` ms change in the real system time.                                                                                                                          |
+| `config.shouldClearNativeTimers` | Boolean     | false                                                                                                                                                                                                                          | tells FakeTimers to clear 'native' (i.e. not fake) timers by delegating to their respective handlers. These are not cleared by default, leading to potentially unexpected behavior if timers existed prior to installing FakeTimers.                                                         |
 
 ### `var id = clock.setTimeout(callback, timeout)`
 
diff --git a/src/fake-timers-src.js b/src/fake-timers-src.js
index e1a0a1e..90346ca 100644
--- a/src/fake-timers-src.js
+++ b/src/fake-timers-src.js
@@ -1584,20 +1584,6 @@ function withGlobal(_global) {
 
         if (performancePresent) {
             clock.performance = Object.create(null);
-
-            if (hasPerformancePrototype) {
-                const proto = _global.Performance.prototype;
-
-                Object.getOwnPropertyNames(proto).forEach(function (name) {
-                    if (name.indexOf("getEntries") === 0) {
-                        // match expected return type for getEntries functions
-                        clock.performance[name] = NOOP_ARRAY;
-                    } else {
-                        clock.performance[name] = NOOP;
-                    }
-                });
-            }
-
             clock.performance.now = function FakeTimersNow() {
                 const hrt = hrtime();
                 const millis = hrt[0] * 1000 + hrt[1] / 1e6;
@@ -1675,6 +1661,25 @@ function withGlobal(_global) {
             clock.attachedInterval = intervalId;
         }
 
+        if (clock.methods.includes("performance")) {
+            if (hasPerformancePrototype) {
+                var proto = _global.Performance.prototype;
+                Object.getOwnPropertyNames(proto).forEach(function (name) {
+                    if (name !== "now") {
+                        clock.performance[name] =
+                            name.indexOf("getEntries") === 0
+                                ? NOOP_ARRAY
+                                : NOOP;
+                    }
+                });
+            } else if (config.toFake.includes("performance")) {
+                // user explicitly tried to fake performance when not present
+                throw new ReferenceError(
+                    "non-existent performance object cannot be faked"
+                );
+            }
+        }
+
         for (i = 0, l = clock.methods.length; i < l; i++) {
             const nameOfMethodToReplace = clock.methods[i];
             if (nameOfMethodToReplace === "hrtime") {


Test patch:
diff --git a/test/fake-timers-test.js b/test/fake-timers-test.js
index 673e8de..ba7a641 100644
--- a/test/fake-timers-test.js
+++ b/test/fake-timers-test.js
@@ -3706,6 +3706,27 @@ describe("FakeTimers", function () {
             });
         }
 
+        it("throws when adding performance to tofake array when performance not present", function () {
+            assert.exception(
+                function () {
+                    const setTimeoutFake = sinon.fake();
+                    const context = {
+                        Date: Date,
+                        setTimeout: setTimeoutFake,
+                        clearTimeout: sinon.fake(),
+                        performance: undefined,
+                    };
+                    FakeTimers.withGlobal(context).install({
+                        toFake: ["performance"],
+                    });
+                },
+                {
+                    name: "ReferenceError",
+                    message: "non-existent performance object cannot be faked",
+                }
+            );
+        });
+
         if (performanceNowPresent) {
             it("replaces global performance.now", function () {
                 this.clock = FakeTimers.install();