88

When testing a function that uses either the TextEncoder or the TextDecoder I get:

ReferenceError: TextEncoder is not defined
ReferenceError: TextDecoder is not defined

I am using jsdom, so why is this not working?

10 Answers 10

156

While it should be bundled with jsdom, it isn't with jsdom 16. Therefore you can polyfill like so:

import { TextEncoder, TextDecoder } from 'util';

Object.assign(global, { TextDecoder, TextEncoder });

You will have to add that to the test or to a setupFile like setup.jest.ts.

Sign up to request clarification or add additional context in comments.

3 Comments

Where should this be added? I added it to the file that housing my failing test I get this error under global.TextDecoder Type 'typeof TextDecoder' is not assignable to type '{ new (label?: string | undefined, options?: TextDecoderOptions | undefined): TextDecoder; prototype: TextDecoder; }'
@dragonfly02 You will have to add that to the test or to a setupFile
I get TypeError: Cannot assign to read only property 'TextDecoder' of object '[object global]'
41

I received this error as well and am using the standard Next.js jest and react testing library test setup outlined in the docs here: https://nextjs.org/docs/testing#setting-up-jest-with-the-rust-compiler.

In particular, it uses the testEnvironment: 'jest-environment-jsdom' test environment in jest.config.js configuration file.

Unfortunately, importing jsdom in one of my backend api routes (Express routes) broke my tests, giving me the TextEncoder is not defined error.

I was able to fix it by adding the following to the top of the file that housed the broken test:

/**
 * @jest-environment node
 */
// my-broken-node-only-test.js

Read more about this technique via the jest docs.

Lastly, the following issue comment by Jest maintainer Simen helped clarify what was happening in my case: https://github.com/jsdom/jsdom/issues/2524#issuecomment-902027138

6 Comments

Note that in the specific case where this is caused by indirectly by isomorphic-fetch (which in turn needs whatwg-url) you can probably get away with just mocking isomorphic-fetch: js jest.mock('isomorphic-fetch', () => () => Promise.resolve({ json: () => ({}), }) );
This saved my bacon today, decided to mark my whole library as a node env testEnvironment: 'node',.
This doesn't work if you need to say access the 'window' object in your test file.
@scottseeker I believe you need to mock the window object for that. Here's a question/answer combo with solutions for that: stackoverflow.com/questions/41885841/…
@GeoffLangenderfer Yes, I believe that to be the case. These should be nodejs only tests.
|
6

To add to @leonheess's answer in TypeScript:

Add to the top of your testfile (in which the error occurs, before the line that causes the error):

import { TextEncoder, TextDecoder } from 'util'
global.TextEncoder = TextEncoder
// @ts-expect-error
global.TextDecoder = TextDecoder

Even when trying nicely, e.g.

import { TextEncoder, TextDecoder } from 'util'
global.TextEncoder = TextEncoder
global.TextDecoder = { prototype: TextDecoder }

I got errors like

Type 'typeof TextDecoder' is missing the following properties from type 'TextDecoder': decode, encoding, fatal, ignoreBOMts(2739)

Or with

global.TextDecoder = {prototype: new TextDecoder("utf-8")}

I get

Type 'import("util").TextDecoder' is not assignable to type 'TextDecoder'.
  Types of property 'decode' are incompatible.
    Type '(input?: ArrayBuffer | ArrayBufferView | null | undefined, options?: { stream?: boolean | undefined; } | undefined) => string' is not assignable to type '(input?: BufferSource | undefined, options?: TextDecodeOptions | undefined) => string'.
      Types of parameters 'input' and 'input' are incompatible.
        Type 'BufferSource | undefined' is not assignable to type 'ArrayBuffer | ArrayBufferView | null | undefined'.
          Type 'ArrayBufferView' is not assignable to type 'ArrayBuffer | ArrayBufferView | null | undefined'.
            Type 'ArrayBufferView' is missing the following properties from type 'DataView': getFloat32, getFloat64, getInt8, getInt16, and 17 more.

So better to just assign and ignore the incompatibilities.

import { TextEncoder, TextDecoder } from 'util'
global.TextEncoder = TextEncoder
// @ts-expect-error
global.TextDecoder = TextDecoder

EDIT: I know this is ugly, but found no other way.

Comments

3

There is a package that fixes this for anyone having this issue.

npm install jest-fixed-jsdom --save-dev

yarn add jest-fixed-jsdom --dev

Then in your jest.config.js|ts file you add this config below likely replacing jsdom

