mirror of https://github.com/langgenius/dify.git
improve tests
This commit is contained in:
parent
2365d237b3
commit
c4336dc560
|
|
@ -3,6 +3,7 @@ import type { ChatConfig } from '../types'
|
|||
import type { AppConversationData, AppData, AppMeta, ConversationItem } from '@/models/share'
|
||||
import { QueryClient, QueryClientProvider } from '@tanstack/react-query'
|
||||
import { act, renderHook, waitFor } from '@testing-library/react'
|
||||
import { ToastProvider } from '@/app/components/base/toast'
|
||||
import {
|
||||
fetchChatList,
|
||||
fetchConversations,
|
||||
|
|
@ -12,14 +13,6 @@ import { shareQueryKeys } from '@/service/use-share'
|
|||
import { CONVERSATION_ID_INFO } from '../constants'
|
||||
import { useChatWithHistory } from './hooks'
|
||||
|
||||
const notifyMock = vi.fn()
|
||||
|
||||
vi.mock('@/app/components/base/toast', () => ({
|
||||
useToastContext: () => ({
|
||||
notify: notifyMock,
|
||||
}),
|
||||
}))
|
||||
|
||||
vi.mock('@/hooks/use-app-favicon', () => ({
|
||||
useAppFavicon: vi.fn(),
|
||||
}))
|
||||
|
|
@ -85,7 +78,9 @@ const createQueryClient = () => new QueryClient({
|
|||
|
||||
const createWrapper = (queryClient: QueryClient) => {
|
||||
return ({ children }: { children: ReactNode }) => (
|
||||
<QueryClientProvider client={queryClient}>{children}</QueryClientProvider>
|
||||
<QueryClientProvider client={queryClient}>
|
||||
<ToastProvider>{children}</ToastProvider>
|
||||
</QueryClientProvider>
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -127,6 +122,7 @@ const setConversationIdInfo = (appId: string, conversationId: string) => {
|
|||
describe('useChatWithHistory', () => {
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks()
|
||||
localStorage.removeItem(CONVERSATION_ID_INFO)
|
||||
mockStoreState.appInfo = {
|
||||
app_id: 'app-1',
|
||||
custom_config: null,
|
||||
|
|
@ -142,6 +138,10 @@ describe('useChatWithHistory', () => {
|
|||
setConversationIdInfo('app-1', 'conversation-1')
|
||||
})
|
||||
|
||||
afterEach(() => {
|
||||
localStorage.removeItem(CONVERSATION_ID_INFO)
|
||||
})
|
||||
|
||||
// Scenario: share query results populate conversation lists and trigger chat list fetch.
|
||||
describe('Share queries', () => {
|
||||
it('should load pinned, unpinned, and chat list data from share queries', async () => {
|
||||
|
|
@ -208,4 +208,63 @@ describe('useChatWithHistory', () => {
|
|||
expect(invalidateSpy).toHaveBeenCalledWith({ queryKey: shareQueryKeys.conversations })
|
||||
})
|
||||
})
|
||||
|
||||
// Scenario: chat list queries stop when reload key is cleared.
|
||||
describe('Chat list gating', () => {
|
||||
it('should not refetch chat list when newConversationId matches current conversation', async () => {
|
||||
// Arrange
|
||||
const listData = createConversationData({
|
||||
data: [createConversationItem({ id: 'conversation-1', name: 'First' })],
|
||||
})
|
||||
mockFetchConversations.mockResolvedValue(listData)
|
||||
mockFetchChatList.mockResolvedValue({ data: [] })
|
||||
mockGenerationConversationName.mockResolvedValue(createConversationItem({ id: 'conversation-1' }))
|
||||
|
||||
const { result } = renderWithClient(() => useChatWithHistory())
|
||||
|
||||
await waitFor(() => {
|
||||
expect(mockFetchChatList).toHaveBeenCalledTimes(1)
|
||||
})
|
||||
|
||||
// Act
|
||||
act(() => {
|
||||
result.current.handleNewConversationCompleted('conversation-1')
|
||||
})
|
||||
|
||||
// Assert
|
||||
await waitFor(() => {
|
||||
expect(result.current.chatShouldReloadKey).toBe('')
|
||||
})
|
||||
expect(mockFetchChatList).toHaveBeenCalledTimes(1)
|
||||
})
|
||||
})
|
||||
|
||||
// Scenario: conversation id updates persist to localStorage.
|
||||
describe('Conversation id persistence', () => {
|
||||
it('should store new conversation id in localStorage after completion', async () => {
|
||||
// Arrange
|
||||
const listData = createConversationData({
|
||||
data: [createConversationItem({ id: 'conversation-1', name: 'First' })],
|
||||
})
|
||||
mockFetchConversations.mockResolvedValue(listData)
|
||||
mockFetchChatList.mockResolvedValue({ data: [] })
|
||||
mockGenerationConversationName.mockResolvedValue(createConversationItem({ id: 'conversation-new' }))
|
||||
|
||||
const { result } = renderWithClient(() => useChatWithHistory())
|
||||
|
||||
// Act
|
||||
act(() => {
|
||||
result.current.handleNewConversationCompleted('conversation-new')
|
||||
})
|
||||
|
||||
// Assert
|
||||
await waitFor(() => {
|
||||
const storedValue = localStorage.getItem(CONVERSATION_ID_INFO)
|
||||
const parsed = storedValue ? JSON.parse(storedValue) : {}
|
||||
const storedUserId = parsed['app-1']?.['user-1']
|
||||
const storedDefaultId = parsed['app-1']?.DEFAULT
|
||||
expect([storedUserId, storedDefaultId]).toContain('conversation-new')
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
|
|
|
|||
|
|
@ -3,22 +3,16 @@ import type { ChatConfig } from '../types'
|
|||
import type { AppConversationData, AppData, AppMeta, ConversationItem } from '@/models/share'
|
||||
import { QueryClient, QueryClientProvider } from '@tanstack/react-query'
|
||||
import { act, renderHook, waitFor } from '@testing-library/react'
|
||||
import { ToastProvider } from '@/app/components/base/toast'
|
||||
import {
|
||||
fetchChatList,
|
||||
fetchConversations,
|
||||
generationConversationName,
|
||||
} from '@/service/share'
|
||||
import { shareQueryKeys } from '@/service/use-share'
|
||||
import { CONVERSATION_ID_INFO } from '../constants'
|
||||
import { useEmbeddedChatbot } from './hooks'
|
||||
|
||||
const notifyMock = vi.fn()
|
||||
|
||||
vi.mock('@/app/components/base/toast', () => ({
|
||||
useToastContext: () => ({
|
||||
notify: notifyMock,
|
||||
}),
|
||||
}))
|
||||
|
||||
vi.mock('@/i18n-config/i18next-config', () => ({
|
||||
changeLanguage: vi.fn().mockResolvedValue(undefined),
|
||||
}))
|
||||
|
|
@ -80,7 +74,9 @@ const createQueryClient = () => new QueryClient({
|
|||
|
||||
const createWrapper = (queryClient: QueryClient) => {
|
||||
return ({ children }: { children: ReactNode }) => (
|
||||
<QueryClientProvider client={queryClient}>{children}</QueryClientProvider>
|
||||
<QueryClientProvider client={queryClient}>
|
||||
<ToastProvider>{children}</ToastProvider>
|
||||
</QueryClientProvider>
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -112,6 +108,7 @@ const createConversationData = (overrides: Partial<AppConversationData> = {}): A
|
|||
describe('useEmbeddedChatbot', () => {
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks()
|
||||
localStorage.removeItem(CONVERSATION_ID_INFO)
|
||||
mockStoreState.appInfo = {
|
||||
app_id: 'app-1',
|
||||
custom_config: null,
|
||||
|
|
@ -128,6 +125,10 @@ describe('useEmbeddedChatbot', () => {
|
|||
mockStoreState.embeddedUserId = 'embedded-user-1'
|
||||
})
|
||||
|
||||
afterEach(() => {
|
||||
localStorage.removeItem(CONVERSATION_ID_INFO)
|
||||
})
|
||||
|
||||
// Scenario: share query results populate conversation lists and trigger chat list fetch.
|
||||
describe('Share queries', () => {
|
||||
it('should load pinned, unpinned, and chat list data from share queries', async () => {
|
||||
|
|
@ -194,4 +195,63 @@ describe('useEmbeddedChatbot', () => {
|
|||
expect(invalidateSpy).toHaveBeenCalledWith({ queryKey: shareQueryKeys.conversations })
|
||||
})
|
||||
})
|
||||
|
||||
// Scenario: chat list queries stop when reload key is cleared.
|
||||
describe('Chat list gating', () => {
|
||||
it('should not refetch chat list when newConversationId matches current conversation', async () => {
|
||||
// Arrange
|
||||
const listData = createConversationData({
|
||||
data: [createConversationItem({ id: 'conversation-1', name: 'First' })],
|
||||
})
|
||||
mockFetchConversations.mockResolvedValue(listData)
|
||||
mockFetchChatList.mockResolvedValue({ data: [] })
|
||||
mockGenerationConversationName.mockResolvedValue(createConversationItem({ id: 'conversation-1' }))
|
||||
|
||||
const { result } = renderWithClient(() => useEmbeddedChatbot())
|
||||
|
||||
await waitFor(() => {
|
||||
expect(mockFetchChatList).toHaveBeenCalledTimes(1)
|
||||
})
|
||||
|
||||
// Act
|
||||
act(() => {
|
||||
result.current.handleNewConversationCompleted('conversation-1')
|
||||
})
|
||||
|
||||
// Assert
|
||||
await waitFor(() => {
|
||||
expect(result.current.chatShouldReloadKey).toBe('')
|
||||
})
|
||||
expect(mockFetchChatList).toHaveBeenCalledTimes(1)
|
||||
})
|
||||
})
|
||||
|
||||
// Scenario: conversation id updates persist to localStorage.
|
||||
describe('Conversation id persistence', () => {
|
||||
it('should store new conversation id in localStorage after completion', async () => {
|
||||
// Arrange
|
||||
const listData = createConversationData({
|
||||
data: [createConversationItem({ id: 'conversation-1', name: 'First' })],
|
||||
})
|
||||
mockFetchConversations.mockResolvedValue(listData)
|
||||
mockFetchChatList.mockResolvedValue({ data: [] })
|
||||
mockGenerationConversationName.mockResolvedValue(createConversationItem({ id: 'conversation-new' }))
|
||||
|
||||
const { result } = renderWithClient(() => useEmbeddedChatbot())
|
||||
|
||||
// Act
|
||||
act(() => {
|
||||
result.current.handleNewConversationCompleted('conversation-new')
|
||||
})
|
||||
|
||||
// Assert
|
||||
await waitFor(() => {
|
||||
const storedValue = localStorage.getItem(CONVERSATION_ID_INFO)
|
||||
const parsed = storedValue ? JSON.parse(storedValue) : {}
|
||||
const storedUserId = parsed['app-1']?.['embedded-user-1']
|
||||
const storedDefaultId = parsed['app-1']?.DEFAULT
|
||||
expect([storedUserId, storedDefaultId]).toContain('conversation-new')
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
|
|
|
|||
|
|
@ -105,12 +105,13 @@ describe('useShareConversations', () => {
|
|||
}
|
||||
|
||||
// Act
|
||||
renderShareHook(() => useShareConversations(params))
|
||||
const { result } = renderShareHook(() => useShareConversations(params))
|
||||
|
||||
// Assert
|
||||
await waitFor(() => {
|
||||
expect(mockFetchConversations).not.toHaveBeenCalled()
|
||||
expect(result.current.fetchStatus).toBe('idle')
|
||||
})
|
||||
expect(mockFetchConversations).not.toHaveBeenCalled()
|
||||
})
|
||||
})
|
||||
|
||||
|
|
@ -151,12 +152,13 @@ describe('useShareChatList', () => {
|
|||
}
|
||||
|
||||
// Act
|
||||
renderShareHook(() => useShareChatList(params))
|
||||
const { result } = renderShareHook(() => useShareChatList(params))
|
||||
|
||||
// Assert
|
||||
await waitFor(() => {
|
||||
expect(mockFetchChatList).not.toHaveBeenCalled()
|
||||
expect(result.current.fetchStatus).toBe('idle')
|
||||
})
|
||||
expect(mockFetchChatList).not.toHaveBeenCalled()
|
||||
})
|
||||
})
|
||||
|
||||
|
|
@ -197,12 +199,13 @@ describe('useShareConversationName', () => {
|
|||
}
|
||||
|
||||
// Act
|
||||
renderShareHook(() => useShareConversationName(params, { enabled: false }))
|
||||
const { result } = renderShareHook(() => useShareConversationName(params, { enabled: false }))
|
||||
|
||||
// Assert
|
||||
await waitFor(() => {
|
||||
expect(mockGenerationConversationName).not.toHaveBeenCalled()
|
||||
expect(result.current.fetchStatus).toBe('idle')
|
||||
})
|
||||
expect(mockGenerationConversationName).not.toHaveBeenCalled()
|
||||
})
|
||||
})
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue