似乎,如果不将Material-UI样式表注入到jest / react-testing-library测试中,则jsdom将无法从组件中获取正确的样式(例如,运行getComputedStyle(component)
将返回错误的组件的样式)。
如何正确设置一个玩笑/反应测试库测试,以便将样式正确地插入到测试中?我已经将这些组件包装在一个主题提供程序中,可以很好地工作。
答案 0 :(得分:3)
作为一种变通方法,在声明前重新插入整个头部(或注入JSS样式的元素)似乎可以同时getComputedStyle()
应用样式,并对测试库的toHaveStyle()
做出反应:
import React from "react";
import "@testing-library/jest-dom/extend-expect";
import { render } from "@testing-library/react";
test("test my styles", () => {
const { getByTestId } = render(
<div data-testid="wrapper">
<MyButtonStyledWithJSS/>
</div>
);
const button = getByTestId("wrapper").firstChild;
document.head.innerHTML = document.head.innerHTML;
expect(button).toHaveStyle(`border-radius: 4px;`);
});
使用dynamic styles时,此操作仍然会失败,例如:
myButton: {
padding: props => props.spacing,
...
}
这是因为JSS使用CSSStyleSheet.insertRule
方法来注入这些样式,并且不会在头部显示为style
节点。解决此问题的一种方法是使用浏览器的insertRule
方法,并将传入的规则作为样式标签添加到头部。要将所有这些提取到函数中:
function mockStyleInjection() {
const defaultInsertRule = window.CSSStyleSheet.prototype.insertRule;
window.CSSStyleSheet.prototype.insertRule = function (rule, index) {
const styleElement = document.createElement("style");
const textNode = document.createTextNode(rule);
styleElement.appendChild(textNode);
document.head.appendChild(styleElement);
return defaultInsertRule.bind(this)(rule, index);
};
// cleanup function, which reinserts the head and cleans up method overwrite
return function applyJSSRules() {
window.CSSStyleSheet.prototype.insertRule = defaultInsertRule;
document.head.innerHTML = document.head.innerHTML;
};
}
此功能在我们先前的测试中的用法示例:
import React from "react";
import "@testing-library/jest-dom/extend-expect";
import { render } from "@testing-library/react";
test("test my styles", () => {
const applyJSSRules = mockStyleInjection();
const { getByTestId } = render(
<div data-testid="wrapper">
<MyButtonStyledWithJSS spacing="8px"/>
</div>
);
const button = getByTestId("wrapper").firstChild;
applyJSSRules();
expect(button).toHaveStyle("border-radius: 4px;");
expect(button).toHaveStyle("padding: 8px;");
});
答案 1 :(得分:0)
我不能专门讲Material-UI样式表,但是您可以将样式表注入渲染的组件中:
import {render} from '@testing-library/react';
import fs from 'fs';
import path from 'path';
const stylesheetFile = fs.reactFileSync(path.resolve(__dirname, '../path-to-stylesheet'), 'utf-8');
const styleTag = document.createElement('style');
styleTag.type = 'text/css';
styleTag.innerHTML = stylesheetFile;
const rendered = render(<MyComponent>);
rendered.append(style);
您不必一定要读取文件,可以使用所需的任何文本。
答案 2 :(得分:0)
这最终看起来像是JSS和各种浏览器实现(例如jsdom和Blink,至少在Chrome中是这样)的问题。尝试修改/启用/禁用这些样式规则时,您可以在Chrome中看到它(不能)。
该行为似乎是由于使用CSSOM insertRule API的JSS库导致的。 DOM中生成了一个样式表,用于我们期望在组件中使用的样式,但是标记为空-它仅用于将阴影CSS链接回DOM。样式永远不会写入DOM中的内联样式表,因此,getComputedStyle
方法不会返回预期的结果。
有an open issue可以解决此问题并简化开发过程。
我将自定义组件切换为styled-components,该组件没有其中的某些特质。 Material-UI is planning on transitioning soon as well。
答案 3 :(得分:0)
您可以将其添加到custom render function中。渲染后,该函数从cssom中提取样式并将其放入样式标签中。这是一个实现:
let customRender = (ui, options) => {
let renderResult = render(ui, options);
let styleElement = document.createElement("style");
let styleText = "";
for (let styleSheet of document.styleSheets) {
for (let rule of styleSheet.cssRules) {
styleText += rule.cssText + "\n";
}
}
styleElement.textContent = styleText.slice(0, -1);
document.head.appendChild(styleElement);
// remove old style elements
let emptyStyleElements = document.head.querySelectorAll('style[data-jss=""]');
for (let element of emptyStyleElements) {
element.remove();
}
return renderResult;
}