module.exports = {
  testEnvironment: 'jest-fixed-jsdom',
}

There some more information about this here Request/Response/TextEncoder is not defined (Jest)

1 Comment

Worked for me. Note that if you prefix your test file with the jest environment comment (like this: @jest-environment jsdom), it will override the testEnvironment specified in config and it won't take effect, so make sure you remove that.
2

Fix for ReferenceError: TextEncoder is not defined with Cheerio and Jest

If you're seeing this error when running Jest tests:

ReferenceError: TextEncoder is not defined

With a stack trace like:

at Object.<anonymous> (node_modules/undici/lib/web/fetch/data-url.js:5:17)
...
at Object.<anonymous> (node_modules/cheerio/...)

The issue is caused by newer versions of cheerio (v2+) which use undici under the hood. undici depends on TextEncoder, which isn't available in Jest's Node environment by default.

Solution

Install a version of Cheerio that doesn't rely on undici:

npm install [email protected]

This version avoids the TextEncoder issue and works properly with Jest without needing polyfills or Node 18+.

Comments

1

jest.setup.ts

import { TextEncoder, TextDecoder } from 'util';

// Polyfill for TextEncoder
if (typeof global.TextEncoder === 'undefined') {
  global.TextEncoder = TextEncoder;
}
if (typeof window.TextEncoder === 'undefined') {
  window.TextEncoder = global.TextEncoder;
}

// Polyfill for TextDecoder
if (typeof global.TextDecoder === 'undefined') {
  // eslint-disable-next-line @typescript-eslint/ban-ts-comment
  // @ts-expect-error
  global.TextDecoder = TextDecoder;
}
if (typeof window.TextDecoder === 'undefined') {
  window.TextDecoder = global.TextDecoder;
}

console.log('Polyfill for TextEncoder/TextDecoder loaded.');

jest.config.ts Add the following property in the config object:

setupFiles: ['<rootDir>/jest.setup.ts']

1 Comment

Please provide some text, not just code. In addition to that, this answer has already been given more or less, so does not add a lot of value. I recommend to delete it.
1

I fixed this issue by using 'jest-fixed-jsdom' environment instead of 'jest-environment-jsdom'. This commonly happens when using 'jest-environment-jsdom' because it intentionally replaces built-in APIs with polyfills, breaking their Node.js compatibility.

You can solve this by doing the following: Terminal:

npm i -D jest-fixed-jsdom

Replacing code in your Jest config file (jest.config.js) from

module.exports = config;

to:

module.exports = { testEnvironment: 'jest-fixed-jsdom' }

Reference: https://mswjs.io/docs/faq/#requestresponsetextencoder-is-not-defined-jest

Comments

1

Why does this occur while using Jest and js-dom?

When using testEnvironment: 'jsdom', Jest uses the JSDOM environment, which doesn't inherit Node’s globals like TextEncoder by default.

So even in Node 18 or 20, you still need to manually inject TextEncoder into the global scope if JSDOM is your test environment.

Solution:

Replace the code in jest.config.js with the below code:

// jest.config.js
const { TextEncoder, TextDecoder } = require('util');

global.TextEncoder = TextEncoder;
global.TextDecoder = TextDecoder;


const config = {
  clearMocks: true,
  collectCoverage: true,
  coverageDirectory: "coverage",
  testEnvironment: "jsdom",
  

  setupFilesAfterEnv: ['<rootDir>/jest.config.js'],
                        //path to config file
};

module.exports = config;

That's it. I hope it solves your problem. Happy testing.

Comments

0

in case anyone using enzyme and gets this error, lock cheerio dependancy to 1.0.0-rc.12 version in your package.json and re-install deps. rc versions of cheerio is the latest compatible with enzyme.

What I found in my node_modules is that enzyme had cheerio at ^1.0.0-rc.3 but ^ allowed your pckg manager to pull and install v1 breaking stuff.

Comments

0

I was getting this error while running tests. There was a parent package which is used in my code, inside this parent package, it is referring to the TextDecoder.

So I have mocked the parent package to get it working.

jest.mock('parent-package', () => ({
    <nameOfModudeImported>: jest.fn(() => ({
        <nameOfMethodCalled>: jest.fn(() =>
                // the above method returns a promise, so mock how your component behaves.
                            Promise.resolve({
                body: {
                    result: {
                        url: 'mocked-url',
                    },
                },
            }),
        ),
    })),
}));

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.