import { render, screen } from "@testing-library/react" import { beforeEach, describe, expect, it, vi } from "vitest" import { BrowserLoadingIndicator, BrowserResourcesSkeleton, BrowserTabLoadingBadge, } from "../../components/browser-loading-indicator" import { useLoadingState, useResourcesStats } from "../../hooks/use-resources" // Мокаем хуки vi.mock("../../hooks/use-resources") // Мокаем UI компоненты vi.mock("@/components/ui/badge", () => ({ Badge: ({ children, variant, className }: any) => ( {children} ), })) vi.mock("@/components/ui/progress", () => ({ Progress: ({ value, className }: any) => (
), })) vi.mock("@/components/ui/skeleton", () => ({ Skeleton: ({ className }: any) =>
, })) describe("BrowserLoadingIndicator", () => { const mockLoadingState = { isLoading: false, error: null, progress: 0, loadedSources: new Set(), loadingQueue: [] as string[], } const mockStats = { total: 0, byType: { effects: 0, filters: 0, transitions: 0, }, } beforeEach(() => { vi.clearAllMocks() vi.mocked(useLoadingState).mockReturnValue(mockLoadingState) vi.mocked(useResourcesStats).mockReturnValue(mockStats) }) describe("Видимость компонента", () => { it("не должен отображаться когда нет загрузки и ошибок", () => { const { container } = render() expect(container.firstChild).toBeNull() }) it("должен отображаться при загрузке", () => { vi.mocked(useLoadingState).mockReturnValue({ ...mockLoadingState, isLoading: true, }) render() expect(screen.getByText("Загрузка ресурсов...")).toBeInTheDocument() }) it("должен отображаться при ошибке", () => { vi.mocked(useLoadingState).mockReturnValue({ ...mockLoadingState, error: "Ошибка загрузки файлов", }) render() expect(screen.getByText("Ошибка загрузки")).toBeInTheDocument() }) }) describe("Состояния загрузки", () => { it("должен показывать анимированный спиннер при загрузке", () => { vi.mocked(useLoadingState).mockReturnValue({ ...mockLoadingState, isLoading: true, }) render() const spinner = screen.getByText("Загрузка ресурсов...").previousElementSibling expect(spinner).toHaveClass("animate-spin") }) it("должен показывать красный индикатор при ошибке", () => { vi.mocked(useLoadingState).mockReturnValue({ ...mockLoadingState, error: "Ошибка", }) render() const errorIndicator = screen.getByText("Ошибка загрузки").previousElementSibling expect(errorIndicator).toHaveClass("bg-destructive") }) it("должен показывать текст ошибки", () => { const errorMessage = "Не удалось загрузить ресурсы из источника" vi.mocked(useLoadingState).mockReturnValue({ ...mockLoadingState, error: errorMessage, }) render() expect(screen.getByText(errorMessage)).toBeInTheDocument() }) }) describe("Прогресс загрузки", () => { it("должен показывать прогресс-бар при загрузке", () => { vi.mocked(useLoadingState).mockReturnValue({ ...mockLoadingState, isLoading: true, progress: 45, }) render() const progress = screen.getByTestId("progress") expect(progress).toHaveAttribute("data-value", "45") expect(screen.getByText("45%")).toBeInTheDocument() }) it("должен показывать очередь загрузки", () => { vi.mocked(useLoadingState).mockReturnValue({ ...mockLoadingState, isLoading: true, loadingQueue: ["effects", "filters"], }) render() expect(screen.getByText("Загружается: effects, filters")).toBeInTheDocument() }) }) describe("Статистика", () => { it("должен показывать общее количество ресурсов", () => { vi.mocked(useLoadingState).mockReturnValue({ ...mockLoadingState, isLoading: true, }) vi.mocked(useResourcesStats).mockReturnValue({ ...mockStats, total: 150, }) render() expect(screen.getByText("150 ресурсов")).toBeInTheDocument() }) it("должен показывать количество загруженных источников", () => { vi.mocked(useLoadingState).mockReturnValue({ ...mockLoadingState, isLoading: true, loadedSources: new Set(["built-in", "custom"]), }) render() expect(screen.getByText("2 источников")).toBeInTheDocument() }) it("должен показывать детальную статистику по типам", () => { vi.mocked(useLoadingState).mockReturnValue({ ...mockLoadingState, isLoading: true, }) vi.mocked(useResourcesStats).mockReturnValue({ total: 100, byType: { effect: 50, filter: 30, transition: 20, }, }) render() expect(screen.getByText("Эффекты: 50")).toBeInTheDocument() expect(screen.getByText("Фильтры: 30")).toBeInTheDocument() expect(screen.getByText("Переходы: 20")).toBeInTheDocument() }) }) }) describe("BrowserResourcesSkeleton", () => { it("должен рендерить скелетон с правильной структурой", () => { render() // Заголовок const skeletons = screen.getAllByTestId("skeleton") expect(skeletons).toHaveLength(38) // 2 в заголовке + 12 * 3 в карточках // Проверяем структуру сетки const grid = skeletons[2].parentElement?.parentElement expect(grid).toHaveClass("grid", "grid-cols-2", "md:grid-cols-3", "lg:grid-cols-4", "xl:grid-cols-6") }) it("должен рендерить 12 карточек", () => { render() // Каждая карточка содержит 3 скелетона const skeletons = screen.getAllByTestId("skeleton") const cardSkeletons = skeletons.slice(2) // Пропускаем 2 скелетона заголовка expect(cardSkeletons).toHaveLength(36) // 12 карточек * 3 скелетона }) }) describe("BrowserTabLoadingBadge", () => { const mockLoadingState = { isLoading: false, error: null, progress: 0, loadedSources: new Set(), loadingQueue: [] as string[], } const mockStats = { total: 0, byType: { effects: 0, filters: 0, transitions: 0, }, } beforeEach(() => { vi.mocked(useLoadingState).mockReturnValue(mockLoadingState) vi.mocked(useResourcesStats).mockReturnValue(mockStats) }) it("не должен отображаться когда нет ресурсов и загрузки", () => { const { container } = render() expect(container.firstChild).toBeNull() }) it("должен показывать количество ресурсов", () => { vi.mocked(useResourcesStats).mockReturnValue({ total: 100, byType: { effects: 25, filters: 0, transitions: 0, }, }) render() expect(screen.getByText("25")).toBeInTheDocument() }) it("должен показывать спиннер при загрузке", () => { vi.mocked(useLoadingState).mockReturnValue({ ...mockLoadingState, isLoading: true, }) render() const badge = screen.getByTestId("badge") expect(badge).toHaveAttribute("data-variant", "secondary") const spinner = badge.querySelector(".animate-spin") expect(spinner).toBeInTheDocument() }) it("должен использовать правильный вариант badge", () => { vi.mocked(useResourcesStats).mockReturnValue({ total: 10, byType: { effects: 10, filters: 0, transitions: 0, }, }) // Без загрузки const { rerender } = render() expect(screen.getByTestId("badge")).toHaveAttribute("data-variant", "outline") // С загрузкой vi.mocked(useLoadingState).mockReturnValue({ ...mockLoadingState, isLoading: true, }) rerender() expect(screen.getByTestId("badge")).toHaveAttribute("data-variant", "secondary") }) it("должен работать с разными типами ресурсов", () => { vi.mocked(useResourcesStats).mockReturnValue({ total: 60, byType: { effects: 20, filters: 15, transitions: 25, }, }) const { rerender } = render() expect(screen.getByText("20")).toBeInTheDocument() rerender() expect(screen.getByText("15")).toBeInTheDocument() rerender() expect(screen.getByText("25")).toBeInTheDocument() }) })