import type { MetadataItemWithValue } from '../types' import { fireEvent, render, screen } from '@testing-library/react' import { describe, expect, it, vi } from 'vitest' import { DataType } from '../types' import InfoGroup from './info-group' type SelectModalProps = { trigger: React.ReactNode onSelect: (item: MetadataItemWithValue) => void onSave: (data: { name: string, type: DataType }) => void onManage: () => void } type FieldProps = { label: string children: React.ReactNode } type InputCombinedProps = { value: string | number | null onChange: (value: string | number) => void type: DataType } // Mock next/navigation vi.mock('next/navigation', () => ({ useRouter: () => ({ push: vi.fn(), }), })) // Mock useTimestamp vi.mock('@/hooks/use-timestamp', () => ({ default: () => ({ formatTime: (timestamp: number) => { if (!timestamp) return '' return new Date(timestamp * 1000).toLocaleDateString() }, }), })) // Mock AddMetadataButton vi.mock('../add-metadata-button', () => ({ default: () => , })) // Mock InputCombined vi.mock('../edit-metadata-batch/input-combined', () => ({ default: ({ value, onChange, type }: InputCombinedProps) => ( onChange(e.target.value)} /> ), })) // Mock SelectMetadataModal vi.mock('../metadata-dataset/select-metadata-modal', () => ({ default: ({ trigger, onSelect, onSave, onManage }: SelectModalProps) => (
{trigger}
), })) // Mock Field vi.mock('./field', () => ({ default: ({ label, children }: FieldProps) => (
{label}
{children}
), })) describe('InfoGroup', () => { const mockList: MetadataItemWithValue[] = [ { id: '1', name: 'field_one', type: DataType.string, value: 'Value 1' }, { id: '2', name: 'field_two', type: DataType.number, value: 42 }, { id: '3', name: 'built-in', type: DataType.time, value: 1609459200 }, ] describe('Rendering', () => { it('should render without crashing', () => { const { container } = render( , ) expect(container.firstChild).toBeInTheDocument() }) it('should render title when provided', () => { render( , ) expect(screen.getByText('Test Title')).toBeInTheDocument() }) it('should not render header when noHeader is true', () => { render( , ) expect(screen.queryByText('Test Title')).not.toBeInTheDocument() }) it('should render all list items', () => { render( , ) const fields = screen.getAllByTestId('field') expect(fields).toHaveLength(3) }) it('should render tooltip when titleTooltip is provided', () => { render( , ) // Tooltip icon should be present const tooltipIcon = screen.getByText('Test').closest('.flex')?.querySelector('svg') expect(tooltipIcon).toBeInTheDocument() }) it('should render headerRight content', () => { render( Action} />, ) expect(screen.getByTestId('header-right-btn')).toBeInTheDocument() }) }) describe('Edit Mode', () => { it('should render add metadata button when isEdit is true', () => { render( , ) expect(screen.getByTestId('add-metadata-btn')).toBeInTheDocument() }) it('should not render add metadata button when isEdit is false', () => { render( , ) expect(screen.queryByTestId('add-metadata-btn')).not.toBeInTheDocument() }) it('should render input combined for each item in edit mode', () => { render( , ) const inputs = screen.getAllByTestId('input-combined') expect(inputs).toHaveLength(3) }) it('should render delete icons in edit mode', () => { const { container } = render( , ) const deleteIcons = container.querySelectorAll('.cursor-pointer svg') expect(deleteIcons.length).toBeGreaterThan(0) }) }) describe('User Interactions', () => { it('should call onChange when input value changes', () => { const handleChange = vi.fn() render( , ) const inputs = screen.getAllByTestId('input-combined') fireEvent.change(inputs[0], { target: { value: 'New Value' } }) expect(handleChange).toHaveBeenCalled() }) it('should call onDelete when delete icon is clicked', () => { const handleDelete = vi.fn() const { container } = render( , ) // Find delete icons (RiDeleteBinLine SVGs inside cursor-pointer divs) const deleteButtons = container.querySelectorAll('svg.size-4') if (deleteButtons.length > 0) fireEvent.click(deleteButtons[0]) expect(handleDelete).toHaveBeenCalled() }) it('should call onSelect when metadata is selected', () => { const handleSelect = vi.fn() render( , ) fireEvent.click(screen.getByTestId('select-action')) expect(handleSelect).toHaveBeenCalledWith({ id: '1', name: 'test', type: DataType.string, value: null, }) }) it('should call onAdd when new metadata is saved', () => { const handleAdd = vi.fn() render( , ) fireEvent.click(screen.getByTestId('save-action')) expect(handleAdd).toHaveBeenCalledWith({ name: 'new_field', type: DataType.string, }) }) it('should navigate to documents page when manage is clicked', () => { render( , ) fireEvent.click(screen.getByTestId('manage-action')) // The onManage callback triggers the navigation expect(screen.getByTestId('manage-action')).toBeInTheDocument() }) }) describe('Props', () => { it('should apply custom className', () => { const { container } = render( , ) expect(container.firstChild).toHaveClass('custom-class') }) it('should apply contentClassName', () => { const { container } = render( , ) const contentDiv = container.querySelector('.content-custom') expect(contentDiv).toBeInTheDocument() }) it('should use uppercase title by default', () => { render( , ) const titleElement = screen.getByText('Test Title') expect(titleElement).toHaveClass('system-xs-semibold-uppercase') }) it('should not use uppercase when uppercaseTitle is false', () => { render( , ) const titleElement = screen.getByText('Test Title') expect(titleElement).toHaveClass('system-md-semibold') }) }) describe('Value Display', () => { it('should display string value directly', () => { const stringList: MetadataItemWithValue[] = [ { id: '1', name: 'field', type: DataType.string, value: 'Test Value' }, ] render( , ) expect(screen.getByText('Test Value')).toBeInTheDocument() }) it('should display number value', () => { const numberList: MetadataItemWithValue[] = [ { id: '1', name: 'field', type: DataType.number, value: 123 }, ] render( , ) expect(screen.getByText('123')).toBeInTheDocument() }) it('should format time value', () => { const timeList: MetadataItemWithValue[] = [ { id: '1', name: 'field', type: DataType.time, value: 1609459200 }, ] render( , ) // The mock formatTime returns formatted date expect(screen.getByTestId('field-content')).toBeInTheDocument() }) }) describe('Edge Cases', () => { it('should handle empty list', () => { const { container } = render( , ) expect(container.firstChild).toBeInTheDocument() }) it('should handle null value in list', () => { const nullList: MetadataItemWithValue[] = [ { id: '1', name: 'field', type: DataType.string, value: null }, ] render( , ) expect(screen.getByTestId('field')).toBeInTheDocument() }) it('should handle items with built-in id', () => { const builtInList: MetadataItemWithValue[] = [ { id: 'built-in', name: 'field', type: DataType.string, value: 'test' }, ] render( , ) expect(screen.getByTestId('field')).toBeInTheDocument() }) }) })