如何在 @testing-library/react 中使用 sx props 测试 Material UI v5 组件?

Iho*_*hor 12 reactjs material-ui react-testing-library testing-library

React 测试库在渲染期间不会应用sxMaterial UI 组件的 props。

例如,我指定属性以在某些断点处隐藏元素。

<>
  <AppBar
    data-testid="mobile"
    ...
    sx={{
      display: { xs: "block", sm: "none" }
    }}
  >
    MOBILE
  </AppBar>
  <AppBar
    data-testid="desktop"
    ...
    sx={{
      display: { xs: "none", sm: "block" }
    }}
  >
    DESKTOP
  </AppBar>
</>
Run Code Online (Sandbox Code Playgroud)

在浏览器中一切都按预期工作。在 React 测试库中渲染时,我得到包含两个元素的结果。从样式上可以明显看出,它们是基本的,并且sx没有应用该属性。链接到codesandbox

import { ThemeProvider } from "@mui/material/styles";
import { screen, render } from "@testing-library/react";
import darkTheme from "./darkTheme";
import App from "./App";

describe("Demo", () => {
  it("should have display props", () => {
    render(
      <ThemeProvider theme={darkTheme}>
        <App />
      </ThemeProvider>
    );

    expect(screen.getByTestId("mobile")).toHaveStyle({ display: "none" });
    expect(screen.getByTestId("desktop")).toHaveStyle({ display: "block" });
  });
});
Run Code Online (Sandbox Code Playgroud)

检测结果

在 React 测试库中测试此类 Material UI props 的正确方法是什么?

Rov*_*ver 1

遇到同样的问题后,我决定编写一个临时辅助工具来对计算样式进行断言。

我发现计算的sx样式正在应用于head. 如果你跑步,screen.debug()你将看不到它……但是跑步document.head.innerHTML确实有效果。

请注意,这个实用程序远非完美。这只是继续测试的快速方法。

export const getElementStyleFromHead = (element: HTMLElement) => {
  const sourceElementClassNames = [...element.classList.values()]; // get all the classNames for source element
  const styleTags = new DOMParser().parseFromString(document.head.innerHTML, 'text/html').querySelectorAll('style'); // parse all style tags in head
  const styleTagsArray = [...styleTags.values()]; // convert NodeList to array
  const elementStyles = styleTagsArray.filter((style) => sourceElementClassNames.some((className) => style.innerHTML?.includes(className))); // filter all matching classNames between sourceElementClassNames and the style tag
  return elementStyles.map(item => item.innerHTML).join(''); // join all classes and parse it as one css string
};
Run Code Online (Sandbox Code Playgroud)

页:

<Box sx={{ 
  display: 'flex', 
  justifyContent: centerContent ? 'center' : 'flex-start'
 }}>
  {'dummy content'}
</Box>
Run Code Online (Sandbox Code Playgroud)

用它:

    expect(getElementStyleFromHead(customScreen.getByText('dummy content'))).toContain('justify-content:center')
Run Code Online (Sandbox Code Playgroud)

编辑:我发现动态样式并不总是及时清理。我的用例是,当我在内容窗格中滚动时,应使用 :after 和 :before 将滚动指示器(阴影)添加到窗格的顶部和底部。我的测试按顺序运行失败,但在单独运行时通过。我已将这一行添加到 beforeEach 中,以确保头部始终被清除。但不确定这是否还有其他副作用......

beforeEach(() => {
    document.head.innerHTML = '';
});
Run Code Online (Sandbox Code Playgroud)