jsdom / jsdom

A JavaScript implementation of various web standards, for use with Node.js

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Add GlobalFetch API - Headers, Request, Response

piuccio opened this issue · comments

commented

+1 would be nice as General availability in browsers means more web apps use it and fail in JSDOM.
Given that JSDOM implements XMLHttpRequest, it could be as simple as running whatwg-fetch polyfill at startup, right?

No, unfortunately the whatwg-fetch polyfill is not very spec compliant. Someone needs to write an implementation of the fetch algorithm in the standard: https://fetch.spec.whatwg.org/#fetching

That said, in your own usage of jsdom you're welcome to include <script src="fetch-polyfill.js"> in the HTML you pass to it, and that should work, if you're willing to accept the non-compliant polyfill semantics. We just can't bake them into jsdom.

commented

Yes, for now I'm using the whatwg polyfill in JSDOM.
I'm not sure how much "non-compliant" it is, but it seems to work well enough for my case.

It would be nice to see something built-in, though. Especially since the Fetch API has become commonplace.

Yeah, it definitely would be nice! I hope someone is able to pull-request an implementation of the spec.

My application tests run in node rather than the browser. The lack of server-side fetch API (and typescript bindings to match) has become a show-stopper.

This issue looks like the most promising solution.

Does anyone else know of a workaround for the mean-time? I can use fetch in the browser. I can use node-fetch for tests, but I can't switch on the two packages dynamically. One environment or the other will always fail.

Edit: whatwg-fetch seems to be the answer. Install the package, dont install the typings. import it only in the test runner so the browser application does not have conflicting definitions

Is this issue still relevant since v15 supports fetch?

v15 does not support fetch.

Sorry, i misread the Changelog about the 15.1, only Headers was provided

+1 to this. fetch is natively supported in nearly all modern browser. While it works in JSDOM since fetch is directly supported by node, it is not being proxied through my custom resource loader.

After further investigation I have discovered this was working because my use of isomorphic-fetch which allows for usage of fetch in node. This did however reveal a possible solution borrowed from this package which might make quick work of this. We would only need to make sure that the fetch calls are proxied through JSDOM's resource loader. I'll be poking at options for integration of these ideas to see what it would take to get this working shortly.

Does anyone else know of a workaround for the mean-time?

My workaround looks like this:

    const firstTodo: Todo = { title: 'Todo 1' };
    const secondTodo: Todo = { title: 'Todo 2' };
    const todos: Todo[] = [firstTodo, secondTodo];

    global['fetch'] = jest.fn().mockImplementation(() =>
      Promise.resolve({
        json: () => Promise.resolve(todos),
      })
    );

    await act(async () => {
      render(<App />, container);
    });

but in all seriousness, I have an easy workaround:

First, npm install --save-dev whatwg-fetch

.. then ..

jest.config.js:

...
  setupFiles: ['<rootDir>/setupJest.js'],
...

setupJest.js:

...
import 'whatwg-fetch';
...

and then you'll automagically have all of the fetch related globals defined in your test code

Node 18 fully supports fetch by default, and was released in April 2022. Does that make this easier?

I ask because my use case would benefit from Cache API support in jsdom (#2422), which seems to depend on this.

@domenic how feasible is for jsdom make fetch available when running on Node >= 18?

It's not; we need our own implementation that integrates with jsdom and is spec-compliant. The Node one is not relevant.

@domenic I thought Node's fetch implementation is spec-compliant. Where does it deviate?

is it a way to extend jsdom to add the missing APIs?

There's an example in this very issue: #1724 (comment)

Is there a way to "import" the actual Fetch API Node is shipping?

If currently there is no standards compliant solution we might as well just use Node's and shave off a dependency.

No more whatwg-fetch, node-fetch... thx to Node.js >= 18:

// FixJSDOMEnvironment.ts

import JSDOMEnvironment from 'jest-environment-jsdom';

// https://github.com/facebook/jest/blob/v29.4.3/website/versioned_docs/version-29.4/Configuration.md#testenvironment-string
export default class FixJSDOMEnvironment extends JSDOMEnvironment {
  constructor(...args: ConstructorParameters<typeof JSDOMEnvironment>) {
    super(...args);

    // FIXME https://github.com/jsdom/jsdom/issues/1724
    this.global.fetch = fetch;
    this.global.Headers = Headers;
    this.global.Request = Request;
    this.global.Response = Response;
  }
}
// jest.config.js

/** @type {import('jest').Config} */
const config = {
  testEnvironment: './FixJSDOMEnvironment.ts',

  ...
}

module.exports = config;

See also #3363 (comment), #1721 (comment)

You might need to add this.global.Request = Request; in too if using @tkrotoff's suggestion.

I've fixed this by using the import 'cross-fetch/polyfill'; on the test file (my library has a mixed environment, half of it is meant for node, the other half for jsdom, SSR and RSC in Next.js makes it hard to know what environment it should be using in tests).

I just want to add on to the suggested workaround of patching jest-environment-jsdom, you might also need AbortController in order to abort fetch requests.

this.global.AbortController = AbortController