cssinjs / jss

JSS is an authoring tool for CSS which uses JavaScript as a host language.
https://cssinjs.org
MIT License
7.06k stars 397 forks source link

[react-jss] Testing components with styles. #804

Open HenriBeck opened 5 years ago

HenriBeck commented 5 years ago

From @kof on June 5, 2017 20:35

Motivation: when you snapshot components with jest, you can also snapshot css generated by the component. Ideally this should also work with styled-jss. Also we should support testing generated CSS without jest.

See for e.g. https://github.com/styled-components/jest-styled-components/

Copied from original issue: cssinjs/react-jss#93

HenriBeck commented 5 years ago

From @kof on June 29, 2017 15:36

Right now one can obtain all sheets by using SheetsRegistryProvider or JssProvider if you are on the next branch/pre release version.

import {SheetsRegistryProvider, SheetsRegistry} from 'react-jss'

it('should match the css snapshot', () => {
  const sheets = new SheetsRegistry()
  render(
    <SheetsRegistryProvider registry={sheets}>
      <Component />
    </SheetsRegistryProvider>
  )
  const snapshot = 'some css'
  expect(sheets.toString()).to.be(snapshot)
})
HenriBeck commented 5 years ago

From @kserjey on June 29, 2017 15:40

But what if i use enzyme for testing instead of jest snapshot?

HenriBeck commented 5 years ago

From @kof on June 29, 2017 15:41

Thats fine, the example above is not using jest. This issue is basically about simplifying it in the future. It is already possible now.

HenriBeck commented 5 years ago

From @kserjey on July 4, 2017 12:1

I often use selectors to find component in Enzyme testing and for that i map classes to new object with next function:

ruleName => sheet.getRule(ruleName).selectorText

Is there more reliable method?

HenriBeck commented 5 years ago

From @kof on July 4, 2017 13:5

  1. for testing you may want to pass your simplified selector generator so that you can use a simpler selector without counters and hardcode the result
  2. you can use a different attribute for testing
HenriBeck commented 5 years ago

From @kof on July 4, 2017 13:18

Also what about classes object?

jb-san commented 5 years ago

any news on this ? i can not get the way that @kof gives as an example to work..

jb-san commented 5 years ago

Got it working with a helper

import React from 'react';
import { JssProvider, SheetsRegistry, ThemeProvider } from 'react-jss';
import { mount } from 'enzyme';
import mockTheme from './defaultTheme';

/**
 *  JSS helper mount
 * Wrapps the passed in component in Themeprovider and captures the styles generated
 * @param {React component} Component
 * @returns {Array} First item in array is the stylesheet generated by jss, the second is the rendered component for further expects
 */
export const themeMount = Component => {
    const sheets = new SheetsRegistry();
    const ClonedComponent = React.cloneElement(Component, { _renderid: 'rendered-component' });

    const tree = mount(
        <JssProvider registry={sheets}>
            <ThemeProvider theme={{ theme: mockTheme }}>{ClonedComponent}</ThemeProvider>
        </JssProvider>
    );
    return [sheets.toString(), tree.findWhere(node => node.props()._renderid === 'rendered-component')];
};

then in the tests

import React from 'react';

import Button from 'components/Button';
describe('Button', () => {
    test('should render a default button', () => {
        const [styleSheets, component] = themeMount(<Button>Test</Button>);
        expect(styleSheets.toString()).toMatchSnapshot();
        expect(component).toMatchSnapshot();
    });
});
LaurensBosscher commented 5 years ago

Thanks for the code @jb-san, we've run into issues where the order of the attributes was not guaranteed with that approach thus the snapshots would fail at random. We're now transitioning to making visual snapshots with storybook, I feel that this might be a better path going forward.

If you're interested hit me up!

HenriBeck commented 5 years ago

Another solution could be to render the generated styles next to the component so they would be included in the component snapshot and someone wouldn't need to wrap every component around a JssProvider and collect the sheets manually.

kilrain commented 5 years ago

What if, when passing a custom theming context to the react-jss HOC, it is able to use the React Context's default value?

// themes.js

const defaultTheme = {
  color: 'red'
}

const ThemeContext = React.createContext(defaultTheme)

export const theming = createTheming(ThemeContext)

// component.jsx
import {theming} from './themes

/* ... */

export default withStyles(styles, {theming})(Component)

Then unit tests which depend on a ThemeProvider can just fallback on the default theme.

EDIT - though on a closer look at the source code, maybe this should be handled by the theming library.

HenriBeck commented 5 years ago

@kilrain This should already be the case.

For the default theming context this is sadly not possible.