课程名称:web前端架构师
课程章节:第11周 第五章
主讲老师:张轩
课程内容:通过完善文件上传组件,TDD 开发的更多特性
文件上传列表
之前已经实现了文件上传的功能,但是体验不好。参考 antd 文件上传组件的实现 https://ant.design/components/upload-cn/
需要下面几个功能
- 展示文件上传列表,每次上传列表就会增加一项
- 文件上传过程中
- 展示上传中loading,
- 上传过程中按钮不可点击
- 上传成功后展示文件上传成功
- 上传失败后展示文件上传失败
- 点击上传文件右侧的删除按钮,删除该文件并从列表中移除掉
然后,根据上面所列的功能,编写对应的单元测试,实现过程很简单
上传成功
it('uplaod success', async () => {
...
await wrapper.get('input').trigger('change')
expect(request).toHaveBeenCalledTimes(1)
// 上传中
expect(wrapper.get('button').attributes('disabled')).toBeTruthy()
expect(wrapper.findAll('li').length).toBe(1)
const firstChiild = wrapper.get('li:first-child')
expect(firstChiild.classes()).toContain('upload-loading')
await flushPromises()
// 上传成功
expect(firstChiild.classes()).toContain('upload-success')
expect(firstChiild.get('.firstname').text()).toBe(testFile.name)
})
下面是上传失败
it('uplaod fail', async () => {
request.mockRejectedValueOnce({ err: 'err' })
await wrapper.get('input').trigger('change')
expect(request).toHaveBeenCalledTimes(2)
await flushPromises()
expect(wrapper.findAll('li').length).toBe(2)
const lastChild = wrapper.get('li:last-child')
expect(lastChild.classes()).toContain('uplaad-fail')
await lastChild.get('.delete-icon').trigger('click')
expect(wrapper.findAll('li').length).toBe(1)
})
然后根据测试case, 一项一项编写代码,跑通测试代码
在这个过程中,不需要关注在浏览器会怎样,只需要让测试一项一项跑通即可
<script setup lang="ts">
import { reactive, ref, computed } from 'vue'
import request from '@/utils/request'
type UploadStatus = 'ready' | 'loading' | 'success' | 'fail'
interface FileItem {
id: string
name: string
size: number
status:UploadStatus
raw: File
}
const fileRef = ref<HTMLInputElement | null>(null)
// const uploadStatus = ref<UploadStatus>('ready')
const uploadFiles = reactive<FileItem[]>([])
const isUploading = computed(() => uploadFiles.some(file => file.status === 'loading'))
async function uploadFile (e: Event) {
const target = e.target as HTMLInputElement
const files = target.files
if (files) {
const uploadFile = files[0]
const formData = new FormData()
formData.append(target.name, uploadFile)
const fileObj = reactive<FileItem>({
id: '' + Date.now(),
name: uploadFile.name,
size: uploadFile.size,
status: 'loading',
raw: uploadFile
})
uploadFiles.push(fileObj)
try {
fileObj.status = 'loading'
const res = await request(formData)
fileObj.status = 'success'
console.log(res)
} catch (e) {
fileObj.status = 'fail'
} finally {
(fileRef.value as HTMLIN).value = ''
}
}
}
function triggerUpload () {
fileRef.value?.click()
}
function delFile (index: number) {
uploadFiles.splice(index, 1)
}
</script>
<template>
<input
type="file"
name="file"
ref="fileRef"
:style="{display: 'none'}"
@change="uploadFile"
>
<button
@click="triggerUpload"
:disabled="isUploading"
>
<span v-if="isUploading">正在上传</span>
<span v-else>点击上传</span>
</button>
<ul>
<li
v-for="(file,index) in uploadFiles"
:key="file.id"
:class="'upload-'+ file.status"
>
<span class="filename">{{ file.name }}</span>
<button
class="delete-icon"
@click="delFile(index)"
>
del
</button>
</li>
</ul>
</template>
通过上面的开发,我们发现很大部分时间都是在写测试,但是写完测试后,编写代码就会编写很容易
根据一项一项测试编写代码,可以使我们在开发过程中的目标比较清晰,而且不需要频繁的在浏览器进行复杂的操作,就可以测试功能是否正常,编写代码报错自动就会完成测试,出现问题也可以很好的定位。
所以编写测试代码未必会浪费时间。再次感受到了 TDD 真的是非常好用