'jest is failing to find `bota` and `atob`

Creating a jest test like:

test("btoa", () => {
  expect(btoa("aaa")).toStrictEqual("YWFh");
});

fails with

ReferenceError: btoa is not defined

however, node does define btoa as of node 16, and so the following:

console.log(bota("aaa"))

correctly outputs YWFh.

How can I configure jest to get this test to pass? Clearly something is happening in the jest test runner to not execute in the current node environment, or otherwise is stripping specific built-ins, only I can't seem to find any documentation on how to debug or adjust this.

Update

There are work arounds by writing the encoding manually in "pure js" or depending on something that's similar, but I'm particularly interested in why the jest execution ending is failing to find built-ins that seem to be present in other environments.

This also works fine in other testing frameworks like mocha, so it's clearly related to the jest runner in particular.



Solution 1:[1]

Update

After much searching and head scratching as to why btoa/atob are available in node but NOT available in jest running in node, I finally figured it out. Jest runs all tests inside a vm, which is an isolated sandbox environment. The btoa/atob methods are not automatically exposed on the global object inside of a VM. Best explained by example:

const vm = require('vm');

// this works outside the vm - but for legacy reasons only
// you shouldn't be doing this in the first place
btoa('aaa'); // -> "YWFh"

const context = vm.createContext({});
const code = 'btoa("aaa")';
vm.runInContext(code, context); //-> Uncaught ReferenceError: btoa is not defined

Note: The answer described below is still the "solution" - you need to define these methods for use in node, and then you need to expose them using jest's globalSetup.


Original answer

The root of your problem is the fact that NodeJS and web browsers have different APIs. For example, I get this deprecation notice when I try to use btoa in my node application.

btoa deprecation message

The first part of the solution is that you need to provide your own atob/btoa methods for use in NodeJs (see examples here). Then you need to make these available using jest's globalSetup config:

/** Encodes a string as base64 format */
global.btoa = (str: string) => Buffer.from(str, 'binary').toString('base64');

/** Decodes a base64 encoded string */
global.atob = (str: string) => Buffer.from(str, 'base64').toString('binary');

If you don't feel comfortable doing this yourself, there are libraries and tools out there that do it for you (jsdom, phantomjs, testing-library). These libraries essentially replicate the browser APIs in a node environment for doing things like running tests, server-side rendering, etc. I recommend reading about testing web frameworks for code examples and techniques.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1