I've written a React component, Button:
import PropTypes from 'prop-types'
import Radium from 'radium'
import React from 'react'
import { Icon } from 'components'
import { COLOURS, GLOBAL_STYLES, ICONS, MEASUREMENTS } from 'app-constants'
@Radium
export default class Button extends React.Component {
static propTypes = {
children: PropTypes.string,
dark: PropTypes.bool,
icon: PropTypes.oneOf(Object.values(ICONS)).isRequired,
style: PropTypes.object,
}
render() {
const { children, dark, icon, style } = this.props
let mergedStyles = Object.assign({}, styles.base, style)
if (!children)
mergedStyles.icon.left = 0
if (dark)
mergedStyles = Object.assign(mergedStyles, styles.dark)
return (
<button
className="btn btn-secondary"
style={mergedStyles}
tabIndex={-1}>
<Icon name={icon} style={mergedStyles.icon} />
{children &&
<span style={mergedStyles.text}>{children}</span>
}
</button>
)
}
}
export const styles = {
base: {
backgroundColor: COLOURS.WHITE,
border: `1px solid ${COLOURS.BORDER_LIGHT}`,
borderRadius: GLOBAL_STYLES.BORDER_RADIUS,
cursor: 'pointer',
padding: GLOBAL_STYLES.BUTTON_PADDING,
':focus': {
outline: 'none',
},
':hover': {
boxShadow: GLOBAL_STYLES.BOX_SHADOW,
},
icon: {
fontSize: GLOBAL_STYLES.ICON_SIZE_TINY,
left: '-3px',
verticalAlign: 'middle',
},
text: {
fontSize: GLOBAL_STYLES.FONT_SIZE_TINY,
fontWeight: GLOBAL_STYLES.FONT_2_WEIGHT_MEDIUM,
marginLeft: `${MEASUREMENTS.BUTTON_PADDING.HORIZONTAL}px`,
verticalAlign: 'middle',
},
},
dark: {
backgroundColor: COLOURS.PRIMARY_3,
border: `1px solid ${COLOURS.PRIMARY_2}`,
color: COLOURS.WHITE,
':hover': {
boxShadow: GLOBAL_STYLES.BOX_SHADOW_DARK,
},
},
}
I've also written a test for Button with Jest and Enzyme, which validates if its dark styles are applied when its dark prop is set to true:
import { ICONS } from 'app-constants'
import Button, { styles } from 'components/Button'
describe("<Button>", () => {
let props
let mountedComponent
const getComponent = () => {
if (!mountedComponent)
mountedComponent = shallow(
<Button {...props} />
)
return mountedComponent
}
beforeEach(() => {
mountedComponent = undefined
props = {
children: undefined,
dark: undefined,
icon: ICONS.VIBE,
style: undefined,
}
})
describe("when `dark` is `true`", () => {
beforeEach(() => {
props.dark = true
})
it("applies the component's `dark` styles", () => {
const componentStyles = getComponent().props().style
expect(componentStyles).toEqual(expect.objectContaining(styles.dark))
})
})
})
As you can see, I do this by checking if the properties of styles.dark are inside the rendered Button's style attribute. If they are, then it means the styles have applied successfully.
The issue is that styles.dark and componentStyles don't match:
Output of console.log(styles.dark)
ObjectContaining{
":hover": {
"boxShadow": "0px 0px 0px 2px rgba(0,0,0,0.2)"
},
"backgroundColor": [Object],
"border": "1px solid rgb(47, 52, 63)",
"color": [Object]
}
Output of console.log(componentStyles)
{
"backgroundColor": "rgb(31, 34, 40)",
"border": "1px solid rgb(47, 52, 63)",
"borderRadius": "4px",
"color": "rgb(255, 255, 255)",
"cursor": "pointer",
"padding": "3px 5px 3px 5px"
}
I notice a few issues here:
styles.darkhas severalColor()[Object]s from thecolorlibrary. They haven't outputted theirrgb()value as a string, but the same properties incomponentStyleshave, thus resulting in a mismatch.componentStyleshas Radium's interactive styles stripped, such as:focusand:hover(I assume Radium does this during rendering triggered by Enzyme'sshallow()function). This causes a mismatch withstyles.dark, which doesn't have these properties stripped.
As a result, I'm not sure how to test this. I can't think of any alternative solutions to validate that styles.dark has been applied. I think that doing the following to styles.dark during testing would be a solution:
- Recursively cause all
Color()[Object]s to process so they output theirrgb()value as a string. - Recursively remove all interactive Radium styles (like
:focusand:hover)
Doing so would cause styles.dark to equal the value of componentStyles, thus passing the test. I'm just not sure how to do it.