dify/web/app/components/app-sidebar/__tests__/dataset-sidebar-dropdown.sp...

194 lines
6.9 KiB
TypeScript

import type { DataSet } from '@/models/datasets'
import { render, screen } from '@testing-library/react'
import userEvent from '@testing-library/user-event'
import * as React from 'react'
import DatasetSidebarDropdown from '../dataset-sidebar-dropdown'
let mockDataset: DataSet
vi.mock('@/context/dataset-detail', () => ({
useDatasetDetailContextWithSelector: (selector: (state: { dataset: DataSet }) => unknown) =>
selector({ dataset: mockDataset }),
}))
vi.mock('@/service/knowledge/use-dataset', () => ({
useDatasetRelatedApps: () => ({ data: [] }),
}))
vi.mock('@/hooks/use-knowledge', () => ({
useKnowledge: () => ({
formatIndexingTechniqueAndMethod: () => 'method-text',
}),
}))
vi.mock('@/app/components/base/portal-to-follow-elem', () => ({
PortalToFollowElem: ({ children, open }: { children: React.ReactNode, open: boolean }) => (
<div data-testid="portal-elem" data-open={open}>{children}</div>
),
PortalToFollowElemTrigger: ({ children, onClick }: { children: React.ReactNode, onClick?: () => void }) => (
<div data-testid="portal-trigger" onClick={onClick}>{children}</div>
),
PortalToFollowElemContent: ({ children }: { children: React.ReactNode }) => (
<div data-testid="portal-content">{children}</div>
),
}))
vi.mock('../../base/app-icon', () => ({
default: ({ size, icon }: { size: string, icon: string }) => (
<div data-testid="app-icon" data-size={size} data-icon={icon} />
),
}))
vi.mock('../../base/divider', () => ({
default: () => <hr data-testid="divider" />,
}))
vi.mock('../../base/effect', () => ({
default: ({ className }: { className?: string }) => <div data-testid="effect" className={className} />,
}))
vi.mock('../../datasets/extra-info', () => ({
default: ({ expand, documentCount }: {
relatedApps?: unknown[]
expand: boolean
documentCount: number
}) => (
<div data-testid="extra-info" data-expand={expand} data-doc-count={documentCount} />
),
}))
vi.mock('../dataset-info/dropdown', () => ({
default: ({ expand }: { expand: boolean }) => (
<div data-testid="dataset-dropdown" data-expand={expand} />
),
}))
vi.mock('../nav-link', () => ({
default: ({ name, href, mode, disabled }: { name: string, href: string, mode?: string, disabled?: boolean }) => (
<a data-testid={`nav-link-${name}`} href={href} data-mode={mode} data-disabled={disabled}>{name}</a>
),
}))
const MockIcon = (props: React.SVGProps<SVGSVGElement>) => <svg {...props} />
const createDataset = (overrides: Partial<DataSet> = {}): DataSet => ({
id: 'dataset-1',
name: 'Test Dataset',
description: 'A test dataset',
provider: 'internal',
icon_info: {
icon: '📙',
icon_type: 'emoji',
icon_background: '#FFF4ED',
icon_url: '',
},
doc_form: 'text_model' as DataSet['doc_form'],
indexing_technique: 'high_quality' as DataSet['indexing_technique'],
document_count: 10,
runtime_mode: 'general',
retrieval_model_dict: {
search_method: 'semantic_search' as DataSet['retrieval_model_dict']['search_method'],
reranking_enable: false,
reranking_model: { reranking_provider_name: '', reranking_model_name: '' },
top_k: 5,
score_threshold_enabled: false,
score_threshold: 0,
},
...overrides,
} as DataSet)
const navigation = [
{ name: 'Documents', href: '/documents', icon: MockIcon, selectedIcon: MockIcon },
{ name: 'Settings', href: '/settings', icon: MockIcon, selectedIcon: MockIcon, disabled: true },
]
describe('DatasetSidebarDropdown', () => {
beforeEach(() => {
vi.clearAllMocks()
mockDataset = createDataset()
})
it('should render trigger with dataset icon', () => {
render(<DatasetSidebarDropdown navigation={navigation} />)
const icons = screen.getAllByTestId('app-icon')
const smallIcon = icons.find(i => i.getAttribute('data-size') === 'small')
expect(smallIcon).toBeInTheDocument()
expect(smallIcon).toHaveAttribute('data-icon', '📙')
})
it('should display dataset name in dropdown content', () => {
render(<DatasetSidebarDropdown navigation={navigation} />)
expect(screen.getByText('Test Dataset')).toBeInTheDocument()
})
it('should display dataset description', () => {
render(<DatasetSidebarDropdown navigation={navigation} />)
expect(screen.getByText('A test dataset')).toBeInTheDocument()
})
it('should not display description when empty', () => {
mockDataset = createDataset({ description: '' })
render(<DatasetSidebarDropdown navigation={navigation} />)
expect(screen.queryByText('A test dataset')).not.toBeInTheDocument()
})
it('should render navigation links', () => {
render(<DatasetSidebarDropdown navigation={navigation} />)
expect(screen.getByTestId('nav-link-Documents')).toBeInTheDocument()
expect(screen.getByTestId('nav-link-Settings')).toBeInTheDocument()
})
it('should render ExtraInfo', () => {
render(<DatasetSidebarDropdown navigation={navigation} />)
const extraInfo = screen.getByTestId('extra-info')
expect(extraInfo).toHaveAttribute('data-expand', 'true')
expect(extraInfo).toHaveAttribute('data-doc-count', '10')
})
it('should render Effect component', () => {
render(<DatasetSidebarDropdown navigation={navigation} />)
expect(screen.getByTestId('effect')).toBeInTheDocument()
})
it('should render Dropdown component with expand=true', () => {
render(<DatasetSidebarDropdown navigation={navigation} />)
expect(screen.getByTestId('dataset-dropdown')).toHaveAttribute('data-expand', 'true')
})
it('should show external tag for external provider', () => {
mockDataset = createDataset({ provider: 'external' })
render(<DatasetSidebarDropdown navigation={navigation} />)
expect(screen.getByText('dataset.externalTag')).toBeInTheDocument()
})
it('should use fallback icon info when icon_info is missing', () => {
mockDataset = createDataset({ icon_info: undefined as unknown as DataSet['icon_info'] })
render(<DatasetSidebarDropdown navigation={navigation} />)
const icons = screen.getAllByTestId('app-icon')
const fallbackIcon = icons.find(i => i.getAttribute('data-icon') === '📙')
expect(fallbackIcon).toBeInTheDocument()
})
it('should toggle dropdown open state on trigger click', async () => {
const user = userEvent.setup()
render(<DatasetSidebarDropdown navigation={navigation} />)
const trigger = screen.getByTestId('portal-trigger')
await user.click(trigger)
expect(screen.getByTestId('portal-elem')).toHaveAttribute('data-open', 'true')
})
it('should render divider', () => {
render(<DatasetSidebarDropdown navigation={navigation} />)
expect(screen.getByTestId('divider')).toBeInTheDocument()
})
it('should render medium app icon in content area', () => {
render(<DatasetSidebarDropdown navigation={navigation} />)
const icons = screen.getAllByTestId('app-icon')
const mediumIcon = icons.find(i => i.getAttribute('data-size') === 'medium')
expect(mediumIcon).toBeInTheDocument()
})
})