jan/web/hooks/useDeleteThread.test.ts
2025-03-26 20:16:17 +07:00

164 lines
5.2 KiB
TypeScript

/**
* @jest-environment jsdom
*/
import { renderHook, act } from '@testing-library/react'
import { useAtom, useAtomValue, useSetAtom } from 'jotai'
import useDeleteThread from './useDeleteThread'
import { extensionManager } from '@/extension/ExtensionManager'
import { useCreateNewThread } from './useCreateNewThread'
import { Thread } from '@janhq/core/dist/types/types'
import { currentPromptAtom } from '@/containers/Providers/Jotai'
// Mock the necessary dependencies
// Mock dependencies
jest.mock('jotai', () => ({
useAtomValue: jest.fn(),
useSetAtom: jest.fn(),
useAtom: jest.fn(),
atom: jest.fn(),
}))
jest.mock('./useCreateNewThread')
jest.mock('@/extension/ExtensionManager')
jest.mock('@/containers/Toast')
describe('useDeleteThread', () => {
beforeEach(() => {
jest.clearAllMocks()
})
it('should delete a thread successfully', async () => {
const mockThreads = [
{ id: 'thread1', title: 'Thread 1' },
{ id: 'thread2', title: 'Thread 2' },
]
const mockSetThreads = jest.fn()
;(useAtom as jest.Mock).mockReturnValue([mockThreads, mockSetThreads])
;(useSetAtom as jest.Mock).mockReturnValue(() => {})
;(useCreateNewThread as jest.Mock).mockReturnValue({})
const mockDeleteThread = jest.fn().mockImplementation(() => ({
catch: () => jest.fn,
}))
extensionManager.get = jest.fn().mockReturnValue({
deleteThread: mockDeleteThread,
getThreadAssistant: jest.fn().mockResolvedValue({}),
})
const { result } = renderHook(() => useDeleteThread())
await act(async () => {
await result.current.deleteThread('thread1')
})
expect(mockDeleteThread).toHaveBeenCalledWith('thread1')
expect(mockSetThreads).toHaveBeenCalledWith([mockThreads[1]])
})
it('should clean a thread successfully', async () => {
const mockThreads = [{ id: 'thread1', title: 'Thread 1', metadata: {} }]
const mockSetThreads = jest.fn()
;(useAtom as jest.Mock).mockReturnValue([mockThreads, mockSetThreads])
const mockCleanMessages = jest.fn()
;(useSetAtom as jest.Mock).mockReturnValue(() => mockCleanMessages)
;(useAtomValue as jest.Mock).mockReturnValue(['thread 1'])
const mockSaveThread = jest.fn()
const mockDeleteMessage = jest.fn().mockResolvedValue({})
const mockModifyThread = jest.fn().mockResolvedValue({})
extensionManager.get = jest.fn().mockReturnValue({
saveThread: mockSaveThread,
getThreadAssistant: jest.fn().mockResolvedValue({}),
listMessages: jest.fn().mockResolvedValue([
{
id: 'message1',
text: 'Message 1',
},
]),
deleteMessage: mockDeleteMessage,
modifyThread: mockModifyThread,
})
const { result } = renderHook(() => useDeleteThread())
await act(async () => {
await result.current.cleanThread('thread1')
})
expect(mockDeleteMessage).toHaveBeenCalled()
expect(mockModifyThread).toHaveBeenCalled()
})
it('should handle errors when deleting a thread', async () => {
const mockThreads = [{ id: 'thread1', title: 'Thread 1' }]
const mockSetThreads = jest.fn()
;(useAtom as jest.Mock).mockReturnValue([mockThreads, mockSetThreads])
const mockCreateNewThread = jest.fn()
;(useCreateNewThread as jest.Mock).mockReturnValue({
requestCreateNewThread: mockCreateNewThread,
})
const mockDeleteThread = jest
.fn()
.mockRejectedValue(new Error('Delete error'))
extensionManager.get = jest.fn().mockReturnValue({
deleteThread: mockDeleteThread,
})
const consoleErrorSpy = jest
.spyOn(console, 'error')
.mockImplementation(() => {})
const { result } = renderHook(() => useDeleteThread())
await act(async () => {
await result.current.deleteThread('thread1')
})
expect(mockDeleteThread).toHaveBeenCalledWith('thread1')
expect(consoleErrorSpy).toHaveBeenCalledWith(expect.any(Error))
consoleErrorSpy.mockRestore()
})
it('should delete all threads successfully', async () => {
const mockThreads = [
{ id: 'thread1', title: 'Thread 1' },
{ id: 'thread2', title: 'Thread 2' },
]
const mockSetThreads = jest.fn()
;(useAtom as jest.Mock).mockReturnValue([mockThreads, mockSetThreads])
// create mock functions
const mockSetCurrentPrompt = jest.fn()
// mock useSetAtom for each atom
let currentAtom: any
;(useSetAtom as jest.Mock).mockImplementation((atom) => {
currentAtom = atom
if (currentAtom === currentPromptAtom) return mockSetCurrentPrompt
return jest.fn()
})
const mockDeleteThread = jest.fn().mockImplementation(() => ({
catch: () => jest.fn,
}))
extensionManager.get = jest.fn().mockReturnValue({
deleteThread: mockDeleteThread,
})
const { result } = renderHook(() => useDeleteThread())
await act(async () => {
await result.current.deleteAllThreads(mockThreads as Thread[])
})
expect(mockDeleteThread).toHaveBeenCalledTimes(2)
expect(mockDeleteThread).toHaveBeenCalledWith('thread1')
expect(mockDeleteThread).toHaveBeenCalledWith('thread2')
expect(mockSetThreads).toHaveBeenCalledWith([])
expect(mockSetCurrentPrompt).toHaveBeenCalledWith('')
})
})