2 minute read

This is a reminder to myself. And to anybody who needs a creative solution to test an event handler directly attached to a DOM node of a react component.

Last Friday I ran into the situation to test my react component to ensure that the state updates correctly when the pointerenter and pointerleave event fires. Testing for these events is usually no big deal when you use reactjs in one way or the other (either web or native). At work we use an internal framework that is react API compatible and implements most of the features of react. Most of them, but not all.

I ran into the situation where I have the pointerenter event handler defined, but they didn’t fire. The team recommended to replace it with an event handler directly attached to the DOM node by

  • assign a ref to the component
  • attach the event handler to the ref

This is how it looked like

class ImageHover extends React.Component {
	constructor(props) {
		super(props);
		
		this._hoverBoxRef = React.CreateRef();
	}

	function _toggleHoverState = (state) => {
		this.setState({ isNormal: state });

    function componentDidMount() {
        this._hoverBoxRef.current.addEventListener('pointerenter', () => this._toggleHoverState(false));
        this._hoverBoxRef.current.addEventListener('pointerleave', () => this._toggleHoverState(true));
    }

    function componentWillUnmount() {
        this._hoverBoxRef.current.removeEventListener('pointerenter', () => this._toggleHoverState(false));
        this._hoverBoxRef.current.removeEventListener('pointerleave', () => this._toggleHoverState(true));
    }

	render () {
		style = this.state.isNormal ? "normal" : "hover";
		return (
			<div
				className={ style }
				onClick={this.props.onClick}
				ref={this._hoverBoxRef}
            />
		);
	}

To ensure that the component changes the style to hover when the pointerenter event fires and back to normal when pointerleave is fired can usually achieved by using the rendered component and call .simulate("pointerEnter") when using jest and enzyme.

Normal test

cb = jest.fn();
imageHover = mount(<ImageHover imageName={"phone"} onClick={cb} />);
	
it("should change state on pointerEnter", () => {
	imageHover.simulate("pointerEnter");
    expect(imageHover.state().isNormal).toEqual(false);
});

Since the pointerEnter event didn’t fire in my situation, I had to find another way to trigger that event. Here is the modified test

cb = jest.fn();
imageHover = mount(<ImageHover imageName={"phone"} onClick={cb} />);

it("should change state on pointerEnter", () => {
	const domNode = imageHover.getDOMNode();
	const event = new Event("pointerenter");
	domNode.dispatchEvent(event);
	expect(imageHover.state().isNormal).toEqual(false);
});

The tiny difference is getting access to the underlying DOM element during the test execution and dispatch the pointerenter event on that DOM node. I’ve omitted the event details in this example since they aren’t relevant for the demo purpose. Perhaps there is an alternative solution to achieve the same result without the direct DOM access. Let me know, and I’m happy to update my test code.

Thank you very much for reading!

comments powered by Disqus