import { render, screen, fireEvent, waitFor, act } from '@testing-library/react' import { describe, it, expect, vi, beforeEach } from 'vitest' import userEvent from '@testing-library/user-event' import { RouterProvider, createRouter, createRootRoute, createMemoryHistory } from '@tanstack/react-router' import ChatInput from '../ChatInput' import { usePrompt } from '@/hooks/usePrompt' import { useThreads } from '@/hooks/useThreads' import { useAppState } from '@/hooks/useAppState' import { useGeneralSetting } from '@/hooks/useGeneralSetting' import { useModelProvider } from '@/hooks/useModelProvider' import { useChat } from '@/hooks/useChat' import type { ThreadModel } from '@/types/threads' // Mock dependencies with mutable state let mockPromptState = { prompt: '', setPrompt: vi.fn(), } vi.mock('@/hooks/usePrompt', () => ({ usePrompt: (selector: any) => { return selector ? selector(mockPromptState) : mockPromptState }, })) vi.mock('@/hooks/useThreads', () => ({ useThreads: (selector: any) => { const state = { currentThreadId: null, getCurrentThread: vi.fn(), } return selector ? selector(state) : state }, })) // Mock the useAppState with a mutable state let mockAppState = { streamingContent: null, abortControllers: {}, loadingModel: false, tools: [], updateTools: vi.fn(), } vi.mock('@/hooks/useAppState', () => ({ useAppState: (selector?: any) => selector ? selector(mockAppState) : mockAppState, })) vi.mock('@/hooks/useGeneralSetting', () => ({ useGeneralSetting: (selector?: any) => { const state = { allowSendWhenUnloaded: false, spellCheckChatInput: true, experimentalFeatures: true, } return selector ? selector(state) : state }, })) vi.mock('@/hooks/useModelProvider', () => ({ useModelProvider: (selector: any) => { const state = { selectedModel: { id: 'test-model', capabilities: ['vision', 'tools'], }, providers: [], getModelBy: vi.fn(), selectModelProvider: vi.fn(), selectedProvider: 'llamacpp', setProviders: vi.fn(), getProviderByName: vi.fn(), updateProvider: vi.fn(), addProvider: vi.fn(), deleteProvider: vi.fn(), deleteModel: vi.fn(), deletedModels: [], } return selector ? selector(state) : state }, })) vi.mock('@/hooks/useChat', () => ({ useChat: vi.fn(() => vi.fn()), // useChat returns sendMessage function directly })) vi.mock('@/i18n/react-i18next-compat', () => ({ useTranslation: () => ({ t: (key: string) => key, }), })) // Mock the global core API Object.defineProperty(globalThis, 'core', { value: { api: { existsSync: vi.fn(() => true), getJanDataFolderPath: vi.fn(() => '/mock/path'), }, }, writable: true, }) // Mock the useTools hook vi.mock('@/hooks/useTools', () => ({ useTools: vi.fn(), })) // Mock the ServiceHub const mockGetConnectedServers = vi.fn(() => Promise.resolve(['server1'])) const mockGetTools = vi.fn(() => Promise.resolve([])) const mockStopAllModels = vi.fn() const mockCheckMmprojExists = vi.fn(() => Promise.resolve(true)) const mockListen = vi.fn(() => Promise.resolve(() => {})) const mockServiceHub = { mcp: () => ({ getConnectedServers: mockGetConnectedServers, getTools: mockGetTools, }), models: () => ({ stopAllModels: mockStopAllModels, checkMmprojExists: mockCheckMmprojExists, }), events: () => ({ listen: mockListen, }), } vi.mock('@/hooks/useServiceHub', () => ({ getServiceHub: () => mockServiceHub, useServiceHub: () => mockServiceHub, })) vi.mock('../MovingBorder', () => ({ MovingBorder: ({ children }: { children: React.ReactNode }) =>
{children}
, })) vi.mock('../DropdownModelProvider', () => ({ __esModule: true, default: () =>
Model Dropdown
, })) vi.mock('../loaders/ModelLoader', () => ({ ModelLoader: () =>
Model Loader
, })) vi.mock('../DropdownToolsAvailable', () => ({ __esModule: true, default: ({ children }: { children: (isOpen: boolean, toolsCount: number) => React.ReactNode }) => { return
{children(false, 0)}
}, })) vi.mock('@/components/ui/button', () => ({ Button: ({ children, onClick, disabled, ...props }: any) => ( ), })) vi.mock('@/components/ui/tooltip', () => ({ Tooltip: ({ children }: { children: React.ReactNode }) =>
{children}
, TooltipContent: ({ children }: { children: React.ReactNode }) =>
{children}
, TooltipProvider: ({ children }: { children: React.ReactNode }) =>
{children}
, TooltipTrigger: ({ children }: { children: React.ReactNode }) =>
{children}
, })) vi.mock('react-textarea-autosize', () => ({ default: ({ value, onChange, onKeyDown, placeholder, disabled, className, minRows, maxRows, onHeightChange, ...props }: any) => (