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

How to test if styles are dynamically applied on a React component

我写了一个 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 属性设置为 [=18= 时是否应用其 dark 样式]:

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 的属性是否在呈现的 Buttonstyle 属性内来执行此操作。如果是,则表示样式应用成功。

问题是 styles.darkcomponentStyles 不匹配:

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。我认为在测试期间对 styles.dark 执行以下操作将是一个解决方案:

这样做会使styles.dark等于componentStyles的值,从而通过测试。我只是不知道该怎么做。

几天后我重新审视这个问题并想到了一个解决方案:

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 prop 包含 styles.dark(这很脆弱),它只是检查 dark 时样式是否发生了变化道具设置为 true.