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

How to use contains with function props? #204

Closed
smacker opened this issue Feb 18, 2016 · 11 comments
Closed

How to use contains with function props? #204

smacker opened this issue Feb 18, 2016 · 11 comments

Comments

@smacker
Copy link
Contributor

smacker commented Feb 18, 2016

contains method makes very simple testing complex component and don't care about markup. Something like this works great:

const MyComponent = () => (<div><Input /></div>)

it('should render input', () => {
    const wrapper = shallow(<MyComponent />);
    expect(wrapper.contains(<Input />)).to.equal(true);
});

But if I have function props, it doesn't work anymore:

class MyComponent extends React.Component {
    _onChange() {}

    render() {
        return <div><Input onChange={this._onChange.bind(this)} /></div>;
    }
}

it('should render input', () => {
    const wrapper = shallow(<MyComponent />);
    expect(wrapper.contains(<Input onChange={what_should_I_put_here?} />)).to.equal(true);
});

In some cases it is possible to make binding in constructor (then I can get function from instance and pass as prop), but not always.

Do you see any possible solution for this problem? (for example https://github.com/algolia/expect-jsx just skip all function props when compere jsx)

@ljharb
Copy link
Member

ljharb commented Feb 18, 2016

@smacker generally you'd want to do this.onChange = this.onChange.bind(this) in the constructor anyways, to avoid creating a function in the render path.

If you do that, then wrapper().instance().onChange would be the function.

@smacker
Copy link
Contributor Author

smacker commented Feb 18, 2016

I mentioned that it's possible to move binding in constructor in some cases, but not always.

Consider this examples:

items.map((item) => <Input onChange={this._onInputChange.bind(this, item.type))} />

items.map((item) => <Input onChange={this._makeOnChangeCallback(item.name)} />

@ljharb
Copy link
Member

ljharb commented Feb 18, 2016

Ah, true you did. In that case, you created a new function and didn't store it anywhere else - it doesn't seem possible to locate things based on that function. You'd probably want to spy on the onInputChange etc prior to calling render, and then assert that the prop function calls through to your original.

@smacker
Copy link
Contributor Author

smacker commented Feb 18, 2016

Could you please give a example with onChange={this._onChange.bind(this, item.type)}?
I ended up with a very ugly code, that overrides bind method :(

My point is, that in some cases I want to find component, but skip some props, i'll tests a behavior in other tests like:

it('should contain input', () => {
  expect(wrapper.contains(<Input myProp='I want to check' />)).to.equal(true);
});

it('change of input should set name state', () => {
  const newValue = 'newValue';
  wrapper.find(Input).props('onChange')(newValue);
  expect(wrapper.getState('name')).to.equal(newValue);
})

In this case I don't care about private callbacks that were passed to component, I test only behavior.

@ljharb
Copy link
Member

ljharb commented Feb 18, 2016

So, in this case, I'd do something like the following:

it('should render input', () => {
    const wrapper = shallow(<MyComponent />);
    const input = wrapper.find(Input);
    expect(input).to.have.length(1);
});

@smacker
Copy link
Contributor Author

smacker commented Feb 18, 2016

Okay, thanks. It will work:

it('should render input', () => {
    const wrapper = shallow(<MyComponent />);
    const inputs = wrapper.find(Input);
    expect(inputs.filter((input) => input.prop('myProp') === 'I want to check').to.have.length(1);
});

but

it('should render input', () => {
    const wrapper = shallow(<MyComponent />);
    expect(wrapper.contains(<Input myProp='I want to check' />)).to.equal(true);
});

looks nicer :(

@smacker smacker closed this as completed Feb 18, 2016
@ljharb
Copy link
Member

ljharb commented Feb 18, 2016

Unfortunately there's just no way to do it cleanly in the case you mentioned (where you are forced to create a function in the render path).

@smacker
Copy link
Contributor Author

smacker commented Feb 18, 2016

there is.

expect(
  wrapper.contains(<Input myProp='I want to check' />, skipProps=['onChange'])
).to.equal(true);

or something like this

@ljharb
Copy link
Member

ljharb commented Feb 18, 2016

hmm, interesting idea. I feel like in that case you might want .contains(Input, subsetOfProps) instead of jsx tho

@smacker
Copy link
Contributor Author

smacker commented Feb 18, 2016

.contains(Input, subsetOfProps) is okay too. jsx just looks prettier.

@mathieuancelin
Copy link
Contributor

Hi guys,

I had exactly the same issue with contains/equals functions, so I wrote some code here to be able to write nicer tests.

mathieuancelin@4ee71fa

It's just 2 new functions, rightEquals and rightContains (of course, I can change the names) that will compare the rendered element to the expected element and not the other way around.

You can look at the tests to see how to use it

https://github.com/mathieuancelin/enzyme/blob/4ee71fade969a7be3f2d90caebb7ce1777991e1c/test/ShallowWrapper-spec.js#L2269-L2312

If you're interested, just let me know.

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

No branches or pull requests

3 participants