252 lines
6.2 KiB
TypeScript
252 lines
6.2 KiB
TypeScript
import { renderHook, act } from '@testing-library/react'
|
|
import { describe, it, expect, vi, beforeEach } from 'vitest'
|
|
import { useChat } from '../useChat'
|
|
|
|
// Mock dependencies
|
|
vi.mock('../usePrompt', () => ({
|
|
usePrompt: Object.assign(
|
|
(selector: any) => {
|
|
const state = {
|
|
prompt: 'test prompt',
|
|
setPrompt: vi.fn(),
|
|
}
|
|
return selector ? selector(state) : state
|
|
},
|
|
{ getState: () => ({ prompt: 'test prompt', setPrompt: vi.fn() }) }
|
|
),
|
|
}))
|
|
|
|
vi.mock('../useAppState', () => ({
|
|
useAppState: Object.assign(
|
|
(selector?: any) => {
|
|
const state = {
|
|
tools: [],
|
|
updateTokenSpeed: vi.fn(),
|
|
resetTokenSpeed: vi.fn(),
|
|
updateTools: vi.fn(),
|
|
updateStreamingContent: vi.fn(),
|
|
updatePromptProgress: vi.fn(),
|
|
updateLoadingModel: vi.fn(),
|
|
setAbortController: vi.fn(),
|
|
}
|
|
return selector ? selector(state) : state
|
|
},
|
|
{
|
|
getState: vi.fn(() => ({
|
|
tokenSpeed: { tokensPerSecond: 10 },
|
|
}))
|
|
}
|
|
),
|
|
}))
|
|
|
|
vi.mock('../useAssistant', () => ({
|
|
useAssistant: (selector: any) => {
|
|
const state = {
|
|
assistants: [{
|
|
id: 'test-assistant',
|
|
instructions: 'test instructions',
|
|
parameters: { stream: true },
|
|
}],
|
|
currentAssistant: {
|
|
id: 'test-assistant',
|
|
instructions: 'test instructions',
|
|
parameters: { stream: true },
|
|
},
|
|
}
|
|
return selector ? selector(state) : state
|
|
},
|
|
}))
|
|
|
|
vi.mock('../useModelProvider', () => ({
|
|
useModelProvider: Object.assign(
|
|
(selector: any) => {
|
|
const state = {
|
|
getProviderByName: vi.fn(() => ({
|
|
provider: 'openai',
|
|
models: [],
|
|
})),
|
|
selectedModel: {
|
|
id: 'test-model',
|
|
capabilities: ['tools'],
|
|
},
|
|
selectedProvider: 'openai',
|
|
updateProvider: vi.fn(),
|
|
}
|
|
return selector ? selector(state) : state
|
|
},
|
|
{
|
|
getState: () => ({
|
|
getProviderByName: vi.fn(() => ({
|
|
provider: 'openai',
|
|
models: [],
|
|
})),
|
|
selectedModel: {
|
|
id: 'test-model',
|
|
capabilities: ['tools'],
|
|
},
|
|
selectedProvider: 'openai',
|
|
updateProvider: vi.fn(),
|
|
})
|
|
}
|
|
),
|
|
}))
|
|
|
|
vi.mock('../useThreads', () => ({
|
|
useThreads: (selector: any) => {
|
|
const state = {
|
|
getCurrentThread: vi.fn(() => ({
|
|
id: 'test-thread',
|
|
model: { id: 'test-model', provider: 'openai' },
|
|
})),
|
|
createThread: vi.fn(() => Promise.resolve({
|
|
id: 'test-thread',
|
|
model: { id: 'test-model', provider: 'openai' },
|
|
})),
|
|
updateThreadTimestamp: vi.fn(),
|
|
}
|
|
return selector ? selector(state) : state
|
|
},
|
|
}))
|
|
|
|
vi.mock('../useMessages', () => ({
|
|
useMessages: (selector: any) => {
|
|
const state = {
|
|
getMessages: vi.fn(() => []),
|
|
addMessage: vi.fn(),
|
|
}
|
|
return selector ? selector(state) : state
|
|
},
|
|
}))
|
|
|
|
vi.mock('../useToolApproval', () => ({
|
|
useToolApproval: (selector: any) => {
|
|
const state = {
|
|
approvedTools: [],
|
|
showApprovalModal: vi.fn(),
|
|
allowAllMCPPermissions: false,
|
|
}
|
|
return selector ? selector(state) : state
|
|
},
|
|
}))
|
|
|
|
vi.mock('../useToolAvailable', () => ({
|
|
useToolAvailable: (selector: any) => {
|
|
const state = {
|
|
getDisabledToolsForThread: vi.fn(() => []),
|
|
}
|
|
return selector ? selector(state) : state
|
|
},
|
|
}))
|
|
|
|
vi.mock('../useModelContextApproval', () => ({
|
|
useContextSizeApproval: (selector: any) => {
|
|
const state = {
|
|
showApprovalModal: vi.fn(),
|
|
}
|
|
return selector ? selector(state) : state
|
|
},
|
|
}))
|
|
|
|
vi.mock('../useModelLoad', () => ({
|
|
useModelLoad: (selector: any) => {
|
|
const state = {
|
|
setModelLoadError: vi.fn(),
|
|
}
|
|
return selector ? selector(state) : state
|
|
},
|
|
}))
|
|
|
|
vi.mock('@tanstack/react-router', () => ({
|
|
useRouter: vi.fn(() => ({
|
|
navigate: vi.fn(),
|
|
})),
|
|
}))
|
|
|
|
vi.mock('@/lib/completion', () => ({
|
|
emptyThreadContent: { thread_id: 'test-thread', content: '' },
|
|
extractToolCall: vi.fn(),
|
|
newUserThreadContent: vi.fn(() => ({ thread_id: 'test-thread', content: 'user message' })),
|
|
newAssistantThreadContent: vi.fn(() => ({ thread_id: 'test-thread', content: 'assistant message' })),
|
|
sendCompletion: vi.fn(),
|
|
postMessageProcessing: vi.fn(),
|
|
isCompletionResponse: vi.fn(),
|
|
captureProactiveScreenshots: vi.fn(() => Promise.resolve([])),
|
|
}))
|
|
|
|
vi.mock('@/lib/messages', () => ({
|
|
CompletionMessagesBuilder: vi.fn(() => ({
|
|
addUserMessage: vi.fn(),
|
|
addAssistantMessage: vi.fn(),
|
|
getMessages: vi.fn(() => []),
|
|
})),
|
|
}))
|
|
|
|
vi.mock('@/services/mcp', () => ({
|
|
getTools: vi.fn(() => Promise.resolve([])),
|
|
}))
|
|
|
|
vi.mock('@/services/models', () => ({
|
|
startModel: vi.fn(() => Promise.resolve()),
|
|
stopModel: vi.fn(() => Promise.resolve()),
|
|
stopAllModels: vi.fn(() => Promise.resolve()),
|
|
}))
|
|
|
|
vi.mock('@/services/providers', () => ({
|
|
updateSettings: vi.fn(() => Promise.resolve()),
|
|
}))
|
|
|
|
vi.mock('@tauri-apps/api/event', () => ({
|
|
listen: vi.fn(() => Promise.resolve(vi.fn())),
|
|
}))
|
|
|
|
vi.mock('sonner', () => ({
|
|
toast: {
|
|
error: vi.fn(),
|
|
},
|
|
}))
|
|
|
|
describe('useChat', () => {
|
|
beforeEach(() => {
|
|
vi.clearAllMocks()
|
|
})
|
|
|
|
it('returns sendMessage function', () => {
|
|
const { result } = renderHook(() => useChat())
|
|
|
|
expect(result.current).toBeDefined()
|
|
expect(typeof result.current).toBe('function')
|
|
})
|
|
|
|
it('sends message successfully', async () => {
|
|
const { result } = renderHook(() => useChat())
|
|
|
|
await act(async () => {
|
|
await result.current('Hello world')
|
|
})
|
|
|
|
expect(result.current).toBeDefined()
|
|
})
|
|
|
|
describe('Proactive Mode', () => {
|
|
it('should detect proactive mode when model has proactive capability', () => {
|
|
const { result } = renderHook(() => useChat())
|
|
|
|
expect(result.current).toBeDefined()
|
|
expect(typeof result.current).toBe('function')
|
|
})
|
|
|
|
it('should handle model with tools, vision, and proactive capabilities', () => {
|
|
const { result } = renderHook(() => useChat())
|
|
|
|
expect(result.current).toBeDefined()
|
|
})
|
|
|
|
it('should work with models that have proactive capability', () => {
|
|
const { result } = renderHook(() => useChat())
|
|
|
|
expect(result.current).toBeDefined()
|
|
expect(typeof result.current).toBe('function')
|
|
})
|
|
})
|
|
})
|