callstack / react-native-testing-library

🦉 Simple and complete React Native testing utilities that encourage good testing practices.
https://callstack.github.io/react-native-testing-library/
MIT License
3.02k stars 264 forks source link

Getting "ReferenceError: jest is not defined" when on userEvent touch events #1568

Closed JoseLion closed 3 months ago

JoseLion commented 4 months ago

Describe the bug

I'm getting a "ReferenceError: jest is not defined" error whenever I use a touch event method of userEvent. I'm not testing with Jest--I'm using Mocha.js and react-native-testing-mocks (a package of my authoring). It seems the problem is that this function uses jest.fn() to mock a couple of properties. A workaround to the problem is to add a replacement of jest.fn to the global object:

function noop(): void {
  // do nothing...
}

Object.assign(global, { jest: { fn: () => noop } });

According to the documentation, RNTL is "tested to work with Jest, but it should work with other test runners as well." However, the library seems highly coupled to Jest. Another example of this is that when I use the workaround, I get a warning about not using fake timers, even when I have RNTL_SKIP_AUTO_DETECT_FAKE_TIMERS=true in my environment variables.

Error stack trace

```console TypeError: event.persist is not a function at handler (Pressability.js:478:15) at callback (node_modules\@testing-library\react-native\src\user-event\utils\dispatch-event.ts:23:5) at C:\Repos\react-native-testing-mocks\node_modules\@testing-library\react-native\src\act.ts:31:24 at actImplementation (node_modules\react\cjs\react.development.js:2512:16) at C:\Repos\react-native-testing-mocks\node_modules\@testing-library\react-native\src\act.ts:30:25 at dispatchEvent (node_modules\@testing-library\react-native\src\user-event\utils\dispatch-event.ts:22:11) at emitPressablePressEvents (node_modules\@testing-library\react-native\src\user-event\press\press.ts:85:16) at async basePress (node_modules\@testing-library\react-native\src\user-event\press\press.ts:64:5) at async basePress (node_modules\@testing-library\react-native\src\user-event\press\press.ts:73:3) at async basePress (node_modules\@testing-library\react-native\src\user-event\press\press.ts:73:3) at async Object.press (node_modules\@testing-library\react-native\src\user-event\press\press.ts:22:3) at async wrapAsync (node_modules\@testing-library\react-native\src\helpers\wrap-async.ts:22:22) ```

Expected behavior

The library should not be so tightly coupled to Jest so it can be used with other test runners. The usage of jest.fn() can be easily replaced with something like a noop() function.

Steps to Reproduce

  1. Setup test without Jest. For example, you can use Mocha.js together with react-native-testing-mocks.
  2. Create a simple test where you can use userEvent.press(..)
  3. The error shows when the test runs

Screenshots

N/A

Versions

npmPackages: @testing-library/react-native: ^12.4.3 => 12.4.3 react: 18.2.0 => 18.2.0 react-native: ^0.73.5 => 0.73.5 react-test-renderer: ^18.2.0 => 18.2.0

mdjastrzebski commented 4 months ago

@JoseLion you are the first user to raise similar issue. Could you shed some context on why are you using Mocha instead of Jest for testing RN apps?

If you find this issue to be high value to you, I would recommend creating a PR, that would loosen the coupling between UserEvent and Jest by providing entry points for other runners similar to: https://github.com/callstack/react-native-testing-library/blob/536d2a979dbcca77c3828ab0dc2b9ef212efc7e9/src/user-event/setup/setup.ts#L29

As far as I can tell Mocka itself, unlike Jest, Vitest, Bun, does not have to have built-in mocking API, so user has to pick one of several mocking options there.

From maintainers perspective, there are few conditions that such change would have to meet:

  1. It should be non-breaking, all existing users should be able to run the code without need for any change
  2. It should provide entry point for any non-Jest runner (Mocha, Vitest, Bun), and not be tied to particular runner
  3. It should be well documented
JoseLion commented 4 months ago

@mdjastrzebski, thanks for the quick reply. I'll be glad to add more context. There are a few reasons why I prefer to use Mocha.js over Jest:

Given all these reasons, I decided to spend some time and effort to find a way to test React Native apps in other runners like Mocha.js. I'll be happy to help with a PR to loosen the coupling between UserEvent and Jest. I agree with the conditions you mentioned for this change, just one implementation detail question:

Thanks a lot for your help! 🙂

mdjastrzebski commented 4 months ago

@JoseLion thanks for thorough explanation.

Re event objects and jest.fn(): I think putting there jest.fn() indeed is not very usable in tests, as users cannot control that object from the testing code, and the tested code should not be aware of Jest either. Therefore, we can swap it as noop and not consider it breaking.

Looking forward for your PR.

JoseLion commented 4 months ago

@mdjastrzebski, thanks for the clarification. With that in mind, I opened #1571, which solves this issue 🙂

mdjastrzebski commented 4 months ago

@JoseLion thanks for the PR. Does replacing jesf.fn() (+ fake timer check) solves the whole issues for your use case? Or is there something else that would need to be changed as well?

JoseLion commented 3 months ago

@mdjastrzebski Yes, replacing jest.fn() solves the problem! 🎉

I was also able to configure the fake timers with Sinon.js, but I found a couple of things:

mdjastrzebski commented 3 months ago

re advancedTimers was specifically added to allow for using Sinon fake timers. If there is still a problem with it, then pls open a separate issue/PR about it.

JoseLion commented 3 months ago

Got it! I'll open a separate issue for the first point. About the second point, the problem was on my end, advanceTimers is working perfectly with Sinon fake timers 🎉

mdjastrzebski commented 3 months ago

This issue has been resolved in RNTL v12.4.4! 🎉