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

Errors when using simulate() api #45

Closed
ingro opened this issue May 16, 2019 · 8 comments
Closed

Errors when using simulate() api #45

ingro opened this issue May 16, 2019 · 8 comments

Comments

@ingro
Copy link

ingro commented May 16, 2019

Hello! I'm trying to test the behaviour of a checkbox component.

    test('should call onChange callback when clicked', () => {
        const spy = jest.fn();

        const context = mount(<Checkbox name="foo" onChange={spy} />);

        context.find('input').simulate('change', { target: { value: true } });

        expect(spy).toHaveBeenCalledWith('foo', true);
    });

But calling the simulate api causes me multiple problems.

First I had this error:

ReferenceError: Event is not defined
at MountRenderer.Object.<anonymous>.MountRenderer.simulateEvent (node_modules/enzyme-adapter-preact-pure/build/src/MountRenderer.js:101:21)

which I resolved by putting Event in the global namespace in my setup file:

const jsdom = new JSDOM('<!doctype html><html><body></body></html>');
const { window } = jsdom;

global.Event = window.Event;

But then I got another error:

TypeError: Cannot set property target of #<Event> which has only a getter
at MountRenderer.Object.<anonymous>.MountRenderer.simulateEvent (node_modules/enzyme-adapter-preact-pure/build/src/MountRenderer.js:106:16)

If I pass as arg a random key the error is not thrown but of course the test fail:

context.find('input').simulate('change', { foo: 'bar' });

For reference I'm using latest Preact (v10.0.0-beta.1) and latest enzyme-adapter-preact-pure (v1.13.1).

@robertknight
Copy link
Member

simulate(...) dispatches a real Event object to the target DOM element using domElement.dispatchEvent(event), so the value of target is set on the event for you and you can't change it. What you can do is get a reference to the DOM node and modify it before dispatching the event using wrapper.find(...).getDOMNode().

This is currently a difference from the React Enzyme adapters which, AFAIK, don't actually dispatch a real event.

Does this answer your question?

@ingro
Copy link
Author

ingro commented May 17, 2019

Well yes, thanks for your time and for the explanation. Maybe it could be worth to add a gotcha on the readme cause the official enzyme documentation suggest another way to handle events.

@robertknight
Copy link
Member

I have added some notes about differences between Enzyme + React and Enzyme + Preact in the README which covers this.

I think in future it may make sense to change the behaviour to match the React adapters, but that would be a breaking change so I have simply documented it for now.

@mutoe
Copy link

mutoe commented Dec 18, 2019

I had the same problem.

What should I do when I test functional component like this?

export default function Login() {
  const [ form, setForm ] = useState({ email: '' })

  return (
    <form>
      <input value={form.email} placeholder="Email" onInput={e => setForm({ email: e.currentTarget.value })}>

      <button disabled={!form.email}>Login</button>
    </form>
  )
}
  it('should set button enabled when form is valid', function () {
    const wrapper = shallow(<Login />)
    wrapper.find('[placeholder="Email"]').simulate('input', { currentTarget: { value: '123' } })

    const loginButton = wrapper.find('form button.btn-lg.btn-primary')
    expect(loginButton.props().disabled).toBe(false)
  })

I can't change the internal state if I can't simulate event. Is there anything wrong with me? @robertknight @ingro

@mutoe
Copy link

mutoe commented Dec 18, 2019

I might add, when I try to use

wrapper.find('[placeholder="Email"]').getDOMNode<HTMLInputElement>().value = '123'

I got an error TypeError: wrapper.find(...).getDOMNode is not a function

@robertknight
Copy link
Member

@mutoe - You can't get a reference to the DOM node if you use shallow to render the component. This is a limitation that is unfortunately inherited from the way React's shallow rendering works. You need to use the mount function instead.

At work, we combine the advantages of mount rendering (full access to the DOM) with those of shallow rendering (having a unit test that is isolated from the details of its child components) by using a Babel plugin to mock imported components. The React docs recommend doing something conceptually similar with Jest.

@mutoe
Copy link

mutoe commented Dec 19, 2019

@robertknight Thanks for your answer, mount solved my problem. I will try to use plugins to optimize my tests as you suggest later.

By the way, can getDOMNode be removed from the shallow wrapper in TS ?

@akellbl4
Copy link

akellbl4 commented Feb 3, 2020

@robertknight I made a small patch which provides an opportunity to pass {target} to simulate() as the second argument. It is a bit hacky but works. What do you think about it?

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

4 participants