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

Multi selection - implements #22 #23

Closed
wants to merge 33 commits into from
Closed

Multi selection - implements #22 #23

wants to merge 33 commits into from

Conversation

geowarin
Copy link

Implements #22.

I've added tests for every use case detailed in the issue.
There are a couple of things that can be improved immediately :

  • Since we have the element() method, I think we can safely remove the params methods in tests.js which becomes somewhat obsolete. This will allow to remove the ugly unarray method
  • The find middleware has become a bit more complicated but I think that's ok. Anyway, we can find much better names for the functions declared in there

I'm looking forward for your reviews and comments :)

This makes the API more uniform.
Previously, elements could be either an array or a single element which forces
middleware authors to handle both cases.

For the users, when calling the test() method, helpers.elements will always contain arrays.
However, they can still benefit from the shortcut syntax test({myElement}).
In that case, elements will be 'unnaraid', which means that that they can either get an array
of elements or a single element depending on their component layout.
@geowarin
Copy link
Author

I also added two middlewares:

The Index middleware

Allows to select a specific element after a multi find

Test(<TableComponent/>)
  .find('table', {multi: false})
  .find('td', {multi: true})
  .index(1)
  .element(td => {
    expect(td.getDOMNode().textContent).to.equal('2')
  })

The setValues middleware

Test(<FormComponent />)
  .find('input', {multi: true})
  .setValues(['username', 'password'])

This middleware allows to select a single element after a multi find
@zackify
Copy link
Member

zackify commented Sep 25, 2015

I'll look this over tonight!

import {Find, SetState, Simulate} from './middleware'
import {Find, SetState, Simulate, SetValues, Index} from './middleware'

const unarray = (elems) => {
Copy link
Member

Choose a reason for hiding this comment

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

Since we're making breaking changes, might as well remove this like you said.

@zackify
Copy link
Member

zackify commented Sep 26, 2015

So it all looks good to me, just remove the params method, update the readme, add a list of the breaking changes, and I'll merge it unless if @dphaener or @kenwheeler see anything wrong. We need to start a wiki with all of the options and examples

@@ -1,18 +1,62 @@
import React from 'react'
let { TestUtils } = React.addons
import React from 'react';
Copy link
Contributor

Choose a reason for hiding this comment

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

Why add semicolons here?? May be you can add no-semicolon rule to the eslintrc. So, the style is consistent across the codebase.

Copy link
Member

Choose a reason for hiding this comment

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

Agreed, call me evil, but let's remove them all.

@@ -1,13 +1,13 @@
import React from 'react'
import {Component} from 'react'
Copy link
Contributor

Choose a reason for hiding this comment

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

You need to do import React, { Component } from 'react'

Copy link
Author

Choose a reason for hiding this comment

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

Why? The React object is not used in those components

Copy link
Contributor

Choose a reason for hiding this comment

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

You are. All the JSX components you write are converted to React.createElement(tagName, ...) Babel transpiles this but it doesn't import it.

const {elements, name} = findSingleOrMulti(rootElement, selector)

if (elements.length === 0)
throw new Error(`Could not find element "${selector}"`)
Copy link
Author

Choose a reason for hiding this comment

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

So there was a debate about this throw between me and @vramana.
I think we should throw early to improve user feedback and be able to write a test to assert this behavior.

On the other hand if we don't throw, elements[name] will be undefined somewhere later.
@zackify, thought?

Copy link
Member

Choose a reason for hiding this comment

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

Throwing an error there is fine with me

This can be useful to simulate a change event on a bunch of inputs for instance
Rename the 'tests' folder to 'test', which is more standard and moved mocha.opts in there
Make the tests fall in two folders: core and middlewares
Make eslint a post-test script.
})
~~~

But if you use the `multi` option, you can find nested elements:
Copy link
Member

Choose a reason for hiding this comment

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

You should make multi the default, like you said. Also, I don't think I like the root option. The component instance is always available inside the test method if you need it

@geowarin
Copy link
Author

Ok guys, the final API is like this:

Test(<FormComponent login={spy}/>)
  .find('input').setValues(['username', 'password']) // multi by default
  .and() // come back to the root
  .find('form').first().simulate('submit')
  .test(() => {
    expect(spy.calledWith('username', 'password')).to.be.true;
  })

You can now use the first(), last() and index() middleware if you want to work on a specific element.

What do you think?

@vramana
Copy link
Contributor

vramana commented Sep 28, 2015

In your example, lets say the sumbit returns error and we want to test with new values for username and password. Then I have to do find('input'), find('form'), simulate('submit') all over again. But we already have references for these elements, Why do we have to find them?

One way to avoid this is to return is to check if the element is present in elements. But this doesn't allow form element to be over overridden by some other form inside the component. I feel that we are testing a component with lots of elements it is slightly hard to this via this api.

For finding nested elements, isn't it simpler to write a query like this find('table td') Then the immediate question is what will be the name of it in elements. May be we can pass an extra argument specifying name of the element i.e, find('cells', 'table td').

Are there any generic cases where test method is useful ?? We can do most of things by element method. I feel it should be the way to go because having one unambiguous way to write tests helps.

@@ -0,0 +1,3 @@
export default function And() {
this.root = this.instance
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 be a method inside Test class because it doesn't fit the definition of a middleware.

@geowarin
Copy link
Author

@vramana I like your suggestions, especially the possibility to name the elements that you find.
However, this does not allow naming only the first button for instance if I have a bunch of them.

Right now, the test() method is useful to assert things on the component like its props or state.
But we could create an instance() method that injects the component instance.

@geowarin
Copy link
Author

I also fear that leaving the possibility for the user to find something complicated like find('table .nested td') will make them want to do find('table > td.nested') and we will soon be reinventing jquery selectors.

@geowarin geowarin mentioned this pull request Sep 28, 2015
7 tasks
@vramana
Copy link
Contributor

vramana commented Sep 28, 2015

@geowarin I don't think we need to reinvent them. Even I was thinking this for a while. I think I found a solution. Once we have the DOM for table we can use querySelectorAll on it to filter of elements. I think it will work in most cases. May be I am missing something. I'll think more about this.

@geowarin
Copy link
Author

That would be awesome, except you cannot call ReactUtils.simulate() on a DOM node, can you?

@vramana
Copy link
Contributor

vramana commented Sep 28, 2015

Yeah. This is going to be hard I guess. Let me think what can be done.

@geowarin
Copy link
Author

This has become a messy PR. I'm closing this waiting for #24 to rebase upon.
I invite @vramana, @zackify and any person who is interested in proposing an API for nested searches to continue the discussion here: #22.

Of course, I am volunteering for a new PR 😄

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.

None yet

3 participants