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

Any possibility of using this for react native project? #22

Closed
amay opened this issue Mar 26, 2018 · 22 comments
Closed

Any possibility of using this for react native project? #22

amay opened this issue Mar 26, 2018 · 22 comments
Labels
enhancement New feature or request help wanted Extra attention is needed needs discussion Whether changes are needed is still undecided and additional discussion is needed.

Comments

@amay
Copy link

amay commented Mar 26, 2018

I really appreciate the guiding principles behind this library, have felt some of the pain associated with the drawbacks you all have identified with enzyme, and I'm curious how much work would be involved to get this library to work for a react native project? Given the reliance on DOM testing, I'm guessing it might not be possible or a good idea. Any guidance is greatly appreciated.

@kentcdodds
Copy link
Member

Hi @amay!

I'd love react native support, but I have no idea how hard that would be... I know very little about react native. I'm pretty sure the only way to make this work is if you could render react-native components to the DOM. I'm not sure whether there's anything out there which makes this possible though...

I'd love to look at a PR for it so long as it doesn't make this library more complex to use or over-complicate the implementation. Anyone can feel free to investigate this 😄

@kentcdodds kentcdodds added enhancement New feature or request help wanted Extra attention is needed needs discussion Whether changes are needed is still undecided and additional discussion is needed. labels Mar 27, 2018
@amay
Copy link
Author

amay commented Mar 27, 2018

Hey @kentcdodds, I had some luck getting this working with some (admittedly nasty) hacking. I will post here in case it is helpful to anyone else:

// in setupTests.js which is configured as jest's setup file.
// Adapted from https://blog.joinroot.com/mounting-react-native-components-with-enzyme-and-jsdom/ and
// https://stackoverflow.com/questions/42039383/how-to-use-enzyme-for-react-native-with-jest

import { JSDOM } from 'jsdom'
// used to fully render native components as react components
import 'react-native-mock-render/mock'

// copy props needed to render HTML from JSDOM window to node's global object
function copyProps(src, target) {
  Object.getOwnPropertyNames(src)
    .filter(prop => typeof target[prop] === 'undefined')
    .forEach(prop => {
      // eslint-disable-next-line no-param-reassign
      target[prop] = src[prop]
    })
}

const { window } = new JSDOM()

global.window = window
global.document = window.document
global.navigator = {
  userAgent: 'node.js',
}
copyProps(window, global)

// react doesn't like some of the props that are set on native components (that eventually are set on DOM nodes, so suppress those warnings
const suppressedErrors = /(React does not recognize the.*prop on a DOM element|Unknown event handler property|is using uppercase HTML|Received `true` for a non-boolean attribute `accessible`|The tag.*is unrecognized in this browser)/
// eslint-disable-next-line no-console
const realConsoleError = console.error
// eslint-disable-next-line no-console
console.error = message => {
  if (message.match(suppressedErrors)) {
    return
  }
  realConsoleError(message)
}

With this, I can get a test like the following to pass in jest:

import React from 'react'
import { StyleSheet, Text, TouchableHighlight } from 'react-native'
import PropTypes from 'prop-types'
import { render, Simulate } from 'react-testing-library'

const Button = ({ onPress, text }) => (
  <TouchableHighlight onPress={onPress}>
    <Text>{text}</Text>
  </TouchableHighlight>
)

test('renders text', () => {
  const { getByText } = render(<Button text="Test Button" />)

  expect(getByText('Test Button').textContent).toBe('Test Button')
})

However, I can't get calls to Simulat to work because press is not a supported event type in react-dom:

test('invokes onPress prop on press', () => {
  const onPress = jest.fn()
  const { container } = render(<Button onPress={onPress} />)

  Simulate.press(container) // throws error
  // Simulate.click(container) doesn't work because it doesn't trigger press event

  expect(onPress).toHaveBeenCalled()
})

So far I haven't been able to think of any way to extend react-dom/test-utils to send the press event.

@kentcdodds
Copy link
Member

Awesome!

I'm pretty sure that you shouldn't have to do the JSDOM stuff because jest will handle that all by default anyway (perhaps in your tests you're specifying a different testEnvironment which is why that's not working).

The console.error stuff is fine but probably not something I want to include in the library, but we could point to an example or some other lib which handles this.

Also the import 'react-native-mock-render/mock' is something I think I don't want included in the library and would rather have that be something you have to do yourself.

As for the simulate stuff, I'm not sure what the code would entail, but you should be able to simply: Object.assign(Simulate, customEventSimulators).

Now that I think about it though, there's not a whole lot that I want to include in the library itself... Perhaps we could just have some examples of the setup you'd need to do to support React Native 🤔

@kentcdodds
Copy link
Member

At the very least, I'd like to have people doing things in the wild before we decide on including anything into the core so we have time to decide what works best.

@amay
Copy link
Author

amay commented Mar 28, 2018

Thanks for the tip on testEnvironment. It was being set to node which is why the JSDOM stuff didn't work.

