I have written serveral components using the code in the next branch and have styled them using withStyles. Everything works as expected expect for unit testing.
The output below are from my attempt at converting a Material-UI component in the next branch to use withStyles
Using the default method of static context types the base nope of the wrapper looks like:
ShallowWrapper {
root: [Circular],
unrendered:
{ '$$typeof': Symbol(react.element),
type:
{ [Function: AppBar]
propTypes: [Object],
defaultProps: [Object],
contextTypes: [Object] },
key: null,
ref: null,
props: { children: 'Hello World', accent: false },
_owner: null,
_store: {} },
renderer:
ReactShallowRenderer {
_instance:
ShallowComponentWrapper {
_debugID: 186,
_currentElement: [Object],
_rootNodeID: 0,
_compositeType: 0,
_instance: [Object],
_hostParent: null,
_hostContainerInfo: null,
_updateBatchNumber: null,
_pendingElement: null,
_pendingStateQueue: null,
_pendingReplaceState: false,
_pendingForceUpdate: false,
_renderedNodeType: 1,
_renderedComponent: [Object],
_context: [Object],
_mountOrder: 2,
_topLevelWrapper: null,
_pendingCallbacks: null,
_calledComponentWillUnmount: false,
_warnedAboutRefsInRender: false },
render: [Function: render],
getRenderOutput: [Function: getRenderOutput] },
node:
{ '$$typeof': Symbol(react.element),
type:
{ [Function: Paper]
propTypes: [Object],
defaultProps: [Object],
contextTypes: [Object] },
key: null,
ref: null,
props:
{ square: true,
elevation: 4,
className: 'MuiAppBar-appBar-1743332029 MuiAppBar-primary-15947162',
children: 'Hello World' },
_owner: null,
_store: {} },
nodes:
[ { '$$typeof': Symbol(react.element),
type: [Object],
key: null,
ref: null,
props: [Object],
_owner: null,
_store: {} } ],
length: 1,
options: { context: { theme: [Object], styleManager: [Object] } },
complexSelector:
ComplexSelector {
buildPredicate: [Function: buildPredicate],
findWhereUnwrapped: [Function: findWhereUnwrapped],
childrenOfNode: [Function: childrenOfNode] } }
As you can see the class name is a string of classes
Using withStyles the wrapper output looks like:
ShallowWrapper {
root: [Circular],
unrendered:
{ '$$typeof': Symbol(react.element),
type: { [Function: WithStyle] contextTypes: [Object], displayName: 'withStyles(AppBar)' },
key: null,
ref: null,
props: { children: 'Hello World' },
_owner: null,
_store: {} },
renderer:
ReactShallowRenderer {
_instance:
ShallowComponentWrapper {
_debugID: 186,
_currentElement: [Object],
_rootNodeID: 0,
_compositeType: 2,
_instance: [Object],
_hostParent: null,
_hostContainerInfo: null,
_updateBatchNumber: null,
_pendingElement: null,
_pendingStateQueue: null,
_pendingReplaceState: false,
_pendingForceUpdate: false,
_renderedNodeType: 1,
_renderedComponent: [Object],
_context: [Object],
_mountOrder: 2,
_topLevelWrapper: null,
_pendingCallbacks: null,
_calledComponentWillUnmount: false,
_warnedAboutRefsInRender: false },
render: [Function: render],
getRenderOutput: [Function: getRenderOutput] },
node:
{ '$$typeof': Symbol(react.element),
type: { [Function: AppBar] propTypes: [Object], defaultProps: [Object] },
key: null,
ref: null,
props: { classes: [Object], children: 'Hello World', accent: false },
_owner: null,
_store: {} },
nodes:
[ { '$$typeof': Symbol(react.element),
type: [Object],
key: null,
ref: null,
props: [Object],
_owner: null,
_store: {} } ],
length: 1,
options: { context: { theme: [Object], styleManager: [Object] } },
complexSelector:
ComplexSelector {
buildPredicate: [Function: buildPredicate],
findWhereUnwrapped: [Function: findWhereUnwrapped],
childrenOfNode: [Function: childrenOfNode] } }
Here is a sample component:
// Core
import React, { PropTypes } from 'react'
import classnames from 'classnames'
import { createStyleSheet, withStyles } from 'material-ui/styles'
import { amber, darkWhite, green, red } from 'material-ui/styles/colors'
// Components
import { Layout } from 'material-ui'
export const styles = createStyleSheet('CpiColorScale', theme => {
const { spacing } = theme
return {
root: {
margin: spacing.unit
},
cell: {
minWidth: spacing.unit * 8,
padding: spacing.unit * 0.4
},
high: {
backgroundColor: red[500]
},
low: {
backgroundColor: green[500]
},
medium: {
backgroundColor: amber[500]
},
text: {
color: darkWhite,
fontSize: 14,
margin: 0
}
}
})
const CpiColorScale = ({ classes, thresholdLow = '0-2%', thresholdMid = '2-4%', thresholdHigh = '5%+', ...other }) => (
<div className={ classes.root } { ...other }>
<Layout container align='center' direction='row'>
<div className={ classnames(classes.cell, classes.low) }>
<span className={ classes.text }>{ thresholdLow }</span>
</div>
<div className={ classnames(classes.cell, classes.medium) }>
<span className={ classes.text }>{ thresholdMid }</span>
</div>
<div className={ classnames(classes.cell, classes.high) }>
<span className={ classes.text }>{ thresholdHigh }</span>
</div>
</Layout>
</div>
)
CpiColorScale.propTypes = {
classes: PropTypes.object,
thresholdLow: PropTypes.string,
thresholdMid: PropTypes.string,
thresholdHigh: PropTypes.string
}
export default withStyles(styles)(CpiColorScale)
Here is its unit test:
// Core
import React from 'react'
import { assert } from 'chai'
import { createShallow } from 'material-ui/test-utils'
// Components
import CpiColorScale, { styles } from './CpiColorScale'
describe('CpiColorScale - Component', () => {
let classes
let shallow
before(()=> {
shallow = createShallow()
classes = shallow.context.styleManager.render(styles)
})
it('should have props', () => {
const wrapper = shallow(<CpiColorScale thresholdLow='0-2%' thresholdMid='2-4%' thresholdHigh='5%+' />)
assert.strictEqual(wrapper.props().thresholdLow, '0-2%')
assert.strictEqual(wrapper.props().thresholdMid, '2-4%')
assert.strictEqual(wrapper.props().thresholdHigh, '5%+')
})
it('should render', () => {
const wrapper = shallow(<CpiColorScale />)
assert.strictEqual(wrapper.hasClass(classes.root), true)
})
})
Questions would be best suited asked on StackOveflow.
You need to shallow render some level deeper in the tree. Enzyme exposes a .dive() helper to do so.
At @doctolib, we also ended up writing a .until() enzyme operator to do it recursively:
function shallowRecursively(wrapper, selector, { context }) {
if (wrapper.isEmptyRender() || typeof wrapper.node.type === 'string') {
return wrapper
}
const instance = wrapper.root.instance()
if (instance.getChildContext) {
context = {
...context,
...instance.getChildContext(),
}
}
const nextWrapper = wrapper.shallow({ context })
if (selector && wrapper.is(selector)) {
return nextWrapper
}
return shallowRecursively(nextWrapper, selector, { context })
}
export default function until(selector, { context } = this.options) {
return this.single('until', () => shallowRecursively(this, selector, { context }))
}
Tips, use console.log(wrapper.debug()) to get a better view of the actual tree, that would have made the issue description easier to understand.
Thanks for the follow up. Dive was exactly what I needed.