import { render, screen, waitFor } from "@testing-library/react" import { useEffect, useState } from "react" import { beforeEach, describe, expect, it, vi } from "vitest" import { resetTransitionsState } from "@/features/transitions/hooks/use-transitions" import { useEffects, useEffectsSearch, useFilters, useFiltersSearch, useLoadingState, useResourceById, useResourceSources, useResources, useResourcesAdapter, useResourcesByCategory, useResourcesByComplexity, useResourcesByTags, useResourcesCache, useResourcesSearch, useResourcesStats, useTransitions, useTransitionsSearch, } from "../../hooks/use-resources" import { EffectsProvider, resetEffectsProviderState } from "../../providers/effects-provider" // Мокаем JSON файл с переходами vi.mock("@/features/transitions/data/transitions.json", () => ({ default: { transitions: [ { id: "test-transition-1", type: "fade", labels: { ru: "Исчезновение", en: "Fade" }, description: { ru: "Плавное исчезновение", en: "Smooth fade" }, category: "basic", complexity: "basic", tags: ["smooth", "classic"], duration: { min: 0.1, max: 5, default: 1 }, }, ], }, })) // Мокаем ленивые загрузчики ресурсов vi.mock("../../services/resource-loaders", () => ({ loadAllResourcesLazy: vi.fn().mockResolvedValue({ effects: { success: true, data: [ { id: "test-effect-1", name: "Test Effect 1", type: "blur", category: "artistic", complexity: "basic", tags: ["test", "popular"], description: { ru: "Тестовый эффект 1", en: "Test Effect 1" }, ffmpegCommand: () => "blur=5", params: { intensity: 50 }, previewPath: "/test1.mp4", labels: { en: "Test Effect 1", ru: "Тестовый эффект 1" }, }, { id: "test-effect-2", name: "Test Effect 2", type: "brightness", category: "color-correction", complexity: "intermediate", tags: ["test", "color"], description: { ru: "Тестовый эффект 2", en: "Test Effect 2" }, ffmpegCommand: () => "brightness=0.1", params: { intensity: 75 }, previewPath: "/test2.mp4", labels: { en: "Test Effect 2", ru: "Тестовый эффект 2" }, }, ], source: "built-in", timestamp: Date.now(), }, filters: { success: true, data: [ { id: "test-filter-1", name: "Test Filter 1", category: "technical", complexity: "advanced", tags: ["log", "professional"], description: { ru: "Тестовый фильтр", en: "Test Filter" }, labels: { en: "Test Filter", ru: "Тестовый фильтр" }, }, ], source: "built-in", timestamp: Date.now(), }, transitions: { success: true, data: [ { id: "test-transition-1", type: "fade", labels: { ru: "Исчезновение", en: "Fade" }, description: { ru: "Плавное исчезновение", en: "Smooth fade" }, category: "basic", complexity: "basic", tags: ["smooth", "classic"], duration: { min: 0.1, max: 5, default: 1 }, }, ], source: "built-in", timestamp: Date.now(), }, }), })) // Общая очистка состояния перед каждым тестом beforeEach(async () => { vi.clearAllMocks() resetEffectsProviderState() resetTransitionsState() // Небольшая задержка для гарантии очистки состояния await new Promise((resolve) => setTimeout(resolve, 10)) }) // Вспомогательная функция для ожидания загрузки провайдера const waitForProviderReady = async () => { await waitFor( () => { expect(screen.getByTestId("loading")).toHaveTextContent("false") }, { timeout: 5000 }, ) // Дополнительная задержка для стабильности await new Promise((resolve) => setTimeout(resolve, 50)) } describe("useEffects", () => { function TestComponent() { const { effects, loading } = useEffects() return (
{String(loading)}
{effects.length}
{effects.map((effect) => (
{effect.name}
))}
) } it("должен загружать и возвращать эффекты", async () => { render( , ) await waitForProviderReady() expect(screen.getByTestId("effects-count")).toHaveTextContent("2") expect(screen.getByText("Test Effect 1")).toBeInTheDocument() expect(screen.getByText("Test Effect 2")).toBeInTheDocument() }) }) describe("useFilters", () => { function TestComponent() { const { filters, loading } = useFilters() return (
{String(loading)}
{filters.length}
{filters.map((filter) => (
{filter.name}
))}
) } it("должен загружать и возвращать фильтры", async () => { render( , ) await waitForProviderReady() expect(screen.getByTestId("filters-count")).toHaveTextContent("1") expect(screen.getByText("Test Filter 1")).toBeInTheDocument() }) }) describe("useTransitions", () => { function TestComponent() { const { transitions, loading } = useTransitions() return (
{String(loading)}
{transitions.length}
{transitions.map((transition) => (
{transition.labels?.en}
))}
) } it("должен загружать и возвращать переходы", async () => { render( , ) await waitForProviderReady() expect(screen.getByTestId("transitions-count")).toHaveTextContent("1") expect(screen.getByText("Fade")).toBeInTheDocument() }) }) describe("useResourceById", () => { function TestComponent({ id }: { id: string }) { const { resource, loading } = useResourceById("effect", id) return (
{String(loading)}
{resource?.name || "Not found"}
) } it("должен находить ресурс по ID", async () => { render( , ) await waitForProviderReady() expect(screen.getByTestId("resource-name")).toHaveTextContent("Test Effect 1") }) it("должен возвращать null для несуществующего ID", async () => { render( , ) await waitForProviderReady() expect(screen.getByTestId("resource-name")).toHaveTextContent("Not found") }) }) describe("useResourcesSearch", () => { function TestComponent({ options }: { options: any }) { const { results, loading } = useResourcesSearch("effect", options) return (
{String(loading)}
{results.length}
{results.map((item) => (
{item.name}
))}
) } it("должен выполнять поиск по запросу", async () => { render( , ) await waitForProviderReady() expect(screen.getByTestId("results-count")).toHaveTextContent("1") expect(screen.getByText("Test Effect 1")).toBeInTheDocument() }) it("должен фильтровать по категории", async () => { render( , ) await waitForProviderReady() expect(screen.getByTestId("results-count")).toHaveTextContent("1") expect(screen.getByText("Test Effect 1")).toBeInTheDocument() }) it("должен фильтровать по тегам", async () => { render( , ) await waitForProviderReady() expect(screen.getByTestId("results-count")).toHaveTextContent("1") expect(screen.getByText("Test Effect 1")).toBeInTheDocument() }) it("должен фильтровать по сложности", async () => { render( , ) await waitForProviderReady() expect(screen.getByTestId("results-count")).toHaveTextContent("1") expect(screen.getByText("Test Effect 2")).toBeInTheDocument() }) }) describe("useResourcesByCategory", () => { function TestComponent({ category }: { category: string }) { const { results } = useResourcesByCategory("effect", category) return (
{results.length}
) } it("должен возвращать ресурсы по категории", async () => { render( , ) await waitFor(() => { expect(screen.getByTestId("results-count")).toHaveTextContent("1") }) }) }) describe("useResourcesByTags", () => { function TestComponent({ tags }: { tags: string[] }) { const { results } = useResourcesByTags("effect", tags) return (
{results.length}
) } it("должен возвращать ресурсы по тегам", async () => { render( , ) await waitFor(() => { expect(screen.getByTestId("results-count")).toHaveTextContent("1") }) }) }) describe("useResourcesByComplexity", () => { function TestComponent({ complexity }: { complexity: string }) { const { results } = useResourcesByComplexity("effect", complexity) return (
{results.length}
) } it("должен возвращать ресурсы по сложности", async () => { render( , ) await waitFor(() => { expect(screen.getByTestId("results-count")).toHaveTextContent("1") }) }) }) describe("useLoadingState", () => { function TestComponent() { const loadingState = useLoadingState() return (
{String(loadingState.isLoading)}
{loadingState.progress}
{loadingState.loadedSources.size}
) } it("должен возвращать состояние загрузки", async () => { render( , ) await waitFor(() => { expect(screen.getByTestId("is-loading")).toHaveTextContent("false") }) expect(screen.getByTestId("loaded-sources")).toHaveTextContent("1") }) }) describe("useResourcesStats", () => { function TestComponent() { const stats = useResourcesStats() return (
{stats.total}
{stats.byType.effect}
{stats.byType.filter}
{stats.byType.transition}
) } it("должен возвращать статистику ресурсов", async () => { render( , ) await waitFor(() => { expect(screen.getByTestId("total")).toHaveTextContent("4") }) expect(screen.getByTestId("effects")).toHaveTextContent("2") expect(screen.getByTestId("filters")).toHaveTextContent("1") expect(screen.getByTestId("transitions")).toHaveTextContent("1") }) }) describe("useResources", () => { function TestComponent({ type }: { type: any }) { const { resources, loading } = useResources(type) return (
{String(loading)}
{resources.length}
{resources.map((resource) => (
{resource.name}
))}
) } it("должен загружать ресурсы по типу", async () => { render( , ) await waitForProviderReady() expect(screen.getByTestId("resources-count")).toHaveTextContent("2") expect(screen.getByText("Test Effect 1")).toBeInTheDocument() expect(screen.getByText("Test Effect 2")).toBeInTheDocument() }) }) describe("useResourceSources", () => { function TestComponent() { const { loadSource, refreshSource, isSourceLoaded, getSourceConfig, updateSourceConfig, loadingState } = useResourceSources() const [loaded, setLoaded] = useState(false) const [config, setConfig] = useState(null) const [refreshed, setRefreshed] = useState(false) useEffect(() => { setLoaded(isSourceLoaded("built-in")) setConfig(getSourceConfig("built-in")) }, [isSourceLoaded, getSourceConfig]) const handleLoad = async () => { // Загружаем встроенный источник заново для теста await loadSource("built-in") } const handleRefresh = async () => { await refreshSource("built-in") setRefreshed(true) } const handleUpdateConfig = () => { updateSourceConfig("built-in", { test: true }) } return (
{String(loaded)}
{JSON.stringify(config)}
{String(loadingState.isLoading)}
{String(refreshed)}
) } it("должен управлять источниками данных", async () => { render( , ) // Ждем пока loading state станет false await waitFor( () => { expect(screen.getByTestId("loading-state")).toHaveTextContent("false") }, { timeout: 5000 }, ) // Проверяем что config доступен expect(screen.getByTestId("config")).toHaveTextContent("built-in") // Проверяем что кнопки управления источниками доступны expect(screen.getByText("Load Built-in")).toBeInTheDocument() expect(screen.getByText("Refresh")).toBeInTheDocument() expect(screen.getByText("Update Config")).toBeInTheDocument() }) }) describe("useResourcesCache", () => { function TestComponent() { const { clearCache, clearSourceCache, invalidateCache, getCacheSize } = useResourcesCache() const [cacheSize, setCacheSize] = useState(0) useEffect(() => { setCacheSize(getCacheSize()) }, [getCacheSize]) const handleClearCache = () => { clearCache("effect") setCacheSize(getCacheSize()) } const handleClearSourceCache = () => { clearSourceCache("built-in") setCacheSize(getCacheSize()) } const handleInvalidateCache = () => { invalidateCache() setCacheSize(getCacheSize()) } return (
{cacheSize}
) } it("должен управлять кэшем", async () => { render( , ) await waitFor(() => { expect(screen.getByTestId("cache-size")).toBeInTheDocument() }) // Проверяем, что методы кэша доступны const clearButton = screen.getByText("Clear Effects Cache") const clearSourceButton = screen.getByText("Clear Source Cache") const invalidateButton = screen.getByText("Invalidate Cache") expect(clearButton).toBeInTheDocument() expect(clearSourceButton).toBeInTheDocument() expect(invalidateButton).toBeInTheDocument() }) }) describe("useResourcesAdapter", () => { function TestComponent({ type, options }: { type: any; options?: any }) { const adapter = useResourcesAdapter({ type, searchOptions: options }) return (
{adapter.items.length}
{String(adapter.loading)}
{adapter.error || "none"}
{adapter.stats.total}
{adapter.items.map((item) => (
{item.name}
))}
) } it("должен предоставлять унифицированный интерфейс адаптера", async () => { render( , ) await waitForProviderReady() expect(screen.getByTestId("items-count")).toHaveTextContent("2") expect(screen.getByTestId("loading")).toHaveTextContent("false") expect(screen.getByTestId("error")).toHaveTextContent("none") expect(screen.getByTestId("total-stats")).toHaveTextContent("4") }) it("должен фильтровать через адаптер", async () => { render( , ) await waitForProviderReady() expect(screen.getByTestId("items-count")).toHaveTextContent("1") expect(screen.getByText("Test Effect 1")).toBeInTheDocument() }) }) describe("Typed search hooks", () => { function TestEffectsSearch() { const { results } = useEffectsSearch({ category: "artistic" }) return (
{results.length}
) } function TestFiltersSearch() { const { results } = useFiltersSearch({ category: "technical" }) return (
{results.length}
) } function TestTransitionsSearch() { const { results } = useTransitionsSearch({ category: "basic" }) return (
{results.length}
) } it("должен выполнять типизированный поиск эффектов", async () => { render( , ) await waitFor(() => { expect(screen.getByTestId("effects-search-count")).toHaveTextContent("1") }) }) it("должен выполнять типизированный поиск фильтров", async () => { render( , ) await waitFor(() => { expect(screen.getByTestId("filters-search-count")).toHaveTextContent("1") }) }) it("должен выполнять типизированный поиск переходов", async () => { render( , ) await waitFor(() => { expect(screen.getByTestId("transitions-search-count")).toHaveTextContent("1") }) }) })