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')
);
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)
My dependencies are
"react": "15.0.2",
"react-tap-event-plugin": "^1.0.0",
"material-ui": "0.15.4"
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
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.