import { fireEvent, render, screen } from "@testing-library/react" import { beforeEach, describe, expect, it, vi } from "vitest" import { factories } from "@/test/utils/factories" import { BrowserContent } from "../../components/browser-content" /** * Тесты для компонента BrowserContent * * Покрытие: * - Рендеринг основных компонентов и классов * - Обработчики событий тулбара (поиск, сортировка, фильтрация, группировка) * - Зум функциональность (увеличение/уменьшение превью) * - Работа с адаптерами для всех типов вкладок * - Обработка выбора элементов для каждого типа контента * - Интеграция с фабриками данных */ // Создаем моки функций для browser state const mockSetSearchQuery = vi.fn() const mockToggleFavorites = vi.fn() const mockSetSort = vi.fn() const mockSetGroupBy = vi.fn() const mockSetFilter = vi.fn() const mockSetViewMode = vi.fn() const mockSetPreviewSize = vi.fn() // Мокаем все зависимости const mockBrowserState = { activeTab: "media", currentTabSettings: { searchQuery: "", showFavoritesOnly: false, viewMode: "grid", sortBy: "name", filterType: "all", groupBy: null, sortOrder: "asc", previewSizeIndex: 1, }, setSearchQuery: mockSetSearchQuery, toggleFavorites: mockToggleFavorites, setSort: mockSetSort, setGroupBy: mockSetGroupBy, setFilter: mockSetFilter, setViewMode: mockSetViewMode, setPreviewSize: mockSetPreviewSize, } vi.mock("../../services/browser-state-provider", () => ({ useBrowserState: () => mockBrowserState, })) // Создаем моки для timeline actions const mockAddMediaToTimeline = vi.fn() const mockAddSingleMediaToTimeline = vi.fn() vi.mock("@/features/timeline/hooks", () => ({ useTimelineActions: () => ({ addMediaToTimeline: mockAddMediaToTimeline, addSingleMediaToTimeline: mockAddSingleMediaToTimeline, }), })) // Создаем мок для BrowserToolbarWrapper vi.mock("../../components/browser-toolbar-wrapper", () => ({ BrowserToolbarWrapper: ({ onSearch, onSort, onFilter, onViewModeChange, onGroupBy, onToggleFavorites, onZoomIn, onZoomOut, sortBy, sortOrder, }: any) => (
), })) // Мок для toolbar configs уже не нужен, так как используется в BrowserToolbarWrapper vi.mock("../../components/browser-loading-indicator", () => ({ BrowserLoadingIndicator: () =>
Loading
, })) // Мок для UniversalList с поддержкой onItemSelect - теперь не используется напрямую vi.mock("../../components/universal-list", () => ({ UniversalList: ({ adapter, onItemSelect }: any) => (
{adapter?.type || "no-adapter"}
), })) // Мок для LazyTabContent vi.mock("../../components/lazy-tab-content", () => ({ LazyTabContent: ({ tabValue, activeTab }: any) => { // Симулируем lazy loading - показываем контент только для активной вкладки if (tabValue !== activeTab) return null // Список известных вкладок const knownTabs = [ "media", "music", "effects", "filters", "transitions", "subtitles", "templates", "style-templates", ] // Если вкладка неизвестная, возвращаем null if (!knownTabs.includes(activeTab)) return null return (
{activeTab}
) }, })) // Создаем простые моки адаптеров const mockMediaAdapter = { type: "media", importHandlers: { importFile: vi.fn() } } const mockMusicAdapter = { type: "music" } const mockEffectsAdapter = { type: "effects" } const mockFiltersAdapter = { type: "filters" } const mockTransitionsAdapter = { type: "transitions" } const mockSubtitlesAdapter = { type: "subtitles" } const mockTemplatesAdapter = { type: "templates" } const mockStyleTemplatesAdapter = { type: "style-templates" } // Переменная для управления тем, какой адаптер возвращается const currentAdapters = { media: mockMediaAdapter, music: mockMusicAdapter, effects: mockEffectsAdapter, filters: mockFiltersAdapter, transitions: mockTransitionsAdapter, subtitles: mockSubtitlesAdapter, templates: mockTemplatesAdapter, styleTemplates: mockStyleTemplatesAdapter, } // Мокаем адаптеры vi.mock("../../adapters/use-media-adapter", () => ({ useMediaAdapter: () => currentAdapters.media, })) vi.mock("../../adapters/use-music-adapter", () => ({ useMusicAdapter: () => currentAdapters.music, })) vi.mock("../../adapters/use-effects-adapter", () => ({ useEffectsAdapter: () => currentAdapters.effects, })) vi.mock("../../adapters/use-filters-adapter", () => ({ useFiltersAdapter: () => currentAdapters.filters, })) vi.mock("../../adapters/use-transitions-adapter", () => ({ useTransitionsAdapter: () => currentAdapters.transitions, })) vi.mock("../../adapters/use-subtitles-adapter", () => ({ useSubtitlesAdapter: () => currentAdapters.subtitles, })) vi.mock("../../adapters/use-templates-adapter", () => ({ useTemplatesAdapter: () => currentAdapters.templates, })) vi.mock("../../adapters/use-style-templates-adapter", () => ({ useStyleTemplatesAdapter: () => currentAdapters.styleTemplates, })) vi.mock("@tauri-apps/plugin-shell", () => ({ open: vi.fn(), })) vi.mock("@/features/browser/providers/effects-provider", () => ({ EffectsProvider: ({ children }: any) => children, useEffectsProvider: vi.fn(() => ({ api: { getEffects: vi.fn(() => []), getFilters: vi.fn(() => []), getTransitions: vi.fn(() => []), getTemplates: vi.fn(() => []), getLoadingState: vi.fn(() => ({ isLoading: false, loadedSources: new Set(["built-in"]), loadingQueue: [], error: null, progress: 100, })), onLoadingStateChange: vi.fn(() => () => {}), getStats: vi.fn(() => ({ total: 0, byType: {}, bySource: {}, cacheSize: 0, memoryUsage: 0, })), onResourcesUpdate: vi.fn(() => () => {}), }, })), })) vi.mock("@/features/media/utils/preview-sizes", () => ({ PREVIEW_SIZES: [ { key: "small", width: 160, height: 90 }, { key: "medium", width: 240, height: 135 }, { key: "large", width: 320, height: 180 }, ], })) describe("BrowserContent", () => { beforeEach(() => { vi.clearAllMocks() // Сбрасываем состояние к дефолтному mockBrowserState.activeTab = "media" mockBrowserState.currentTabSettings.sortOrder = "asc" mockBrowserState.currentTabSettings.previewSizeIndex = 1 }) describe("Рендеринг", () => { it("должен рендерить основные компоненты", () => { render() expect(screen.getByTestId("media-toolbar")).toBeInTheDocument() expect(screen.getByTestId("loading-indicator")).toBeInTheDocument() // LazyTabContent загружается лениво expect(screen.getByTestId("lazy-tab-content")).toBeInTheDocument() expect(screen.getByTestId("universal-list")).toBeInTheDocument() }) it("должен применять правильные классы к контенту", () => { render() // Теперь ищем контейнер контента по классу const content = document.querySelector(".bg-background.m-0.flex-1.overflow-auto") expect(content).toBeInTheDocument() }) it("должен не показывать контент для неизвестной вкладки", () => { // Устанавливаем неизвестную вкладку mockBrowserState.activeTab = "unknown" as any currentAdapters.media = undefined as any render() // LazyTabContent возвращает null для неизвестных вкладок expect(screen.queryByTestId("lazy-tab-content")).not.toBeInTheDocument() }) }) describe("Обработчики событий тулбара", () => { it("должен вызывать setSearchQuery при поиске", () => { render() const searchBtn = screen.getByTestId("search-btn") fireEvent.click(searchBtn) expect(mockSetSearchQuery).toHaveBeenCalledWith("test query") }) it("должен вызывать setSort при сортировке", () => { render() const sortBtn = screen.getByTestId("sort-btn") fireEvent.click(sortBtn) expect(mockSetSort).toHaveBeenCalledWith("size", "asc") }) it("должен вызывать setFilter при фильтрации", () => { render() const filterBtn = screen.getByTestId("filter-btn") fireEvent.click(filterBtn) expect(mockSetFilter).toHaveBeenCalledWith("video") }) it("должен переключать порядок сортировки", () => { render() const orderBtn = screen.getByTestId("order-btn") fireEvent.click(orderBtn) expect(mockSetSort).toHaveBeenCalledWith("name", "desc") }) it("должен переключать порядок сортировки с desc на asc", () => { mockBrowserState.currentTabSettings.sortOrder = "desc" render() const orderBtn = screen.getByTestId("order-btn") fireEvent.click(orderBtn) expect(mockSetSort).toHaveBeenCalledWith("name", "asc") }) it("должен вызывать setViewMode при изменении режима отображения", () => { render() const viewBtn = screen.getByTestId("view-btn") fireEvent.click(viewBtn) expect(mockSetViewMode).toHaveBeenCalledWith("list") }) it("должен вызывать setGroupBy при группировке", () => { render() const groupBtn = screen.getByTestId("group-btn") fireEvent.click(groupBtn) expect(mockSetGroupBy).toHaveBeenCalledWith("type") }) it("должен вызывать toggleFavorites при переключении избранного", () => { render() const favBtn = screen.getByTestId("fav-btn") fireEvent.click(favBtn) expect(mockToggleFavorites).toHaveBeenCalledWith() }) }) describe("Зум функциональность", () => { it("должен увеличивать размер превью при zoom in", () => { render() const zoomInBtn = screen.getByTestId("zoom-in-btn") fireEvent.click(zoomInBtn) expect(mockSetPreviewSize).toHaveBeenCalledWith(2) }) it("должен уменьшать размер превью при zoom out", () => { render() const zoomOutBtn = screen.getByTestId("zoom-out-btn") fireEvent.click(zoomOutBtn) expect(mockSetPreviewSize).toHaveBeenCalledWith(0) }) it("должен вызывать setPreviewSize с увеличенным индексом даже при максимальном размере", () => { mockBrowserState.currentTabSettings.previewSizeIndex = 2 // максимальный индекс render() const zoomInBtn = screen.getByTestId("zoom-in-btn") fireEvent.click(zoomInBtn) // BrowserContent не проверяет границы, это делает BrowserToolbarWrapper expect(mockSetPreviewSize).toHaveBeenCalledWith(3) }) it("должен вызывать setPreviewSize с уменьшенным индексом даже при минимальном размере", () => { mockBrowserState.currentTabSettings.previewSizeIndex = 0 // минимальный индекс render() const zoomOutBtn = screen.getByTestId("zoom-out-btn") fireEvent.click(zoomOutBtn) // BrowserContent не проверяет границы, это делает BrowserToolbarWrapper expect(mockSetPreviewSize).toHaveBeenCalledWith(-1) }) }) describe("Работа с адаптерами для разных вкладок", () => { it("должен использовать media адаптер для media вкладки", () => { mockBrowserState.activeTab = "media" currentAdapters.media = mockMediaAdapter // Убеждаемся что адаптер есть render() const adapterType = screen.getByTestId("adapter-type") expect(adapterType.textContent).toBe("media") }) it("должен использовать music адаптер для music вкладки", () => { mockBrowserState.activeTab = "music" render() const adapterType = screen.getByTestId("adapter-type") expect(adapterType.textContent).toBe("music") }) it("должен использовать effects адаптер для effects вкладки", () => { mockBrowserState.activeTab = "effects" render() const adapterType = screen.getByTestId("adapter-type") expect(adapterType.textContent).toBe("effects") }) it("должен использовать filters адаптер для filters вкладки", () => { mockBrowserState.activeTab = "filters" render() const adapterType = screen.getByTestId("adapter-type") expect(adapterType.textContent).toBe("filters") }) it("должен использовать transitions адаптер для transitions вкладки", () => { mockBrowserState.activeTab = "transitions" render() const adapterType = screen.getByTestId("adapter-type") expect(adapterType.textContent).toBe("transitions") }) it("должен использовать subtitles адаптер для subtitles вкладки", () => { mockBrowserState.activeTab = "subtitles" render() const adapterType = screen.getByTestId("adapter-type") expect(adapterType.textContent).toBe("subtitles") }) it("должен использовать templates адаптер для templates вкладки", () => { mockBrowserState.activeTab = "templates" render() const adapterType = screen.getByTestId("adapter-type") expect(adapterType.textContent).toBe("templates") }) it("должен использовать style-templates адаптер для style-templates вкладки", () => { mockBrowserState.activeTab = "style-templates" render() const adapterType = screen.getByTestId("adapter-type") expect(adapterType.textContent).toBe("style-templates") }) }) describe("Обработка выбора элементов", () => { it("должен добавлять медиафайл на таймлайн при выборе", () => { mockBrowserState.activeTab = "media" currentAdapters.media = mockMediaAdapter // Убеждаемся что адаптер есть render() const selectBtn = screen.getByTestId("item-select-btn") fireEvent.click(selectBtn) expect(mockAddSingleMediaToTimeline).toHaveBeenCalledWith({ id: "1", name: "test-item.mp4", path: "/test/path", }) }) it("должен логировать выбор музыкального файла", () => { const consoleSpy = vi.spyOn(console, "log") mockBrowserState.activeTab = "music" render() const selectBtn = screen.getByTestId("item-select-btn") fireEvent.click(selectBtn) expect(consoleSpy).toHaveBeenCalledWith("Музыкальный файл выбран:", "test-item.mp4") }) it("должен логировать выбор эффекта", () => { const consoleSpy = vi.spyOn(console, "log") mockBrowserState.activeTab = "effects" render() const selectBtn = screen.getByTestId("item-select-btn") fireEvent.click(selectBtn) expect(consoleSpy).toHaveBeenCalledWith("Эффект выбран:", "test-item.mp4") }) it("должен логировать выбор фильтра", () => { const consoleSpy = vi.spyOn(console, "log") mockBrowserState.activeTab = "filters" render() const selectBtn = screen.getByTestId("item-select-btn") fireEvent.click(selectBtn) expect(consoleSpy).toHaveBeenCalledWith("Фильтр выбран:", "test-item.mp4") }) it("должен логировать выбор перехода", () => { const consoleSpy = vi.spyOn(console, "log") mockBrowserState.activeTab = "transitions" render() const selectBtn = screen.getByTestId("item-select-btn") fireEvent.click(selectBtn) expect(consoleSpy).toHaveBeenCalledWith("Переход выбран:", "test-item.mp4") }) it("должен логировать выбор стиля субтитров", () => { const consoleSpy = vi.spyOn(console, "log") mockBrowserState.activeTab = "subtitles" render() const selectBtn = screen.getByTestId("item-select-btn") fireEvent.click(selectBtn) expect(consoleSpy).toHaveBeenCalledWith("Стиль субтитров выбран:", "test-item.mp4") }) it("должен логировать выбор шаблона", () => { const consoleSpy = vi.spyOn(console, "log") mockBrowserState.activeTab = "templates" render() const selectBtn = screen.getByTestId("item-select-btn") fireEvent.click(selectBtn) expect(consoleSpy).toHaveBeenCalledWith("Шаблон выбран:", "1") }) it("должен логировать выбор стилистического шаблона", () => { const consoleSpy = vi.spyOn(console, "log") mockBrowserState.activeTab = "style-templates" render() const selectBtn = screen.getByTestId("item-select-btn") fireEvent.click(selectBtn) expect(consoleSpy).toHaveBeenCalledWith("Стилистический шаблон выбран:", undefined) }) }) describe("Интеграция с фабриками данных", () => { it("должен создавать корректные тестовые данные с помощью фабрик", () => { // Создаем тестовые данные с помощью фабрик const mockMediaFile = factories.mediaFile({ name: "test-video.mp4" }) const mockAudioFile = factories.audioFile({ name: "test-audio.mp3" }) // Проверяем что фабрики создают правильные данные expect(mockMediaFile).toHaveProperty("name", "test-video.mp4") expect(mockMediaFile).toHaveProperty("width", 1920) expect(mockMediaFile).toHaveProperty("height", 1080) expect(mockAudioFile).toHaveProperty("name", "test-audio.mp3") expect(mockAudioFile.width).toBeUndefined() expect(mockAudioFile.height).toBeUndefined() }) }) })