Skip to content

Commit

Permalink
feat: mock intl api (#474)
Browse files Browse the repository at this point in the history
* feat: mock intl api

* chore: changelog

* fix: Intl tests

Update author info

---------

Co-authored-by: Benjamin Isenstein <ben@Benjamins-MacBook-Pro.local>
  • Loading branch information
BenIsenstein and Benjamin Isenstein committed Aug 7, 2023
1 parent 4203265 commit 871f5c8
Show file tree
Hide file tree
Showing 3 changed files with 171 additions and 1 deletion.
1 change: 0 additions & 1 deletion CHANGELOG.md
@@ -1,4 +1,3 @@

11.0.0 / 2023-06-12
==================
* Re-release 10.2.0 as a new major version as mocking Node "timers" module broke some setups
Expand Down
49 changes: 49 additions & 0 deletions src/fake-timers-src.js
Expand Up @@ -186,6 +186,7 @@ function withGlobal(_global) {
typeof _global.cancelIdleCallback === "function";
const setImmediatePresent =
_global.setImmediate && typeof _global.setImmediate === "function";
const intlPresent = _global.Intl && typeof _global.Intl === "object";

// Make properties writable in IE, as per
// https://www.adequatelygood.com/Replacing-setTimeout-Globally.html
Expand All @@ -204,11 +205,13 @@ function withGlobal(_global) {
_global.setImmediate = _global.setImmediate;
_global.clearImmediate = _global.clearImmediate;
}

/* eslint-enable no-self-assign */

_global.clearTimeout(timeoutResult);

const NativeDate = _global.Date;
const NativeIntl = _global.Intl;
let uniqueTimerId = idCounterStart;

/**
Expand Down Expand Up @@ -500,6 +503,40 @@ function withGlobal(_global) {
return mirrorDateProperties(ClockDate, NativeDate);
}

//eslint-disable-next-line jsdoc/require-jsdoc
function createIntl() {
const ClockIntl = { ...NativeIntl };

ClockIntl.DateTimeFormat = function (...args) {
const realFormatter = new NativeIntl.DateTimeFormat(...args);
const formatter = {};

["formatRange", "formatRangeToParts", "resolvedOptions"].forEach(
(method) => {
formatter[method] =
realFormatter[method].bind(realFormatter);
}
);

["format", "formatToParts"].forEach((method) => {
formatter[method] = function (date) {
return realFormatter[method](date || ClockIntl.clock.now);
};
});

return formatter;
};

ClockIntl.DateTimeFormat.prototype = Object.create(
NativeIntl.DateTimeFormat.prototype
);

ClockIntl.DateTimeFormat.supportedLocalesOf =
NativeIntl.DateTimeFormat.supportedLocalesOf;

return ClockIntl;
}

//eslint-disable-next-line jsdoc/require-jsdoc
function enqueueJob(clock, job) {
// enqueues a microtick-deferred task - ecma262/#sec-enqueuejob
Expand Down Expand Up @@ -934,6 +971,8 @@ function withGlobal(_global) {
if (method === "Date") {
const date = mirrorDateProperties(clock[method], target[method]);
target[method] = date;
} else if (method === "Intl") {
target[method] = clock[method];
} else if (method === "performance") {
const originalPerfDescriptor = Object.getOwnPropertyDescriptor(
target,
Expand Down Expand Up @@ -988,6 +1027,7 @@ function withGlobal(_global) {
* @property {setInterval} setInterval
* @property {clearInterval} clearInterval
* @property {Date} Date
* @property {Intl} Intl
* @property {SetImmediate=} setImmediate
* @property {function(NodeImmediate): void=} clearImmediate
* @property {function(number[]):number[]=} hrtime
Expand Down Expand Up @@ -1046,6 +1086,10 @@ function withGlobal(_global) {
timers.cancelIdleCallback = _global.cancelIdleCallback;
}

if (intlPresent) {
timers.Intl = _global.Intl;
}

const originalSetTimeout = _global.setImmediate || _global.setTimeout;

/**
Expand Down Expand Up @@ -1124,6 +1168,11 @@ function withGlobal(_global) {
};
}

if (intlPresent) {
clock.Intl = createIntl();
clock.Intl.clock = clock;
}

clock.requestIdleCallback = function requestIdleCallback(
func,
timeout
Expand Down
122 changes: 122 additions & 0 deletions test/fake-timers-test.js
Expand Up @@ -5179,3 +5179,125 @@ describe("Node Timer: ref(), unref(),hasRef()", function () {
clock.uninstall();
});
});

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}
*/
function isFirstOfMonth(ianaTimeZone, timestamp) {
return (
new Intl.DateTimeFormat(undefined, { timeZone: ianaTimeZone })
.formatToParts(timestamp)
.find((part) => part.type === "day").value === "1"
);
}

let clock;

before(function () {
clock = FakeTimers.install();
});

after(function () {
clock.uninstall();
});

it("Executes formatRange like normal", function () {
const start = new Date(Date.UTC(2020, 0, 1, 0, 0));
const end = new Date(Date.UTC(2020, 0, 1, 0, 1));
const options = {
timeZone: "UTC",
hour12: false,
hour: "numeric",
minute: "numeric",
};
assert.equals(
new Intl.DateTimeFormat("en-GB", options).formatRange(start, end),
"00:00–00:01"
);
});

it("Executes formatRangeToParts like normal", function () {
const start = new Date(Date.UTC(2020, 0, 1, 0, 0));
const end = new Date(Date.UTC(2020, 0, 1, 0, 1));
const options = {
timeZone: "UTC",
hour12: false,
hour: "numeric",
minute: "numeric",
};
assert.equals(
new Intl.DateTimeFormat("en-GB", options).formatRangeToParts(
start,
end
),
[
{ type: "hour", value: "00", source: "startRange" },
{ type: "literal", value: ":", source: "startRange" },
{ type: "minute", value: "00", source: "startRange" },
{ type: "literal", value: "–", source: "shared" },
{ type: "hour", value: "00", source: "endRange" },
{ type: "literal", value: ":", source: "endRange" },
{ type: "minute", value: "01", source: "endRange" },
]
);
});

it("Executes resolvedOptions like normal", function () {
const options = {
timeZone: "UTC",
hour12: false,
hour: "2-digit",
minute: "2-digit",
};
assert.equals(
new Intl.DateTimeFormat("en-GB", options).resolvedOptions(),
{
locale: "en-GB",
calendar: "gregory",
numberingSystem: "latn",
timeZone: "UTC",
hour12: false,
hourCycle: "h23",
hour: "2-digit",
minute: "2-digit",
}
);
});

it("formatToParts via isFirstOfMonth -> Returns true when passed a timestamp argument that is first of the month", function () {
// June 1 04:00 UTC - Toronto is June 1 00:00
assert.isTrue(
isFirstOfMonth("America/Toronto", Date.UTC(2022, 5, 1, 4))
);
});

it("formatToParts via isFirstOfMonth -> Returns false when passed a timestamp argument that is not first of the month", function () {
// June 1 00:00 UTC - Toronto is May 31 20:00
assert.isFalse(isFirstOfMonth("America/Toronto", Date.UTC(2022, 5, 1)));
});

it("formatToParts via isFirstOfMonth -> Returns true when passed no timestamp and system time is first of the month", function () {
// June 1 04:00 UTC - Toronto is June 1 00:00
clock.now = Date.UTC(2022, 5, 1, 4);
assert.isTrue(isFirstOfMonth("America/Toronto"));
});

it("formatToParts via isFirstOfMonth -> Returns false when passed no timestamp and system time is not first of the month", function () {
// June 1 00:00 UTC - Toronto is May 31 20:00
clock.now = Date.UTC(2022, 5, 1);
assert.isFalse(isFirstOfMonth("America/Toronto"));
});

it("Executes supportedLocalesOf like normal", function () {
assert.equals(
Intl.DateTimeFormat.supportedLocalesOf(),
//eslint-disable-next-line no-underscore-dangle
clock._Intl.DateTimeFormat.supportedLocalesOf()
);
});
});

0 comments on commit 871f5c8

Please sign in to comment.