Skip to content

Commit 871f5c8

Browse files
BenIsensteinBenjamin Isenstein
and
Benjamin Isenstein
authored
feat: mock intl api (#474)
* feat: mock intl api * chore: changelog * fix: Intl tests Update author info --------- Co-authored-by: Benjamin Isenstein <[email protected]>
1 parent 4203265 commit 871f5c8

File tree

3 files changed

+171
-1
lines changed

3 files changed

+171
-1
lines changed

CHANGELOG.md

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
21
11.0.0 / 2023-06-12
32
==================
43
* Re-release 10.2.0 as a new major version as mocking Node "timers" module broke some setups

src/fake-timers-src.js

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -186,6 +186,7 @@ function withGlobal(_global) {
186186
typeof _global.cancelIdleCallback === "function";
187187
const setImmediatePresent =
188188
_global.setImmediate && typeof _global.setImmediate === "function";
189+
const intlPresent = _global.Intl && typeof _global.Intl === "object";
189190

190191
// Make properties writable in IE, as per
191192
// https://www.adequatelygood.com/Replacing-setTimeout-Globally.html
@@ -204,11 +205,13 @@ function withGlobal(_global) {
204205
_global.setImmediate = _global.setImmediate;
205206
_global.clearImmediate = _global.clearImmediate;
206207
}
208+
207209
/* eslint-enable no-self-assign */
208210

209211
_global.clearTimeout(timeoutResult);
210212

211213
const NativeDate = _global.Date;
214+
const NativeIntl = _global.Intl;
212215
let uniqueTimerId = idCounterStart;
213216

214217
/**
@@ -500,6 +503,40 @@ function withGlobal(_global) {
500503
return mirrorDateProperties(ClockDate, NativeDate);
501504
}
502505

506+
//eslint-disable-next-line jsdoc/require-jsdoc
507+
function createIntl() {
508+
const ClockIntl = { ...NativeIntl };
509+
510+
ClockIntl.DateTimeFormat = function (...args) {
511+
const realFormatter = new NativeIntl.DateTimeFormat(...args);
512+
const formatter = {};
513+
514+
["formatRange", "formatRangeToParts", "resolvedOptions"].forEach(
515+
(method) => {
516+
formatter[method] =
517+
realFormatter[method].bind(realFormatter);
518+
}
519+
);
520+
521+
["format", "formatToParts"].forEach((method) => {
522+
formatter[method] = function (date) {
523+
return realFormatter[method](date || ClockIntl.clock.now);
524+
};
525+
});
526+
527+
return formatter;
528+
};
529+
530+
ClockIntl.DateTimeFormat.prototype = Object.create(
531+
NativeIntl.DateTimeFormat.prototype
532+
);
533+
534+
ClockIntl.DateTimeFormat.supportedLocalesOf =
535+
NativeIntl.DateTimeFormat.supportedLocalesOf;
536+
537+
return ClockIntl;
538+
}
539+
503540
//eslint-disable-next-line jsdoc/require-jsdoc
504541
function enqueueJob(clock, job) {
505542
// enqueues a microtick-deferred task - ecma262/#sec-enqueuejob
@@ -934,6 +971,8 @@ function withGlobal(_global) {
934971
if (method === "Date") {
935972
const date = mirrorDateProperties(clock[method], target[method]);
936973
target[method] = date;
974+
} else if (method === "Intl") {
975+
target[method] = clock[method];
937976
} else if (method === "performance") {
938977
const originalPerfDescriptor = Object.getOwnPropertyDescriptor(
939978
target,
@@ -988,6 +1027,7 @@ function withGlobal(_global) {
9881027
* @property {setInterval} setInterval
9891028
* @property {clearInterval} clearInterval
9901029
* @property {Date} Date
1030+
* @property {Intl} Intl
9911031
* @property {SetImmediate=} setImmediate
9921032
* @property {function(NodeImmediate): void=} clearImmediate
9931033
* @property {function(number[]):number[]=} hrtime
@@ -1046,6 +1086,10 @@ function withGlobal(_global) {
10461086
timers.cancelIdleCallback = _global.cancelIdleCallback;
10471087
}
10481088

1089+
if (intlPresent) {
1090+
timers.Intl = _global.Intl;
1091+
}
1092+
10491093
const originalSetTimeout = _global.setImmediate || _global.setTimeout;
10501094

10511095
/**
@@ -1124,6 +1168,11 @@ function withGlobal(_global) {
11241168
};
11251169
}
11261170

1171+
if (intlPresent) {
1172+
clock.Intl = createIntl();
1173+
clock.Intl.clock = clock;
1174+
}
1175+
11271176
clock.requestIdleCallback = function requestIdleCallback(
11281177
func,
11291178
timeout

test/fake-timers-test.js

Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5179,3 +5179,125 @@ describe("Node Timer: ref(), unref(),hasRef()", function () {
51795179
clock.uninstall();
51805180
});
51815181
});
5182+
5183+
describe("Intl API", function () {
5184+
/**
5185+
* Tester function to check if the globally hijacked Intl object is plugging into the faked Clock
5186+
*
5187+
* @param {string} ianaTimeZone - IANA time zone name
5188+
* @param {number} timestamp - UNIX timestamp
5189+
* @returns {boolean}
5190+
*/
5191+
function isFirstOfMonth(ianaTimeZone, timestamp) {
5192+
return (
5193+
new Intl.DateTimeFormat(undefined, { timeZone: ianaTimeZone })
5194+
.formatToParts(timestamp)
5195+
.find((part) => part.type === "day").value === "1"
5196+
);
5197+
}
5198+
5199+
let clock;
5200+
5201+
before(function () {
5202+
clock = FakeTimers.install();
5203+
});
5204+
5205+
after(function () {
5206+
clock.uninstall();
5207+
});
5208+
5209+
it("Executes formatRange like normal", function () {
5210+
const start = new Date(Date.UTC(2020, 0, 1, 0, 0));
5211+
const end = new Date(Date.UTC(2020, 0, 1, 0, 1));
5212+
const options = {
5213+
timeZone: "UTC",
5214+
hour12: false,
5215+
hour: "numeric",
5216+
minute: "numeric",
5217+
};
5218+
assert.equals(
5219+
new Intl.DateTimeFormat("en-GB", options).formatRange(start, end),
5220+
"00:00–00:01"
5221+
);
5222+
});
5223+
5224+
it("Executes formatRangeToParts like normal", function () {
5225+
const start = new Date(Date.UTC(2020, 0, 1, 0, 0));
5226+
const end = new Date(Date.UTC(2020, 0, 1, 0, 1));
5227+
const options = {
5228+
timeZone: "UTC",
5229+
hour12: false,
5230+
hour: "numeric",
5231+
minute: "numeric",
5232+
};
5233+
assert.equals(
5234+
new Intl.DateTimeFormat("en-GB", options).formatRangeToParts(
5235+
start,
5236+
end
5237+
),
5238+
[
5239+
{ type: "hour", value: "00", source: "startRange" },
5240+
{ type: "literal", value: ":", source: "startRange" },
5241+
{ type: "minute", value: "00", source: "startRange" },
5242+
{ type: "literal", value: "–", source: "shared" },
5243+
{ type: "hour", value: "00", source: "endRange" },
5244+
{ type: "literal", value: ":", source: "endRange" },
5245+
{ type: "minute", value: "01", source: "endRange" },
5246+
]
5247+
);
5248+
});
5249+
5250+
it("Executes resolvedOptions like normal", function () {
5251+
const options = {
5252+
timeZone: "UTC",
5253+
hour12: false,
5254+
hour: "2-digit",
5255+
minute: "2-digit",
5256+
};
5257+
assert.equals(
5258+
new Intl.DateTimeFormat("en-GB", options).resolvedOptions(),
5259+
{
5260+
locale: "en-GB",
5261+
calendar: "gregory",
5262+
numberingSystem: "latn",
5263+
timeZone: "UTC",
5264+
hour12: false,
5265+
hourCycle: "h23",
5266+
hour: "2-digit",
5267+
minute: "2-digit",
5268+
}
5269+
);
5270+
});
5271+
5272+
it("formatToParts via isFirstOfMonth -> Returns true when passed a timestamp argument that is first of the month", function () {
5273+
// June 1 04:00 UTC - Toronto is June 1 00:00
5274+
assert.isTrue(
5275+
isFirstOfMonth("America/Toronto", Date.UTC(2022, 5, 1, 4))
5276+
);
5277+
});
5278+
5279+
it("formatToParts via isFirstOfMonth -> Returns false when passed a timestamp argument that is not first of the month", function () {
5280+
// June 1 00:00 UTC - Toronto is May 31 20:00
5281+
assert.isFalse(isFirstOfMonth("America/Toronto", Date.UTC(2022, 5, 1)));
5282+
});
5283+
5284+
it("formatToParts via isFirstOfMonth -> Returns true when passed no timestamp and system time is first of the month", function () {
5285+
// June 1 04:00 UTC - Toronto is June 1 00:00
5286+
clock.now = Date.UTC(2022, 5, 1, 4);
5287+
assert.isTrue(isFirstOfMonth("America/Toronto"));
5288+
});
5289+
5290+
it("formatToParts via isFirstOfMonth -> Returns false when passed no timestamp and system time is not first of the month", function () {
5291+
// June 1 00:00 UTC - Toronto is May 31 20:00
5292+
clock.now = Date.UTC(2022, 5, 1);
5293+
assert.isFalse(isFirstOfMonth("America/Toronto"));
5294+
});
5295+
5296+
it("Executes supportedLocalesOf like normal", function () {
5297+
assert.equals(
5298+
Intl.DateTimeFormat.supportedLocalesOf(),
5299+
//eslint-disable-next-line no-underscore-dangle
5300+
clock._Intl.DateTimeFormat.supportedLocalesOf()
5301+
);
5302+
});
5303+
});

0 commit comments

Comments
 (0)