import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'; import { act, fireEvent, render, screen, waitFor } from '@testing-library/react'; import ImageMode from '../ImageMode'; // Mock CopyButton vi.mock('@/components/CopyButton', () => ({ default: ({ text, tooltip }: { text: string; tooltip?: string }) => ( ), })); beforeEach(() => { localStorage.clear(); vi.useFakeTimers({ shouldAdvanceTime: true }); }); afterEach(() => { vi.useRealTimers(); }); const waitForStorageReady = () => act(() => Promise.resolve()); describe('ImageMode', () => { it('应该渲染图像上传区域', async () => { render(); await waitForStorageReady(); expect(screen.getByText('clickOrDropToImage')).toBeInTheDocument(); expect(screen.getByText('supportedFormats')).toBeInTheDocument(); }); it('应该接受有效的图像文件', async () => { render(); await waitForStorageReady(); const file = new File(['fake-image-data'], 'test.png', { type: 'image/png' }); const hiddenInput = document.querySelector('input[type="file"]') as HTMLInputElement; fireEvent.change(hiddenInput, { target: { files: [file] } }); await waitFor(() => { expect(screen.getByText('test.png')).toBeInTheDocument(); expect(screen.getByText('base64Output')).toBeInTheDocument(); }); }); it('应该拒绝非图像文件', async () => { render(); await waitForStorageReady(); const file = new File(['not an image'], 'test.txt', { type: 'text/plain' }); const hiddenInput = document.querySelector('input[type="file"]') as HTMLInputElement; fireEvent.change(hiddenInput, { target: { files: [file] } }); await waitFor(() => { expect(screen.getByRole('alert')).toHaveTextContent('unsupportedImageType'); }); }); it('应该拒绝超出大小限制的图像', async () => { render(); await waitForStorageReady(); const largeContent = new Uint8Array(11 * 1024 * 1024); const file = new File([largeContent], 'large.png', { type: 'image/png' }); const hiddenInput = document.querySelector('input[type="file"]') as HTMLInputElement; fireEvent.change(hiddenInput, { target: { files: [file] } }); await waitFor(() => { expect(screen.getByRole('alert')).toHaveTextContent('fileSizeExceeded'); }); }); it('应该通过扩展名识别图像', async () => { render(); await waitForStorageReady(); // 没有 MIME 类型但有正确扩展名 const file = new File(['fake'], 'test.jpg'); const hiddenInput = document.querySelector('input[type="file"]') as HTMLInputElement; fireEvent.change(hiddenInput, { target: { files: [file] } }); await waitFor(() => { expect(screen.getByText('test.jpg')).toBeInTheDocument(); }); }); it('点击清除按钮应该清空图像状态', async () => { render(); await waitForStorageReady(); const file = new File(['fake'], 'test.png', { type: 'image/png' }); const hiddenInput = document.querySelector('input[type="file"]') as HTMLInputElement; fireEvent.change(hiddenInput, { target: { files: [file] } }); await waitFor(() => { expect(screen.getByText('test.png')).toBeInTheDocument(); }); fireEvent.click(screen.getByText('clear')); await waitFor(() => { expect(screen.queryByText('test.png')).not.toBeInTheDocument(); expect(screen.getByText('clickOrDropToImage')).toBeInTheDocument(); }); }); it('应该显示图像预览', async () => { render(); await waitForStorageReady(); const file = new File(['fake-image'], 'test.png', { type: 'image/png' }); const hiddenInput = document.querySelector('input[type="file"]') as HTMLInputElement; fireEvent.change(hiddenInput, { target: { files: [file] } }); await waitFor(() => { const img = screen.getByAltText('preview'); expect(img).toBeInTheDocument(); expect(img.tagName.toLowerCase()).toBe('img'); }); }); it('应该渲染 encode/decode 切换按钮', async () => { render(); await waitForStorageReady(); expect(screen.getByText('encode')).toBeInTheDocument(); expect(screen.getByText('decode')).toBeInTheDocument(); }); it('解码 PNG Base64 后应该显示图像预览', async () => { render(); await waitForStorageReady(); fireEvent.click(screen.getByText('decode')); const input = await screen.findByPlaceholderText('decodeBase64Placeholder'); fireEvent.change(input, { target: { value: 'iVBORw0KGgo=' } }); act(() => { vi.advanceTimersByTime(250); }); await waitFor(() => { expect(screen.getByText('decodedImageOutput')).toBeInTheDocument(); const img = screen.getByAltText('decoded preview'); expect(img).toBeInTheDocument(); expect(img.tagName.toLowerCase()).toBe('img'); }); }); it('解码后默认文件名应该为 decoded.png', async () => { render(); await waitForStorageReady(); fireEvent.click(screen.getByText('decode')); const input = await screen.findByPlaceholderText('decodeBase64Placeholder'); fireEvent.change(input, { target: { value: 'iVBORw0KGgo=' } }); act(() => { vi.advanceTimersByTime(250); }); expect(await screen.findByDisplayValue('decoded.png')).toBeInTheDocument(); }); it('解码非法 Base64 应该显示 invalidBase64 错误', async () => { render(); await waitForStorageReady(); fireEvent.click(screen.getByText('decode')); const input = await screen.findByPlaceholderText('decodeBase64Placeholder'); fireEvent.change(input, { target: { value: '!!!not base64' } }); act(() => { vi.advanceTimersByTime(250); }); await waitFor(() => { expect(screen.getByText('invalidBase64')).toBeInTheDocument(); }); }); });