实战、使用TDD开发vue3 ColorPicker组件

1,274 阅读3分钟

什么是TDD开发

Test-Driven Development 测试驱动开发,简单来说就是先写测试用例代码、再编写产品需求代码, 维基百科

原型

初始化

image.png

点击左侧选择框后

image.png

需求

显示

  • 左侧显示当前传入的颜色

  • 右侧显示十种常用的颜色

  • 右侧最后一个是透明、点击后清空

点击

  • 左侧点击后弹出选择颜色框,选择完后新值将以事件形式发射出去

  • 右侧选择完对应的颜色后,新值将以事件形式发射出去,并且左侧框颜色改变成当前选择的

实现思路

左侧我们可以使用 input 设置type为 color 就可以达到我们的要求,具体参考 文档。 右侧只需要定义一些默认颜色、然后进行布局就行。

注意事项

遵循组件设计原则、以属性 value 为传入值、以事件 change 发射出新的要改变的值

有了上面的思路、我们在脑海大体有以下的实现布局

    <div><input></div>
    <ul class="picked-color-list">
    <li class="item-0" or class="transparent-back">
    <div></div>
    </li></ul>

用例编写

这里使用 @vue/test-utilsvue-jest 作为测试库。

在 tests/unit下新建 ColorPicker.spec.ts, 首先倒入 ColorPicker、并进行实例

import { mount, VueWrapper } from '@vue/test-utils'
import ColorPicker from '@/components/ColorPicker.vue'
const defaultColors = ['#ffffff', '#f5222d', '#fa541c', '#fadb14', '#52c41a', '#1890ff', '#722ed1', '#8c8c8c', '#000000', '']
let wrapper: VueWrapper<any>

describe("ColorPick Component", () => {
    beforeAll(() => {
        wrapper = mount(ColorPicker, {
            props: {
                value: '#ffffff'
            }
        })
    })
})

用来检查是否显示正确

    it("should render the correct interface", () => {
        expect(wrapper.find('input').exists()).toBeTruthy()
        const input = wrapper.get('input').element
        expect(input.type).toBe('color')
        expect(input.value).toBe('#ffffff')
        // 测试右侧是否有颜色的列表
        expect(wrapper.findAll('.picked-color-list li').length).toBe(defaultColors.length)
        // 测试第一个元素的css backgroundColor属性是否相等对应的颜色
        const firstItem: HTMLElement = wrapper.get('li:first-child div').element as HTMLElement
        expect(fistItem.style.backgroundColor).toBe(defaultColors[0])
        // 测试最后一个元素的css 是否有特殊的类名
        const lastItem: HTMLElement = wrapper.get('li:last-child div').element as HTMLElement
        expect(lastItem.classList.contains('transparent-back')).toBeTruthy()
    })

测试对应方法可以参考 conditional-rendering

检测 input 修改后是否发送对应事件和值

    it("should send the correct event when change input", async () => {
        // 测试 input 修改后是否发送对应的事件和值
        const color = '#ffffff'
        const input = wrapper.get('input')
        await input.setValue(color)
        // 检查 props 是否有 change 属性
        expect(wrapper.emitted()).toHaveProperty('change')
        const events = wrapper.emitted('change') || []
        // 检测回调值时候正确
        expect(events[0]).toEqual([color])
    })

测试对应方法可以参考 events

检测点击右侧列表是否发送对应的值

    it('should send the correct event when clicking the color list', async () => {
        const firstItem = wrapper.get('li:first-child div')
        firstItem.trigger('click')
        const events = wrapper.emitted('change') || []
        expect(events[0]).toEqual(defaultColors[0])
    })

ok、用例编写的差不多了、我们再根据用例去实现我们的代码

代码实现

<template>
  <div class="color-picker">
    <div class="native-color-container">
      <input
        type="color"
        :value="value"
        @input="onChange($event.target.value)"
      />
    </div>
    <div class="picked-color-list">
      <li
        v-for="(item, key) in colors"
        :key="key"
        :class="`item-${key}`"
        @click.prevent="onChange(item)"
      >
        <div
          :style="{ backgroundColor: item }"
          class="color-item"
          v-if="item.startsWith('#')"
        ></div>
        <div v-else class="color-item transparent-back"></div>
      </li>
    </div>
  </div>
</template>
<script lang="ts">
import { defineComponent, PropType } from "vue"
const defaultColors = ['#ffffff', '#f5222d', '#fa541c', '#fadb14', '#52c41a', '#1890ff', '#722ed1', '#8c8c8c', '#000000', '']
export default defineComponent({
  props: {
    value: {
      type: String,
    },
    colors: {
      type: Array as PropType<string[]>,
      default: defaultColors,
    },
  },
  emits: ["change"],
  setup(props, context) {
    const onChange = (v: string) => {
      context.emit("change", v)
    }
    return {
      onChange,
    }
  },
})
</script>

package.json 添加 "test:unit": "vue-cli-service test:unit" 并执行

截屏2021-04-05下午7.01.26.png

可以看到有两个报错,不要急,根据错误提升发现第一个错误是因为 item 的 background 是用 rbg 显示的,因为我们需要转换下。

使用 rgb-hex 进行下转换、修改下用例

   expect('#' + rgbHex(firstItem.style.backgroundColor)).toBe(defaultColors[0])

第二个错误是因为拼写导致错误

expect(events[0]).toEqual([defaultColors[0]])

再次运行用例

截屏2021-04-05下午7.03.13.png

所有用例顺利通过,😄 这这是一次简单尝试,但是也得正视、大多数前端人员不重视测试的重要性(包括我自己),在今后开发组件中,应该尽量尝试这种开发模式,保证我们组件的稳定性 💪

广告

最后来波广告

分享我私藏的TS教程,从0到高阶全系列,点击链接,0元获取 链接