我希望从React测试库社区获得一些Maven建议和指导,以确保在拍摄快照之前完成Material UI涟漪动画的最佳方法。
这个问题一直导致我们进行flakey测试,所有内容都在本地通过,但当我们在CI服务器上运行时,测试是间歇性的,并且由于动画完成的速度比本地快,这意味着它与存储的快照不匹配。
只有在我们将fireEvent
替换为userEvent
时才会出现这个问题,这对userEvent提供的附加操作是有意义的,但我想得到的是存储不包含任何UI转换的快照。
下面我创建了一个简单的测试来帮助说明这个问题(也是在codesandbox上):
import React from "react";
import { render, screen, fireEvent, waitFor } from "@testing-library/react";
import userEvent from "@testing-library/user-event";
import Button from "@material-ui/core/Button";
test("clicks material button with fireEvent - no ripple implication", () => {
render(
<Button variant="contained" color="primary">
Ripple
</Button>
);
const btnContainer = screen.getByRole("button", { name: /ripple/i });
expect(btnContainer).toBeInTheDocument();
screen.debug(btnContainer);
fireEvent.click(btnContainer);
screen.debug(btnContainer);
});
test("clicks material button with userEvent", async () => {
render(
<Button variant="contained" color="primary">
Ripple
</Button>
);
const btnContainer = screen.getByRole("button", { name: /ripple/i });
expect(btnContainer).toBeInTheDocument();
screen.debug(btnContainer);
userEvent.click(btnContainer);
screen.debug(btnContainer);
});
test("clicks material button with userEvent wait for ripples", async () => {
render(
<Button variant="contained" color="primary">
Ripple
</Button>
);
const btnContainer = screen.getByRole("button", { name: /ripple/i });
expect(btnContainer).toBeInTheDocument();
screen.debug(btnContainer);
userEvent.click(btnContainer);
screen.debug(btnContainer);
await waitForRippleToRemove(btnContainer);
// without the ripple transition being complete inconsitent tests runs can occur
// when using snapshots as per commented line below
// (ie some have ripples some don't)
// expect(asFragment()).toMatchSnapshot();
screen.debug(btnContainer);
});
function waitForRippleToRemove(container) {
return waitFor(() => {
expect(
container.querySelector("span.MuiTouchRipple-root")
).toBeEmptyDOMElement();
});
}
第一个测试使用fireEvent
运行良好,按钮总是看起来像:
<button
class="MuiButtonBase-root MuiButton-root MuiButton-contained MuiButton-containedPrimary"
tabindex="0"
type="button"
>
<span
class="MuiButton-label"
>
Ripple
</span>
<span
class="MuiTouchRipple-root"
/>
</button>
但是,当在测试2中使用userEvent
时,单击后按钮看起来如下所示:
<button
class="MuiButtonBase-root MuiButton-root MuiButton-contained MuiButton-containedPrimary"
tabindex="0"
type="button"
>
<span
class="MuiButton-label"
>
Ripple
</span>
<span
class="MuiTouchRipple-root"
>
<span
class="MuiTouchRipple-ripple MuiTouchRipple-rippleVisible"
style="width: 2.8284271247461903px; height: 2.8284271247461903px; top: -1.4142135623730951px; left: -1.4142135623730951px;"
>
<span
class="MuiTouchRipple-child MuiTouchRipple-childLeaving"
/>
</span>
</span>
</button>
span.MuiTouchRipple-ripple
元素将作为过渡的一部分被删除,但我的问题是什么是最好的方法来等待这种情况发生,因为我的测试显示我正在检查实现细节(即使用第三方依赖的类名),这感觉有点可怕。
await waitFor(() => {
expect(
container.querySelector("span.MuiTouchRipple-root")
).toBeEmptyDOMElement();
});
此外,值得指出的是,根据测试中的组件,我们可能不得不多次使用userEvent.click
,因此在整个测试过程中多次调用waitForRippleToRemove(btnContainer)
似乎是错误的,但目前是一个可行的解决方案。
2条答案
按热度按时间bkhjykvo1#
正如您已经发现的,这里的问题是单击按钮时的涟漪动画。
我没有看过
MaterialUI
代码,但是动画通常是用某种计时器实现的,比如setTimeout
。您通常可以通过使用
jest.useFakeTimers();
来模拟它们,如下所示。注意事项
在一些测试中使用
fireEvent.click
似乎是一个公平的妥协。特别是如果你已经在不同的测试中在同一个按钮上触发了userEvent.click
。来源
ss2ws0br2#
当应用在test-runner中运行时,我全局禁用了涟漪: