dify/web/app/components/base/chat/embedded-chatbot/index.spec.tsx
Poojan b8fbd7b0f6
test: add unit tests for chat/embedded-chatbot components (#32361)
Co-authored-by: akashseth-ifp <akash.seth@infocusp.com>
2026-02-24 20:58:45 +08:00

241 lines
7.2 KiB
TypeScript

import type { RefObject } from 'react'
import type { ChatConfig } from '../types'
import type { AppData, AppMeta, ConversationItem } from '@/models/share'
import { render, screen } from '@testing-library/react'
import { vi } from 'vitest'
import { useGlobalPublicStore } from '@/context/global-public-context'
import useBreakpoints, { MediaType } from '@/hooks/use-breakpoints'
import { defaultSystemFeatures } from '@/types/feature'
import { useEmbeddedChatbot } from './hooks'
import EmbeddedChatbot from './index'
vi.mock('./hooks', () => ({
useEmbeddedChatbot: vi.fn(),
}))
vi.mock('@/hooks/use-breakpoints', () => ({
default: vi.fn(),
MediaType: {
mobile: 'mobile',
tablet: 'tablet',
pc: 'pc',
},
}))
vi.mock('@/hooks/use-document-title', () => ({
default: vi.fn(),
}))
vi.mock('@/context/global-public-context', () => ({
useGlobalPublicStore: vi.fn(),
}))
vi.mock('./chat-wrapper', () => ({
__esModule: true,
default: () => <div>chat area</div>,
}))
vi.mock('./header', () => ({
__esModule: true,
default: () => <div>chat header</div>,
}))
vi.mock('./theme/theme-context', () => ({
useThemeContext: vi.fn(() => ({
buildTheme: vi.fn(),
theme: {
backgroundHeaderColorStyle: '',
},
})),
}))
const mockIsDify = vi.fn(() => false)
vi.mock('./utils', () => ({
isDify: () => mockIsDify(),
}))
type EmbeddedChatbotHookReturn = ReturnType<typeof useEmbeddedChatbot>
const createHookReturn = (overrides: Partial<EmbeddedChatbotHookReturn> = {}): EmbeddedChatbotHookReturn => {
const appData: AppData = {
app_id: 'app-1',
can_replace_logo: true,
custom_config: {
remove_webapp_brand: false,
replace_webapp_logo: '',
},
enable_site: true,
end_user_id: 'user-1',
site: {
title: 'Embedded App',
chat_color_theme: 'blue',
chat_color_theme_inverted: false,
},
}
const base: EmbeddedChatbotHookReturn = {
appSourceType: 'webApp' as EmbeddedChatbotHookReturn['appSourceType'],
isInstalledApp: false,
appId: 'app-1',
currentConversationId: '',
currentConversationItem: undefined,
removeConversationIdInfo: vi.fn(),
handleConversationIdInfoChange: vi.fn(),
appData,
appParams: {} as ChatConfig,
appMeta: { tool_icons: {} } as AppMeta,
appPinnedConversationData: { data: [], has_more: false, limit: 20 },
appConversationData: { data: [], has_more: false, limit: 20 },
appConversationDataLoading: false,
appChatListData: { data: [], has_more: false, limit: 20 },
appChatListDataLoading: false,
appPrevChatList: [],
pinnedConversationList: [] as ConversationItem[],
conversationList: [] as ConversationItem[],
setShowNewConversationItemInList: vi.fn(),
newConversationInputs: {},
newConversationInputsRef: { current: {} } as unknown as RefObject<Record<string, unknown>>,
handleNewConversationInputsChange: vi.fn(),
inputsForms: [],
handleNewConversation: vi.fn(),
handleStartChat: vi.fn(),
handleChangeConversation: vi.fn(),
handleNewConversationCompleted: vi.fn(),
newConversationId: '',
chatShouldReloadKey: 'reload-key',
allowResetChat: true,
handleFeedback: vi.fn(),
currentChatInstanceRef: { current: { handleStop: vi.fn() } },
clearChatList: false,
setClearChatList: vi.fn(),
isResponding: false,
setIsResponding: vi.fn(),
currentConversationInputs: {},
setCurrentConversationInputs: vi.fn(),
allInputsHidden: false,
initUserVariables: {},
}
return {
...base,
...overrides,
}
}
describe('EmbeddedChatbot index', () => {
beforeEach(() => {
vi.clearAllMocks()
vi.mocked(useBreakpoints).mockReturnValue(MediaType.mobile)
vi.mocked(useEmbeddedChatbot).mockReturnValue(createHookReturn())
vi.mocked(useGlobalPublicStore).mockImplementation(selector => selector({
systemFeatures: {
...defaultSystemFeatures,
branding: {
...defaultSystemFeatures.branding,
enabled: true,
workspace_logo: '',
},
},
setSystemFeatures: vi.fn(),
}))
})
describe('Loading and chat content', () => {
it('should show loading state before chat content', () => {
vi.mocked(useEmbeddedChatbot).mockReturnValue(createHookReturn({ appChatListDataLoading: true }))
render(<EmbeddedChatbot />)
expect(screen.getByRole('status')).toBeInTheDocument()
expect(screen.queryByText('chat area')).not.toBeInTheDocument()
})
it('should render chat content when loading finishes', () => {
render(<EmbeddedChatbot />)
expect(screen.getByText('chat area')).toBeInTheDocument()
})
})
describe('Powered by branding', () => {
it('should show workspace logo on mobile when branding is enabled', () => {
vi.mocked(useGlobalPublicStore).mockImplementation(selector => selector({
systemFeatures: {
...defaultSystemFeatures,
branding: {
...defaultSystemFeatures.branding,
enabled: true,
workspace_logo: 'https://example.com/workspace-logo.png',
},
},
setSystemFeatures: vi.fn(),
}))
render(<EmbeddedChatbot />)
expect(screen.getByText('share.chat.poweredBy')).toBeInTheDocument()
expect(screen.getByAltText('logo')).toHaveAttribute('src', 'https://example.com/workspace-logo.png')
})
it('should show custom logo when workspace branding logo is unavailable', () => {
vi.mocked(useEmbeddedChatbot).mockReturnValue(createHookReturn({
appData: {
app_id: 'app-1',
can_replace_logo: true,
custom_config: {
remove_webapp_brand: false,
replace_webapp_logo: 'https://example.com/custom-logo.png',
},
enable_site: true,
end_user_id: 'user-1',
site: {
title: 'Embedded App',
chat_color_theme: 'blue',
chat_color_theme_inverted: false,
},
},
}))
render(<EmbeddedChatbot />)
expect(screen.getByText('share.chat.poweredBy')).toBeInTheDocument()
expect(screen.getByAltText('logo')).toHaveAttribute('src', 'https://example.com/custom-logo.png')
})
it('should hide powered by section when branding is removed', () => {
vi.mocked(useEmbeddedChatbot).mockReturnValue(createHookReturn({
appData: {
app_id: 'app-1',
can_replace_logo: true,
custom_config: {
remove_webapp_brand: true,
replace_webapp_logo: '',
},
enable_site: true,
end_user_id: 'user-1',
site: {
title: 'Embedded App',
chat_color_theme: 'blue',
chat_color_theme_inverted: false,
},
},
}))
render(<EmbeddedChatbot />)
expect(screen.queryByText('share.chat.poweredBy')).not.toBeInTheDocument()
})
it('should not show powered by section on desktop', () => {
vi.mocked(useBreakpoints).mockReturnValue(MediaType.pc)
vi.mocked(useEmbeddedChatbot).mockReturnValue(createHookReturn({ appData: null }))
mockIsDify.mockReturnValue(true)
render(<EmbeddedChatbot />)
expect(screen.queryByText('share.chat.poweredBy')).not.toBeInTheDocument()
expect(screen.getByText('chat header')).toBeInTheDocument()
})
})
})