Repository: sinonjs/fake-timers
Pull request #479 (2023-08-22 13:15:04)

feat: add RelativeTimeFormat to Intl API
#### Purpose (TL;DR) - mandatory

PR #474 has introduced a mock for the intl API, and this has introduced a regression for me, where I'm not able to call `Intl.RelativeTimeFormat`.

#### Background (Problem in detail)
The problem showed in some of my test cases, once I updated `fake-timers`, when trying to use `Intl.RelativeTimeFormat` I got the following error: `Intl.RelativeTimeFormat is not a constructor`

#### Solution
The solution I opted for is to assign `RelativeTimeFormat` to the mocked `Intl` API with.
```js
ClockIntl.RelativeTimeFormat = NativeIntl.RelativeTimeFormat;
```

codecov[bot]: ## [Codecov](https://app.codecov.io/gh/sinonjs/fake-timers/pull/479?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=sinonjs) Report
Patch coverage: **`100.00%`** and no project coverage change.
> Comparison is base [(`fc312b9`)](https://app.codecov.io/gh/sinonjs/fake-timers/commit/fc312b9ce96a4ea2c7e60bb0cccd2c604b75cdbd?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=sinonjs) 96.47% compared to head [(`ee1f0bd`)](https://app.codecov.io/gh/sinonjs/fake-timers/pull/479?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=sinonjs) 96.47%.

> :exclamation: Current head ee1f0bd differs from pull request most recent head f4469bf. Consider uploading reports for the commit f4469bf to get more accurate results

<details><summary>Additional details and impacted files</summary>


```diff
@@           Coverage Diff           @@
##             main     #479   +/-   ##
=======================================
  Coverage   96.47%   96.47%           
=======================================
  Files           2        2           
  Lines         680      681    +1     
=======================================
+ Hits          656      657    +1     
  Misses         24       24           
```

| [Flag](https://app.codecov.io/gh/sinonjs/fake-timers/pull/479/flags?src=pr&el=flags&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=sinonjs) | Coverage Δ | |
|---|---|---|
| [unit](https://app.codecov.io/gh/sinonjs/fake-timers/pull/479/flags?src=pr&el=flag&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=sinonjs) | `96.47% <100.00%> (+<0.01%)` | :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.

| [Files Changed](https://app.codecov.io/gh/sinonjs/fake-timers/pull/479?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://app.codecov.io/gh/sinonjs/fake-timers/pull/479?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=sinonjs#diff-c3JjL2Zha2UtdGltZXJzLXNyYy5qcw==) | `96.49% <100.00%> (+<0.01%)` | :arrow_up: |


</details>

[:umbrella: View full report in Codecov by Sentry](https://app.codecov.io/gh/sinonjs/fake-timers/pull/479?src=pr&el=continue&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=sinonjs).   
:loudspeaker: Have feedback on the report? [Share it here](https://about.codecov.io/codecov-pr-comment-feedback/?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=sinonjs).

sebastinez: Thanks for your reviews, what are the next steps?
Do you need me to sign something or can the PR get merged?
sebastinez: Hey folks, this issue doesn't allow me to update fake-timers, since it would break my production.
Is there work be done on a better solution? Or is anything holding this PR up?
sebastinez: Didn't want to close the PR, just comment on it
SimenB: Since there's no compilation step in this repo, you can also depend on your fork directly

Patch:
diff --git a/src/fake-timers-src.js b/src/fake-timers-src.js
index 607336d..11b587d 100644
--- a/src/fake-timers-src.js
+++ b/src/fake-timers-src.js
@@ -481,9 +481,24 @@ function withGlobal(_global) {
         return mirrorDateProperties(ClockDate, NativeDate);
     }
 
-    //eslint-disable-next-line jsdoc/require-jsdoc
+    /**
+     * Mirror Intl by default on our fake implementation
+     *
+     * 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() {
-        const ClockIntl = { ...NativeIntl };
+        const ClockIntl = {};
+        /*
+         * All properties of Intl are non-enumerable, so we need
+         * to do a bit of work to get them out.
+         */
+        Object.getOwnPropertyNames(NativeIntl).forEach(
+            (property) => (ClockIntl[property] = NativeIntl[property])
+        );
 
         ClockIntl.DateTimeFormat = function (...args) {
             const realFormatter = new NativeIntl.DateTimeFormat(...args);
@@ -1133,6 +1148,17 @@ function withGlobal(_global) {
             return [secsSinceStart, remainderInNanos];
         }
 
+        /**
+         * A high resolution timestamp in milliseconds.
+         *
+         * @typedef {number} DOMHighResTimeStamp
+         */
+
+        /**
+         * performance.now()
+         *
+         * @returns {DOMHighResTimeStamp}
+         */
         function fakePerformanceNow() {
             const hrt = hrtime();
             const millis = hrt[0] * 1000 + hrt[1] / 1e6;


Test patch:
diff --git a/test/fake-timers-test.js b/test/fake-timers-test.js
index c0ae9af..149ec9c 100644
--- a/test/fake-timers-test.js
+++ b/test/fake-timers-test.js
@@ -4688,7 +4688,7 @@ describe("FakeTimers", function () {
          * @template E
          * @param {E[]} [list1]
          * @param {E[]} [list2]
-         * @return {E[]}
+         * @returns {E[]}
          */
         function getIntersection(list1, list2) {
             return list1.filter((value) => list2.indexOf(value) !== -1);
@@ -4699,7 +4699,7 @@ describe("FakeTimers", function () {
          *
          * @function
          * @param {string[]} [toFake]
-         * @return {{propertyName: string, originalValue: any}[]}
+         * @returns {{propertyName: string, originalValue: any}[]}
          */
         function getOriginals(toFake) {
             return toFake.map((propertyName) => ({
@@ -5300,4 +5300,15 @@ describe("Intl API", function () {
             clock._Intl.DateTimeFormat.supportedLocalesOf()
         );
     });
+
+    it("Creates a RelativeTimeFormat like normal", function () {
+        if (typeof Intl?.RelativeTimeFormat === "undefined") {
+            this.skip();
+        }
+
+        const rtf = new Intl.RelativeTimeFormat("en-GB", {
+            numeric: "auto",
+        });
+        assert.equals(rtf.format(2, "day"), "in 2 days");
+    });
 });