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

Support for React-testing-library #198

Closed
gkiely opened this issue Jun 24, 2022 · 35 comments
Closed

Support for React-testing-library #198

gkiely opened this issue Jun 24, 2022 · 35 comments
Labels
ecosystem Something that relates to package or framework compatibility enhancement New feature or request

Comments

@gkiely
Copy link

gkiely commented Jun 24, 2022

Allow running tests with @testing-library/react.

Example repo: https://github.com/gkiely/bun-rtl

Steps:

bun install
bun wiptest

Error without environment:
image

Error after enabling jsdom:
image

@Jarred-Sumner
Copy link
Collaborator

Jarred-Sumner commented Jun 25, 2022

re: JSDOM, the next step here is to implement a vm polyfill using ShadowRealm

The vm polyfill can be done in JS.

Another option is to submit a PR to JSDOM that leverages ShadowRealm instead of the vm polyfill.

@Electroid
Copy link
Contributor

Electroid commented Nov 1, 2022

We'll be tracking support for the vm module here: #401

@gkiely
Copy link
Author

gkiely commented Nov 1, 2022

@Electroid Can we keep this open to confirm that react-testing-library works once the vm module work is completed?
I am happy to do the testing.

@Electroid Electroid reopened this Nov 1, 2022
@Electroid Electroid added enhancement New feature or request ecosystem Something that relates to package or framework compatibility and removed duplicate labels Nov 1, 2022
EvHaus added a commit to EvHaus/test-runner-benchmarks that referenced this issue Mar 4, 2023
@EvHaus
Copy link

EvHaus commented Mar 22, 2023

Tested this with bun 0.5.8 and looks like the blocker isn't @testing-library itself, but rather we need support for some underlying DOM implementation. Running @testing-library tests directly gives you:

