import { createEvent, fireEvent, render, screen } from '@testing-library/react' import AIOriginalUploader from '@/components/upload/AIOriginalUploader' import RealPhotoUploader from '@/components/upload/RealPhotoUploader' import TaskPageUploader from '@/components/upload/TaskPageUploader' import ThreeDOriginalUploader from '@/components/upload/ThreeDOriginalUploader' import VideoScreenshotUploader from '@/components/upload/VideoScreenshotUploader' const defaultProps = { onSuccess: jest.fn(), onCancel: jest.fn(), categories: [], scenes: [], platforms: [], productCategories: [], onRefreshCategories: jest.fn(), onRefreshScenes: jest.fn(), } const uploaders = [ ['AI 原图', AIOriginalUploader], ['实拍原图', RealPhotoUploader], ['3D 原图', ThreeDOriginalUploader], ['视频截图', VideoScreenshotUploader], ['任务页面', TaskPageUploader], ] as const describe.each(uploaders)('%s拖拽上传', (_name, Uploader) => { beforeEach(() => { jest.clearAllMocks() Object.defineProperty(URL, 'createObjectURL', { configurable: true, writable: true, value: jest.fn(() => 'blob:preview'), }) Object.defineProperty(URL, 'revokeObjectURL', { configurable: true, writable: true, value: jest.fn(), }) }) test('经过上传框内部按钮时应保持拖拽高亮', () => { const { container } = render() const input = container.querySelector('input[type="file"][accept="image/*"][multiple]') as HTMLInputElement const dropZone = input.parentElement as HTMLDivElement const childButton = dropZone.querySelector('button') as HTMLButtonElement Object.defineProperty(dropZone, 'getBoundingClientRect', { configurable: true, value: () => ({ x: 0, y: 0, top: 0, right: 100, bottom: 100, left: 0, width: 100, height: 100, toJSON: () => ({}), }), }) const dragData = { clientX: 20, clientY: 20, dataTransfer: { files: [], types: ['Files'], dropEffect: 'none' }, } fireEvent.dragEnter(dropZone, dragData) fireEvent.dragOver(dropZone, dragData) expect(dropZone).toHaveClass('border-blue-500') const internalDragLeave = createEvent.dragLeave(dropZone) Object.defineProperties(internalDragLeave, { clientX: { value: 50 }, clientY: { value: 50 }, relatedTarget: { value: childButton }, dataTransfer: { value: { files: [], types: ['Files'], dropEffect: 'copy' } }, }) fireEvent(dropZone, internalDragLeave) expect(dropZone).toHaveClass('border-blue-500') const outerDragLeave = createEvent.dragLeave(dropZone) Object.defineProperties(outerDragLeave, { clientX: { value: 120 }, clientY: { value: 50 }, relatedTarget: { value: document.body }, dataTransfer: { value: { files: [], types: ['Files'], dropEffect: 'copy' } }, }) fireEvent(dropZone, outerDragLeave) expect(dropZone).not.toHaveClass('border-blue-500') }) }) test('混合拖入有效图片和非法文件时应保留图片并显示错误', () => { const { container } = render() const input = container.querySelector('input[type="file"][accept="image/*"][multiple]') as HTMLInputElement const dropZone = input.parentElement as HTMLDivElement const image = new File(['image'], 'valid.png', { type: 'image/png' }) const invalid = new File(['text'], 'invalid.txt', { type: 'text/plain' }) fireEvent.drop(dropZone, { dataTransfer: { files: [image, invalid], types: ['Files'], dropEffect: 'copy' }, }) expect(screen.getByAltText('原图 1')).toBeInTheDocument() expect(screen.getByText(/invalid\.txt/)).toBeInTheDocument() })