How to enable Intl in node for tests using Jest?

Viewed 2333

I am trying to test currency formatting functions and while they work in the browser, they are not working in my test environment. I understand that intl does not come standard with node, so I have added intl and full-icu to my devDependencies, but this has not helped

var IntlPolyfill = require('intl');
require('full-icu');
Intl.NumberFormat = IntlPolyfill.NumberFormat;
Intl.DateTimeFormat = IntlPolyfill.DateTimeFormat;

expect((4000).toLocaleString('fr-ca',{style: 'currency',currency: 'CAD',currencyDisplay: 'symbol'})).toBe('4 000,00 $')

/* test output: 
 Expected: "4 000,00 $"
 Received: "CA$ 4,000.00"
*/

In IE11, Chrome and Firefox I get the expected result ("14 337,00 $") but in Node I am not.

I have found little help online, but did find reference to the 2 libraries I am using here. What do I need to do for the node environment used by Jest to have access to the proper Intl functions. The function is there, but seems to be returning the US formatting for every locale.

1 Answers

First comes my answer that immediately came into my mind and solves the problem, but it in fact is an anti-pattern - I simply leave it here so others can learn from it. The good solution follows further down in my answer:

You simply can import the node module intl into you Jest test and pass it to the JS you are testing:

import Intl from 'intl';
import CurrencyFormatter from '../../../js/modules/CurrencyFormatter.js';

describe('CurrencyFormatter', () => {
    let formatter = null;

    beforeEach(() => {
        formatter = new CurrencyFormatter(Intl);
    });

    test([...]);
});

And in your JS you conditionally use the intl module if passed by Jest:

export default class CurrencyFormatter {
    constructor(optionalIntl) {
        // Needed for Jest, because it doesn't support Intl natively:
        if (optionalIntl != null) {
            global.Intl = optionalIntl;
        }
        [...]
    }

    [...]
}

This way you keep the superfluous intl module out of your production code and make it available to Jest for tests only. But as you can clearly see, this means adjusting the production code for the sole purpose of satisfying the test's needs. This is called an anti-pattern. The production code should always serve production needs (and readability/understandability for other devs, ofc), not the testing environment.

So for that reason you should rather follow the second solution, which adidtionally has the advantage of fixing the issue for all similar tests in the suite and of using much less code changes. In fact it does not change the code but only the configuration:

[Edit:] As a colleague mentioned, this indeed is an anti-pattern - do not adapt your production code in order to pass the test. Yet, there is a better option, using the node module full-icu by passing it to node in the scripts entry for jest, i.e. do something like this in your package.json:

[...]
"scripts": {
    "test": "node --icu-data-dir=node_modules/full-icu node_modules/jest/bin/jest.js --version --config=./jest.config.js"
},
[...]
Related