326 |   } = _temp === void 0 ? {} : _temp;
327 |
328 |   if (!baseElement) {
329 |     // default to document.body instead of documentElement to avoid output of potentially-large
330 |     // head elements (such as JSS style blocks) in debug output
331 |     baseElement = document.body;
                      ^
ReferenceError: Can't find variable: document
      at render (/project/node_modules/@testing-library/react/dist/@testing-library/react.esm.js:331:18)
      at /project/myfile.test.tsx:15:22

I think bun just needs a way to load some kind of DOM library like happy-dom or jsdom. At the moment they don't work.

Trying to import happy-dom fails with:

25 | Object.defineProperty(exports, "__esModule", { value: true });
26 | const NodeFetch = __importStar(require("node-fetch"));
27 | /**
28 |  * Fetch headers.
29 |  */
30 | class Headers extends NodeFetch.Headers {
   ^
TypeError: The superclass is not a constructor.
      at /project/node_modules/happy-dom/lib/fetch/Headers.js:30:0
      at /project/node_modules/happy-dom/lib/window/Window.js:108:18
      at /project/node_modules/happy-dom/lib/window/GlobalWindow.js:6:17
      at /project/node_modules/happy-dom/lib/index.js:8:23

Trying to import jsdom fails with:

error: Cannot find package "vm" from "/project/node_modules/jsdom/lib/api.js"

EvHaus added a commit to EvHaus/test-runner-benchmarks that referenced this issue Mar 22, 2023
noahehall added a commit to noahehall/react-idealer-image that referenced this issue May 13, 2023
@gkiely
Copy link
Author

gkiely commented May 19, 2023

I'll be testing this out tmrw unless @EvHaus beats me to it.

@Jarred-Sumner
Copy link
Collaborator

It uses kind of an insane hack where the context object's prototype becomes a special global object proxy (like window in browsers)

Lmk if that breaks stuff

EvHaus added a commit to EvHaus/test-runner-benchmarks that referenced this issue May 19, 2023
@gkiely
Copy link
Author

gkiely commented May 19, 2023

It works!

image

@gkiely
Copy link
Author

gkiely commented May 19, 2023

I am seeing an issue using screen.getByText that I am investigating.
image

@Jarred-Sumner
Copy link
Collaborator

oh interesitng

@EvHaus
Copy link

EvHaus commented May 19, 2023

@gkiely Can you share your code? I'm having a hard time figuring out how to get JSDom to be loaded in bun before any tests run.

@Jarred-Sumner
Copy link
Collaborator

Jarred-Sumner commented May 19, 2023

@EvHaus can you try bun test --preload 'script-or-package-name'

@gkiely
Copy link
Author

gkiely commented May 19, 2023

I'm using a variation on this, will update shortly: https://twitter.com/jarredsumner/status/1659391501871513607/photo/1

JSDom is not working for me, using happy-dom.

@gkiely
Copy link
Author

gkiely commented May 19, 2023

Ok posted an update with a working version. Preload fixed the above error.
https://github.com/gkiely/bun-rtl

bun test --preload './src/preload.js' App.test.jsx

@Jarred-Sumner
Copy link
Collaborator

I'll have a fix for the issue with node-fetch shortly. TLDR Is we polyfill node-fetch since in Bun it should just use the globalThis.fetch implementation, but our polyfill was only exporting fetch and not all the other things node-fetch exports.

EvHaus added a commit to EvHaus/test-runner-benchmarks that referenced this issue May 20, 2023
@Jarred-Sumner
Copy link
Collaborator

Jarred-Sumner commented May 20, 2023

@EvHaus I tried to get your commit running and there is definitely a bug with --preload. Will investigate that.

In the meantime, if you make helpers/happydom.ts look more like this:

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

GlobalRegistrator.register();

and add a bunfig.toml that looks like this:

[test]
preload = ["./helpers/happydom.ts"]

It gets a lot closer to working.

There are 218 test failures, but 20 pass and it console.log's a LOT of text

image

EvHaus added a commit to EvHaus/test-runner-benchmarks that referenced this issue May 20, 2023
EvHaus added a commit to EvHaus/test-runner-benchmarks that referenced this issue May 20, 2023
@EvHaus
Copy link

EvHaus commented May 20, 2023

Nice! The main thing that was missing is a way for bun:test to have a global afterEach step because react-testing-library requires that you run their cleanup function after each test.

For now I manually added that to each test file and that got me a lot closer. All tests pass when run individually, but running them all causes bun to crash mid-way through with...

$ TZ=UTC bun test
bun test v0.6.2 (8d90d795)

StatusBadge.test.tsx:
✓ <StatusBadge /> > should render without failure [9.08ms]

Pagination.test.tsx:
✓ <Pagination /> > should render a button for each page [17.88ms]
✓ <Pagination /> > should render two ellipses if current page is far from both ends [5.68ms]
✓ <Pagination /> > should render an ellipsis if current page is far the end [4.57ms]
✓ <Pagination /> > should render an ellipsis if current page is far the start [2.86ms]
✓ <Pagination /> > should navigate to the page when clicking on that specific page [17.10ms]
error: script "test" exited with code 6 (SIGABRT)

...before all the tests have finshed.

You can try with latest commit in EvHaus/test-runner-benchmarks@3d13051

noahehall added a commit to noahehall/react-idealer-image that referenced this issue May 20, 2023
noahehall added a commit to noahehall/react-idealer-image that referenced this issue May 20, 2023
* Feat(core)!: refactor and upgrade

- remove non typescript-first dependencies
- upgrade all deps to latest versions
- refactor components to react.fc
- migrate to bun.sh

* Feat(tests)!: migrate tests to bun:test

- skipped tests requiring dom: oven-sh/bun#198

* Fix(MR): remove Navigator typings

* Chore(ts): update tsconfig
@Jarred-Sumner
Copy link
Collaborator

We will add support for registering hooks via bun:test in preload

@Jarred-Sumner
Copy link
Collaborator

Jarred-Sumner commented May 20, 2023

Nice! The main thing that was missing is a way for bun:test to have a global afterEach step because react-testing-library requires that you run their cleanup function after each test.

For now I manually added that to each test file and that got me a lot closer. All tests pass when run individually, but running them all causes bun to crash mid-way through with...

$ TZ=UTC bun test
bun test v0.6.2 (8d90d795)

StatusBadge.test.tsx:
✓ <StatusBadge /> > should render without failure [9.08ms]

Pagination.test.tsx:
✓ <Pagination /> > should render a button for each page [17.88ms]
✓ <Pagination /> > should render two ellipses if current page is far from both ends [5.68ms]
✓ <Pagination /> > should render an ellipsis if current page is far the end [4.57ms]
✓ <Pagination /> > should render an ellipsis if current page is far the start [2.86ms]
✓ <Pagination /> > should navigate to the page when clicking on that specific page [17.10ms]
error: script "test" exited with code 6 (SIGABRT)

...before all the tests have finshed.

You can try with latest commit in EvHaus/test-runner-benchmarks@3d13051

I can repro the crash.

Call stack:

WTFCrashWithInfo(int, char const*, char const*, int) (/Users/jarred/Code/bun/node_modules/bun-webkit-macos-arm64/include/wtf/Assertions.h:758)
JSC::LocalAllocator::allocateSlowCase(JSC::Heap&, unsigned long, JSC::GCDeferralContext*, JSC::AllocationFailureMode) (@JSC::LocalAllocator::allocateSlowCase(JSC::Heap&, unsigned long, JSC::GCDeferralContext*, JSC::AllocationFailureMode):67)
JSC::LocalAllocator::allocate(JSC::Heap&, unsigned long, JSC::GCDeferralContext*, JSC::AllocationFailureMode)::'lambda'()::operator()() const (/Users/jarred/Code/bun/node_modules/bun-webkit-macos-arm64/include/JavaScriptCore/LocalAllocatorInlines.h:41)
JSC::HeapCell* JSC::FreeList::allocateWithCellSize<JSC::LocalAllocator::allocate(JSC::Heap&, unsigned long, JSC::GCDeferralContext*, JSC::AllocationFailureMode)::'lambda'()>(JSC::LocalAllocator::allocate(JSC::Heap&, unsigned long, JSC::GCDeferralContext*, JSC::AllocationFailureMode)::'lambda'() const&, unsigned long) (/Users/jarred/Code/bun/node_modules/bun-webkit-macos-arm64/include/JavaScriptCore/FreeListInlines.h:44)
JSC::LocalAllocator::allocate(JSC::Heap&, unsigned long, JSC::GCDeferralContext*, JSC::AllocationFailureMode) (/Users/jarred/Code/bun/node_modules/bun-webkit-macos-arm64/include/JavaScriptCore/LocalAllocatorInlines.h:38)
JSC::GCClient::IsoSubspace::allocate(JSC::VM&, unsigned long, JSC::GCDeferralContext*, JSC::AllocationFailureMode) (/Users/jarred/Code/bun/node_modules/bun-webkit-macos-arm64/include/JavaScriptCore/IsoSubspaceInlines.h:34)
void* JSC::tryAllocateCellHelper<JSC::JSString, (JSC::AllocationFailureMode)0>(JSC::VM&, unsigned long, JSC::GCDeferralContext*) (/Users/jarred/Code/bun/node_modules/bun-webkit-macos-arm64/include/JavaScriptCore/JSCellInlines.h:190)
void* JSC::allocateCell<JSC::JSString>(JSC::VM&, unsigned long) (/Users/jarred/Code/bun/node_modules/bun-webkit-macos-arm64/include/JavaScriptCore/JSCellInlines.h:206)
JSC::JSString::create(JSC::VM&, WTF::Ref<WTF::StringImpl, WTF::RawPtrTraits<WTF::StringImpl>>&&) (/Users/jarred/Code/bun/node_modules/bun-webkit-macos-arm64/include/JavaScriptCore/JSString.h:187)
JSC::JSFunction::originalName(JSC::JSGlobalObject*) (@JSC::JSFunction::originalName(JSC::JSGlobalObject*):253)
JSC::JSBoundFunction::nameSlow(JSC::VM&) (@JSC::JSBoundFunction::nameSlow(JSC::VM&):47)
JSC::JSFunction::name(JSC::VM&) (@JSC::JSFunction::name(JSC::VM&):58)
JSC::getCalculatedDisplayName(JSC::VM&, JSC::JSObject*) (@JSC::getCalculatedDisplayName(JSC::VM&, JSC::JSObject*):65)
JSC::StackFrame::functionName(JSC::VM&) const (@JSC::StackFrame::functionName(JSC::VM&) const:43)
JSC::StackFrame::toString(JSC::VM&) const (@JSC::StackFrame::toString(JSC::VM&) const:13)
JSC::Interpreter::stackTraceAsString(JSC::VM&, WTF::Vector<JSC::StackFrame, 0ul, WTF::CrashOnOverflow, 16ul, WTF::FastMalloc> const&) (@JSC::Interpreter::stackTraceAsString(JSC::VM&, WTF::Vector<JSC::StackFrame, 0ul, WTF::CrashOnOverflow, 16ul, WTF::FastMalloc> const&):35)
JSC::ErrorInstance::computeErrorInfo(JSC::VM&) (@JSC::ErrorInstance::computeErrorInfo(JSC::VM&):23)
JSC::Heap::finalizeUnconditionalFinalizers() (@JSC::Heap::finalizeUnconditionalFinalizers():306)
JSC::Heap::runEndPhase(JSC::GCConductor) (@JSC::Heap::runEndPhase(JSC::GCConductor):355)
JSC::Heap::runCurrentPhase(JSC::GCConductor, JSC::CurrentThreadState*) (@JSC::Heap::runCurrentPhase(JSC::GCConductor, JSC::CurrentThreadState*):97)
WTF::ScopedLambdaFunctor<void (JSC::CurrentThreadState&), JSC::Heap::collectInMutatorThread()::$_25>::implFunction(void*, JSC::CurrentThreadState&) (@WTF::ScopedLambdaFunctor<void (JSC::CurrentThreadState&), JSC::Heap::collectInMutatorThread()::$_25>::implFunction(void*, JSC::CurrentThreadState&):12)
JSC::callWithCurrentThreadState(WTF::ScopedLambda<void (JSC::CurrentThreadState&)> const&) (@JSC::callWithCurrentThreadState(WTF::ScopedLambda<void (JSC::CurrentThreadState&)> const&):45)
JSC::Heap::collectInMutatorThread() (@JSC::Heap::collectInMutatorThread():27)
JSC::Heap::waitForCollection(unsigned long long) (@JSC::Heap::waitForCollection(unsigned long long):42)
JSC::Heap::collectSync(JSC::GCRequest) (@JSC::Heap::collectSync(JSC::GCRequest):30)
::JSC__VM__runGC(JSC__VM *, bool) (/Users/jarred/Code/bun/src/bun.js/bindings/bindings.cpp:3453)
src.bun.js.bindings.shimmer.Shimmer("JSC","VM",src.bun.js.bindings.bindings.VM).cppFn (/Users/jarred/Code/bun/src/bun.js/bindings/shimmer.zig:186)
src.bun.js.bindings.bindings.VM.runGC (/Users/jarred/Code/bun/src/bun.js/bindings/bindings.zig:4683)
src.cli.test_command.TestCommand.run (/Users/jarred/Code/bun/src/cli/test_command.zig:720)
src.string_types.PathString.slice (/Users/jarred/Code/bun/src/string_types.zig:43)
src.cli.test_command.TestCommand.runAllTests.Context.begin (/Users/jarred/Code/bun/src/cli/test_command.zig:618)
src.bun.js.javascript.OpaqueWrap__anon_144322__struct_253123.callback (/Users/jarred/Code/bun/src/bun.js/javascript.zig:121)
::JSC__VM__holdAPILock(JSC__VM *, void *, void (*)(void *)) (/Users/jarred/Code/bun/src/bun.js/bindings/bindings.cpp:3492)
src.bun.js.bindings.shimmer.Shimmer("JSC","VM",src.bun.js.bindings.bindings.VM).cppFn (/Users/jarred/Code/bun/src/bun.js/bindings/shimmer.zig:186)
src.bun.js.bindings.bindings.VM.holdAPILock (/Users/jarred/Code/bun/src/bun.js/bindings/bindings.zig:4627)
src.bun.js.javascript.VirtualMachine.runWithAPILock (/Users/jarred/Code/bun/src/bun.js/javascript.zig:1710)
src.cli.test_command.TestCommand.runAllTests (/Users/jarred/Code/bun/src/cli/test_command.zig:632)
src.cli.test_command.TestCommand.exec (/Users/jarred/Code/bun/src/cli/test_command.zig:462)
src.cli.Command.start (/Users/jarred/Code/bun/src/cli.zig:1213)

We manually run the garbage collector after each file finishes executing and that seems to cause a crash due to a memory allocation failure happening inside the finalizer when getting function names from Error objects being finalized. I'm not sure yet if this is a bug in bun or in JSC (probably Bun)

@Jarred-Sumner
Copy link
Collaborator

After working around the JSC thing

The next crash happens when trying to diff a 2.7 GB string compared to null. It occurs in the test following this one:

✓ > should render the children for the element

image

Jarred-Sumner added a commit that referenced this issue May 21, 2023
Jarred-Sumner added a commit that referenced this issue May 21, 2023
@Jarred-Sumner
Copy link
Collaborator

Current status:

  • You can now use beforeAll etc in a --preload script
  • The hang has been fixed.
  • waitFor seems to have a fixed 500ms delay. We don't have a way to simulate timers yet.

react-testing-library should work now, minus the above issue with the test simulation.

@EvHaus
Copy link

EvHaus commented May 23, 2023

Confirmed. Working well for me in bun 0.6.3. 👍

I was able to add it to my test runner benchmarks. If you're curious, here are the results:

'yarn workspace bun test' ran
    1.16 ± 0.02 times faster than 'yarn workspace vitest test --isolate=false'
    1.32 ± 0.04 times faster than 'yarn workspace jasmine test'
    4.46 ± 0.06 times faster than 'yarn workspace jest test'
    9.08 ± 0.13 times faster than 'yarn workspace vitest test'

Looks like bun is the new performance winner of my benchmark. 🎉

@Jarred-Sumner
Copy link
Collaborator

The very marginal improvement there over vitest, I think, is because of the timers. If you remove the waitFor code or (make it so it only waits one tick), what does it look like? When I checked last, waitFor was a hardcoded 500ms delay in each test that it was used.

@Jarred-Sumner
Copy link
Collaborator

ah there's only one usage of waitFor, interesting

@EvHaus
Copy link

EvHaus commented May 23, 2023

If I remove the 1 usage of waitFor I get slightly better comparative results:

'yarn workspace bun test' ran
    1.45 ± 0.04 times faster than 'yarn workspace vitest test --isolate=false'
    1.98 ± 0.06 times faster than 'yarn workspace jasmine test'
    7.96 ± 0.22 times faster than 'yarn workspace jest test'
   17.32 ± 0.48 times faster than 'yarn workspace vitest test'

@Jarred-Sumner
Copy link
Collaborator

yeah i think our functions for printing the errors is pretty slow right now. There is a test failing in Bun's version due to lack of process.env.TZ support. Adding that shortly.

image

@Jarred-Sumner
Copy link
Collaborator

alright give it another try in the canary build

should be about 80% faster

image

@Jarred-Sumner
Copy link
Collaborator

the main remaining TODOs here are:

  1. defer generating the Error message to when the getter is called. This is what Jest & Vitest already do, but we do not. We eagerly generate error messages which is super expensive (why await waitFor ... had such an impact)
  2. run the transpiler in paralell. We are running it single-threaded. This is good for memory usage. But after a few files, it slows stuff down a bit

@EvHaus
Copy link

EvHaus commented May 24, 2023

Confirmed. Latest canary is a bit better (results). Bun is now a clear winner 🎉

@Jarred-Sumner
Copy link
Collaborator

@EvHaus the waitFor thing still needs to be addressed - Bun needs to implement Jest fake timers, I guess. In both Vite & Bun about 5 seconds of the total is just the hardcoded waitFor delay. Vite is running multi-threaded so the delay is spread across the number of cores

@gkiely
Copy link
Author

gkiely commented Nov 13, 2023

@Jarred-Sumner Feel free to close this.

@donaldpipowitch
Copy link

I try to switch from Jest+Testing Library to Bun+Testing Library and stumbled over this issue. It looks like I have to run afterEach(() => cleanup()); within each test file.

When I do it globally in a preload function like this:

import { cleanup } from '@testing-library/react';
import {
  getIsReactActEnvironment,
  setReactActEnvironment,
} from '@testing-library/react/dist/act-compat';
import { afterAll, afterEach, beforeAll } from 'bun:test';

let previousIsReactActEnvironment = getIsReactActEnvironment();
beforeAll(() => {
  previousIsReactActEnvironment = getIsReactActEnvironment();
  setReactActEnvironment(true);
});
afterEach(() => cleanup());
afterAll(() => {
  setReactActEnvironment(previousIsReactActEnvironment);
});

then I got For queries bound to document.body a global document has to be available.... Is someone running Bun with Testing Library without manually adding afterEach(() => cleanup()); in each test file?

@hmidmrii
Copy link

@donaldpipowitch, I had the same issue, I opened an issue on react testing library repo
testing-library/react-testing-library#1348

@gkiely
Copy link
Author

gkiely commented Jul 30, 2024

@donaldpipowitch @hmidmrii You need to set up happy-dom before calling cleanup.
#198 (comment)

@hmidmrii
Copy link

@gkiely If you check the issue I opened on react testing library repo in my previous commit, you will find that I did that in the preload file, I even proposed two workarounds,
but the casual way of using the exported screen will not work.
here's the issue again: testing-library/react-testing-library#1348

@hmidmrii
Copy link

I opened an issue on their repo because I did some logs on both preload cleanup (placing the cleanup code in the preload file) and local cleanup (placing the cleanup code in each test file) and saw that both act the same, so I expected it the issue to be from cleanup function from react testing library and not from afterEach exported by Bun.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
ecosystem Something that relates to package or framework compatibility enhancement New feature or request
Projects
None yet
Development

No branches or pull requests

6 participants