// Мокаем хук до импорта компонента
vi.mock("../../hooks/use-player-ai-analysis", () => ({
usePlayerAIAnalysis: vi.fn(),
}))
import { render, screen } from "@testing-library/react"
import { vi } from "vitest"
import type { PlayerAIAnalysisHook } from "../../hooks/use-player-ai-analysis"
import { usePlayerAIAnalysis } from "../../hooks/use-player-ai-analysis"
import { PlayerAIOverlay } from "../player-ai-overlay"
describe("PlayerAIOverlay", () => {
const mockAIAnalysis: PlayerAIAnalysisHook = {
state: {
isAnalyzing: false,
currentScene: null,
detectedObjects: [],
upcomingMoments: [],
frameAnalysisRate: 2,
},
startRealtimeAnalysis: vi.fn(),
stopRealtimeAnalysis: vi.fn(),
setFrameAnalysisRate: vi.fn(),
getCurrentSceneInfo: vi.fn().mockReturnValue(null),
getObjectsInFrame: vi.fn().mockReturnValue([]),
getUpcomingMoments: vi.fn().mockReturnValue([]),
}
beforeEach(() => {
vi.clearAllMocks()
vi.mocked(usePlayerAIAnalysis).mockReturnValue(mockAIAnalysis)
})
describe("Видимость компонента", () => {
it("не отображается когда анализ не активен", () => {
const { container } = render()
expect(container.firstChild).toBeNull()
})
it("отображается когда анализ активен", () => {
vi.mocked(usePlayerAIAnalysis).mockReturnValue({
...mockAIAnalysis,
state: {
...mockAIAnalysis.state,
isAnalyzing: true,
},
})
render()
expect(screen.getByText("AI анализ активен")).toBeInTheDocument()
})
})
describe("Информация о сцене", () => {
it("отображает информацию о текущей сцене", () => {
const currentScene = {
id: "scene-1",
type: "dialogue",
startTime: 0,
endTime: 10,
duration: 10,
confidence: 0.85,
}
vi.mocked(usePlayerAIAnalysis).mockReturnValue({
...mockAIAnalysis,
state: {
...mockAIAnalysis.state,
isAnalyzing: true,
},
getCurrentSceneInfo: vi.fn().mockReturnValue(currentScene),
})
render()
expect(screen.getByText("Сцена: dialogue")).toBeInTheDocument()
})
it("не отображает информацию о сцене если showSceneInfo=false", () => {
const currentScene = {
id: "scene-1",
type: "dialogue",
startTime: 0,
endTime: 10,
duration: 10,
confidence: 0.85,
}
vi.mocked(usePlayerAIAnalysis).mockReturnValue({
...mockAIAnalysis,
state: {
...mockAIAnalysis.state,
isAnalyzing: true,
},
getCurrentSceneInfo: vi.fn().mockReturnValue(currentScene),
})
render()
expect(screen.queryByText("Сцена: dialogue")).not.toBeInTheDocument()
})
})
describe("Обнаруженные объекты", () => {
it("отображает список обнаруженных объектов", () => {
const objects = [
{
id: "obj-1",
label: "person",
confidence: 0.92,
boundingBox: { x: 20, y: 15, width: 30, height: 60 },
frameNumbers: [100],
},
{
id: "obj-2",
label: "car",
confidence: 0.87,
boundingBox: { x: 50, y: 40, width: 25, height: 20 },
frameNumbers: [100],
},
]
vi.mocked(usePlayerAIAnalysis).mockReturnValue({
...mockAIAnalysis,
state: {
...mockAIAnalysis.state,
isAnalyzing: true,
},
getObjectsInFrame: vi.fn().mockReturnValue(objects),
})
render()
expect(screen.getByText("Объекты:")).toBeInTheDocument()
expect(screen.getByText("person (92%)")).toBeInTheDocument()
expect(screen.getByText("car (87%)")).toBeInTheDocument()
})
it("показывает только первые 5 объектов", () => {
const objects = Array.from({ length: 8 }, (_, i) => ({
id: `obj-${i}`,
label: `object-${i}`,
confidence: 0.9,
boundingBox: { x: 10, y: 10, width: 20, height: 20 },
frameNumbers: [100],
}))
vi.mocked(usePlayerAIAnalysis).mockReturnValue({
...mockAIAnalysis,
state: {
...mockAIAnalysis.state,
isAnalyzing: true,
},
getObjectsInFrame: vi.fn().mockReturnValue(objects),
})
render()
// Проверяем первые 5 объектов
for (let i = 0; i < 5; i++) {
expect(screen.getByText(`object-${i} (90%)`)).toBeInTheDocument()
}
// Проверяем счетчик оставшихся
expect(screen.getByText("+3")).toBeInTheDocument()
// Проверяем что 6-8 не отображаются
expect(screen.queryByText("object-5 (90%)")).not.toBeInTheDocument()
expect(screen.queryByText("object-6 (90%)")).not.toBeInTheDocument()
expect(screen.queryByText("object-7 (90%)")).not.toBeInTheDocument()
})
it("не отображает объекты если showObjects=false", () => {
const objects = [
{
id: "obj-1",
label: "person",
confidence: 0.92,
boundingBox: { x: 20, y: 15, width: 30, height: 60 },
frameNumbers: [100],
},
]
vi.mocked(usePlayerAIAnalysis).mockReturnValue({
...mockAIAnalysis,
state: {
...mockAIAnalysis.state,
isAnalyzing: true,
},
getObjectsInFrame: vi.fn().mockReturnValue(objects),
})
render()
expect(screen.queryByText("Объекты:")).not.toBeInTheDocument()
})
it("отображает рамки вокруг объектов", () => {
const objects = [
{
id: "obj-1",
label: "person",
confidence: 0.92,
boundingBox: { x: 20, y: 15, width: 30, height: 60 },
frameNumbers: [100],
},
]
vi.mocked(usePlayerAIAnalysis).mockReturnValue({
...mockAIAnalysis,
state: {
...mockAIAnalysis.state,
isAnalyzing: true,
},
getObjectsInFrame: vi.fn().mockReturnValue(objects),
})
const { container } = render()
const boundingBox = container.querySelector('[style*="left: 20%"]')
expect(boundingBox).toBeInTheDocument()
expect(boundingBox).toHaveStyle({
left: "20%",
top: "15%",
width: "30%",
height: "60%",
})
expect(boundingBox).toHaveClass("border-blue-400/50")
})
})
describe("Предстоящие моменты", () => {
it("отображает предстоящие ключевые моменты", () => {
const moments = [
{
id: "moment-1",
timestamp: 65,
type: "highlight",
confidence: 0.9,
description: "Эмоциональный момент",
},
{
id: "moment-2",
timestamp: 120,
type: "scene_change",
confidence: 0.85,
description: "Смена сцены",
},
]
vi.mocked(usePlayerAIAnalysis).mockReturnValue({
...mockAIAnalysis,
state: {
...mockAIAnalysis.state,
isAnalyzing: true,
},
getUpcomingMoments: vi.fn().mockReturnValue(moments),
})
render()
expect(screen.getByText("Ключевые моменты:")).toBeInTheDocument()
expect(screen.getByText("1:05: Эмоциональный момент")).toBeInTheDocument()
expect(screen.getByText("2:00: Смена сцены")).toBeInTheDocument()
})
it("показывает только первые 3 момента", () => {
const moments = Array.from({ length: 5 }, (_, i) => ({
id: `moment-${i}`,
timestamp: 60 + i * 10,
type: "highlight",
confidence: 0.9,
description: `Момент ${i}`,
}))
vi.mocked(usePlayerAIAnalysis).mockReturnValue({
...mockAIAnalysis,
state: {
...mockAIAnalysis.state,
isAnalyzing: true,
},
getUpcomingMoments: vi.fn().mockReturnValue(moments),
})
render()
// Проверяем первые 3 момента
expect(screen.getByText("1:00: Момент 0")).toBeInTheDocument()
expect(screen.getByText("1:10: Момент 1")).toBeInTheDocument()
expect(screen.getByText("1:20: Момент 2")).toBeInTheDocument()
// Проверяем что 4-5 не отображаются
expect(screen.queryByText("1:30: Момент 3")).not.toBeInTheDocument()
expect(screen.queryByText("1:40: Момент 4")).not.toBeInTheDocument()
})
it("не отображает моменты если showMoments=false", () => {
const moments = [
{
id: "moment-1",
timestamp: 65,
type: "highlight",
confidence: 0.9,
description: "Эмоциональный момент",
},
]
vi.mocked(usePlayerAIAnalysis).mockReturnValue({
...mockAIAnalysis,
state: {
...mockAIAnalysis.state,
isAnalyzing: true,
},
getUpcomingMoments: vi.fn().mockReturnValue(moments),
})
render()
expect(screen.queryByText("Ключевые моменты:")).not.toBeInTheDocument()
})
})
describe("Форматирование времени", () => {
it("правильно форматирует время для моментов", () => {
const moments = [
{ id: "1", timestamp: 5, type: "highlight", confidence: 0.9, description: "Момент 1" },
{ id: "2", timestamp: 65, type: "highlight", confidence: 0.9, description: "Момент 2" },
{ id: "3", timestamp: 125, type: "highlight", confidence: 0.9, description: "Момент 3" },
{ id: "4", timestamp: 3661, type: "highlight", confidence: 0.9, description: "Момент 4" },
]
vi.mocked(usePlayerAIAnalysis).mockReturnValue({
...mockAIAnalysis,
state: {
...mockAIAnalysis.state,
isAnalyzing: true,
},
getUpcomingMoments: vi.fn().mockReturnValue(moments.slice(0, 3)),
})
render()
expect(screen.getByText("0:05: Момент 1")).toBeInTheDocument()
expect(screen.getByText("1:05: Момент 2")).toBeInTheDocument()
expect(screen.getByText("2:05: Момент 3")).toBeInTheDocument()
})
})
describe("CSS классы и стили", () => {
it("применяет пользовательский className", () => {
vi.mocked(usePlayerAIAnalysis).mockReturnValue({
...mockAIAnalysis,
state: {
...mockAIAnalysis.state,
isAnalyzing: true,
},
})
const { container } = render()
expect(container.firstChild).toHaveClass("custom-class")
})
it("имеет правильные базовые классы", () => {
vi.mocked(usePlayerAIAnalysis).mockReturnValue({
...mockAIAnalysis,
state: {
...mockAIAnalysis.state,
isAnalyzing: true,
},
})
const { container } = render()
expect(container.firstChild).toHaveClass("absolute", "inset-0", "pointer-events-none")
})
})
describe("Индикатор анализа", () => {
it("показывает анимированный индикатор анализа", () => {
vi.mocked(usePlayerAIAnalysis).mockReturnValue({
...mockAIAnalysis,
state: {
...mockAIAnalysis.state,
isAnalyzing: true,
},
})
const { container } = render()
const sparklesIcon = container.querySelector(".animate-pulse")
expect(sparklesIcon).toBeInTheDocument()
expect(sparklesIcon).toHaveClass("text-blue-400")
})
})
})