enzymejs / enzyme

JavaScript Testing utilities for React
https://enzymejs.github.io/enzyme/
MIT License
19.95k stars 2.01k forks source link

simulate behavior is not as expected #218

Open cliren opened 8 years ago

cliren commented 8 years ago

Please suggest how to fix this scenario to simulate currentTarget object :

let TestComponent = React.createClass({
 updateField(event){
    console.log(event.target.value);
    console.log(event.currentTarget.value);

    this.setState({
      value: event.currentTarget.value
    });
  },

  render(){
    return <input value={this.props.value} onChange={this.updateField} />
  }
});

target.value is available in the event object as expected

let component = mount(<TestComponent />);
let input = component.find("input");
input.simulate("change", {target:{value:"hello"});

currentTarget.value is NOT available in the event object as expected, currentTarget is a real dom object. Looks like jsdom is overwriting it?

let component = mount(<TestComponent />);
let input = component.find("input");
input.simulate("change", {currentTarget:{value:"world"});
cliren commented 8 years ago

Any update on this or has any suggested alternatives?

cliren commented 8 years ago

Checking for an update, is this going to fixed or any suggestion for alternative solution?

blainekasten commented 8 years ago

You can properly simulate this in shallow renders because it does not have a DOM. But mounted renders actually use the dom, which then the event system kicks out the real event with real nodes.

I'm not sure if there would be a way to over-ride the values in enzyme or not. If you could take a look into it and submit a working PR that would be great.

Otherwise, I am seeing for myself that if I do this test, the currentTarget.value === input.value

e.g.,

wrapper = mount(<input value="foo" onChange={e => console.log(e.currentTarget.value)} />);
wrapper.simulate('change');
// logs `"foo"`
chipit24 commented 6 years ago

It's been over a year, any updates on this? I'm having the same problem.

// MyInput.js

<input onChange={(event) => {
  console.log(event.currentTarget.value)} // undefined
  console.log(event.target.value)} // 'foo'
}/>

In my test:

const eventObject = value => ({ currentTarget: { value }, target: { value } });
const wrapper = mount(<MyInput />);
wrapper.find('input').simulate('change', eventObject('foo'));

My current solution is to use:

wrapper.find('input').props().onChange(eventObject('foo')));

Through my limited research, this seems to be an issue related to mount and jsdom.

(Sorry for reviving an old thread, but this is what came up in a Google search, and I see this issue is still open.)

Edit: Relevant conversation here as well: https://github.com/airbnb/enzyme/issues/76

kellyrmilligan commented 6 years ago

discovered this issue just now. was refactoring event handling code for flow type, and it wants to use current target. so now my tests that simulate change don't work anymore ☹️

GeorgeTaveras1231 commented 6 years ago

This seemed to work for me:

const wrapper = mount(/*Omitted*/).find('form');
wrapper.simulate('submit', { currentTarget: wrapper.getDOMNode() });

I think this should be natively supported though.

oviava commented 6 years ago

I understand this is an old topic but just ran into this issue and I can't seem to find any clarity on it.

As I understand Enzyme uses TestUtils, so unless I'm missing something testUtils never adds the currentTarget on the 'fake event"

TestUtils ( so this 'manually' builds the SyntheticEvent )

Enzyme Adapter - this uses the above

Now went and looked at SyntheticEvent and the currentTarget isn't added there, it's added when it's 'dispatched' - I don't really understand this part that well

... Actually, I don't get it, if I do an .simulate('click', { currentTarget: { value: ''} }) this should reach my handler passed to an input element ( based on the code above ), but somehow it doesn't, value is null

osdiab commented 6 years ago

I too am finding this issue. I was using event.target.value in my <input /> change handler before, but after getting some Typescript type errors for using it and reading this post about how event.currentTarget instead of event.target is the actual correct value to use, I found my enzyme tests broke.

Now, when I try setting event.currentTarget in my simulated change event, it currentTarget doesn't get set. Like so:

const enzymeWrapper = mount(<MyComponent />);
const input = enzymeWrapper.find('.myInput');
input.simulate("change", { target: { value: "new_value" } }); // event.target is properly set in the change handler
input.simulate("change", { currentTarget: { value: "another_new_value" } }); // event.currentTarget is not set in the change handler 

Don't think this is how this is supposed to behave. It does seem to work if I use shallow() though.

garycourt commented 6 years ago

I solved this by doing:

input.getDOMNode().value = "another_new_value";
input.simulate("change");
OzzyTheGiant commented 6 years ago

Yeah this really broke all of my tests that simulated doubleClick. The only workaround I used was to do the following (the custom method shown updates the state and is used as an event handler):

wrapper.instance().someMethodForChangingState({currentTarget:{value:"value"}});
wrapper.update();

This really needs to get fixed, it feels hackish to call the instance object.

milesj commented 6 years ago

Two years later and still no update?

ljharb commented 6 years ago

A PR with a failing test case would be helpful.

In general though, I suggest never using simulate, as it does not faithfully simulate anything. If you want to invoke a prop callback like onClick, invoke it directly with .prop('onClick')().

moos commented 5 years ago

The underlying culprit has been closed! https://github.com/facebook/react/issues/4950. Methinks the Enzyme folks should weigh-in. currentTarget is a no-brainer MUST HAVE for simulate() to be useful.

ljharb commented 5 years ago

I don't think simulate can ever be useful as it's currently implemented. See the immediately preceding comment.

antgonzales commented 5 years ago

@ljharb isn't getting into props an implementation detail though? I don't particularly want to know a lot about the component/React API, I just want to know something fired correctly when I update a value in an input element.

ljharb commented 5 years ago

@antgonzales as is simulate, as currently implemented. If enzyme had a proper "simulate event" API, then that'd be ideal - but nobody actually has that, because it would require an independent implementation of the DOM event system.

antgonzales commented 5 years ago

do you mean something kind of like what dom-testing-library has, but for enzyme?

ljharb commented 5 years ago

@antgonzales no, because that library doesn't implement that - it just leverages the DOM. enzyme needs to work for server rendering, too, so we can't just be a thin wrapper around React and the browser.