问题
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