Material-ui: TypeError: Cannot read property 'prepareStyles' of undefined

Created on 5 Oct 2016  Â·  17Comments  Â·  Source: mui-org/material-ui

Problem description

When I write test I get
TypeError: Cannot read property 'prepareStyles' of undefined

My Component looks like

import React, {PropTypes} from 'react';
import TransactionListRow from './TransactionListRow';
import {Table, TableBody, TableHeader, TableHeaderColumn, TableRow} from 'material-ui/Table';

const TransactionList = ({transactions}) => {
  return (
    <Table>
      <TableHeader displaySelectAll={false}>
        <TableRow>
          <TableHeaderColumn>Name</TableHeaderColumn>
          <TableHeaderColumn>Amount</TableHeaderColumn>
          <TableHeaderColumn>Transaction</TableHeaderColumn>
          <TableHeaderColumn>Category</TableHeaderColumn>
        </TableRow>
      </TableHeader>
      <TableBody>
        {transactions.map(transaction =>
          <TransactionListRow key={transaction.id} transaction={transaction}/>
        )}
      </TableBody>
    </Table>
  );
};

TransactionList.propTypes = {
  transactions: PropTypes.array.isRequired
};

export default TransactionList;

My Test looks like

import expect from 'expect';
import React from 'react';
import {mount} from 'enzyme';
import TransactionList from './TransactionList';
import TableHeaderColumn from 'material-ui/Table';

describe("<TransactionList />", ()=> {
  it('renders four <TableHeaderColumn /> components', () => {
    const wrapper = mount(<TransactionList transactions={[]}/>);
    expect(wrapper.find(TableHeaderColumn)).to.have.length(4);
  });
});

My index.js (which is not related to test in my understanding) is

/*eslint-disable import/default */
import 'babel-polyfill';
import React from 'react';
import {render} from 'react-dom';
import configureStore from './store/configureStore.prod';
import {Provider} from 'react-redux';
import {Router, browserHistory} from 'react-router';
import routes from './routes';
import {loadAuthors} from './actions/authorActions';
import {loadCourses} from './actions/courseActions';
import './styles/styles.css'; //Webpack can import CSS files too!
import '../node_modules/bootstrap/dist/css/bootstrap.min.css';
import '../node_modules/toastr/build/toastr.min.css';
import MuiThemeProvider from 'material-ui/styles/MuiThemeProvider';
import getMuiTheme from 'material-ui/styles/getMuiTheme';

const store = configureStore();
store.dispatch(loadCourses());
store.dispatch(loadAuthors());

render(
  <MuiThemeProvider muiTheme={getMuiTheme()}>
    <Provider store={store}>
      <Router history={browserHistory} routes={routes}/>
    </Provider>
  </MuiThemeProvider>,
  document.getElementById('app')
);

Steps to reproduce

When I run npm test:watch, I see