After doing the research into what was needed to get this working, I definitely agree that it doesn't make sense to try to incorporate these changes as this package is really about DOM testing.

I did end up getting Simulate.press to work w/ again some trickery. I'll post it here in case a future dev wants to go down this road, but I'm okay if you want to close out this issue for now.

Here is the final setupTests.js file for setting up jest to work with this test lib:

import 'react-native-mock-render/mock'
import { Simulate } from 'react-dom/test-utils'

function suppressDomErrors() {
  const suppressedErrors = /(React does not recognize the.*prop on a DOM element|Unknown event handler property|is using uppercase HTML|Received `true` for a non-boolean attribute `accessible`|The tag.*is unrecognized in this browser)/
  // eslint-disable-next-line no-console
  const realConsoleError = console.error
  // eslint-disable-next-line no-console
  console.error = message => {
    if (message.match(suppressedErrors)) {
      return
    }
    realConsoleError(message)
  }
}

function setupSimulatePress() {
  Simulate.press = Simulate.click

  jest.mock('TouchableHighlight', () => {
    // eslint-disable-next-line global-require
    const React = require('react')
    const RealComponent = require.requireActual('TouchableHighlight')
    const TouchableHighlight = props =>
      React.createElement(
        'TouchableHighlight',
        {
          ...props,
          onClick: props.onPress,
        },
        props.children
      )
    TouchableHighlight.propTypes = RealComponent.propTypes
    return TouchableHighlight
  })
}

suppressDomErrors()
setupSimulatePress()

@kentcdodds
Copy link
Member

I think that code would make a handy package by itself. I definitely would love to reduce friction for folks testing with React Native, I'm just not 100% certain I want to commit to building-in a RN implementation at the moment. Would you be interested in making your code a package then make a PR to this repo to show how to integrate it?

@kentcdodds
Copy link
Member

