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

Add snapshot test suite for React Native support #328

Merged
merged 8 commits into from
Mar 6, 2018

Conversation

eliperkins
Copy link
Contributor

@eliperkins eliperkins commented Feb 7, 2018

What:
This adds a rudimentary test suite for the React Native build of Downshift, leveraging Jest snapshots. The goal of this suite is to ensure that Downshift can render React Native components, without calling into methods that are specific to ReactDOM.

Why:

Related to a conversation in #185 (comment)

How:

Adds in a new test suite in other/react-native, as well as a new command, npm test:native. To support testing in React Native, we require a few more dev dependencies: babel-jest, babel-preset-env, babel-preset-react-native, and of course react-native itself.

Checklist:

  • Documentation N/A
  • Tests
  • Ready to be merged
  • Added myself to contributors table N/A


module.exports = Object.assign(jestConfig, {
preset: 'react-native',
testEnvironment: 'node',
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This song and dance felt pretty gnarly, but this is the base config I've found that allows for sharing of kcd-scripts/config and supporting React Native in react-test-renderer. This ensures that the react-native module gets transformed by Babel, tells Jest to look for sources and tests from the root, but then scopes tests down to only other/react-native/__tests__.

package.json Outdated
@@ -15,6 +15,7 @@
"lint": "kcd-scripts lint",
"test": "kcd-scripts test",
"test:cover": "kcd-scripts test --coverage",
"test:native": "kcd-scripts test --config other/react-native/jest.config.js --rootDir .",
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Additionally, doing some more reading around Jest and what people use for configs, using a config file combined with --rootDir . was the most flexible way to set this up:
jestjs/jest#2670
jestjs/jest#3613


import React from 'react'
import {Text, TextInput, TouchableOpacity, View} from 'react-native'
import Downshift from '../../dist/downshift.native.cjs'
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, CI throws a linting error when importing a *.cjs file, but without specifying this, jest-resolve can't track this module down: https://travis-ci.org/paypal/downshift/builds/338729428#L563

Should we disable the linting rule here?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah. You could probably do the same thing at the bottom of this file as we do at the bottom of this file 👍

Copy link
Member

@kentcdodds kentcdodds left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for this @eliperkins! Just a few thoughts and ideas.


import React from 'react'
import {Text, TextInput, TouchableOpacity, View} from 'react-native'
import Downshift from '../../dist/downshift.native.cjs'
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah. You could probably do the same thing at the bottom of this file as we do at the bottom of this file 👍

@@ -0,0 +1,315 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm normally not a huge fan of huge snapshots (learn more). But I think this is ok because I don't think we plan on making changes that would affect this snapshot very often anyway. But if there's a way we can avoid these snapshots by making more explicit assertions I think I'd be happier with that.

Copy link
Contributor Author

@eliperkins eliperkins Feb 8, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed. I tried thinking of a better way to get here, instead of using snapshots, but I'm unsure of what other methods we could use to test the React Native renderer.

We could emulate the Preact tests and use a spy to ensure that render successfully gets called, but I don't think that's any better than these snapshots.

I'll see if I can come up some better assertions here.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I ended up writing some tests that were similar to those Preact tests and I feel like it's much clearer as to what we're testing now! 216c531

Let me know what you think.

package.json Outdated
@@ -23,7 +24,7 @@
"storybook": "start-storybook -p 6006 -c stories",
"storybook:build": "cd stories && npm install && cd .. && build-storybook -c stories",
"setup": "npm install && npm run storybook:build -s && npm run validate",
"validate": "kcd-scripts validate lint,build-and-test,test:cover,test:ts,test:ssr",
"validate": "kcd-scripts validate lint,build-and-test,test:cover,test:ts,test:ssr,test:native",
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We need to make sure that the test:native runs after the build finishes, so let's actually make this part of the npm run test:build script. Could we do this?

"test:build": "jest --projects other/misc-tests other/react-native"

I think that'll work. Might need to make a few changes (like set the rootDir in the config (using the path module?) rather than via a flag). That way it runs both of the tests in parallel, then we don't need to change the validate script, and we also wouldn't need to add a test:native script.

Does that make sense?

Copy link
Contributor Author

@eliperkins eliperkins Feb 8, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think so!

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Man, Jest rocks. Using --projects works perfectly!

@kentcdodds
Copy link
Member

Thanks so much for your help! I've added you as a collaborator on the project. Please make sure that you review the other/MAINTAINING.md and CONTRIBUTING.md files (specifically the bit about the commit messages and the git hooks) and familiarize yourself with the code of conduct (we're using the contributor covenant). You might also want to watch the repo to be notified when someone files an issue/PR. Please continue to make PRs as you feel the need (you can make your branches directly on the repo rather than your fork if you want). Thanks! And welcome to the team :)

@codecov-io
Copy link

codecov-io commented Feb 8, 2018

Codecov Report

Merging #328 into master will not change coverage.
The diff coverage is n/a.

Impacted file tree graph

