Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

TypeError: Cannot read property 'prepareStyles' of undefined #5330

Closed
hhimanshu opened this issue Oct 4, 2016 · 17 comments
Closed

TypeError: Cannot read property 'prepareStyles' of undefined #5330

hhimanshu opened this issue Oct 4, 2016 · 17 comments

Comments

@hhimanshu
Copy link

hhimanshu commented Oct 4, 2016

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))
@lucasbento
Copy link

I guess this is related to #4021.

TL;DR: try to wrap the component being tested in <MuiThemeProvider /> or #4021 (comment).

@hhimanshu
Copy link
Author

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?

@hhimanshu
Copy link
Author

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

@lucasbento
Copy link

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

@hhimanshu
Copy link
Author

@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

@lucasbento
Copy link

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!

@janoist1
Copy link

janoist1 commented Oct 7, 2016

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

@lucasbento
Copy link

lucasbento commented Oct 7, 2016

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

@janoist1
Copy link

janoist1 commented Oct 7, 2016

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.

@morenoh149
Copy link

@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

@lucasbento
Copy link

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

@mattapperson
Copy link

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

@mbifulco
Copy link

mbifulco commented Jan 7, 2017

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>.

@npoirey
Copy link

npoirey commented Mar 4, 2017

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

@ghost
Copy link

ghost commented Oct 29, 2017

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.

@fernandozamoraj
Copy link

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

@vaidyanathanas
Copy link

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

9 participants