<TransactionList /> renders four <TableHeaderColumn /> components:
     TypeError: Cannot read property 'prepareStyles' of undefined
      at Table.render (node_modules/material-ui/Table/Table.js:155:48)
      at ReactCompositeComponentMixin._renderValidatedComponentWithoutOwnerOrContext (node_modules/react/lib/ReactCompositeComponent.js:687:34)
      at ReactCompositeComponentMixin._renderValidatedComponent (node_modules/react/lib/ReactCompositeComponent.js:707:32)
      at wrapper [as _renderValidatedComponent] (node_modules/react/lib/ReactPerf.js:66:21)
      at ReactCompositeComponentMixin.performInitialMount (node_modules/react/lib/ReactCompositeComponent.js:291:30)
      at ReactCompositeComponentMixin.mountComponent (node_modules/react/lib/ReactCompositeComponent.js:222:21)
      at wrapper [as mountComponent] (node_modules/react/lib/ReactPerf.js:66:21)
      at Object.ReactReconciler.mountComponent (node_modules/react/lib/ReactReconciler.js:39:35)
      at ReactCompositeComponentMixin.performInitialMount (node_modules/react/lib/ReactCompositeComponent.js:297:34)
      at ReactCompositeComponentMixin.mountComponent (node_modules/react/lib/ReactCompositeComponent.js:222:21)
      at wrapper [as mountComponent] (node_modules/react/lib/ReactPerf.js:66:21)
      at Object.ReactReconciler.mountComponent (node_modules/react/lib/ReactReconciler.js:39:35)
      at ReactCompositeComponentMixin.performInitialMount (node_modules/react/lib/ReactCompositeComponent.js:297:34)
      at ReactCompositeComponentMixin.mountComponent (node_modules/react/lib/ReactCompositeComponent.js:222:21)
      at wrapper [as mountComponent] (node_modules/react/lib/ReactPerf.js:66:21)
      at Object.ReactReconciler.mountComponent (node_modules/react/lib/ReactReconciler.js:39:35)
      at ReactCompositeComponentMixin.performInitialMount (node_modules/react/lib/ReactCompositeComponent.js:297:34)
      at ReactCompositeComponentMixin.mountComponent (node_modules/react/lib/ReactCompositeComponent.js:222:21)
      at wrapper [as mountComponent] (node_modules/react/lib/ReactPerf.js:66:21)
      at Object.ReactReconciler.mountComponent (node_modules/react/lib/ReactReconciler.js:39:35)
      at mountComponentIntoNode (node_modules/react/lib/ReactMount.js:103:32)
      at ReactReconcileTransaction.Mixin.perform (node_modules/react/lib/Transaction.js:136:20)
      at batchedMountComponentIntoNode (node_modules/react/lib/ReactMount.js:124:15)
      at ReactDefaultBatchingStrategyTransaction.Mixin.perform (node_modules/react/lib/Transaction.js:136:20)
      at Object.ReactDefaultBatchingStrategy.batchedUpdates (node_modules/react/lib/ReactDefaultBatchingStrategy.js:63:19)
      at Object.batchedUpdates (node_modules/react/lib/ReactUpdates.js:97:20)
      at Object.ReactMount._renderNewRootComponent (node_modules/react/lib/ReactMount.js:277:18)
      at Object.wrapper [as _renderNewRootComponent] (node_modules/react/lib/ReactPerf.js:66:21)
      at Object.ReactMount._renderSubtreeIntoContainer (node_modules/react/lib/ReactMount.js:354:32)
      at Object.ReactMount.render (node_modules/react/lib/ReactMount.js:374:23)
      at Object.wrapper [as render] (node_modules/react/lib/ReactPerf.js:66:21)
      at Object.ReactTestUtils.renderIntoDocument (node_modules/react/lib/ReactTestUtils.js:78:21)
      at renderWithOptions (node_modules/enzyme/build/react-compat.js:175:26)
      at new ReactWrapper (node_modules/enzyme/build/ReactWrapper.js:87:59)
      at mount (node_modules/enzyme/build/mount.js:21:10)
      at Context.<anonymous> (TransactionList.test.js:9:21)

Versions

My dependencies are

    "react": "15.0.2",
    "react-tap-event-plugin": "^1.0.0",
    "material-ui": "0.15.4"
  • Material-UI: mentioned above
  • React: mentioned above
  • Browser: Google Chrome (Version 53.0.2785.116 (64-bit))
discussion

Most helpful comment

I guess this is related to https://github.com/callemall/material-ui/issues/4021.

TL;DR: try to wrap the component being tested in <MuiThemeProvider /> or https://github.com/callemall/material-ui/issues/4021#issuecomment-210998829.

All 17 comments

I guess this is related to https://github.com/callemall/material-ui/issues/4021.

TL;DR: try to wrap the component being tested in <MuiThemeProvider /> or https://github.com/callemall/material-ui/issues/4021#issuecomment-210998829.

Did you mean something like following?

import expect from 'expect';
import React from 'react';
import {mount} from 'enzyme';
import TransactionList from './TransactionList';
import {TableHeaderColumn} from 'material-ui/Table';
import getMuiTheme from 'material-ui/styles/getMuiTheme';

function setup() {
  return mount(
    <MuiThemeProvider>
      <TransactionList transactions={[]}/>
    </MuiThemeProvider>, {
      getChildContext() {
        return {muiTheme: getMuiTheme()};
      },
      childContextTypes: {muiTheme: React.PropTypes.object}
    });
}

describe("<TransactionList />", ()=> {
  it('renders four <TableHeaderColumn /> components', () => {
    const wrapper = mount(<TransactionList transactions={[]}/>);
    expect(wrapper.find(TableHeaderColumn)).to.have.length(4);
  });
});

