import { render, screen } from "@testing-library/react" import { beforeEach, describe, expect, it, vi } from "vitest" import { EffectIndicators } from "../../components/effect-indicators" import type { VideoEffect } from "../../types" // Мокаем react-i18next vi.mock("react-i18next", () => ({ useTranslation: () => ({ t: (key: string, fallback?: string) => fallback || key, }), })) describe("EffectIndicators", () => { const baseEffect: VideoEffect = { id: "test-effect", name: "Test Effect", type: "blur", category: "artistic", complexity: "basic", tags: ["popular"], description: { ru: "Тестовый эффект", en: "Test Effect" }, labels: { ru: "Тест", en: "Test", }, params: {}, ffmpegCommand: () => "test", previewPath: "/effects/test.mp4", duration: 0, } beforeEach(() => { vi.clearAllMocks() }) describe("Рендеринг компонента", () => { it("должен отображать индикатор категории", () => { render() expect(screen.getByText("ART")).toBeInTheDocument() }) it("должен отображать индикатор popular тега", () => { render() expect(screen.getByText("POP")).toBeInTheDocument() }) it("должен использовать правильные размеры для size='sm'", () => { render() const container = screen.getByText("ART").closest("div") expect(container).toHaveClass("text-[9px]", "px-1", "py-0.5") }) it("должен использовать правильные размеры для size='md'", () => { render() const container = screen.getByText("ART").closest("div") expect(container).toHaveClass("text-[10px]", "px-1.5", "py-0.5") }) it("должен использовать size='sm' по умолчанию", () => { render() const container = screen.getByText("ART").closest("div") expect(container).toHaveClass("text-[9px]") }) }) describe("Аббревиатуры категорий", () => { it("должен отображать CC для color-correction", () => { const effect = { ...baseEffect, category: "color-correction" } render() expect(screen.getByText("CC")).toBeInTheDocument() }) it("должен отображать ART для artistic", () => { const effect = { ...baseEffect, category: "artistic" } render() expect(screen.getByText("ART")).toBeInTheDocument() }) it("должен отображать VIN для vintage", () => { const effect = { ...baseEffect, category: "vintage" } render() expect(screen.getByText("VIN")).toBeInTheDocument() }) it("должен отображать CIN для cinematic", () => { const effect = { ...baseEffect, category: "cinematic" } render() expect(screen.getByText("CIN")).toBeInTheDocument() }) it("должен отображать CRE для creative", () => { const effect = { ...baseEffect, category: "creative" } render() expect(screen.getByText("CRE")).toBeInTheDocument() }) it("должен отображать TEC для technical", () => { const effect = { ...baseEffect, category: "technical" } render() expect(screen.getByText("TEC")).toBeInTheDocument() }) it("должен отображать MOT для motion", () => { const effect = { ...baseEffect, category: "motion" } render() expect(screen.getByText("MOT")).toBeInTheDocument() }) it("должен отображать DIS для distortion", () => { const effect = { ...baseEffect, category: "distortion" } render() expect(screen.getByText("DIS")).toBeInTheDocument() }) it("должен отображать EFF для неизвестной категории", () => { const effect = { ...baseEffect, category: "unknown" as any } render() expect(screen.getByText("EFF")).toBeInTheDocument() }) }) describe("Индикаторы тегов", () => { it("должен отображать POP для popular тега", () => { const effect = { ...baseEffect, tags: ["popular"] } render() expect(screen.getByText("POP")).toBeInTheDocument() }) it("должен отображать PRO для professional тега", () => { const effect = { ...baseEffect, tags: ["professional"] } render() expect(screen.getByText("PRO")).toBeInTheDocument() }) it("должен отображать EXP для experimental тега", () => { const effect = { ...baseEffect, tags: ["experimental"] } render() expect(screen.getByText("EXP")).toBeInTheDocument() }) it("должен отображать несколько тегов одновременно", () => { const effect = { ...baseEffect, tags: ["popular", "professional", "experimental"] } render() expect(screen.getByText("POP")).toBeInTheDocument() expect(screen.getByText("PRO")).toBeInTheDocument() expect(screen.getByText("EXP")).toBeInTheDocument() }) it("не должен отображать индикаторы для неподдерживаемых тегов", () => { const effect = { ...baseEffect, tags: ["some-other-tag"] } render() expect(screen.queryByText("POP")).not.toBeInTheDocument() expect(screen.queryByText("PRO")).not.toBeInTheDocument() expect(screen.queryByText("EXP")).not.toBeInTheDocument() }) it("должен работать с пустым массивом тегов", () => { const effect = { ...baseEffect, tags: [] } render() // Только категория должна отображаться expect(screen.getByText("ART")).toBeInTheDocument() expect(screen.queryByText("POP")).not.toBeInTheDocument() }) it("должен работать с undefined тегами", () => { const effect = { ...baseEffect, tags: undefined as any } render() // Только категория должна отображаться expect(screen.getByText("ART")).toBeInTheDocument() expect(screen.queryByText("POP")).not.toBeInTheDocument() }) }) describe("CSS классы и стили", () => { it("должен применять правильные CSS классы для контейнера", () => { render() const container = screen.getByText("ART").closest("div")?.parentElement expect(container).toHaveClass("flex", "items-center", "gap-1") }) it("должен использовать gap-1.5 для size='md'", () => { render() const container = screen.getByText("ART").closest("div")?.parentElement expect(container).toHaveClass("gap-1.5") }) it("должен применять правильные стили к индикаторам", () => { render() const categoryIndicator = screen.getByText("ART").closest("div") expect(categoryIndicator).toHaveClass("bg-black/70", "text-white", "font-medium", "rounded-xs") }) it("должен добавлять title атрибут к индикатору категории", () => { render() const categoryIndicator = screen.getByText("ART").closest("div") expect(categoryIndicator).toHaveAttribute("title", "effects.categories.artistic") }) }) describe("Обратная совместимость", () => { it("должен работать с эффектом без тегов", () => { const effect = { ...baseEffect } ;(effect as any).tags = undefined expect(() => { render() }).not.toThrow() expect(screen.getByText("ART")).toBeInTheDocument() }) it("должен обрабатывать null теги", () => { const effect = { ...baseEffect, tags: null as any } expect(() => { render() }).not.toThrow() expect(screen.getByText("ART")).toBeInTheDocument() }) }) describe("Accessibility", () => { it("должен предоставлять понятные title атрибуты", () => { render() const categoryIndicator = screen.getByText("ART").closest("div") expect(categoryIndicator).toHaveAttribute("title") }) it("должен использовать семантически правильную структуру", () => { render() const container = screen.getByText("ART").closest("div")?.parentElement expect(container?.tagName).toBe("DIV") expect(container).toHaveClass("flex") }) }) describe("Интеграционные тесты", () => { it("должен корректно работать с реальными данными эффекта", () => { const realEffect: VideoEffect = { id: "vintage-film", name: "Vintage Film", type: "vintage", category: "vintage", complexity: "intermediate", tags: ["popular", "professional"], description: { ru: "Винтажная пленка", en: "Vintage Film" }, labels: { ru: "Винтажная пленка", en: "Vintage Film" }, params: { intensity: 0.8 }, ffmpegCommand: () => "vintage", previewPath: "/vintage.mp4", duration: 1000, } render() expect(screen.getByText("VIN")).toBeInTheDocument() expect(screen.getByText("POP")).toBeInTheDocument() expect(screen.getByText("PRO")).toBeInTheDocument() }) it("должен работать с минимальными данными эффекта", () => { const minimalEffect: VideoEffect = { id: "minimal", name: "Minimal", type: "blur", category: "artistic", complexity: "basic", tags: [], description: { ru: "Минимальный", en: "Minimal" }, labels: { ru: "Минимальный", en: "Minimal" }, params: {}, ffmpegCommand: () => "minimal", previewPath: "/minimal.mp4", duration: 0, } render() expect(screen.getByText("ART")).toBeInTheDocument() expect(screen.queryByText("POP")).not.toBeInTheDocument() }) }) describe("Производительность", () => { it("должен эффективно рендерить большое количество индикаторов", () => { const effect = { ...baseEffect, tags: ["popular", "professional", "experimental"], } const startTime = performance.now() render() const endTime = performance.now() // Рендер должен завершиться быстро expect(endTime - startTime).toBeLessThan(50) }) it("должен правильно обрабатывать частые перерендеры", () => { const { rerender } = render() for (let i = 0; i < 10; i++) { const updatedEffect = { ...baseEffect, id: `effect-${i}` } rerender() } expect(screen.getByText("ART")).toBeInTheDocument() }) }) })