I just noticed that code says: React.createElement('TouchableHighlight', ... but I think it should probably say: React.createElement(RealComponent, ...

@amay
Copy link
Author

amay commented Apr 9, 2018

Hey @kentcdodds I agree with your proposal that this functionality not be included in this simple core library. I'm dogfooding my approach on a project right now, and if it goes well, I'm definitely interested in adding a simple package to simplify the test setup for react native projects. I'll post back here when I've made progress, but I'll close this out for now.

@amay amay closed this as completed Apr 9, 2018
@kentcdodds
Copy link
Member

Super. Thanks @amay! I can't wait to see the package if it turns up on npm :) I'd love to link to it in the README here :)

@kentcdodds
Copy link
Member

Hey @amay! Folks are curious to hear how your experiment went! Is this something that'll work for react native?

@hnordt
Copy link

hnordt commented Jun 4, 2018

@kentcdodds @amay any news on this topic? Thanks!

@amay
Copy link
Author

amay commented Jun 4, 2018

Hey @kentcdodds @hnordt I didn't get to do as an exhaustive of a deep dive as I would have liked for this, but early indications suggest there would need to be some more work put in to get this to work well. The specific issues come down to bridging native behavior to the dom. E.g. you wan't react native's TextInput to behave as expected, so when rendering in the DOM, you need to bind it to an input element and then fixup any differences between how inputs work in RN vs the DOM. And that is just one example.

The react-native-mock-render project I was using doesn't attempt to do anything "smart" when mocking out the react native components, which leaves that up to us to figure out. I ended up tackling some of these issues as they came up, but in no way did I get to a place where I feel like I have a very good solution, and it definitely felt like a lot of work each time an issue came up.

I recently saw the announcement for this project: https://github.com/vincentriemer/react-native-dom, and I'm curious if using that could result in a much better and comprehensive test setup. That project ostensibly does the bridging between react native components to true DOM components, so in theory it could work very well. I haven't had time to test it out unfortunately.

@hnordt
Copy link

hnordt commented Jun 4, 2018

@amay thanks for the quick answer.

I think we might need to clone dom-testing-library and create a native-testing-library. We probably need to use something like the "Direct Manipulation API": https://facebook.github.io/react-native/docs/direct-manipulation.html

I think emulating React Native into a fake dom is not a good idea cause you are not testing the app in the real environment it's gonna run, so you won't be confident about your tests anyway.

@kumarpatel
Copy link

Any update on this?

@kentcdodds
Copy link
Member

I've not been made aware of any progress on making react-testing-library work nicely with react native. I'd love it someone could build something though!

@jlongster
Copy link
Contributor

I'm using it by rendering component with react-native-web. It works pretty well but you have to structure your components so you can render what you want to test outside of very RN-specific libs like react-navigation, etc. While there are a few components I can't test, generally I'm able to test most of the ones that have complex business logic. The rendering doesn't look 100% perfect with what's on mobile, but it works for just testing behavior and stuff.

It actually works well enough that this is all you really need:

fireEvent['press'] = (node, init) => {
  fireEvent.mouseDown(node, init);
  fireEvent.mouseUp(node, init);
};

I use fireEvent.press to simulate a press.

Then you just renderIntoDocument your component that uses react-native. Well, you do have to configure tooling to make it work with react-native-web. This is my jest config which I think is all you need:

module.exports = {
  moduleFileExtensions: [
    'web.js',
    'ios.js',
    'mjs',
    'js',
    'json'
  ],
  moduleDirectories: ['<rootDir>/node_modules', 'node_modules'],
  testEnvironment: 'jsdom',
  setupTestFrameworkScriptFile: '<rootDir>/src/setupTests.js',
  testMatch: ['<rootDir>/src/components/mobile/**/*.test.js'],
  moduleNameMapper: {
    '^react-native$': 'react-native-web',
    // Ignore react-art. react-native-web tries to pull it in but we
    // never use or need it, and it throws errors in jsdom
    '^react-art$': 'node-noop'
  },
  globals: {
    IS_REACT_NATIVE: true
  }
};

There might be a few other things I forget, but it mostly just works. The only downsides is you run into problems pretty quickly with stuff that doesn't work under react-native-web, but if you're just looking to test the logic of isolated components, it works well enough. Anything that uses more complex stuff like a native dependency, react-navigation, etc will have to live outside of those components.

@kentcdodds
Copy link
Member

Awesome! Thanks James! Have you tried mocking those react native components to be able to test more?

Would anyone like to try this out and verify it works for you as well? Then we can add this to the docs 👌

@jlongster
Copy link
Contributor

You can't really mock react-navigation. It's a huge lib and your component will depend on it passing specific things to you. You might be able to mock native modules, depends on what they do, I haven't run in to the need for it yet.

I think I saw some commits in react-navigation talking about web support, but I haven't looked closely yet. Last I tried it didn't work, and didn't care too much about it. I need to test my critical components and I already pull them out of route-specific ones so it doesn't bother me.

@kentcdodds
Copy link
Member

Sounds good 👍 thanks again!

@thchia
Copy link
Contributor

thchia commented Sep 21, 2018

@kentcdodds this works for me. I've put up a repo showing the bare minimum setup with a single test.

@kentcdodds
Copy link
Member

That's fantastic @thchia! Could you please make a pull request to the README with a little documentation about this along with a link to your repo? Thank you team!

@thchia thchia mentioned this issue Sep 21, 2018
4 tasks
kentcdodds pushed a commit that referenced this issue Sep 21, 2018
<!-- What changes are being made? (What feature/bug is being fixed here?) -->

**What**:
Add some documentation regarding usage with React Native.

<!-- Why are these changes necessary? -->

**Why**:
First step to tackling a long(ish) standing [issue](#22) regarding this.

<!-- How were these changes implemented? -->

**How**:
Added an FAQ point discussing how one might go about using this library with React Native. Main credit to @jlongster [here](#22 (comment)). I just corroborated the findings 🙂 

<!-- Have you done all of these things?  -->

**Checklist**:

<!-- add "N/A" to the end of each line that's irrelevant to your changes -->

<!-- to check an item, place an "x" in the box like so: "- [x] Documentation" -->

- [x] Documentation
- [ ] Tests
- [x] Ready to be merged
      <!-- In your opinion, is this ready to be merged as soon as it's reviewed? -->
- [x] Added myself to contributors table
      <!-- this is optional, see the contributing guidelines for instructions -->

<!-- feel free to add additional comments -->
julienw pushed a commit to julienw/react-testing-library that referenced this issue Dec 20, 2018
@divyanshu013
Copy link
Contributor

Though this is closed, linking a library which looks very promising for people who land here (haven't used the linked library in prod yet). https://github.com/callstack/react-native-testing-library

lucbpz pushed a commit to lucbpz/react-testing-library that referenced this issue Jul 26, 2020
xbucks pushed a commit to xbucks/tauri-crypto-trading that referenced this issue Oct 15, 2023
<!-- What changes are being made? (What feature/bug is being fixed here?) -->

**What**:
Add some documentation regarding usage with React Native.

<!-- Why are these changes necessary? -->

**Why**:
First step to tackling a long(ish) standing [issue](testing-library/react-testing-library#22) regarding this.

<!-- How were these changes implemented? -->

**How**:
Added an FAQ point discussing how one might go about using this library with React Native. Main credit to @jlongster [here](testing-library/react-testing-library#22 (comment)). I just corroborated the findings 🙂 

<!-- Have you done all of these things?  -->

**Checklist**:

<!-- add "N/A" to the end of each line that's irrelevant to your changes -->

<!-- to check an item, place an "x" in the box like so: "- [x] Documentation" -->

- [x] Documentation
- [ ] Tests
- [x] Ready to be merged
      <!-- In your opinion, is this ready to be merged as soon as it's reviewed? -->
- [x] Added myself to contributors table
      <!-- this is optional, see the contributing guidelines for instructions -->

<!-- feel free to add additional comments -->
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request help wanted Extra attention is needed needs discussion Whether changes are needed is still undecided and additional discussion is needed.
Projects
None yet
Development

No branches or pull requests

7 participants