Javascript 当document.addListener处理程序中的状态更新时,React测试通过,但组件无法正常工作
我有一个日期选择器组件,如下所示:Javascript 当document.addListener处理程序中的状态更新时,React测试通过,但组件无法正常工作,javascript,reactjs,jestjs,react-testing-library,Javascript,Reactjs,Jestjs,React Testing Library,我有一个日期选择器组件,如下所示: const MonthPanel = ({ setMode = () => {} }) => { return ( <div> <button onClick={(e) => setMode("year")} data-testid="datepicker-year-button" > 2021
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();
});
const MonthPanel=({setMode=()=>{}})=>{
返回(
设置模式(“年份”)}
data testid=“日期选择器年份按钮”
>
2021
);
};
const YearPanel=()=>{
回返问题小组;
};
常量日期选择器=(道具)=>{
const[open,setOpen]=useState(false);
const[mode,setMode]=使用状态(“月”);
const containerRef=useRef();
const handleOpen=()=>{
setOpen(真);
};
常量handleClose=()=>{
setOpen(假);
};
const handleModeChange=(newMode)=>{
设置模式(新模式);
};
const generatePanel=()=>{
开关(模式){
案例“月份”:
返回;
案件“年份”:
返回;
违约:
返回null;
}
};
useffect(()=>{
const handleOutsideClick=(e)=>{
if(containerRef.current&!containerRef.current.contains(e.target)){
handleClose();
}
};
window.addEventListener(“单击”,手柄外侧单击);
return()=>{
window.removeEventListener(“单击”,手柄外侧单击);
};
}, []);
返回(
选择一个日期
{open&&{generatePanel()}}
);
};
我有一个这样的测试文件:
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();
});
it.only(“仅在日期选择器外部单击时应关闭日期选择器对话框”,()=>{
常量{getByTestId,queryByTestId}=render(
);
单击(getByTestId(“日期选择器输入”);
点击(getByTestId(“日期选择器年份按钮”);
expect(queryByTestId(“日期选择器对话框”).toBeInTheDocument();
点击(document.body);
expect(queryByTestId(“日期选择器对话框”)).toBeNull();
});
所需状态:
当您单击外部日期选择器时,它应该关闭。当您单击[data testid=“datepicker year button”]
时,它应该将datepicker模式更改为“year”,因此将显示year面板
当前状态:
当您单击[data testid=“datepicker year button”]
时,它会将datepicker模式更改为“year”,并删除MonthPanel(及其按钮本身)。由于该按钮是事件目标且已被删除,containerRef.current.contains(e.target)
条件为false
,对话框也将被删除。但是测试显示对话框在文档中
问题是如何正确测试此功能。您可以在
中的按钮单击处理程序上调用e.stopPropagation()
,以防止事件冒泡并被窗口的事件侦听器捕获
<button
onClick={(e) => {
e.stopPropagation();
setMode("year");
}}
data-testid="datepicker-year-button"
>
{
e、 停止传播();
设定模式(“年”);
}}
data testid=“日期选择器年份按钮”
>
尝试使用likewaitFor
或waitForElementToBeRemoved
:
await waitFor(() => {
expect(queryByTestId("datepicker-dialog")).toBeNull();
});
React-dom
引入了act-API
来包装呈现或更新组件的代码,这使测试运行更接近React在浏览器中的工作方式。React测试库将它的一些API封装在act函数中,但在某些情况下,您仍然需要使用waitFor或waitForElementToBeRemoved,我相信您的userEvent.click(document.body)代码>工作不正常
最有可能的是document.body
的解析方式不正确。
您可能正在使用测试库用户事件,其基本示例是:
从'@testing library/dom'导入{screen}
从“@测试库/用户事件”导入userEvent
测试('文本区域内的类型',()=>{
document.body.innerHTML=``
userEvent.type(screen.getByRole('textbox'),'Hello,World!')
expect(screen.getByRole('textbox')).toHaveValue('Hello,World!')
})
检查您的document.body.innerHTML
是否设置为某个值,我知道。问题是测试必须失败,但它通过了。我想知道我的测试出了什么问题,我应该如何测试。那么你是故意让测试失败的?你可能应该在最初的帖子中提到这一点。代码工作不正常,对吗。因此,测试必须失败,但它通过了。我问我的测试出了什么问题。我的意思是,你故意更改了代码,使其不能正常工作,这样测试就会失败。但在这种情况下,它实际上并没有失败。就是这样。您确定您的测试运行程序知道如何处理document.body
?您希望的状态实际上是测试在应该的时候失败了。您可以更新问题以反映这一点。