What is the best way to test Window Scroll Event Handlers with Enzyme?

旧城冷巷雨未停 提交于 2019-12-04 01:22:35

问题


I have been working on a React app with a new team and the discussion came up around writing unit tests for components that trigger methods on window.scroll events.

So, let's take this component as an example.

import React, { Component } from 'react';

class MyComponent extends Component {
  componentDidMount() {
    window.addEventListener('scroll', this.props.myScrollMethod);
  }

  componentWillUnmount() {
    window.removeEventListener('scroll', this.props.myScrollMethod);
  }

  render() {
    return (
      <div>
        <h1>Hello MyComponent!</h1>
      </div>
    )
  };
};

export default MyComponent;

As you can see, I am taking a method which is passed into the component via a prop and binding it to the window event listener, where the event is scroll. In the real world this component would call myScrollMethod as the user is scrolling down a page (let's assume the use case here is to show a sticky navigation bar when the user has scrolled beyond a certain point on the page).

The problem is...I need to find a suitable way of testing this. My end goal is to create a spy method which is passed into the component via the myScrollMethod prop, then trigger a scroll (or mock a scroll in the test) and finally assert whether the scroll handler method has fired or not. Here is my attempt at this:

import React from 'react';
import sinon from 'sinon';
import expect, { createSpy }  from 'expect';
import { shallow } from 'enzyme';

import MyComponent from './MyComponent';

describe('The <MyComponent /> component', () => {
  let onScroll;
  let MyTestComponent;

  beforeEach(() => {
    onScroll = createSpy();
    MyTestComponent = shallow(
      <MyComponent
        myScrollMethod={onScroll}
        />
    );
  });

  it('Should call the onScroll method when a user scrolls', () => {
    expect(onScroll).toNotHaveBeenCalled();
    window.dispatchEvent(new window.UIEvent('scroll', { detail: 0 }));
    expect(onScroll).toHaveBeenCalled();
  });
});

The issue that I am having is that the final assertion is failing because the spy is never called. I've referred to a number of other posts on this site but so far have not found a suitable solution. Any suggestions would be greatly appreciated as it's been racking my brain for a while now!

Thanks very much!


回答1:


Unfortunately, I don't think Enzyme is going to be much help here. The library only handles events within React's synthetic event system. So a component you render with Enzyme is not going to work with event listeners added to the window. This issues thread on Enzyme's github gives more details and there are some suggested workarounds that might help you.

For example, you might want to spy on window.addEventListener, then you can check on mount that it was called with the arguments "scroll" and your callback.

Regarding your specific code, the scroll listener is set in componentDidMount but your component is shallow rendered, so componentDidMount isn't actually called (so there is no listener). Try adding this line to your beforeEach: MyTestComponent.instance().componentDidMount()




回答2:


You could make the case that events are just simple messages - since enzyme is using JSDOM under the hood, you can adequately track these messages as they are attached to nodes using plain javascript, regardless of whether the event is 'scroll', 'foo', or 'bar'.

In a test environment, we don't really care the event is called, the system just has to know how to respond it.

Heres an example of tracking non-synthetic events like scroll with enzyme:

class Scrollable extends Component {
  componentDidMount() {
    if (this.myCustomRef) {
      this.myCustomRef.addEventListener('scroll', this.handleScroll)
    }
  }

  handleScroll = (e) => this.props.onScroll(e) 
}

// scollable-test.js
import React from 'react'
import { mount } from 'enzyme'
import Scrollable from '../Scrollable'

describe('shared/Scrollable', () => {
  it('triggers handler when scrolled', () => {
    const onScroll = jest.fn()
    const wrapper = mount(
      <Scrollable onScroll={onScroll}><div /></Scrollable>
    )
    const customEvent = new Event('scroll')
    // the first element is myCustomRef
    wrapper.first().getDOMNode().dispatchEvent(customEvent)
    expect(wrapper.prop('onScroll')).toHaveBeenCalled()
  })
})

After we attach the events to the dom, we can trigger their handlers by using getDOMNode and dispatchEvent, which fires our prop onScroll

There are some limitations here with JSDOM, in that if you need to do things like track the size or height, or scrollTop of a node after an event has fired, you are out of luck - this is because JSDOM doesn't actually render the page for you, but instead 'emulates' the DOM for usage with libs like enzyme - It could also be argued that a test with these needs would be better suited for end to end testing and entirely different tooling.



来源:https://stackoverflow.com/questions/45376974/what-is-the-best-way-to-test-window-scroll-event-handlers-with-enzyme

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!