import { fireEvent, screen, waitFor } from "@testing-library/react" import { beforeEach, describe, expect, it, vi } from "vitest" import { renderWithProviders } from "@/test/test-utils" import { MediaScanner } from "../../components/media-scanner" // Мокаем Tauri dialog API vi.mock("@tauri-apps/plugin-dialog", () => ({ open: vi.fn(), })) // Мокаем useMediaProcessor vi.mock("../../hooks/use-media-processor", () => ({ useMediaProcessor: vi.fn(), })) describe("MediaScanner", () => { beforeEach(() => { vi.clearAllMocks() }) it("should render media scanner interface", async () => { // Мокаем useMediaProcessor для этого теста const { useMediaProcessor } = await import("../../hooks/use-media-processor") vi.mocked(useMediaProcessor).mockReturnValue({ scanFolderWithThumbnails: vi.fn(), isProcessing: false, progress: { current: 0, total: 0 }, errors: new Map(), clearErrors: vi.fn(), }) renderWithProviders() // Проверяем основные элементы интерфейса expect(screen.getByText("Сканирование медиафайлов")).toBeInTheDocument() expect(screen.getByText("Выберите папку для асинхронного сканирования и обработки медиафайлов")).toBeInTheDocument() expect(screen.getByRole("button", { name: /выбрать папку/i })).toBeInTheDocument() expect(screen.getByRole("button", { name: /начать сканирование/i })).toBeInTheDocument() }) it("should disable scan button when no folder selected", async () => { // Мокаем useMediaProcessor для этого теста const { useMediaProcessor } = await import("../../hooks/use-media-processor") vi.mocked(useMediaProcessor).mockReturnValue({ scanFolderWithThumbnails: vi.fn(), isProcessing: false, progress: { current: 0, total: 0 }, errors: new Map(), clearErrors: vi.fn(), }) renderWithProviders() // Кнопка сканирования должна быть отключена, если папка не выбрана expect(screen.getByRole("button", { name: /начать сканирование/i })).toBeDisabled() }) it("should handle folder selection", async () => { const { open } = await import("@tauri-apps/plugin-dialog") const { useMediaProcessor } = await import("../../hooks/use-media-processor") vi.mocked(open).mockResolvedValue("/path/to/test/folder") vi.mocked(useMediaProcessor).mockReturnValue({ scanFolderWithThumbnails: vi.fn(), isProcessing: false, progress: { current: 0, total: 0 }, errors: new Map(), clearErrors: vi.fn(), }) renderWithProviders() // Кликаем на кнопку выбора папки fireEvent.click(screen.getByRole("button", { name: /выбрать папку/i })) await waitFor(() => { expect(open).toHaveBeenCalledWith({ directory: true, multiple: false, title: "Выберите папку для сканирования", }) }) await waitFor(() => { // Проверяем, что отображается выбранная папка expect(screen.getByText("Выбрана папка: /path/to/test/folder")).toBeInTheDocument() // Кнопка должна поменять текст expect(screen.getByRole("button", { name: /изменить папку/i })).toBeInTheDocument() // Кнопка сканирования должна стать доступной expect(screen.getByRole("button", { name: /начать сканирование/i })).not.toBeDisabled() }) }) it("should handle folder selection cancellation", async () => { const { open } = await import("@tauri-apps/plugin-dialog") const { useMediaProcessor } = await import("../../hooks/use-media-processor") vi.mocked(open).mockResolvedValue(null) // Пользователь отменил выбор vi.mocked(useMediaProcessor).mockReturnValue({ scanFolderWithThumbnails: vi.fn(), isProcessing: false, progress: { current: 0, total: 0 }, errors: new Map(), clearErrors: vi.fn(), }) renderWithProviders() fireEvent.click(screen.getByRole("button", { name: /выбрать папку/i })) await waitFor(() => { expect(open).toHaveBeenCalled() }) // Проверяем, что состояние не изменилось expect(screen.queryByText(/выбрана папка/i)).not.toBeInTheDocument() expect(screen.getByRole("button", { name: /начать сканирование/i })).toBeDisabled() }) it("should handle folder scanning", async () => { const mockFiles = [ { id: "file1", name: "video1.mp4", path: "/path/to/video1.mp4", isVideo: true, isAudio: false, isImage: false, duration: 120, }, { id: "file2", name: "audio1.mp3", path: "/path/to/audio1.mp3", isVideo: false, isAudio: true, isImage: false, duration: 180, }, ] const { open } = await import("@tauri-apps/plugin-dialog") const { useMediaProcessor } = await import("../../hooks/use-media-processor") vi.mocked(open).mockResolvedValue("/path/to/test/folder") const mockScanFolderWithThumbnails = vi.fn().mockResolvedValue(mockFiles) vi.mocked(useMediaProcessor).mockReturnValue({ scanFolderWithThumbnails: mockScanFolderWithThumbnails, isProcessing: false, progress: { current: 0, total: 0 }, errors: new Map(), clearErrors: vi.fn(), }) renderWithProviders() // Выбираем папку fireEvent.click(screen.getByRole("button", { name: /выбрать папку/i })) await waitFor(() => { expect(screen.getByText("Выбрана папка: /path/to/test/folder")).toBeInTheDocument() }) // Запускаем сканирование fireEvent.click(screen.getByRole("button", { name: /начать сканирование/i })) await waitFor(() => { expect(mockScanFolderWithThumbnails).toHaveBeenCalledWith("/path/to/test/folder", 320, 180) }) await waitFor(() => { // Проверяем, что отобразились результаты сканирования expect(screen.getByText("Обработано файлов: 2")).toBeInTheDocument() expect(screen.getByText("video1.mp4")).toBeInTheDocument() expect(screen.getByText("audio1.mp3")).toBeInTheDocument() expect(screen.getByText(/Видео/)).toBeInTheDocument() expect(screen.getByText(/Аудио/)).toBeInTheDocument() }) }) it("should show processing state", async () => { const { useMediaProcessor } = await import("../../hooks/use-media-processor") // Мокаем состояние обработки vi.mocked(useMediaProcessor).mockReturnValue({ scanFolderWithThumbnails: vi.fn(), isProcessing: true, progress: { current: 5, total: 10 }, errors: new Map(), clearErrors: vi.fn(), }) renderWithProviders() // Проверяем индикатор загрузки expect(screen.getByText("Сканирование...")).toBeInTheDocument() expect(screen.getByText("Обработка файлов")).toBeInTheDocument() expect(screen.getByText("5 / 10")).toBeInTheDocument() // Кнопки должны быть отключены во время обработки expect(screen.getByRole("button", { name: /выбрать папку/i })).toBeDisabled() expect(screen.getByRole("button", { name: /сканирование/i })).toBeDisabled() }) it("should display processing errors", async () => { const { useMediaProcessor } = await import("../../hooks/use-media-processor") const mockErrors = new Map([ ["file1", "Failed to read metadata"], ["file2", "Thumbnail generation failed"], ]) // Мокаем состояние с ошибками vi.mocked(useMediaProcessor).mockReturnValue({ scanFolderWithThumbnails: vi.fn(), isProcessing: false, progress: { current: 0, total: 0 }, errors: mockErrors, clearErrors: vi.fn(), }) renderWithProviders() // Проверяем отображение ошибок expect(screen.getByText("Ошибки при обработке (2)")).toBeInTheDocument() expect(screen.getByText("file1:")).toBeInTheDocument() expect(screen.getByText("Failed to read metadata")).toBeInTheDocument() expect(screen.getByText("file2:")).toBeInTheDocument() expect(screen.getByText("Thumbnail generation failed")).toBeInTheDocument() }) it("should show file type indicators", async () => { const mockFiles = [ { id: "file1", name: "video.mp4", path: "/path/to/video.mp4", isVideo: true, isAudio: false, isImage: false, duration: 120, }, { id: "file2", name: "audio.mp3", path: "/path/to/audio.mp3", isVideo: false, isAudio: true, isImage: false, duration: 180, }, { id: "file3", name: "image.jpg", path: "/path/to/image.jpg", isVideo: false, isAudio: false, isImage: true, }, ] const { open } = await import("@tauri-apps/plugin-dialog") const { useMediaProcessor } = await import("../../hooks/use-media-processor") vi.mocked(open).mockResolvedValue("/path/to/test/folder") const mockScanFolderWithThumbnails = vi.fn().mockResolvedValue(mockFiles) vi.mocked(useMediaProcessor).mockReturnValue({ scanFolderWithThumbnails: mockScanFolderWithThumbnails, isProcessing: false, progress: { current: 0, total: 0 }, errors: new Map(), clearErrors: vi.fn(), }) renderWithProviders() // Выбираем папку и запускаем сканирование fireEvent.click(screen.getByRole("button", { name: /выбрать папку/i })) await waitFor(() => { expect(screen.getByText("Выбрана папка: /path/to/test/folder")).toBeInTheDocument() }) fireEvent.click(screen.getByRole("button", { name: /начать сканирование/i })) await waitFor(() => { // Проверяем индикаторы типов файлов expect(screen.getByText(/Видео/)).toBeInTheDocument() expect(screen.getByText(/Аудио/)).toBeInTheDocument() expect(screen.getByText(/Изображение/)).toBeInTheDocument() // Проверяем отображение длительности для видео и аудио expect(screen.getByText(/\(120с\)/)).toBeInTheDocument() expect(screen.getByText(/\(180с\)/)).toBeInTheDocument() }) }) it("should clear errors when selecting new folder", async () => { const { open } = await import("@tauri-apps/plugin-dialog") const { useMediaProcessor } = await import("../../hooks/use-media-processor") const mockErrors = new Map([["file1", "Some error"]]) const mockClearErrors = vi.fn() vi.mocked(useMediaProcessor).mockReturnValue({ scanFolderWithThumbnails: vi.fn(), isProcessing: false, progress: { current: 0, total: 0 }, errors: mockErrors, clearErrors: mockClearErrors, }) vi.mocked(open).mockResolvedValue("/new/path") renderWithProviders() // Кликаем на выбор папки fireEvent.click(screen.getByRole("button", { name: /выбрать папку/i })) await waitFor(() => { expect(mockClearErrors).toHaveBeenCalled() }) }) it("should handle scan errors gracefully", async () => { const consoleSpy = vi.spyOn(console, "error").mockImplementation(() => {}) const { open } = await import("@tauri-apps/plugin-dialog") const { useMediaProcessor } = await import("../../hooks/use-media-processor") vi.mocked(open).mockResolvedValue("/path/to/test/folder") const mockScanFolderWithThumbnails = vi.fn().mockRejectedValue(new Error("Scan failed")) vi.mocked(useMediaProcessor).mockReturnValue({ scanFolderWithThumbnails: mockScanFolderWithThumbnails, isProcessing: false, progress: { current: 0, total: 0 }, errors: new Map(), clearErrors: vi.fn(), }) renderWithProviders() // Выбираем папку fireEvent.click(screen.getByRole("button", { name: /выбрать папку/i })) await waitFor(() => { expect(screen.getByText("Выбрана папка: /path/to/test/folder")).toBeInTheDocument() }) // Запускаем сканирование fireEvent.click(screen.getByRole("button", { name: /начать сканирование/i })) await waitFor(() => { expect(consoleSpy).toHaveBeenCalledWith("Ошибка сканирования:", expect.any(Error)) }) consoleSpy.mockRestore() }) it("should not scan without selected folder", async () => { const { useMediaProcessor } = await import("../../hooks/use-media-processor") const mockScanFolderWithThumbnails = vi.fn() vi.mocked(useMediaProcessor).mockReturnValue({ scanFolderWithThumbnails: mockScanFolderWithThumbnails, isProcessing: false, progress: { current: 0, total: 0 }, errors: new Map(), clearErrors: vi.fn(), }) renderWithProviders() // Попытка запустить сканирование без выбранной папки (кнопка отключена) const scanButton = screen.getByRole("button", { name: /начать сканирование/i }) expect(scanButton).toBeDisabled() // scanFolderWithThumbnails не должен быть вызван expect(mockScanFolderWithThumbnails).not.toHaveBeenCalled() }) })