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) => Icon,
}))
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(
,
)
expect(container.firstChild).toBeInTheDocument()
})
it('should render search input', () => {
render(
,
)
expect(screen.getByRole('textbox')).toBeInTheDocument()
})
it('should render all metadata items', () => {
render(
,
)
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(
,
)
// New action button should be present (from i18n)
expect(screen.getByText(/new/i)).toBeInTheDocument()
})
it('should render manage action button', () => {
render(
,
)
// Manage action button should be present (from i18n)
expect(screen.getByText(/manage/i)).toBeInTheDocument()
})
it('should display type for each item', () => {
render(
,
)
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(
,
)
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(
,
)
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(
,
)
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(
,
)
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(
,
)
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(
,
)
// 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(
,
)
// 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(
,
)
expect(container.firstChild).toBeInTheDocument()
})
it('should still show new and manage buttons with empty list', () => {
render(
,
)
expect(screen.getByText(/new/i)).toBeInTheDocument()
expect(screen.getByText(/manage/i)).toBeInTheDocument()
})
})
describe('Styling', () => {
it('should have correct container styling', () => {
const { container } = render(
,
)
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(
,
)
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(
,
)
expect(screen.getByText('this_is_a_very_long_field_name_that_might_overflow')).toBeInTheDocument()
})
it('should handle rapid search input changes', () => {
render(
,
)
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()
})
})
})