Tsabary
Tsabary

Reputation: 3948

How can I test a click event which changes a useState state with enzyme?

I have the following component:

import React, { useState } from "react";

import { Button, ThirteenBold } from "@selfdecode/sd-component-library";
import { PlayIcon } from "assets/icons";
import { TourButtonProps } from "./interfaces";
import { WelcomeVideoModal } from "../../modals/welcome-video-modal";

/**
 * The tour button.
 */
export const TourButton: React.FC<TourButtonProps> = (props) => {
  const [isIntroVideoShowing, setIsIntroVideoShowing] = useState(false);

  return (
    <>
      <WelcomeVideoModal
        isOpen={isIntroVideoShowing}
        onClickX={() => setIsIntroVideoShowing(false)}
        data-test="tour-button-welcome-video"
      />

      <Button
        {...props}
        width={["max-content"]}
        variant="tour"
        onClick={() => setIsIntroVideoShowing(true)}
        data-test="tour-button"
      >
        <ThirteenBold
          mr={["12px"]}
          color="cl_blue"
          width={["max-content"]}
          letterSpacing={["1px"]}
          display={["none", "block"]}
          textTransform="uppercase"
        >
          welcome tour
        </ThirteenBold>

        <PlayIcon style={{ height: "30px", fill: "#4568F9" }} />
      </Button>
    </>
  );
};

And the test coverage report is complaining that I am not testing both of the onClick events, which change the state.

I've tried two approaches, and both fail. Approach one was to mock the useState and see if it gets called as I'd have expected it. This was the test I tried:

 const setState = jest.fn();
 const useStateMock: any = (initState: any) => [initState, setState];
 jest.spyOn(React, "useState").mockImplementation(useStateMock);
 
 const button = wrapper.find(`[data-test="tour-button"]`);
 expect(button).toHaveLength(1);
 button.simulate("click");
 expect(setState).toHaveBeenCalled();

This shouldn't even be the final test, as it doesn't check what was the valuee it was called with, but still, it failed because useState wasn't even called.

The second approach I've tried was to check the prop value on this component:

 <WelcomeVideoModal
    isOpen={isIntroVideoShowing}
    onClickX={() => setIsIntroVideoShowing(false)}
    data-test="tour-button-welcome-video"
 />

And this is the test I've tried

  test("Check the isIntroVideoShowing changes to true on buton click", () => {
    jest.spyOn(React, "useState").mockImplementation(useStateMock);
    const button = wrapper.find(`[data-test="tour-button"]`);
    const welcomeVideo = wrapper.find(
      `[data-test="tour-button-welcome-video"]`
    );
    expect(button).toHaveLength(1);
    expect(welcomeVideo.prop("isOpen")).toEqual(false);
    button.simulate("click");
    expect(welcomeVideo.prop("isOpen")).toEqual(true);
  });

This one failed claiming it was called with false even after the click.

Is there a way to make these work? Or a different approach to cover these?

Upvotes: 2

Views: 7360

Answers (1)

Sarun UK
Sarun UK

Reputation: 6746

You need to give wrapper.update for updating the template with state changes after simulating the click event.

test("Check the isIntroVideoShowing changes to true on buton click", () => {
    jest.spyOn(React, "useState").mockImplementation(useStateMock);
    const button = wrapper.find(`[data-test="tour-button"]`);
    const welcomeVideo = wrapper.find(
      `[data-test="tour-button-welcome-video"]`
    );
    expect(button).toHaveLength(1);
    expect(welcomeVideo.prop("isOpen")).toEqual(false);
    button.simulate("click");
    wrapper.update();
    expect(welcomeVideo.prop("isOpen")).toEqual(true);
  });

Reference - https://enzymejs.github.io/enzyme/docs/api/ShallowWrapper/update.html

Upvotes: 2

Related Questions