Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to inject Material-UI stylesheets into a jest/react-testing-library test?

It seems that if you don't inject Material-UI stylesheets into a jest/react-testing-library test then jsdom will fail to get the correct styles from your components (e.g. running getComputedStyle(component) will return the incorrect styles for the component).

How you properly setup a jest/react-testing-library test so that the styles are correctly injected into the test? I've already wrapped the components in a theme provider, which works fine.

like image 311
Jake Avatar asked May 20 '19 08:05

Jake


People also ask

Can Jest be used for UI testing?

Advantages of using Jest on React projects:Snapshot testing to prevent accidental UI changes. Mocking function to work with fake data that must be provided by a database or other source. Extensible framework - we can integrate other tools to work with it. Fast and performance-focused - tests run in parallel.

Does React testing library use Jest?

React Testing Library is not specific to any testing framework; we can use it with any other testing library, although Jest is recommended and preferred by many developers. create-react-app uses both Jest and React Testing Library by default.


1 Answers

As a workaround reinserting the whole head (or the element where JSS styles are injected) before assertion seems to apply styles correctly with both getComputedStyle() and react testing library's 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;`);
});

This will still fail though when you're using dynamic styles, like:

myButton: {
  padding: props => props.spacing,
  ...
}

That's because JSS uses CSSStyleSheet.insertRule method to inject these styles, and it won't appear as a style node in the head. One solution to this issue is to hook into the browser's insertRule method and add incoming rules to the head as style tags. To extract all this into a function:

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;
  };
}

Example usage of this function in our previous test:

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;");
});

like image 168
davidkarolyi Avatar answered Oct 19 '22 11:10

davidkarolyi