I tried this as well, but still see

  1) <TransactionList /> renders four <TableHeaderColumn /> components:
     TypeError: Cannot read property 'prepareStyles' of undefined
      at Table.render (node_modules/material-ui/Table/Table.js:155:48)
      at node_modules/react/lib/ReactCompositeComponent.js:793:21
      at measureLifeCyclePerf (node_modules/react/lib/ReactCompositeComponent.js:74:12)
      at ReactCompositeComponentMixin._renderValidatedComponentWithoutOwnerOrContext (node_modules/react/lib/ReactCompositeComponent.js:792:27)
      at ReactCompositeComponentMixin._renderValidatedComponent (node_modules/react/lib/ReactCompositeComponent.js:819:34)
      at ReactCompositeComponentMixin.performInitialMount (node_modules/react/lib/ReactCompositeComponent.js:361:30)
      at ReactCompositeComponentMixin.mountComponent (node_modules/react/lib/ReactCompositeComponent.js:257:21)
      at Object.ReactReconciler.mountComponent (node_modules/react/lib/ReactReconciler.js:47:35)
      at ReactCompositeComponentMixin.performInitialMount (node_modules/react/lib/ReactCompositeComponent.js:370:34)
      at ReactCompositeComponentMixin.mountComponent (node_modules/react/lib/ReactCompositeComponent.js:257:21)
      at Object.ReactReconciler.mountComponent (node_modules/react/lib/ReactReconciler.js:47:35)
      at ReactCompositeComponentMixin.performInitialMount (node_modules/react/lib/ReactCompositeComponent.js:370:34)
      at ReactCompositeComponentMixin.mountComponent (node_modules/react/lib/ReactCompositeComponent.js:257:21)
      at Object.ReactReconciler.mountComponent (node_modules/react/lib/ReactReconciler.js:47:35)
      at ReactCompositeComponentMixin.performInitialMount (node_modules/react/lib/ReactCompositeComponent.js:370:34)
      at ReactCompositeComponentMixin.mountComponent (node_modules/react/lib/ReactCompositeComponent.js:257:21)
      at Object.ReactReconciler.mountComponent (node_modules/react/lib/ReactReconciler.js:47:35)
      at mountComponentIntoNode (node_modules/react/lib/ReactMount.js:105:32)
      at ReactReconcileTransaction.Mixin.perform (node_modules/react/lib/Transaction.js:138:20)
      at batchedMountComponentIntoNode (node_modules/react/lib/ReactMount.js:127:15)
      at ReactDefaultBatchingStrategyTransaction.Mixin.perform (node_modules/react/lib/Transaction.js:138:20)
      at Object.ReactDefaultBatchingStrategy.batchedUpdates (node_modules/react/lib/ReactDefaultBatchingStrategy.js:63:19)
      at Object.batchedUpdates (node_modules/react/lib/ReactUpdates.js:98:20)
      at Object.ReactMount._renderNewRootComponent (node_modules/react/lib/ReactMount.js:321:18)
      at Object.ReactMount._renderSubtreeIntoContainer (node_modules/react/lib/ReactMount.js:402:32)
      at Object.ReactMount.render (node_modules/react/lib/ReactMount.js:423:23)
      at Object.ReactTestUtils.renderIntoDocument (node_modules/react/lib/ReactTestUtils.js:84:21)
      at renderWithOptions (node_modules/enzyme/build/react-compat.js:175:26)
      at new ReactWrapper (node_modules/enzyme/build/ReactWrapper.js:87:59)
      at mount (node_modules/enzyme/build/mount.js:21:10)
      at Context.<anonymous> (TransactionList.test.js:24:20)

Let me know what am I doing wrong?

Based on the test that I see in material-ui, I tried the following but still same result

import expect from 'expect';
import React from 'react';
import {mount} from 'enzyme';
import TransactionList from './TransactionList';
import {TableHeaderColumn} from 'material-ui/Table';
import getMuiTheme from 'material-ui/styles/getMuiTheme';

