如何测试样式是否动态应用于React组件

时间:2017-11-04 10:39:20

标签: reactjs jestjs enzyme radium

我写了一个React组件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,
    },
  },
}

我还使用Jest和Enzyme编写了Button的测试,当dark道具设置为dark时,它会验证是否应用了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))
    })
  })
})

正如您所看到的,我通过检查styles.dark的属性是否在呈现的Button的{​​{1}}属性中来执行此操作。如果是,则表示样式已成功应用。

问题是stylestyles.dark不匹配:

componentStyles

的输出
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] }

的输出
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" } 有来自styles.dark库的多个Color() [Object]个。他们没有将color值作为字符串输出,但rgb()中的属性相同,从而导致不匹配。
  • componentStyles删除了Radium的交互式样式,例如componentStyles:focus(我假设Radium在由Enzyme的:hover函数触发的渲染过程中执行此操作)。这会导致与shallow()不匹配,而styles.dark没有剥离这些属性。

因此,我不确定如何测试。我想不出任何替代解决方案来验证styles.dark已被应用。我认为在测试期间对styles.dark执行以下操作将是一个解决方案:

  • 递归导致所有Color() [Object]进行处理,以便将rgb()值作为字符串输出。
  • 递归删除所有互动式Radium样式(例如:focus:hover

这样做会导致styles.dark等于componentStyles的值,从而通过测试。我只是不确定该怎么做。

1 个答案:

答案 0 :(得分:0)

几天后,我带着新鲜的眼睛回想起来,想到了一个解决方案:

describe("<Button>", () => {
  let props
  let mountedComponent
  let defaultComponent
  const getComponent = () => {
    if (!mountedComponent)
      mountedComponent = shallow(
        <Button {...props} />
      )
    return mountedComponent
  }

  beforeEach(() => {
    props = {
      children: undefined,
      dark: undefined,
      icon: ICONS.VIBE,
      style: undefined,
    }
    defaultComponent = getComponent()
    mountedComponent = undefined
  })

  describe("when `dark` is `true`", () => {
    beforeEach(() => {
      props.dark = true
    })

    it("applies the component's `dark` styles", () => {
      const darkStyles = getComponent().props().style
      expect(defaultComponent.props().style).not.toEqual(darkStyles)
    })
  })
})

而不是断言渲染的组件style道具包含styles.dark(这是脆弱的),它只是在{{1}时检查样式是否完全改变了} prop设置为dark