mirror of
https://github.com/langgenius/dify.git
synced 2026-03-10 03:00:20 +08:00
Co-authored-by: CodingOnStar <hanxujiang@dify.ai> Co-authored-by: yyh <92089059+lyzno1@users.noreply.github.com>
333 lines
9.2 KiB
TypeScript
333 lines
9.2 KiB
TypeScript
import type { MetadataItem } from '../types'
|
|
import { fireEvent, render, screen } from '@testing-library/react'
|
|
import { describe, expect, it, vi } from 'vitest'
|
|
import { DataType } from '../types'
|
|
import SelectMetadata from './select-metadata'
|
|
|
|
type IconProps = {
|
|
className?: string
|
|
}
|
|
|
|
// Mock getIcon utility
|
|
vi.mock('../utils/get-icon', () => ({
|
|
getIcon: () => (props: IconProps) => <span data-testid="icon" className={props.className}>Icon</span>,
|
|
}))
|
|
|
|
describe('SelectMetadata', () => {
|
|
const mockList: MetadataItem[] = [
|
|
{ id: '1', name: 'field_one', type: DataType.string },
|
|
{ id: '2', name: 'field_two', type: DataType.number },
|
|
{ id: '3', name: 'field_three', type: DataType.time },
|
|
]
|
|
|
|
describe('Rendering', () => {
|
|
it('should render without crashing', () => {
|
|
const { container } = render(
|
|
<SelectMetadata
|
|
list={mockList}
|
|
onSelect={vi.fn()}
|
|
onNew={vi.fn()}
|
|
onManage={vi.fn()}
|
|
/>,
|
|
)
|
|
expect(container.firstChild).toBeInTheDocument()
|
|
})
|
|
|
|
it('should render search input', () => {
|
|
render(
|
|
<SelectMetadata
|
|
list={mockList}
|
|
onSelect={vi.fn()}
|
|
onNew={vi.fn()}
|
|
onManage={vi.fn()}
|
|
/>,
|
|
)
|
|
expect(screen.getByRole('textbox')).toBeInTheDocument()
|
|
})
|
|
|
|
it('should render all metadata items', () => {
|
|
render(
|
|
<SelectMetadata
|
|
list={mockList}
|
|
onSelect={vi.fn()}
|
|
onNew={vi.fn()}
|
|
onManage={vi.fn()}
|
|
/>,
|
|
)
|
|
expect(screen.getByText('field_one')).toBeInTheDocument()
|
|
expect(screen.getByText('field_two')).toBeInTheDocument()
|
|
expect(screen.getByText('field_three')).toBeInTheDocument()
|
|
})
|
|
|
|
it('should render new action button', () => {
|
|
render(
|
|
<SelectMetadata
|
|
list={mockList}
|
|
onSelect={vi.fn()}
|
|
onNew={vi.fn()}
|
|
onManage={vi.fn()}
|
|
/>,
|
|
)
|
|
// New action button should be present (from i18n)
|
|
expect(screen.getByText(/new/i)).toBeInTheDocument()
|
|
})
|
|
|
|
it('should render manage action button', () => {
|
|
render(
|
|
<SelectMetadata
|
|
list={mockList}
|
|
onSelect={vi.fn()}
|
|
onNew={vi.fn()}
|
|
onManage={vi.fn()}
|
|
/>,
|
|
)
|
|
// Manage action button should be present (from i18n)
|
|
expect(screen.getByText(/manage/i)).toBeInTheDocument()
|
|
})
|
|
|
|
it('should display type for each item', () => {
|
|
render(
|
|
<SelectMetadata
|
|
list={mockList}
|
|
onSelect={vi.fn()}
|
|
onNew={vi.fn()}
|
|
onManage={vi.fn()}
|
|
/>,
|
|
)
|
|
expect(screen.getAllByText(DataType.string).length).toBeGreaterThan(0)
|
|
expect(screen.getAllByText(DataType.number).length).toBeGreaterThan(0)
|
|
expect(screen.getAllByText(DataType.time).length).toBeGreaterThan(0)
|
|
})
|
|
})
|
|
|
|
describe('Search Functionality', () => {
|
|
it('should filter items based on search query', () => {
|
|
render(
|
|
<SelectMetadata
|
|
list={mockList}
|
|
onSelect={vi.fn()}
|
|
onNew={vi.fn()}
|
|
onManage={vi.fn()}
|
|
/>,
|
|
)
|
|
|
|
const searchInput = screen.getByRole('textbox')
|
|
fireEvent.change(searchInput, { target: { value: 'one' } })
|
|
|
|
expect(screen.getByText('field_one')).toBeInTheDocument()
|
|
expect(screen.queryByText('field_two')).not.toBeInTheDocument()
|
|
expect(screen.queryByText('field_three')).not.toBeInTheDocument()
|
|
})
|
|
|
|
it('should be case insensitive search', () => {
|
|
render(
|
|
<SelectMetadata
|
|
list={mockList}
|
|
onSelect={vi.fn()}
|
|
onNew={vi.fn()}
|
|
onManage={vi.fn()}
|
|
/>,
|
|
)
|
|
|
|
const searchInput = screen.getByRole('textbox')
|
|
fireEvent.change(searchInput, { target: { value: 'ONE' } })
|
|
|
|
expect(screen.getByText('field_one')).toBeInTheDocument()
|
|
})
|
|
|
|
it('should show all items when search is cleared', () => {
|
|
render(
|
|
<SelectMetadata
|
|
list={mockList}
|
|
onSelect={vi.fn()}
|
|
onNew={vi.fn()}
|
|
onManage={vi.fn()}
|
|
/>,
|
|
)
|
|
|
|
const searchInput = screen.getByRole('textbox')
|
|
|
|
// Search for something
|
|
fireEvent.change(searchInput, { target: { value: 'one' } })
|
|
expect(screen.queryByText('field_two')).not.toBeInTheDocument()
|
|
|
|
// Clear search
|
|
fireEvent.change(searchInput, { target: { value: '' } })
|
|
expect(screen.getByText('field_two')).toBeInTheDocument()
|
|
})
|
|
|
|
it('should show no results when search matches nothing', () => {
|
|
render(
|
|
<SelectMetadata
|
|
list={mockList}
|
|
onSelect={vi.fn()}
|
|
onNew={vi.fn()}
|
|
onManage={vi.fn()}
|
|
/>,
|
|
)
|
|
|
|
const searchInput = screen.getByRole('textbox')
|
|
fireEvent.change(searchInput, { target: { value: 'xyz' } })
|
|
|
|
expect(screen.queryByText('field_one')).not.toBeInTheDocument()
|
|
expect(screen.queryByText('field_two')).not.toBeInTheDocument()
|
|
expect(screen.queryByText('field_three')).not.toBeInTheDocument()
|
|
})
|
|
})
|
|
|
|
describe('User Interactions', () => {
|
|
it('should call onSelect with item data when item is clicked', () => {
|
|
const handleSelect = vi.fn()
|
|
render(
|
|
<SelectMetadata
|
|
list={mockList}
|
|
onSelect={handleSelect}
|
|
onNew={vi.fn()}
|
|
onManage={vi.fn()}
|
|
/>,
|
|
)
|
|
|
|
fireEvent.click(screen.getByText('field_one'))
|
|
|
|
expect(handleSelect).toHaveBeenCalledWith({
|
|
id: '1',
|
|
name: 'field_one',
|
|
type: DataType.string,
|
|
})
|
|
})
|
|
|
|
it('should call onNew when new button is clicked', () => {
|
|
const handleNew = vi.fn()
|
|
render(
|
|
<SelectMetadata
|
|
list={mockList}
|
|
onSelect={vi.fn()}
|
|
onNew={handleNew}
|
|
onManage={vi.fn()}
|
|
/>,
|
|
)
|
|
|
|
// Find and click the new action button
|
|
const newButton = screen.getByText(/new/i)
|
|
fireEvent.click(newButton.closest('div') || newButton)
|
|
|
|
expect(handleNew).toHaveBeenCalled()
|
|
})
|
|
|
|
it('should call onManage when manage button is clicked', () => {
|
|
const handleManage = vi.fn()
|
|
render(
|
|
<SelectMetadata
|
|
list={mockList}
|
|
onSelect={vi.fn()}
|
|
onNew={vi.fn()}
|
|
onManage={handleManage}
|
|
/>,
|
|
)
|
|
|
|
// Find and click the manage action button
|
|
const manageButton = screen.getByText(/manage/i)
|
|
fireEvent.click(manageButton.closest('div') || manageButton)
|
|
|
|
expect(handleManage).toHaveBeenCalled()
|
|
})
|
|
})
|
|
|
|
describe('Empty State', () => {
|
|
it('should render empty list', () => {
|
|
const { container } = render(
|
|
<SelectMetadata
|
|
list={[]}
|
|
onSelect={vi.fn()}
|
|
onNew={vi.fn()}
|
|
onManage={vi.fn()}
|
|
/>,
|
|
)
|
|
expect(container.firstChild).toBeInTheDocument()
|
|
})
|
|
|
|
it('should still show new and manage buttons with empty list', () => {
|
|
render(
|
|
<SelectMetadata
|
|
list={[]}
|
|
onSelect={vi.fn()}
|
|
onNew={vi.fn()}
|
|
onManage={vi.fn()}
|
|
/>,
|
|
)
|
|
expect(screen.getByText(/new/i)).toBeInTheDocument()
|
|
expect(screen.getByText(/manage/i)).toBeInTheDocument()
|
|
})
|
|
})
|
|
|
|
describe('Styling', () => {
|
|
it('should have correct container styling', () => {
|
|
const { container } = render(
|
|
<SelectMetadata
|
|
list={mockList}
|
|
onSelect={vi.fn()}
|
|
onNew={vi.fn()}
|
|
onManage={vi.fn()}
|
|
/>,
|
|
)
|
|
expect(container.firstChild).toHaveClass('w-[320px]', 'rounded-xl')
|
|
})
|
|
})
|
|
|
|
describe('Edge Cases', () => {
|
|
it('should handle single item list', () => {
|
|
const singleItem: MetadataItem[] = [
|
|
{ id: '1', name: 'only_one', type: DataType.string },
|
|
]
|
|
render(
|
|
<SelectMetadata
|
|
list={singleItem}
|
|
onSelect={vi.fn()}
|
|
onNew={vi.fn()}
|
|
onManage={vi.fn()}
|
|
/>,
|
|
)
|
|
expect(screen.getByText('only_one')).toBeInTheDocument()
|
|
})
|
|
|
|
it('should handle item with long name', () => {
|
|
const longNameItem: MetadataItem[] = [
|
|
{ id: '1', name: 'this_is_a_very_long_field_name_that_might_overflow', type: DataType.string },
|
|
]
|
|
render(
|
|
<SelectMetadata
|
|
list={longNameItem}
|
|
onSelect={vi.fn()}
|
|
onNew={vi.fn()}
|
|
onManage={vi.fn()}
|
|
/>,
|
|
)
|
|
expect(screen.getByText('this_is_a_very_long_field_name_that_might_overflow')).toBeInTheDocument()
|
|
})
|
|
|
|
it('should handle rapid search input changes', () => {
|
|
render(
|
|
<SelectMetadata
|
|
list={mockList}
|
|
onSelect={vi.fn()}
|
|
onNew={vi.fn()}
|
|
onManage={vi.fn()}
|
|
/>,
|
|
)
|
|
|
|
const searchInput = screen.getByRole('textbox')
|
|
|
|
// Rapid typing
|
|
fireEvent.change(searchInput, { target: { value: 'f' } })
|
|
fireEvent.change(searchInput, { target: { value: 'fi' } })
|
|
fireEvent.change(searchInput, { target: { value: 'fie' } })
|
|
fireEvent.change(searchInput, { target: { value: 'fiel' } })
|
|
fireEvent.change(searchInput, { target: { value: 'field' } })
|
|
|
|
expect(screen.getByText('field_one')).toBeInTheDocument()
|
|
expect(screen.getByText('field_two')).toBeInTheDocument()
|
|
expect(screen.getByText('field_three')).toBeInTheDocument()
|
|
})
|
|
})
|
|
})
|