Skip to content
This repository has been archived by the owner on Mar 12, 2020. It is now read-only.

[Testing] Test layout in RelatedArtist + Gene test fix #409

Merged
merged 8 commits into from
Dec 13, 2016
Merged

Conversation

sarahscott
Copy link
Contributor

@sarahscott sarahscott commented Dec 2, 2016

Jest doesn't automatically call onLayout, so this test started failing after I updated Related Artists with a more responsive approach.

I put together a proof-of-concept test for injecting width into onLayout in the RelatedArtist component. The nice thing about this approach is we can specify any dimensions we want in the mocked nativeEvent and can make them correspond to actual expected values for iPhone and iPad.

}
}>
<ImageView
imageURL=""
style={
Object {
"height": 105,
Copy link
Contributor Author

@sarahscott sarahscott Dec 2, 2016

Choose a reason for hiding this comment

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

This was happening because the layout would rely on Dimensions, which would assume iPhone dimensions if window returned nothing.

Object {
"margin": 5,
"paddingBottom": 20,
"width": 242,
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Now we have a calculated value for width based on the mocked nativeEvent width.

@sarahscott sarahscott changed the title [Testing] Updated gene test [Testing] Test layout in RelatedArtist + Gene test fix Dec 2, 2016
tree.props.onLayout(mockNativeEvent)

// re-render
tree = component.toJSON()
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 dance is admittedly a bit cumbersome but could be wrapped?

Copy link
Contributor Author

Choose a reason for hiding this comment

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


jest.mock('../../opaque_image_view.js', () => 'ImageView')

it('lays out correctly', () => {
Copy link
Contributor

Choose a reason for hiding this comment

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

so we could make a few of these, for iPhone and iPad, say?

const mockNativeEvent = {
nativeEvent: {
layout: {
width: 768,
Copy link
Contributor

Choose a reason for hiding this comment

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

and then change the associated widths here - and get different dom outputs

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes. The problem with this approach though is that the parent components can't propagate layout down to the children, so the higher-level snapshots will be sort of inaccurate. i.e. in this example, the Gene's About section snapshot has a RelatedArtists component with a width of 1.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

So this is great for testing layout logic in the component itself but not in anything it instantiates. It would be lovely to have some sort of global LayoutEvent type thing, so I'll look into that next.

Copy link
Contributor

Choose a reason for hiding this comment

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

ohhh interesting. Thanks for explaining!

}

let component = renderer.create(<RelatedArtists artists={artists}/>)
let tree = component.toJSON()
Copy link
Contributor

Choose a reason for hiding this comment

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

so you have to json-ify the component, trigger onlayout, and then jsonify again? e.g. skipping L29 wouldn't work? :o

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yeah... that's the annoying thing. I am using the approach here for manually triggering a callback.

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 could be wrapped in a helper method though. Something like runOnLayout(component).

Copy link
Contributor

Choose a reason for hiding this comment

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

Agreed, the toJSON and tree var are a little confusing. I'd suggest having that helper take the dimensions and do the mocking in there as well.

Copy link
Contributor

Choose a reason for hiding this comment

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

yeah, this all looks 👍

@mennenia
Copy link
Contributor

mennenia commented Dec 2, 2016

Great stuff!

@sarahscott
Copy link
Contributor Author

Maybe we can mock LayoutEvents the way we mock ImageView here

@orta
Copy link
Contributor

orta commented Dec 3, 2016

Shouldn't need to do mocking, as an event is just a normal javascript object - you can pass in whatever you want, here's a test where I pass in a SwitchEvent -> https://github.com/artsy/emission/pull/406/files#diff-0bc03f13698e18df3c3bb6d44c2f6e75R41

Ah yeah- that's exactly what you did

@orta
Copy link
Contributor

orta commented Dec 3, 2016

Aye, looks a tad cumbersome - but that's what they're doing - seems like the right way to do it to me @sarahscott 💯

Want to look into a helper method in this PR or anohter?

@sarahscott
Copy link
Contributor Author

I think I can timebox myself for the wrapper method in this PR - it would be a nice precedent to set. The reason I would consider mocking is so that a parent component like the About view could instantiate all of its children using the same LayoutEvent instead of having to pass that down through props or something.

I think if we make the assumption that parent components are only testing logic and fonts and maybe their own layouts, and that the children in their snapshots will be layout-less, the approach in this pr is fine. It would be nice if the snapshots were more accurate though.

@orta
Copy link
Contributor

orta commented Dec 7, 2016

@sarahscott did you look at this?

@sarahscott
Copy link
Contributor Author

All set for review ☑️

@alloy
Copy link
Contributor

alloy commented Dec 7, 2016

@sarahscott Awesome, loving that test helper 👌 💯

Just a question about the location on disk, though. I’m still getting used to the “tests next to the lib files” convention of Jest, is lib/testing_utilities the convention used in the community? And seeing as the directory is called testing_utilities shouldn’t individual helpers be in separate files (that use export default TheFunction) rather than having a single helpers.js file?

@sarahscott
Copy link
Contributor Author

I was looking at how metaphysics organizes smaller helper functions and modeled the helpers.js file after this because I assume there will be other helper methods in the near future. I'm happy to adopt the convention of one-file-per-function if there's a good reason to, though.

I'll look into conventions within the community today. I was thinking the testing_utilities folder would be a place for methods that can be used by tests for any container or component, even though the tests themselves are next to the lib files.

@orta
Copy link
Contributor

orta commented Dec 8, 2016

I feel things like these should probably go in the __tests__ folders, I wouldn't think to look right back at the root, closest to what they are the domain of, so I'd have expected this file in lib/containers/__test__/helpers/layout.js

I looked through the Jest source code, and couldn't find any examples of using helper functions in there tests. So I couldn't get anything useful from there.

@@ -0,0 +1,19 @@
import renderer from 'react-test-renderer'

export const setupWithLayout = (component, layout) => {
Copy link
Contributor

Choose a reason for hiding this comment

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

this should probably be more descriptive, and ideally be typed - I wouldn't be able to know from the name what objects to put in, perhaps something like?

/** Renders a React Component after applying a nativeEvent to an onLayout function passed in to props */
export const jsonAfterApplyingNativeLayoutDimentions = (component: any, layout: { width?: number, height?:number } ) => {

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 agree with adding the comments but the method name is too verbose 😉 !

@sarahscott
Copy link
Contributor Author

But what if a component (instead of a container) needs those methods? Would we traverse from the components folder into the containers folder's __tests__?

@sarahscott
Copy link
Contributor Author

I know I'm going to need the exact same method for testing components. If we really don't want a lib- or project-wide testing_utilities folder, I think the only other way would be to make a separate utilities folder or file for both components and containers. The reason I didn't do this here is because that would require code duplication. If it would really improve our testing experience though, I'm open to that.

@@ -1,6 +1,7 @@
import renderer from 'react-test-renderer'

export const setupWithLayout = (component, layout) => {
/** Renders a React Component with specified layout using onLayout callback */
export const renderWithLayout = (component: any, layout: { width?: number, height?:number }) => {
Copy link
Contributor

Choose a reason for hiding this comment

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

Inconsistent spacing in layout type info.

@sarahscott
Copy link
Contributor Author

@orta bump

@orta
Copy link
Contributor

orta commented Dec 12, 2016

Alright - yeah, that makes sense, those two are both siblings on the same hierarchy, so IMO it should probably go in lib/__tests_/helpers/file.js

@sarahscott
Copy link
Contributor Author

I think using a __tests__ folder inside lib would be confusing because it implies there are actual tests in that folder when actually there are only utilities. I'd like to keep the folder named as it is for now and if it proves to be an issue we can change it later.

@orta
Copy link
Contributor

orta commented Dec 12, 2016

Personally, I'm not sold, but if you're sure 👍 - we do need to put tests on the files in there anyway

But you shouldn't move the __mocks__ folder into that folder, I'm surprised my tests pass at all with that changed.

@sarahscott
Copy link
Contributor Author

I see - __mocks__ is supposed to go in the same parent directory as node_modules (it was previously inside lib).

@alloy do you have an opinion on the name for this shared directory of testing utilities? Is __tests__ significantly clearer?

@orta
Copy link
Contributor

orta commented Dec 12, 2016

I see - mocks is supposed to go in the same parent directory as node_modules (it was previously inside lib).

ah nice one 👍

My point is to not make a generic testing_utilities folder, but to say it provides functions whose scope is from the lib folder downwards, and that like all testing related code - you can find it inside a __tests__ folder.

@alloy
Copy link
Contributor

alloy commented Dec 13, 2016

@sarahscott I agree with @orta 👍

@orta
Copy link
Contributor

orta commented Dec 13, 2016

👍

@orta orta merged commit 11906b4 into master Dec 13, 2016
@orta orta deleted the sarah-test-fix branch December 13, 2016 16:33
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants