Files
happy-life-star/web-new/tests/unit/components/FileUpload.test.ts
T

246 lines
7.1 KiB
TypeScript

/**
* 文件上传组件测试
*/
import { describe, it, expect, vi, beforeEach } from 'vitest'
import { mount } from '@vue/test-utils'
import { ElUpload, ElButton } from 'element-plus'
import FileUpload from '@/components/upload/FileUpload.vue'
// 模拟 Element Plus 组件
vi.mock('element-plus', () => ({
ElUpload: {
name: 'ElUpload',
template: '<div class="el-upload"><slot /></div>',
props: ['action', 'headers', 'data', 'multiple', 'accept', 'limit', 'fileList', 'beforeUpload', 'onProgress', 'onSuccess', 'onError', 'onRemove', 'onExceed', 'autoUpload', 'showFileList', 'drag', 'disabled']
},
ElButton: {
name: 'ElButton',
template: '<button class="el-button"><slot /></button>',
props: ['type', 'disabled']
},
ElIcon: {
name: 'ElIcon',
template: '<i class="el-icon"><slot /></i>'
},
ElProgress: {
name: 'ElProgress',
template: '<div class="el-progress"></div>',
props: ['percentage', 'status', 'strokeWidth']
}
}))
// 模拟图标组件
vi.mock('@element-plus/icons-vue', () => ({
UploadFilled: { name: 'UploadFilled' },
Upload: { name: 'Upload' },
Document: { name: 'Document' },
Picture: { name: 'Picture' }
}))
// 模拟认证状态
vi.mock('@/stores/auth', () => ({
useAuthStore: () => ({
token: 'mock-token'
})
}))
// 模拟配置
vi.mock('@/config/constants', () => ({
UPLOAD_CONFIG: {
DEFAULT_UPLOAD_URL: '/api/upload',
IMAGE_TYPES: ['image/jpeg', 'image/png', 'image/gif'],
DOCUMENT_TYPES: ['application/pdf', 'application/msword'],
VIDEO_TYPES: ['video/mp4', 'video/avi'],
AUDIO_TYPES: ['audio/mp3', 'audio/wav']
}
}))
// 模拟格式化工具
vi.mock('@/utils/format', () => ({
formatFileSize: (size: number) => `${size} B`
}))
describe('FileUpload', () => {
let wrapper: any
beforeEach(() => {
wrapper = mount(FileUpload, {
props: {
action: '/api/upload',
multiple: false,
accept: 'image/*',
limit: 5,
maxSize: 1024 * 1024, // 1MB
autoUpload: true
}
})
})
afterEach(() => {
wrapper?.unmount()
})
it('should render correctly', () => {
expect(wrapper.exists()).toBe(true)
expect(wrapper.find('.file-upload').exists()).toBe(true)
})
it('should render upload button when not drag mode', () => {
expect(wrapper.find('.el-button').exists()).toBe(true)
expect(wrapper.find('.upload-dragger').exists()).toBe(false)
})
it('should render drag area when drag mode is enabled', async () => {
await wrapper.setProps({ drag: true })
expect(wrapper.find('.upload-dragger').exists()).toBe(true)
})
it('should show upload hint', () => {
expect(wrapper.find('.upload-tip').exists()).toBe(true)
})
it('should emit events correctly', async () => {
const file = new File(['test'], 'test.txt', { type: 'text/plain' })
// 模拟文件上传成功
await wrapper.vm.handleSuccess({ url: 'http://example.com/file.txt' }, { uid: '1', name: 'test.txt' })
expect(wrapper.emitted('success')).toBeTruthy()
})
it('should validate file type', () => {
const validFile = new File(['test'], 'test.jpg', { type: 'image/jpeg' })
const invalidFile = new File(['test'], 'test.txt', { type: 'text/plain' })
// 设置接受的文件类型
wrapper.vm.acceptTypes = 'image/jpeg,image/png'
expect(wrapper.vm.isValidFileType(validFile)).toBe(true)
expect(wrapper.vm.isValidFileType(invalidFile)).toBe(false)
})
it('should validate file size', async () => {
const smallFile = new File(['small'], 'small.txt', { type: 'text/plain' })
Object.defineProperty(smallFile, 'size', { value: 500 })
const largeFile = new File(['large'], 'large.txt', { type: 'text/plain' })
Object.defineProperty(largeFile, 'size', { value: 2 * 1024 * 1024 }) // 2MB
// 测试文件大小验证
const result1 = await wrapper.vm.handleBeforeUpload(smallFile)
expect(result1).toBe(true)
const result2 = await wrapper.vm.handleBeforeUpload(largeFile)
expect(result2).toBe(false)
})
it('should handle upload progress', () => {
const progressEvent = { percent: 50 }
const file = { uid: '1', name: 'test.txt' }
wrapper.vm.handleProgress(progressEvent, file)
expect(wrapper.vm.uploadPercent).toBe(50)
expect(wrapper.emitted('progress')).toBeTruthy()
})
it('should handle upload error', () => {
const error = new Error('Upload failed')
const file = { uid: '1', name: 'test.txt' }
wrapper.vm.handleError(error, file)
expect(wrapper.vm.uploadStatus).toBe('exception')
expect(wrapper.emitted('error')).toBeTruthy()
})
it('should handle file removal', () => {
const file = { uid: '1', name: 'test.txt' }
wrapper.vm.handleRemove(file)
expect(wrapper.emitted('remove')).toBeTruthy()
})
it('should handle exceed limit', () => {
wrapper.vm.handleExceed()
// 应该显示警告消息(这里我们只能检查方法是否被调用)
expect(true).toBe(true) // 占位断言
})
it('should clear files', () => {
wrapper.vm.fileList = [
{ uid: '1', name: 'test1.txt' },
{ uid: '2', name: 'test2.txt' }
]
wrapper.vm.clearFiles()
expect(wrapper.vm.fileList).toEqual([])
})
it('should compute upload headers correctly', () => {
const headers = wrapper.vm.uploadHeaders
expect(headers).toHaveProperty('Authorization')
expect(headers.Authorization).toBe('Bearer mock-token')
expect(headers['X-Requested-With']).toBe('XMLHttpRequest')
})
it('should compute upload data correctly', async () => {
await wrapper.setProps({
fileType: 'image',
data: { category: 'avatar' }
})
const data = wrapper.vm.uploadData
expect(data.type).toBe('image')
expect(data.category).toBe('avatar')
})
it('should compute accept types correctly', async () => {
await wrapper.setProps({ fileType: 'image' })
expect(wrapper.vm.acceptTypes).toBe('image/jpeg,image/png,image/gif')
await wrapper.setProps({ fileType: 'document' })
expect(wrapper.vm.acceptTypes).toBe('application/pdf,application/msword')
await wrapper.setProps({ accept: 'custom/*' })
expect(wrapper.vm.acceptTypes).toBe('custom/*')
})
it('should compute upload hint correctly', async () => {
await wrapper.setProps({
fileType: 'image',
limit: 3,
maxSize: 1024 * 1024
})
const hint = wrapper.vm.uploadHint
expect(hint).toContain('最多3个文件')
expect(hint).toContain('JPG、PNG、GIF')
expect(hint).toContain('1024 B') // 模拟的格式化结果
})
it('should handle disabled state', async () => {
await wrapper.setProps({ disabled: true })
expect(wrapper.find('.el-button').attributes('disabled')).toBeDefined()
})
it('should handle custom button text and type', async () => {
await wrapper.setProps({
buttonText: 'Custom Upload',
buttonType: 'success'
})
const button = wrapper.find('.el-button')
expect(button.text()).toContain('Custom Upload')
expect(button.attributes('type')).toBe('success')
})
})