Reputation: 2502
I would like to test a filter function I wrote which return a date formatted using Intl.DateTimeFormat('en-GB', options):
// module "date.js"
export default function (dateISOString) {
const options = {
year: 'numeric',
month: '2-digit',
day: '2-digit',
timeZone: 'UTC'
};
let d = new Date(dateISOString);
return new Intl.DateTimeFormat('en-GB', options).format(d);
};
This is my test, using Jest:
import date from '@/filters/date';
describe('date', () => {
it('should format the date into dd/mm/yyyy', () => {
expect(date('2014-02-11')).toEqual('11/02/2014');
});
});
but it fails with:
Expected value to equal:
"11/02/2014"
Received:
"02/11/2014"
Is it possible to test (or mock) the Intl API with Jest? It looks like the problem is due to a different behaviour of the Impl API in the browser and in a Node environment.
Upvotes: 25
Views: 17275
Reputation: 3795
These are all convoluted answers. The simplest is extending the prototype like so in jest. Modify any property or function with define property
Object.defineProperty(Intl.DateTimeFormat.prototype, 'formatToParts', {
value: () => ['day', 'month', 'year'].map((type) => ({ type })),
})
Upvotes: 0
Reputation: 608
Just found a solution if you want to change the zone per test.
The benefit here is minimal change to the Intl object since we are just using the normal api to set a default
const timezoneMock = function(zone: string) {
const DateTimeFormat = Intl.DateTimeFormat
jest
.spyOn(global.Intl, 'DateTimeFormat')
.mockImplementation((locale, options) => new DateTimeFormat(locale, {...options, timeZone: zone}))
}
afterEach(() => {
jest.restoreAllMocks();
})
and then
describe('when Europe/London', () => {
it('returns local time', () => {
timezoneMock('Europe/London')
//etc....
Upvotes: 11
Reputation: 443
In package.json add the intl
package
"intl": "*",
In the jest.config.js
module.exports = {
moduleNameMapper: {
Intl: '<rootDir>/node_modules/intl/'
}
};
Then in Date.spec.js
describe(`Date by locale`, () => {
beforeAll(() => { global.Intl = require('intl'); });
// Add your tests here.
// Add a temporary console.log to verify the correct output
}
Upvotes: 2
Reputation: 51
You can use polyfill, as described here
import IntlPolyfill from 'intl';
import 'intl/locale-data/jsonp/ru';
if (global.Intl) {
Intl.NumberFormat = IntlPolyfill.NumberFormat;
Intl.DateTimeFormat = IntlPolyfill.DateTimeFormat;
} else {
global.Intl = IntlPolyfill;
}
Upvotes: 3
Reputation: 2189
The only solution that I managed to find to this problem was to install full-icu which seemed to provide the right locales to node during the testing process.
That package is needed because node by default only ships with a limited set of locales, explained here: https://nodejs.org/docs/latest-v9.x/api/intl.html
With that package installed, the additional step that I had to take was to change my test command to use:
"test": "NODE_ICU_DATA=node_modules/full-icu jest --config jest.config.js"
I ran into this problem in a couple of different environments. When running the tests locally on Mac OS and also when running the tests inside a Docker container during CI.
Interestingly, I don't need to use the export when running the tests via WebStorm's Jest integration. It definitely seems like the behaviour of the Intl library is far from stable in node.
Upvotes: 18