3

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.

Erik
  • 6,470
  • 5
  • 36
  • 37

1 Answers1

4

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.

Ryan Wheale
  • 26,022
  • 8
  • 76
  • 96
  • The problem is that I want to use them explicitly for web compatibility, so replicating them with node APIs isn't helpful. – Erik Feb 02 '22 at 20:18
  • If you're going to run web tests in a node environment, it's the only way sadly. You can set these up using jests [`globalSetup`](https://jestjs.io/docs/configuration#globalsetup-string) config. You can also read about [testing web frameworks](https://jestjs.io/docs/testing-frameworks) - most of which use some sort of technique for shimming the node environment with web APIs (phantomjs, jsdom, testing-library, et al). – Ryan Wheale Feb 03 '22 at 19:43
  • Why is this the only way? Other testing environments like mocha work fine, as does node itself. If this weren't defined in node, I'd understand, what I don't understand is why jest is selectively removing certain built-ins. – Erik Feb 04 '22 at 17:44
  • 1
    I found out why - see the updated answer. – Ryan Wheale Feb 04 '22 at 22:58
  • It's still weird to me that those specific symbols aren't exposed in the vm, but this does explain it! Thanks for all the digging! – Erik Feb 05 '22 at 18:07
  • 2
    Thank you so much for this answer! I had been looking for so long as to why some of my Jest tests were failing and actually there aren't any proper answers on the Internet that actually describe the root cause – Devansu Yadav Apr 30 '22 at 15:02