describe("<TransactionList />", ()=> {
  const muiTheme = getMuiTheme();
  const mountWithContext = (node) => mount(node, {context: {muiTheme}});


  it('renders four <TableHeaderColumn /> components', () => {
    const wrapper = mountWithContext(<TransactionList transactions={[]}/>);
    expect(wrapper.find(TableHeaderColumn)).to.have.length(4);
  });

@hhimanshu: can you provide a pen so I can check it out?

@lucasbento , I do not know how to pen, so I uploaded by code https://github.com/101bits/pluralsight-react

Steps to reproduce

- npm install
- npm start -s

Let me know if I missed anything, thanks

hey @hhimanshu, thanks for sharing that, really helpful.

about your issue, I never use context so I don't really know how it works, but...

I saw that this was undefined in your component, I switched it to a class instead of a stateless component and like magic this became available:

import React, {Component} from 'react';
import TransactionListRow from './TransactionListRow';
import {Table, TableBody, TableHeader, TableHeaderColumn, TableRow} from 'material-ui/Table';

class TransactionList extends Component {
  render() {
    const { transactions } = this.props;

    return (
      <Table>
        <TableHeader displaySelectAll={false}>
          <TableRow>
            <TableHeaderColumn>Name</TableHeaderColumn>
            <TableHeaderColumn>Amount</TableHeaderColumn>
            <TableHeaderColumn>Transaction</TableHeaderColumn>
            <TableHeaderColumn>Category</TableHeaderColumn>
          </TableRow>
        </TableHeader>
        <TableBody>
          {transactions.map(transaction =>
            <TransactionListRow key={transaction.id} transaction={transaction}/>
          )}
        </TableBody>
      </Table>
    );
  }
};

After trying _a lot_ your tests, here is how it worked:

import { expect } from 'chai';
import React from 'react';
import { mount } from 'enzyme';
import TransactionList from './TransactionList';
import {TableHeaderColumn} from 'material-ui/Table';
import getMuiTheme from 'material-ui/styles/getMuiTheme';

describe("<TransactionList />", ()=> {
  const mountWithContext = (node) => mount(node, {
    context: {
      muiTheme: getMuiTheme(),
    },
    childContextTypes: {
      muiTheme: React.PropTypes.object.isRequired,
    }
  });

  it('renders five <TableHeaderColumn /> components', () => {
    const wrapper = mount(<TransactionList transactions={[]}/>, {
      context: {
        muiTheme: getMuiTheme(),
      },
      childContextTypes: {
        muiTheme: React.PropTypes.object.isRequired,
      },
    });

    expect(wrapper.find(TableHeaderColumn)).to.have.length(5);
  });
});

I'm also using chai instead of expect, your syntax was actually using chai.

I strongly recommend using Jest, Snapshot testing is much easier.


I also changed the number of <TableHeaderColumn /> to 5 instead of 4, even though there are 4 <TableHeaderColumn /> on the file, enzyme returns 5. ¯_(ツ)_/¯

Hope this works well for you!

I'm also experiencing this issue. Have not been able to solve it yet.

@janoist1: did you try what I sent? works pretty well here.

Thanks, and yes I did. It works but it's still just a workaround. It should be work with shallow too. Now I had to update the way I find child components as the prop selector stopped working. stackoverflow question
Also, I'd like to mention that I did not have to turn my component into React class.

@janoist1 when I follow your example I get

Uncaught Exception: test/components/Foo/List/index.js
  RangeError: Invalid string length
    _combinedTickCallback (internal/process/next_tick.js:67:7)
    process._tickCallback (internal/process/next_tick.js:98:9)



  ✖ Test results were not received from test/components/Foo/List/index.js

I'm closing this issue since there's no answer form the creator, if you have further questions please ask on stackoverflow.com.

@lucasbento I am seeing this issue as well, but not when using tests. Rather when compiling my app using next.js

Just chiming in to see that I spent the last day or so debugging this issue - and while the answer should have been obvious to me, I thought it might bear mentioning:

If you've got an app set up that uses serverside rendering, you may need to wrap both the serverside and clientside apps in <MuiThemeProvider>.

Just in case, to use mount with mocha you need something like jsdom or you will get document.createElement is not a function

see setup here: https://github.com/airbnb/enzyme/blob/master/docs/guides/jsdom.md

I experienced the same issue recently.. It was the issue of my naming of the component class.. So I renamed my component classname, filename and import statement in the main App component to the new name and it seemed to work.

I had the same problem. My problem was that I had a class attribute in one of my jsx divs instead of className

You need to wrap your table in a MuiThemeProvider tag
https://stackoverflow.com/questions/39863436/typeerror-cannot-read-property-preparestyles-of-undefined is the solution

Was this page helpful?
0 / 5 - 0 ratings

Related issues

finaiized picture finaiized  Â·  3Comments

ryanflorence picture ryanflorence  Â·  3Comments

iamzhouyi picture iamzhouyi  Â·  3Comments

zabojad picture zabojad  Â·  3Comments

mb-copart picture mb-copart  Â·  3Comments