31

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?

Yves M.
  • 29,855
  • 23
  • 108
  • 144
leonheess
  • 16,068
  • 14
  • 77
  • 112

4 Answers4

63

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 });
leonheess
  • 16,068
  • 14
  • 77
  • 112
  • 4
    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; }' – dev_in_training Aug 05 '22 at 15:20
  • Added to `jest.conf.js` then got `Cannot use import statement outside a module`. – dragonfly02 Aug 23 '22 at 02:54
  • 1
    @dragonfly02 You will have to add that to the test or to a [setupFile](https://jestjs.io/docs/configuration#setupfiles-array) – leonheess Aug 23 '22 at 09:33
  • 3
    This helps, but I get Type 'typeof TextDecoder' is not assignable to type '{ new (label?: string | undefined, options?: TextDecoderOptions | undefined): TextDecoder; prototype: TextDecoder; }'. The types of 'prototype.decode' are incompatible between these types. in TS – trainoasis Feb 03 '23 at 07:15
  • @trainoasis just add a `// @ts-ignore` to the line above it. Even when trying nicely, e.g. `global.TextEncoder = { prototype: TextEncoder };` gives TS errors, so better to just ignore the errors and do the same everywhere. – Fee Mar 29 '23 at 10:15
  • I am receiving an error in angular test that global is not defined, I declare a global variable on top of setupFile like 'declare let global: any' and assign global objects the TextEncoder and TextDecoder and everything seems to be working. – Farrukh Faizy Jun 08 '23 at 10:44
  • Just a short note, this should be inside the `jest.setup.ts` file. – zrna Jul 20 '23 at 13:22
17

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

Jason R Stevens CFA
  • 2,232
  • 1
  • 22
  • 28
  • 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: () => ({}), }) ); ``` – Guy Sartorelli Jan 13 '23 at 02:54
  • 1
    This saved my bacon today, decided to mark my whole library as a node env `testEnvironment: 'node',`. – Carlos Esteban Lopez Jaramillo Jun 05 '23 at 05:42
  • This doesn't work if you need to say access the 'window' object in your test file. – scottseeker Jul 10 '23 at 15:08
  • @scottseeker I believe you need to mock the `window` object for that. Here's a question/answer combo with solutions for that: https://stackoverflow.com/questions/41885841/how-can-i-mock-the-javascript-window-object-using-jest – Jason R Stevens CFA Jul 10 '23 at 15:50
-1

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
Fee
  • 177
  • 1
  • 3
-5

Make sure your whatwg-url package has version ^10.0.0 at least

denieler
  • 237
  • 3
  • 8