solid-client-authn-js icon indicating copy to clipboard operation
solid-client-authn-js copied to clipboard

jest jsdom: ReferenceError: TextEncoder is not defined

Open angelo-v opened this issue 4 years ago • 9 comments

Search terms you've used

jsdom, TextEncoder

Impacted package

Which packages do you think might be impacted by the bug ?

  • [x] solid-client-authn-browser
  • [ ] solid-client-authn-node
  • [ ] solid-client-authn-core
  • [ ] oidc-client-ext
  • [ ] Other (please specify): ...

Bug description

importing from @inrupt/solid-client-authn-browser fails in a jest test using jsdom environment:

Test suite failed to run

    ReferenceError: TextEncoder is not defined

      3 |  */
      4 |
    > 5 | import { login } from '@inrupt/solid-client-authn-browser';
        | ^
      6 |
      7 | describe('test', () => {
      8 |   it('should work', () => {

To Reproduce

Run this test:

/**
 * @jest-environment jsdom
 */

import { login } from '@inrupt/solid-client-authn-browser';

describe('test', () => {
  it('should work', () => {
    expect(login).toBeDefined();
  });
});

Expected result

Test runs successfully

Actual result

ReferenceError: TextEncoder is not defined

Environment

 @inrupt/solid-client-authn-browser: ^1.11.2 => 1.11.2

Additional information

  • The problem did not occur with @inrupt/solid-client-authn-browser 1.6.1
  • It works fine with @jest-environment node

angelo-v avatar Sep 10 '21 15:09 angelo-v

A workarround (or solution?) is to polyfill TextEncoder and TextDecoder in a setupFilesAfterEnv script:

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

angelo-v avatar Sep 10 '21 15:09 angelo-v

Hi @angelo-v ! I think this issue unfortunately goes beyond the scope of @inrupt/solid-client-authn-browser, and we have to work around it ourselves: jose expects TextEncoder to be present in the environment, but jsdom does not put it there (see https://github.com/jsdom/jsdom/issues/2524). Our solution (suggested somewhere in the linked issue) is to use a custom Jest environment: https://github.com/inrupt/solid-client-authn-js/blob/main/tests/environment/customEnvironment.js, referenced e.g. in https://github.com/inrupt/solid-client-authn-js/blob/main/packages/browser/jest.config.js.

NSeydoux avatar Sep 10 '21 16:09 NSeydoux

Thanks, I understand that. Using setupFilesAfterEnv works as well and is more lightweight then creating a custom jest env imo. Perhaps you can at least add a hint to the documentation about that, because usually people just using the inrupt lib do not know about jose and those internals, they just wonder why the inrupt lib causes an error in the tests

angelo-v avatar Sep 10 '21 16:09 angelo-v

Still an issue today. Jest 29, added the util script

CalebJamesStevens avatar Sep 21 '22 20:09 CalebJamesStevens

just import it with require const { JSDOM } = require("jsdom"); I got the same issue and this solution worked for me

JaldinDeveloper avatar Oct 12 '22 16:10 JaldinDeveloper

A workarround (or solution?) is to polyfill TextEncoder and TextDecoder in a setupFilesAfterEnv script:

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

How exactly do I add this to a setupFilesAfterEnv script? I am using typescript and I have a jest-config.d.ts and it has this inside,

declare const root: any;
export { root as rootDir };
export declare const setupFilesAfterEnv: string[];
export declare const testMatch: string[];
export declare const testPathIgnorePatterns: string[];
export declare const coveragePathIgnorePatterns: string[];
export declare const coverageDirectory: string;

hjohnsick avatar Oct 12 '22 20:10 hjohnsick

We actually have a bit of an update here: we've just released a package that helps by polyfilling the required Web APIs: https://www.npmjs.com/package/@inrupt/jest-jsdom-polyfills

It's still being rolled out across the SDK repos, but in our testing so far it should provide everything needed. We're aware that there may be an issue with UInt8Arrays, and that's due to an underlying jsdom issue: https://github.com/jsdom/whatwg-encoding/pull/13

ThisIsMissEm avatar Oct 14 '22 18:10 ThisIsMissEm

@hjohnsick you can see an example here: https://github.com/inrupt/solid-client-access-grants-js/pull/366/files

You should likely have a jest.config.ts file in your repo, unless you're somehow using jest without any configuration at all (which I've only rarely seen)

ThisIsMissEm avatar Oct 14 '22 18:10 ThisIsMissEm

Thanks, I understand that. Using setupFilesAfterEnv works as well and is more lightweight then creating a custom jest env imo. Perhaps you can at least add a hint to the documentation about that, because usually people just using the inrupt lib do not know about jose and those internals, they just wonder why the inrupt lib causes an error in the tests

@angelo-v Yup, we agree, we've since moved from custom environments to setupFilesAfterEnv

ThisIsMissEm avatar Oct 14 '22 18:10 ThisIsMissEm

Seems like the answers didnt state where to put the said code.

allestaire avatar Jan 20 '23 06:01 allestaire

You can use this repository as an example: see https://github.com/inrupt/solid-client-authn-js/blob/main/jest.setup.ts, where the polyfills are imported, and https://github.com/inrupt/solid-client-authn-js/blob/72cd3486efd627d01c8f272a502639075cf16c7c/jest.config.ts#L39, where jest is configured to pick up this setup.

NSeydoux avatar Jan 20 '23 09:01 NSeydoux

Only solution worked to me is to add this line of code to src/setupTests.ts

import '@testing-library/jest-dom';
import { TextEncoder } from 'util';

global.TextEncoder = TextEncoder;

clarkeustaquio avatar Feb 02 '23 11:02 clarkeustaquio

Only solution worked to me is to add this line of code to src/setupTests.ts

import '@testing-library/jest-dom';
import { TextEncoder } from 'util';

global.TextEncoder = TextEncoder;

It's strange you needed that as we do that polyfill of textencoder in our polyfill.. I can't comment on testing lib & jest-dom though. If you've a repo to share I'd love to run a few experiments to check our package is working as expected

ThisIsMissEm avatar Feb 02 '23 18:02 ThisIsMissEm

Problem still present. Adding this to setupTests.js still works:

import { TextEncoder } from 'util';

global.TextEncoder = TextEncoder;

danielbakas avatar Jul 03 '23 00:07 danielbakas

Closing this issue as it is an error in Jest with a workaround (i.e. https://github.com/inrupt/solid-client-authn-js/issues/1676#issuecomment-1617024437)

jeswr avatar Jul 03 '23 00:07 jeswr

Only solution worked to me is to add this line of code to src/setupTests.ts

import '@testing-library/jest-dom';
import { TextEncoder } from 'util';

global.TextEncoder = TextEncoder;

This worked for me when using CRA.

vojdan avatar Jan 04 '24 23:01 vojdan

This did not work for me. Still getting ' ReferenceError: TextEncoder is not defined'

kokombo avatar Jan 09 '24 09:01 kokombo

Solution above did not work for me either, after some trail and error found this to work for me:

// inside your env.setup.js
testEnvironment: "<rootDir>/test/test-config/env-setup.js",

// env-setup.js

const Environment = require("jest-environment-jsdom").default;

module.exports = class CustomTestEnvironment extends Environment {
    async setup() {
        await super.setup();
        this.global.TextEncoder = TextEncoder;
        this.global.TextDecoder = TextDecoder;
        this.global.Response = Response;
        this.global.Request = Request;
        
    }
};

Alfredoeb9 avatar Jan 25 '24 16:01 Alfredoeb9

@BowaDAO can you describe your setup a bit more so that I would be able to help?

@Alfredoeb9 thanks for providing something that works around the issue in your case. Can you elaborate a bit more on your setup as well, so that I may figure out why the solution we use for ourselves now doesn't cover your use case?

NSeydoux avatar Jan 25 '24 16:01 NSeydoux

I've been able to resolve the issue.

kokombo avatar Feb 28 '24 13:02 kokombo

.jest/setup.js

import { TextEncoder, TextDecoder } from 'util';

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

christopher-theagen avatar Mar 19 '24 13:03 christopher-theagen