cda*_*d15 6 reactjs jestjs material-ui react-testing-library
我希望从 React 测试库社区获得一些专家建议和指导,以了解确保在拍摄快照之前完成 Material UI 波纹动画的最佳方法。
这个问题一直导致我们进行不稳定的测试,所有内容始终在本地通过,但是当我们在 CI 服务器上运行时,测试是间歇性的并且失败,因为动画完成的速度比本地快,这意味着它与存储的快照不匹配。
这个问题只有在我们换掉后才出现fireEvent,这对于userEventuserEvent 提供的额外操作是有意义的,但我想要的是存储不包含任何 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();
});
}
Run Code Online (Sandbox Code Playgroud)
第一个测试工作正常,使用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>
Run Code Online (Sandbox Code Playgroud)
但是,在测试 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>
Run Code Online (Sandbox Code Playgroud)
该span.MuiTouchRipple-ripple元素将作为过渡的一部分被删除,但我的问题是等待这种情况发生的最佳方法是什么,因为我的测试显示我正在检查实现细节(即使用第 3 方依赖项的类名),这感觉有点可怕。
await waitFor(() => {
expect(
container.querySelector("span.MuiTouchRipple-root")
).toBeEmptyDOMElement();
});
Run Code Online (Sandbox Code Playgroud)
另外,值得指出的是,根据被测组件的不同,我们可能不得不userEvent.click多次使用,因此waitForRippleToRemove(btnContainer)在整个测试过程中进行多次调用感觉像是错误的做法,但目前这是一个可行的解决方案。
正如您已经发现的,这里的问题在于单击按钮时的波纹动画。
我没有研究过MaterialUI代码,但动画通常是用某种计时器来实现的,即setTimeout。
您通常可以使用jest.useFakeTimers();如下所示的方法来模拟它们。
beforeEach(() => jest.useFakeTimers());
test('matches snapshot after clicking button', () => {
const { asFragment } = render(<Button>Ripple</Button>);
userEvent.click(screen.getByRole('button', { name: 'Ripple' }));
jest.runAllTimers();
expect(asFragment()).toMatchSnapshot();
});
Run Code Online (Sandbox Code Playgroud)
fireEvent.click在一些测试中使用似乎是一个公平的妥协。特别是如果您已经userEvent.click在不同的测试中按下同一个按钮。
| 归档时间: |
|
| 查看次数: |
1930 次 |
| 最近记录: |