我有一个DatePicker组件,如下所示:
const MonthPanel = ({ setMode = () => {} }) => {
return (
<div>
<button
onClick={(e) => setMode("year")}
data-testid="datepicker-year-button"
>
2021
</button>
</div>
);
};
const YearPanel = () => {
return <div data-testid="datepicker-year-panel">YearPanel</div>;
};
const DatePicker = (props) => {
const [open, setOpen] = useState(false);
const [mode, setMode] = useState("month");
const containerRef = useRef();
const handleOpen = () => {
setOpen(true);
};
const handleClose = () => {
setOpen(false);
};
const handleModeChange = (newMode) => {
setMode(newMode);
};
const generatePanel = () => {
switch (mode) {
case "month":
return <MonthPanel setMode={handleModeChange} />;
case "year":
return <YearPanel />;
default:
return null;
}
};
useEffect(() => {
const handleOutsideClick = (e) => {
if (containerRef.current && !containerRef.current.contains(e.target)) {
handleClose();
}
};
window.addEventListener("click", handleOutsideClick);
return () => {
window.removeEventListener("click", handleOutsideClick);
};
}, []);
return (
<div
ref={containerRef}
data-testid="datepicker-container"
>
<div onClick={handleOpen} data-testid="datepicker-input">
Select a date
</div>
{open && <div data-testid="datepicker-dialog">{generatePanel()}</div>}
</div>
);
};
我有一个这样的测试文件:
it.only("should close DatePicker dialog when clicked only outside DatePicker", () => {
const { getByTestId, queryByTestId } = render(
<DatePicker />
);
userEvent.click(getByTestId("datepicker-input"));
userEvent.click(getByTestId("datepicker-year-button"));
expect(queryByTestId("datepicker-dialog")).toBeInTheDocument();
userEvent.click(document.body);
expect(queryByTestId("datepicker-dialog")).toBeNull();
});
所需状态:
当您在DatePicker外部单击时,它应该会关闭。当您单击[data-testid="datepicker-year-button"]
时,它应该将DatePicker模式更改为";年";,因此将显示年份面板。
当前状态:
当您单击[data-testid="datepicker-year-button"]
时,它会将Datepicker模式更改为";年;和MonthPanel(以及按钮本身(被移除。因为按钮是事件目标并且已经删除,所以containerRef.current.contains(e.target)
条件是false
,对话框也将被删除。但测试显示对话框在文档中。
问题是我应该如何正确地测试这个功能。
您可以在<MonthPanel>
中的按钮单击处理程序上调用e.stopPropagation()
,以防止事件冒泡并被window
的eventListener
捕获。
<button
onClick={(e) => {
e.stopPropagation();
setMode("year");
}}
data-testid="datepicker-year-button"
>
尝试使用waitFor
或waitForElementToBeRemoved
:等异步方法
await waitFor(() => {
expect(queryByTestId("datepicker-dialog")).toBeNull();
});
React-dom
引入了act API
来包装渲染或更新组件的代码,这使您的测试运行更接近React在浏览器中的工作方式。React测试库将其一些API封装在act函数中,但在某些情况下,您仍然需要使用waitFor或waitForElementToBeRemoved
我认为您的userEvent.click(document.body);
工作不正常。
很可能document.body
没有按应有的方式解决。您可能正在使用测试库用户事件,它们的基本示例是:
import { screen } from '@testing-library/dom'
import userEvent from '@testing-library/user-event'
test('types inside textarea', () => {
document.body.innerHTML = `<textarea />`
userEvent.type(screen.getByRole('textbox'), 'Hello, World!')
expect(screen.getByRole('textbox')).toHaveValue('Hello, World!')
})
检查您的document.body.innerHTML
是否设置为