ant design v4 中断了 Select 和自动完成的反应测试库测试
ant design v4 breaks react testing library tests for Select and Autocomplete
我最近将我的 React 项目升级到 ant design v4,所有使用 Select、AutoComplete 或 Tooltip 的测试都已损坏。基本上,当单击组件时,JSDOM 中不存在模态或 select 选项。这曾经在 v3 中运行良好。
有人可以告诉我如何使用 React 测试库测试 antd v4 吗?
示例:
我的组件:
import React from "react";
import "./styles.css";
import { Select } from "antd";
const { Option } = Select;
function handleChange(value) {
console.log(`selected ${value}`);
}
export default function App() {
return (
<div className="App" style={{ marginTop: "40px" }}>
<Select
defaultValue="lucy"
style={{ width: 120 }}
onChange={handleChange}
>
<Option value="jack">Jack</Option>
<Option value="lucy">Lucy</Option>
<Option value="disabled" disabled>
Disabled
</Option>
<Option value="Yiminghe">yiminghe</Option>
</Select>
</div>
);
}
我的测试
import "@testing-library/jest-dom/extend-expect";
import React from "react";
import { render, fireEvent, prettyDOM } from "@testing-library/react";
import App from "./App";
test("App Test", () => {
const { queryAllByText, getByText, container } = render(<App />);
expect(queryAllByText("Lucy").length).toBe(1);
expect(queryAllByText("Jack").length).toBe(0);
fireEvent.click(getByText("Lucy"));
console.log(prettyDOM(container));
// This line fails although I would expect the dropdown to be open and all the options visible
expect(queryAllByText("Jack").length).toBe(1);
});
这是重现该问题的 codesandbox 的 link。 (如前所述,该代码曾经在 v3 中工作)。
https://codesandbox.io/s/staging-shape-0xkrl?file=/src/App.test.js:0-494
在这上面浪费了 2 天之后,这里是问题和解决方案:
问题
在 antd v3 中,以前可以通过 selectHtmlElement.click()
打开一个 Select。您可以在 chrome 开发工具控制台中进行测试。在 v4 中这不起作用。
这意味着在底层使用 JSDOM 的 RTL 将具有相同的行为。当您执行 fireEvent.click(selectElement);
时没有任何反应 !
解决方案
这让我走上了正确的轨道:https://github.com/ant-design/ant-design/issues/22074
您需要触发的事件不是 click()
,而是 select 的第一个 child 上的 mouseDown()
。
const elt = getByTestId('your-select-test-id').firstElementChild;
fireEvent.mouseDown(elt); // THIS WILL OPEN THE SELECT !
此时您可能想要 select 列表中的一个选项,但是正在播放动画,因此以下代码(曾经在 v3 中工作)也将失败。
expect(getByText('Option from Select')).toBeVisible(); // FAILS !
您有 2 个选项,使用 toBeInTheDocument()
或使用 waitFor(...)
等待动画结束
选项 1:更快但不完全准确,我更喜欢将它用于简单的用例,因为它使测试更快且同步
expect(getByText('Option from Select')).toBeInTheDocument(); // WORKS !
选项 2:速度较慢,因为您需要等待动画完成,但对于复杂情况更准确
await waitFor(() => expect(getByText('Option from Select')).toBeVisible()); // WORKS !
不幸的是,@klugjo 的回答对我来说不适用于 antd-mobile 的 Radio
组件。
我通过 向组件添加额外的 onClick
属性 解决了这个问题:
<Radio.RadioItem
key={key}
checked={isSelected}
onChange={onChange}
onClick={onChange} // needed for rtl
>
{label}
</Radio.RadioItem>
这不是一个干净的解决方案,因为它修改了用于测试的生产代码。它可能会错过 TouchEvent
上的失败,但这应该是 antd-mobile 库的问题——而不是这个测试。
现在看起来像 ("antd": "4.17.3", "@testing-library/user-event": "^13.5.0") userEvent.click
with { skipPointerEventsCheck: true }
有效:
const options = [
{ label: "", value: "cat", },
{ label: "", value: "dog", }
];
const onChangeMock = jest.fn();
render(
<Select
options={options}
onChange={onChangeMock}
/>,
);
const select = screen.getByRole("combobox");
userEvent.click(select);
const option = screen.getByText("");
userEvent.click(option, undefined, { skipPointerEventsCheck: true });
expect(onChangeMock).toHaveBeenCalledWith("dog", {
label: "",
value: "dog",
});
我最近将我的 React 项目升级到 ant design v4,所有使用 Select、AutoComplete 或 Tooltip 的测试都已损坏。基本上,当单击组件时,JSDOM 中不存在模态或 select 选项。这曾经在 v3 中运行良好。
有人可以告诉我如何使用 React 测试库测试 antd v4 吗?
示例:
我的组件:
import React from "react";
import "./styles.css";
import { Select } from "antd";
const { Option } = Select;
function handleChange(value) {
console.log(`selected ${value}`);
}
export default function App() {
return (
<div className="App" style={{ marginTop: "40px" }}>
<Select
defaultValue="lucy"
style={{ width: 120 }}
onChange={handleChange}
>
<Option value="jack">Jack</Option>
<Option value="lucy">Lucy</Option>
<Option value="disabled" disabled>
Disabled
</Option>
<Option value="Yiminghe">yiminghe</Option>
</Select>
</div>
);
}
我的测试
import "@testing-library/jest-dom/extend-expect";
import React from "react";
import { render, fireEvent, prettyDOM } from "@testing-library/react";
import App from "./App";
test("App Test", () => {
const { queryAllByText, getByText, container } = render(<App />);
expect(queryAllByText("Lucy").length).toBe(1);
expect(queryAllByText("Jack").length).toBe(0);
fireEvent.click(getByText("Lucy"));
console.log(prettyDOM(container));
// This line fails although I would expect the dropdown to be open and all the options visible
expect(queryAllByText("Jack").length).toBe(1);
});
这是重现该问题的 codesandbox 的 link。 (如前所述,该代码曾经在 v3 中工作)。
https://codesandbox.io/s/staging-shape-0xkrl?file=/src/App.test.js:0-494
在这上面浪费了 2 天之后,这里是问题和解决方案:
问题
在 antd v3 中,以前可以通过 selectHtmlElement.click()
打开一个 Select。您可以在 chrome 开发工具控制台中进行测试。在 v4 中这不起作用。
这意味着在底层使用 JSDOM 的 RTL 将具有相同的行为。当您执行 fireEvent.click(selectElement);
时没有任何反应 !
解决方案
这让我走上了正确的轨道:https://github.com/ant-design/ant-design/issues/22074
您需要触发的事件不是 click()
,而是 select 的第一个 child 上的 mouseDown()
。
const elt = getByTestId('your-select-test-id').firstElementChild;
fireEvent.mouseDown(elt); // THIS WILL OPEN THE SELECT !
此时您可能想要 select 列表中的一个选项,但是正在播放动画,因此以下代码(曾经在 v3 中工作)也将失败。
expect(getByText('Option from Select')).toBeVisible(); // FAILS !
您有 2 个选项,使用 toBeInTheDocument()
或使用 waitFor(...)
选项 1:更快但不完全准确,我更喜欢将它用于简单的用例,因为它使测试更快且同步
expect(getByText('Option from Select')).toBeInTheDocument(); // WORKS !
选项 2:速度较慢,因为您需要等待动画完成,但对于复杂情况更准确
await waitFor(() => expect(getByText('Option from Select')).toBeVisible()); // WORKS !
不幸的是,@klugjo 的回答对我来说不适用于 antd-mobile 的 Radio
组件。
我通过 向组件添加额外的 onClick
属性 解决了这个问题:
<Radio.RadioItem
key={key}
checked={isSelected}
onChange={onChange}
onClick={onChange} // needed for rtl
>
{label}
</Radio.RadioItem>
这不是一个干净的解决方案,因为它修改了用于测试的生产代码。它可能会错过 TouchEvent
上的失败,但这应该是 antd-mobile 库的问题——而不是这个测试。
现在看起来像 ("antd": "4.17.3", "@testing-library/user-event": "^13.5.0") userEvent.click
with { skipPointerEventsCheck: true }
有效:
const options = [
{ label: "", value: "cat", },
{ label: "", value: "dog", }
];
const onChangeMock = jest.fn();
render(
<Select
options={options}
onChange={onChangeMock}
/>,
);
const select = screen.getByRole("combobox");
userEvent.click(select);
const option = screen.getByText("");
userEvent.click(option, undefined, { skipPointerEventsCheck: true });
expect(onChangeMock).toHaveBeenCalledWith("dog", {
label: "",
value: "dog",
});