Spaces:
Paused
Paused
| /** | |
| * ChatPanel Component Tests | |
| */ | |
| import { describe, it, expect, vi, beforeEach, afterEach } from "vitest"; | |
| describe("ChatPanel useElapsedTimer Logic", () => { | |
| beforeEach(() => { | |
| vi.useFakeTimers(); | |
| }); | |
| afterEach(() => { | |
| vi.useRealTimers(); | |
| }); | |
| describe("Timer Initialization", () => { | |
| it("initializes with 0ms values", () => { | |
| const timer = { liveMs: 0, finalMs: 0, displayMs: 0 }; | |
| expect(timer.liveMs).toBe(0); | |
| expect(timer.finalMs).toBe(0); | |
| expect(timer.displayMs).toBe(0); | |
| }); | |
| }); | |
| describe("Timer Behavior", () => { | |
| it("calculates elapsed time correctly", () => { | |
| const startTime = Date.now(); | |
| vi.advanceTimersByTime(1500); | |
| const elapsed = Date.now() - startTime; | |
| expect(elapsed).toBe(1500); | |
| }); | |
| it("updates every 100ms", () => { | |
| let updateCount = 0; | |
| const interval = setInterval(() => { | |
| updateCount++; | |
| }, 100); | |
| vi.advanceTimersByTime(500); | |
| clearInterval(interval); | |
| expect(updateCount).toBe(5); | |
| }); | |
| }); | |
| describe("Active State Transitions", () => { | |
| it("starts counting when isActive becomes true", () => { | |
| let isActive = false; | |
| let startTime: number | null = null; | |
| // Transition to active | |
| isActive = true; | |
| startTime = Date.now(); | |
| expect(isActive).toBe(true); | |
| expect(startTime).not.toBeNull(); | |
| }); | |
| it("captures final value when isActive becomes false", () => { | |
| let isActive = true; | |
| let finalMs = 0; | |
| const startTime = Date.now(); | |
| vi.advanceTimersByTime(2000); | |
| // Transition to inactive | |
| isActive = false; | |
| finalMs = Date.now() - startTime; | |
| expect(isActive).toBe(false); | |
| expect(finalMs).toBe(2000); | |
| }); | |
| }); | |
| describe("Display Logic", () => { | |
| it("shows liveMs during active state", () => { | |
| const isActive = true; | |
| const liveMs = 1500; | |
| const finalMs = 0; | |
| const displayMs = isActive ? liveMs : finalMs; | |
| expect(displayMs).toBe(1500); | |
| }); | |
| it("shows finalMs after deactivation", () => { | |
| const isActive = false; | |
| const liveMs = 0; | |
| const finalMs = 2000; | |
| const displayMs = isActive ? liveMs : finalMs; | |
| expect(displayMs).toBe(2000); | |
| }); | |
| }); | |
| }); | |
| describe("ChatPanel formatTime Logic", () => { | |
| const formatTime = (seconds: number): string => { | |
| if (seconds < 60) { | |
| return `${seconds}s`; | |
| } | |
| const mins = Math.floor(seconds / 60); | |
| const secs = seconds % 60; | |
| return `${mins}:${secs.toString().padStart(2, "0")}`; | |
| }; | |
| it("formats seconds under 60", () => { | |
| expect(formatTime(0)).toBe("0s"); | |
| expect(formatTime(30)).toBe("30s"); | |
| expect(formatTime(59)).toBe("59s"); | |
| }); | |
| it("formats exactly 60 seconds as 1:00", () => { | |
| expect(formatTime(60)).toBe("1:00"); | |
| }); | |
| it("formats minutes and seconds", () => { | |
| expect(formatTime(90)).toBe("1:30"); | |
| expect(formatTime(125)).toBe("2:05"); | |
| expect(formatTime(601)).toBe("10:01"); | |
| }); | |
| it("pads single digit seconds with zero", () => { | |
| expect(formatTime(65)).toBe("1:05"); | |
| expect(formatTime(301)).toBe("5:01"); | |
| }); | |
| }); | |
| describe("ChatPanel Message Component Logic", () => { | |
| describe("Edit Mode State", () => { | |
| it("initializes with editing false", () => { | |
| const isEditing = false; | |
| expect(isEditing).toBe(false); | |
| }); | |
| it("enters edit mode", () => { | |
| let isEditing = false; | |
| isEditing = true; | |
| expect(isEditing).toBe(true); | |
| }); | |
| it("exits edit mode on save", () => { | |
| let isEditing = true; | |
| isEditing = false; | |
| expect(isEditing).toBe(false); | |
| }); | |
| it("exits edit mode on cancel", () => { | |
| let isEditing = true; | |
| isEditing = false; | |
| expect(isEditing).toBe(false); | |
| }); | |
| }); | |
| describe("Edit Content Management", () => { | |
| it("syncs editContent with message content on edit start", () => { | |
| const messageContent = "Original message"; | |
| let editContent = ""; | |
| // Start editing | |
| editContent = messageContent; | |
| expect(editContent).toBe("Original message"); | |
| }); | |
| it("allows editContent modification", () => { | |
| let editContent = "Original"; | |
| editContent = "Modified content"; | |
| expect(editContent).toBe("Modified content"); | |
| }); | |
| it("restores original on cancel", () => { | |
| const originalContent = "Original"; | |
| let editContent = "Modified"; | |
| // Cancel | |
| editContent = originalContent; | |
| expect(editContent).toBe("Original"); | |
| }); | |
| }); | |
| describe("Thinking Section Toggle", () => { | |
| it("initializes with thinking collapsed", () => { | |
| const showThinking = false; | |
| expect(showThinking).toBe(false); | |
| }); | |
| it("toggles thinking section open", () => { | |
| let showThinking = false; | |
| showThinking = !showThinking; | |
| expect(showThinking).toBe(true); | |
| }); | |
| it("toggles thinking section closed", () => { | |
| let showThinking = true; | |
| showThinking = !showThinking; | |
| expect(showThinking).toBe(false); | |
| }); | |
| }); | |
| describe("Message Role Detection", () => { | |
| it("identifies user message", () => { | |
| const message = { role: "user", content: "Hello" }; | |
| const isUser = message.role === "user"; | |
| expect(isUser).toBe(true); | |
| }); | |
| it("identifies assistant message", () => { | |
| const message = { role: "assistant", content: "Hello" }; | |
| const isUser = message.role === "user"; | |
| expect(isUser).toBe(false); | |
| }); | |
| }); | |
| describe("Status Display Logic", () => { | |
| it("shows status when showStatus is true and streaming", () => { | |
| const showStatus = true; | |
| const isStreaming = true; | |
| const shouldShow = showStatus && isStreaming; | |
| expect(shouldShow).toBe(true); | |
| }); | |
| it("hides status when showStatus is false", () => { | |
| const showStatus = false; | |
| const isStreaming = true; | |
| const shouldShow = showStatus && isStreaming; | |
| expect(shouldShow).toBe(false); | |
| }); | |
| }); | |
| describe("Action Button Visibility", () => { | |
| it("shows action buttons when not editing and not disabled", () => { | |
| const isEditing = false; | |
| const disabled = false; | |
| const showActions = !isEditing && !disabled; | |
| expect(showActions).toBe(true); | |
| }); | |
| it("hides action buttons when editing", () => { | |
| const isEditing = true; | |
| const disabled = false; | |
| const showActions = !isEditing && !disabled; | |
| expect(showActions).toBe(false); | |
| }); | |
| it("hides action buttons when disabled", () => { | |
| const isEditing = false; | |
| const disabled = true; | |
| const showActions = !isEditing && !disabled; | |
| expect(showActions).toBe(false); | |
| }); | |
| }); | |
| describe("Error Display", () => { | |
| it("shows error when message has error", () => { | |
| const message = { error: "Network error" }; | |
| const hasError = !!message.error; | |
| expect(hasError).toBe(true); | |
| }); | |
| it("hides error when message has no error", () => { | |
| const message = { error: undefined }; | |
| const hasError = !!message.error; | |
| expect(hasError).toBe(false); | |
| }); | |
| }); | |
| }); | |
| describe("ChatPanel Input Logic", () => { | |
| describe("Input Value Management", () => { | |
| it("initializes with empty input", () => { | |
| const input = ""; | |
| expect(input).toBe(""); | |
| }); | |
| it("updates input value", () => { | |
| let input = ""; | |
| input = "Hello, world!"; | |
| expect(input).toBe("Hello, world!"); | |
| }); | |
| it("clears input after send", () => { | |
| let input = "Message to send"; | |
| input = ""; | |
| expect(input).toBe(""); | |
| }); | |
| }); | |
| describe("Send Validation", () => { | |
| it("allows send when input has content", () => { | |
| const input = "Hello"; | |
| const isStreaming = false; | |
| const canSend = input.trim().length > 0 && !isStreaming; | |
| expect(canSend).toBe(true); | |
| }); | |
| it("prevents send when input is empty", () => { | |
| const input = " "; | |
| const isStreaming = false; | |
| const canSend = input.trim().length > 0 && !isStreaming; | |
| expect(canSend).toBe(false); | |
| }); | |
| it("prevents send when streaming", () => { | |
| const input = "Hello"; | |
| const isStreaming = true; | |
| const canSend = input.trim().length > 0 && !isStreaming; | |
| expect(canSend).toBe(false); | |
| }); | |
| }); | |
| describe("Keyboard Shortcuts", () => { | |
| it("detects Enter key for send", () => { | |
| const key = "Enter"; | |
| const shiftKey = false; | |
| const shouldSend = key === "Enter" && !shiftKey; | |
| expect(shouldSend).toBe(true); | |
| }); | |
| it("detects Shift+Enter for newline", () => { | |
| const key = "Enter"; | |
| const shiftKey = true; | |
| const shouldSend = key === "Enter" && !shiftKey; | |
| expect(shouldSend).toBe(false); | |
| }); | |
| }); | |
| }); | |