@@          Coverage Diff          @@
##           master   #328   +/-   ##
=====================================
  Coverage     100%   100%           
=====================================
  Files           4      4           
  Lines         330    330           
  Branches       85     85           
=====================================
  Hits          330    330

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 1a80733...4d35baf. Read the comment docs.

}),
)
const tree = renderer.toJSON()
expect(tree).toMatchSnapshot()
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We could remove this snapshot, but I think ensuring that we render the basic set of React Native components here, while applying the functions from Downshift, is a fair trade-off for using a snapshot.

Copy link
Member

@kentcdodds kentcdodds left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looking great! Just a few questions.

expect(renderSpy).toHaveBeenCalledTimes(1)
})

test('getInputProps composes onChange with onChangeText', () => {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this is more incidental. I think most people will want to be able to pass onChangeText instead of onChange if that's what they're used to. Is that currently possible? Could we have our tests ensure that?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, this is actually a poor test, and one difference between React Native and ReactDOM that should be documented.

onInput is not a valid prop for TextInput, but in this case, Downshift is still composing the onChange prop with a new function for onInput, and then calling both. The onInput prop has no affect on the TextInput in this case.

To emulate this behavior on React Native, perhaps we would compose onChange with onChangeText, where onChangeText is just a convenience wrapper for onChange(event.nativeEvent.text). There's no other events that come down through the onChange prop on TextInput, like there would be on <input> in ReactDOM; TextInput has the prop onSelectChange to handle things like that.

When I get to documenting this difference (as we talked about in #265), I'll be sure to call this out. We might even be able to optimize this a bit for React Native by forgoing any prop composition, since this case isn't quite valid for us anyway.

Right now, the behavior of the Downshift RN implementation is for getInputProps to accept onChange as the function and apply it to the TextInput as onChangeText. Since onChange and onChangeText have different arguments, perhaps it'd be better to make a special case for this in the RN implementation that allows for using either function with getInputProps and applying it accordingly.

What do you think?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I want people to assume that the arguments to the prop getters to be the props they want applied. So if normally they would use onChangeText={this.handleOnChangeText} in their JSX, then they should use onChangeText: this.handleOnChangeText in the function call.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What's the status here?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, sorry, the status of this is that I went on vacation for a bit 😬

I think there's an API change that'll need to happen to address this (for React Native). When I originally implemented this React Native support, I didn't have a full grasp of the Downshift API, so I allowed for the same onChange prop to be supplied, but then used it as onChangeText.

I think the correct API way forward would be to support both onChange and onChangeText, but that may be out of the scope of this PR, which is to simply add a React Native test suite.

I'll make an issue to track this.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I filed #361 to track this for now. I'll remove this test since it's not entirely valid.

package.json Outdated
@@ -18,7 +18,7 @@
"test:ssr": "kcd-scripts test --config other/ssr/jest.config.js --no-watch",
"test:update": "npm run test:cover -s -- --updateSnapshot",
"test:ts": "tsc --noEmit -p ./tsconfig.json",
"test:build": "kcd-scripts test --config other/misc-tests/jest.config.js --no-watch",
"test:build": "jest --projects other/misc-tests other/react-native --no-watch",
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can probably remove the --no-watch from this.

This adds a rudimentary test suite for the React Native build of Downshift, leveraging Jest snapshots. The goal of this suite is to ensure that Downshift can render React Native components, without calling into methods that are specific to ReactDOM.
Since we're using a unique "file extension" here (read: `downshift.native.cjs.js`), ESLint thinks the module isn't a valid module.
This leverages Jest projects to use both different configs for misc-tests and react-native
This emulates the style of the Preact tests, ensuring that we can use different parts of the Downshift API in another environment.
This is the default behavior for `jest` anyway.
Copy link
Member

@kentcdodds kentcdodds left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is super! Thanks!

@kentcdodds kentcdodds merged commit 87e8827 into downshift-js:master Mar 6, 2018
@eliperkins eliperkins deleted the react-native-tests branch March 26, 2018 19:59
Rendez pushed a commit to Rendez/downshift that referenced this pull request Sep 30, 2018
* Add snapshot test suite for React Native support

This adds a rudimentary test suite for the React Native build of Downshift, leveraging Jest snapshots. The goal of this suite is to ensure that Downshift can render React Native components, without calling into methods that are specific to ReactDOM.

* Disable ESLint import/extensions and import/no-unresolved

Since we're using a unique "file extension" here (read: `downshift.native.cjs.js`), ESLint thinks the module isn't a valid module.

* Add React Native test to test:build command

This leverages Jest projects to use both different configs for misc-tests and react-native

* Create explicit assertions for React Native test cases

This emulates the style of the Preact tests, ensuring that we can use different parts of the Downshift API in another environment.

* Remove unnecessary --no-watch argument

This is the default behavior for `jest` anyway.

* Remove invalid test for React Native

This test doesn't assert the intended behavior, which is composing onChange and onChangeText

* Update snapshots

* Add test to call onChangeText on TextInput components for React Native
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants