React testing: `fireEvent.focus(input)` does not change state

柔情痞子 提交于 2021-01-07 02:52:43

问题


I have create a number input component, but when I try to test it everything works great, except from the onMouserEnter(), onMouseOver(), onMouserLeave() and onFocus methods. On the component and on browser, the state changes (as I use the useState hook to know whether onHover), but on the tests no. Basically the state changes, but the children components that are meant to be displayed when onHover, do not display.

Note: When I use a second conditional to display something, the test is able to find both elements then.

Number.test.tsx

import { fireEvent, render, screen, waitFor } from "@testing-library/react";
import userEvent from "@testing-library/user-event";
import React from "react";
import { act } from "react-dom/test-utils";
import Input from "../index";

const TEST_DEFAULT_VALUE = "Hey! How are you?";

describe("<Input.Number />", () => {
  test("default behaviour", () => {
    render(<Input.Number />);
    const input = screen.getByTestId("input-number");

    expect(input).toBeInTheDocument();
    expect(input).not.toHaveAttribute("placeholder");
    expect(input.getAttribute("value")).toBe("0");
    expect(input.getAttribute("type")).toBe("number");
    expect(input.getAttribute("data-hasfloatingplaceholder")).toBe("false");
    expect(input.getAttribute("aria-valuenow")).toBe("0");
    expect(input.getAttribute("data-isbtnshown")).toBe("false");
    expect(screen.queryByTestId("input-number-btn-container")).toBeNull();
  });
  test("onFocus", async () => {
    render(<Input.Number />);
    const input = screen.getByTestId("input-number");

    act(() => {
      fireEvent.focus(input);
    });
    await waitFor(() => {
      expect(screen.getByTestId("hey")).toBeInTheDocument();
    });

    expect(input.getAttribute("value")).toBe("");
    expect(input.getAttribute("aria-valuenow")).toBe("");
    expect(input.getAttribute("data-isbtnshown")).toBeTruthy;
  });
  test('state="hover"', async () => {
    render(<Input.Number />);
    const input = screen.getByTestId("input-number-container");

    fireEvent.focus(input);

    await waitFor(() => {
      expect(screen.getByTestId("input-number-btn-container")).toBeInTheDocument();
    });
  });
  test('state="disabled"', async () => {
    render(<Input.Number state="disabled" />);
    const input = screen.getByTestId("input-number");

    expect(input.getAttribute("data-isbtnshown")).toBeTruthy();
    expect(screen.getByTestId("input-number-btn-container")).toBeInTheDocument();
  });
});

Number.tsx component

const animationVariants = {
  hidden: { opacity: 0 },
  visible: { opacity: 1 },
};

const Number: React.FunctionComponent<FProps> = ({
  min = 0,
  max = 9999999,
  step = 1,
  value: propsValue,
  defaultValue = min,
  state,
  width = "7.5em",
  overrideOnChange,
  onFocus,
  onBlur,
  onMouseOver,
  ...props
}) => {
  const isDisabled = useDisabled(props, state);
  const [{ value }, dispatch] = useReducer(reducer, initialState, (): typeof initialState => {
    return {
      ...initialState,
      value: propsValue || defaultValue || min,
      isDisabled,
      min,
      max,
    };
  });
  const [isBtnShown, setIsBtnShown] = useState<boolean>(false || state === "hover" || state === "focus" || state === "disabled");
  const [isUp, setIsUp] = useState<boolean>(false);
  const [isDown, setIsDown] = useState<boolean>(false);

  const increment = (stepNumber = step) => {
    dispatch({ type: ACTIONS.INCREMENT, payload: { step: stepNumber } });
  };

  const decrement = (stepNumber = step) => {
    dispatch({ type: ACTIONS.DECREMENT, payload: { step: stepNumber } });
  };

  const toggleBtn = (boolean: boolean) => {
    if (!isDisabled && state === "default") {
      setIsBtnShown(boolean);
    }
  };

  return (
    <div onMouseOver={() => toggleBtn(true)} onMouseLeave={() => toggleBtn(false)} data-testid="input-number-container">
      <InputContextProvider
        value={{
          ...props,
          value,
          dispatch,
          defaultValue,
          state,
          min,
          max,
          step,
          width,
          overrideOnChange,
          onMouseOver: e => {
            toggleBtn(true);
            if (onMouseOver) {
              onMouseOver(e);
            }
          },
          onFocus: e => {
            toggleBtn(true);
            if (onFocus) {
              onFocus(e);
            }
          },
          onBlur: e => {
            toggleBtn(false);
            if (onBlur) {
              onBlur(e);
            }
          },
        }}
      >
        <BaseNumber isBtnShown={isBtnShown} data-testid="input-number">
          {isBtnShown && <div data-testid="hey"></div>}
          <AnimatePresence>
            {isBtnShown && (
              <motion.div
                initial="hidden"
                animate="visible"
                variants={animationVariants}
                transition={{ duration: 0.3 }}
                exit={animationVariants.hidden}
                className="input-number__btn__container"
                data-testid="input-number-btn-container"
              >
                <Button
                  type="secondary"
                  style={{ height: isUp ? IS_UP_HEIGHT : isDown ? IS_DOWN_HEIGHT : undefined }}
                  onMouseEnter={() => setIsUp(true)}
                  onMouseLeave={() => setIsUp(false)}
                  onClick={() => increment()}
                  data-testid="input-number-btn-increase"
                >
                  <svg width="14" height="8" viewBox="0 0 14 8" fill="none" xmlns="http://www.w3.org/2000/svg">
                    <path d="M12.25 6.91665L7 1.08331L1.75 6.91665" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" />
                  </svg>
                </Button>
                <Button
                  type="secondary"
                  style={{ height: isDown ? IS_UP_HEIGHT : isUp ? IS_DOWN_HEIGHT : undefined }}
                  onMouseEnter={() => setIsDown(true)}
                  onMouseLeave={() => setIsDown(false)}
                  onClick={() => decrement()}
                  data-testid="input-number-btn-decrease"
                >
                  <svg width="14" height="8" viewBox="0 0 14 8" fill="none" xmlns="http://www.w3.org/2000/svg">
                    <path d="M12.25 1.08332L7 6.91666L1.75 1.08332" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" />
                  </svg>
                </Button>
              </motion.div>
            )}
          </AnimatePresence>
        </BaseNumber>
      </InputContextProvider>
    </div>
  );
};

export default Number;

Any ideas?

来源:https://stackoverflow.com/questions/65406810/react-testing-fireevent-focusinput-does-not-change-state

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