Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fetch function causes a weird error with bun test + react + happy-dom #8774

Closed
lumap opened this issue Feb 7, 2024 · 4 comments
Closed

fetch function causes a weird error with bun test + react + happy-dom #8774

lumap opened this issue Feb 7, 2024 · 4 comments
Labels
bug Something isn't working

Comments

@lumap
Copy link

lumap commented Feb 7, 2024

What version of Bun is running?

1.0.26+c75e768a6

What platform is your computer?

Darwin 23.3.0 arm64 arm

What steps can reproduce the bug?

What is the expected behavior?

An error telling me fetch is not mocked

What do you see instead?

bun test v1.0.26 (c75e768a)

src/App.test.js:
312 |             });
313 |             this.nodeRequest.on('error', this.onError.bind(this));
314 |             this.nodeRequest.on('socket', this.onSocket.bind(this));
315 |             this.nodeRequest.on('response', this.onResponse.bind(this));
316 |             if (this.request.body === null) {
317 |                 this.nodeRequest.end();
                      ^
TypeError: undefined is not a function
      at node:http:601:95
      at _final (node:http:865:17)
      at callFinal (node:stream:2763:45)
      at prefinish (node:stream:2786:45)
      at finishMaybe (node:stream:2793:49)
      at node:stream:2726:151
      at /Users/lumap/tmp/node_modules/happy-dom/lib/fetch/Fetch.js:317:17
      at new Promise (:1:21)
      at sendRequest (/Users/lumap/tmp/node_modules/happy-dom/lib/fetch/Fetch.js:268:16)
      at /Users/lumap/tmp/node_modules/happy-dom/lib/fetch/Fetch.js:119:22

32 |   // throw errors w/ suggestions for better queries. Opt in so off by default.
33 |   throwSuggestions: false,
34 |   // called when getBy* queries fail. (message, container) => Error
35 |   getElementError(message, container) {
36 |     const prettifiedDOM = (0, _prettyDom.prettyDOM)(container);
37 |     const error = new Error([message, `Ignored nodes: comments, ${config.defaultIgnore}\n${prettifiedDOM}`].filter(Boolean).join('\n\n'));
                       ^
TestingLibraryElementError: Unable to find an element with the text: Home. This could be because the text is broken up by multiple elements. In this case, you can provide a function for your text matcher to make your matcher more flexible.

Ignored nodes: comments, script, style
<body>
  <div>
    <div
      class="App"
    >
      <header
        class="App-header"
      >
        <p>
          Edit
          <code>
            src/App.js
          </code>
           and save to reload.
        </p>
        <a
          class="App-link"
          href="https://reactjs.org"
          rel="noopener noreferrer"
          target="_blank"
        >
          Learn React
        </a>
      </header>
    </div>
  </div>
</body>
      at getElementError (/Users/lumap/tmp/node_modules/@testing-library/dom/dist/config.js:37:19)
      at /Users/lumap/tmp/node_modules/@testing-library/dom/dist/query-helpers.js:76:17
      at /Users/lumap/tmp/node_modules/@testing-library/dom/dist/query-helpers.js:52:17
      at /Users/lumap/tmp/node_modules/@testing-library/dom/dist/query-helpers.js:95:19
      at /Users/lumap/tmp/src/App.test.js:12:12
 Home button renders [14.24ms]
11 |      *
12 |      * @param message Message.
13 |      * @param name Name.
14 |      */
15 |     constructor(message, name = null) {
16 |         super(message);
             ^
NetworkError: Fetch to "http://localhost:3000/" failed. Error: undefined is not a function
      at new DOMException (/Users/lumap/tmp/node_modules/happy-dom/lib/exception/DOMException.js:16:9)
      at onError (/Users/lumap/tmp/node_modules/happy-dom/lib/fetch/Fetch.js:376:21)
      at _final (node:http:865:17)
      at callFinal (node:stream:2763:45)
      at prefinish (node:stream:2786:45)
      at finishMaybe (node:stream:2793:49)
      at node:stream:2726:151
      at /Users/lumap/tmp/node_modules/happy-dom/lib/fetch/Fetch.js:317:17
      at new Promise (:1:21)

 0 pass
 1 fail

Additional information

Sorry if this is a not-so-good issue, i'm tired. I need a nap.

@lumap lumap added the bug Something isn't working label Feb 7, 2024
@friday
Copy link

friday commented Mar 10, 2024

This workaround works for me.

import { GlobalRegistrator } from "@happy-dom/global-registrator";

const bunFetch = fetch;
GlobalRegistrator.register();
window.fetch = bunFetch;

But it will not work when happy-dom is evaluating script tags. I found that wrapping the request method worked for that though (I'm confused about why because I think the end method is supported by bun, but I can't get proper debug output):

import { GlobalRegistrator } from "@happy-dom/global-registrator";
import HTTP, { request as HTTPRequest } from "http";
import HTTPS, { request as HTTPSRequest } from "https";

type RequestParams = Parameters<typeof HTTPRequest>;

HTTP.request = function (...args: any[]) {
  return Object.assign(HTTPRequest(...(args as RequestParams)), {
    end() {},
  });
};

HTTPS.request = function (...args: any[]) {
  return Object.assign(HTTPSRequest(...(args as RequestParams)), {
    end() {},
  });
};

GlobalRegistrator.register();

You can also disable evaluating script tags:

GlobalRegistrator.register({
  settings: {
    disableJavaScriptEvaluation: true,
    disableJavaScriptFileLoading: true,
  },
});

But it will just produce another error instead NotSupportedError Failed to load external script "https://...". JavaScript file loading is disabled.

@nicreichert
Copy link

@friday thanks a lot for your replies. Your solutions kinda help, but, in my case, there's still some issues that neither solve actually handles completely.

With the approach to override the end of HTTP and HTTPS my test just hang, never finishing. The first approach makes it so that the response of any fetch request is Welcome to Bun! (as you can see in the issue I raised #10787 )

I tried using other fetch libraries for testing, but, I just couldn't get it to work properly.

Have you faced such issue?

@keverw
Copy link

keverw commented May 29, 2024

I also just bumped into this and not sure what to do :( My thought is to make my own custom fetch wrapper that uses the Node API to do http requests instead of Fetch (Nope, Happy-Dom overwrites that too!), and then on browser use Fetch so it's isomorphic but feels like busy work hmm.

Edit a week later:

Hmm... I attempted to look into this more. After trial and error, I did this in my preload:

import { GlobalRegistrator } from '@happy-dom/global-registrator';
import { afterEach } from 'bun:test';

global._isHappyDomEnabled = true;
GlobalRegistrator.register({});

afterEach(() => {
  if (global._isHappyDomEnabled) {
    document.body.innerHTML = '';
  }
});

globalThis.disableHappyDom = async (): Promise<void> => {
  if (global._isHappyDomEnabled) {
    await GlobalRegistrator.unregister();
    global._isHappyDomEnabled = false;
  }
};

Then in tests I want to opt-out, like API testing.... I did:

// eslint-disable-next-line @typescript-eslint/no-unsafe-call
await globalThis.disableHappyDom();

I was trying to setup happy-dom in the react tests only but it complained "For queries bound to document.body a global document has to"... For some reason I could only get working if in my preload...

It feels hacky but it works if calling the test script directly.

Hover if doing bun test at the root of the repo where it will test everything, will cause it to complain about document not being found before I disabled happy dom in a previous test... temped to write my own test-all custom script that will look though each of my libs and apps folders, and call them separately so always a clean state. In-addition, unregister seems to affect window.crypto since I had a file using that for my own auth tokens server-side.

Then most of my React code is pure functions as not too far yet, so not using Fetch in them yet. Just was testing my API at this point.... but I think maybe I'd just end up mocking fetch( or a similar endpoint for my components where I want to grab data, but a lot will just be dealing with pure data passed in by another one I believe.

Hopefully my comment can help others looking for a workaround.

@Jarred-Sumner
Copy link
Collaborator

Fixed in Bun v1.1.21

 bun-1.1.21 test --preload=./happydom.ts
bun test v1.1.21 (70ca2b76)

src/App.test.js:
 Home button renders [9.43ms]

 1 pass
 0 fail
 1 expect() calls
Ran 1 tests across 1 files. [134.00ms]

(with the App.js file modified to have a "Home" button)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
None yet
Development

No branches or pull requests

5 participants