text: import React from 'react';
  import { spy } from 'sinon';
  import { assert } from 'chai';
  import JssProvider from 'react-jss/lib/JssProvider';
  import { create, SheetsRegistry } from 'jss';
  import consoleErrorMock from 'test/utils/consoleErrorMock';
  import jssPreset from './jssPreset';
  import withStyles from './withStyles';
  import MuiThemeProvider from './MuiThemeProvider';
  import createMuiTheme from './createMuiTheme';
  import createGenerateClassName from './createGenerateClassName';
  import { createShallow, createMount, getClasses } from '../test-utils';
  
  // eslint-disable-next-line react/prefer-stateless-function
  class Empty extends React.Component {
    render() {
      return <div />;
    }
  }
  
  describe('withStyles', () => {
    let shallow;
    let mount;
  
    before(() => {
      shallow = createShallow();
      mount = createMount();
    });
  
    after(() => {
      mount.cleanUp();
    });
  
    describe('props', () => {
      let StyledComponent1;
      let classes;
  
      before(() => {
        const styles = { root: { display: 'flex' } };
        StyledComponent1 = withStyles(styles, { name: 'MuiTextField' })(Empty);
        classes = getClasses(<StyledComponent1 />);
      });
  
      it('should provide a classes property', () => {
        const wrapper = shallow(<StyledComponent1 />);
        assert.deepEqual(wrapper.props().classes, classes, 'Should provide the classes property');
      });
  
      describe('prop: classes', () => {
        before(() => {
          consoleErrorMock.spy();
        });
  
        after(() => {
          consoleErrorMock.reset();
        });
  
        it('should accept a classes property', () => {
          const wrapper = shallow(<StyledComponent1 classes={{ root: 'h1' }} />);
          assert.deepEqual(wrapper.props().classes, { root: `${classes.root} h1` });
          assert.strictEqual(consoleErrorMock.callCount(), 0);
        });
  
        it('should ignore undefined property', () => {
          const wrapper = shallow(<StyledComponent1 classes={{ root: undefined }} />);
          assert.deepEqual(wrapper.props().classes, { root: classes.root });
          assert.strictEqual(consoleErrorMock.callCount(), 0);
        });
  
        it('should warn if providing a unknown key', () => {
          const wrapper = shallow(<StyledComponent1 classes={{ bar: 'foo' }} />);
  
          assert.deepEqual(wrapper.props().classes, { root: classes.root, bar: 'undefined foo' });
          assert.strictEqual(consoleErrorMock.callCount(), 1);
          assert.match(
            consoleErrorMock.args()[0][0],
            /Material-UI: the key `bar` provided to the classes property is not implemented/,
          );
        });
  
        it('should warn if providing a non string', () => {
          const wrapper = shallow(<StyledComponent1 classes={{ root: {} }} />);
  
          assert.deepEqual(wrapper.props().classes, { root: `${classes.root} [object Object]` });
          assert.strictEqual(consoleErrorMock.callCount(), 2);
          assert.match(
            consoleErrorMock.args()[1][0],
            /Material-UI: the key `root` provided to the classes property is not valid/,
          );
        });
      });
  
      describe('prop: innerRef', () => {
        it('should provide a ref on the inner component', () => {
          const handleRef = spy();
          mount(<StyledComponent1 innerRef={handleRef} />);
          assert.strictEqual(handleRef.callCount, 1);
        });
      });
  
      describe('cache', () => {
        it('should recycle with no classes property', () => {
          const wrapper = mount(<StyledComponent1 />);
          const classes1 = wrapper.find(Empty).props().classes;
          wrapper.update();
          const classes2 = wrapper.find(Empty).props().classes;
          assert.strictEqual(classes1, classes2);
        });
  
        it('should recycle even when a classes property is provided', () => {
          const inputClasses = { root: 'foo' };
          const wrapper = mount(<StyledComponent1 classes={inputClasses} />);
          const classes1 = wrapper.find(Empty).props().classes;
          wrapper.setProps({
            classes: inputClasses,
          });
          const classes2 = wrapper.find(Empty).props().classes;
          assert.strictEqual(classes1, classes2);
        });
  
        it('should invalidate the cache', () => {
          const wrapper = mount(<StyledComponent1 classes={{ root: 'foo' }} />);
          const classes1 = wrapper.find(Empty).props().classes;
          assert.deepEqual(classes1, {
            root: `${classes.root} foo`,
          });
          wrapper.setProps({
            classes: { root: 'bar' },
          });
          const classes2 = wrapper.find(Empty).props().classes;
          assert.notStrictEqual(classes1, classes2);
          assert.deepEqual(classes2, {
            root: `${classes.root} bar`,
          });
        });
      });
    });
  
    describe('mount', () => {
      let sheetsRegistry;
      let jss;
      let generateClassName;
  
      beforeEach(() => {
        jss = create(jssPreset());
        generateClassName = createGenerateClassName();
        sheetsRegistry = new SheetsRegistry();
      });
  
      it('should run lifecycles with no theme', () => {
        const styles = { root: { display: 'flex' } };
        const StyledComponent = withStyles(styles)(Empty);
  
        const wrapper = mount(
          <MuiThemeProvider theme={createMuiTheme()}>
            <JssProvider registry={sheetsRegistry} jss={jss} generateClassName={generateClassName}>
              <StyledComponent />
            </JssProvider>
          </MuiThemeProvider>,
        );
        assert.strictEqual(sheetsRegistry.registry.length, 1);
        assert.deepEqual(sheetsRegistry.registry[0].classes, { root: 'Empty-root-1' });
        wrapper.update();
        assert.strictEqual(sheetsRegistry.registry.length, 1, 'should only attach once');
        assert.deepEqual(sheetsRegistry.registry[0].classes, { root: 'Empty-root-1' });
        wrapper.setProps({ theme: createMuiTheme() });
        assert.strictEqual(sheetsRegistry.registry.length, 1, 'should only attach once');
        assert.deepEqual(sheetsRegistry.registry[0].classes, { root: 'Empty-root-1' });
  
        wrapper.unmount();
        assert.strictEqual(sheetsRegistry.registry.length, 0);
      });
  
      it('should work when depending on a theme', () => {
        const styles = theme => ({ root: { padding: theme.spacing.unit } });
        const StyledComponent = withStyles(styles, { name: 'MuiTextField' })(Empty);
  
        const wrapper = mount(
          <MuiThemeProvider theme={createMuiTheme()}>
            <JssProvider registry={sheetsRegistry} jss={jss} generateClassName={generateClassName}>
              <StyledComponent />
            </JssProvider>
          </MuiThemeProvider>,
        );
        assert.strictEqual(sheetsRegistry.registry.length, 1, 'should only attach once');
        assert.deepEqual(sheetsRegistry.registry[0].classes, { root: 'MuiTextField-root-1' });
        wrapper.setProps({ theme: createMuiTheme() });
        assert.strictEqual(sheetsRegistry.registry.length, 1, 'should only attach once');
        assert.deepEqual(sheetsRegistry.registry[0].classes, { root: 'MuiTextField-root-2' });
      });
  
      it('should support the overrides key', () => {
        const styles = { root: { padding: 8 } };
        const StyledComponent = withStyles(styles, { name: 'MuiTextField' })(Empty);
  
        mount(
          <MuiThemeProvider
            theme={createMuiTheme({
              overrides: {
                MuiTextField: {
                  root: {
                    padding: 9,
                  },
                },
              },
            })}
          >
            <JssProvider registry={sheetsRegistry} jss={jss} generateClassName={generateClassName}>
              <StyledComponent />
            </JssProvider>
          </MuiThemeProvider>,
        );
  
        assert.strictEqual(sheetsRegistry.registry.length, 1, 'should only attach once');
        assert.deepEqual(sheetsRegistry.registry[0].rules.raw, { root: { padding: 9 } });
      });
  
      describe('options: disableStylesGeneration', () => {
        it('should not generate the styles', () => {
          const styles = { root: { display: 'flex' } };
          const StyledComponent = withStyles(styles)(Empty);
  
          const wrapper = mount(
            <MuiThemeProvider theme={createMuiTheme()} disableStylesGeneration>
              <JssProvider registry={sheetsRegistry} jss={jss} generateClassName={generateClassName}>
                <StyledComponent />
              </JssProvider>
            </MuiThemeProvider>,
          );
          assert.strictEqual(sheetsRegistry.registry.length, 0);
          assert.deepEqual(wrapper.find(Empty).props().classes, {});
          wrapper.unmount();
          assert.strictEqual(sheetsRegistry.registry.length, 0);
        });
      });
    });
  
    describe('HMR with same state', () => {
      it('should take the new stylesCreator into account', () => {
        const styles1 = { root: { padding: 1 } };
        const StyledComponent1 = withStyles(styles1, { name: 'MuiTextField' })(Empty);
        const wrapper = mount(<StyledComponent1 />);
  
        const styles2 = { root: { padding: 2 } };
        const StyledComponent2 = withStyles(styles2, { name: 'MuiTextField' })(Empty);
  
        // Simulate react-hot-loader behavior
        wrapper.instance().componentDidUpdate = StyledComponent2.prototype.componentDidUpdate;
  
        const classes1 = wrapper.childAt(0).props().classes.root;
        wrapper.setProps({});
        wrapper.update();
        const classes2 = wrapper.childAt(0).props().classes.root;
  
        assert.notStrictEqual(classes1, classes2, 'should generate new classes');
      });
    });
  });
  