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()
})
})