import { describe, it, expect, vi, beforeEach } from 'vitest'
import { render, screen, fireEvent } from '@testing-library/react'
import { EditableValue, TagPillList } from './EditableValue'
describe('EditableValue', () => {
const onSave = vi.fn()
const onCancel = vi.fn()
const onStartEdit = vi.fn()
beforeEach(() => {
vi.clearAllMocks()
})
it('displays value text in view mode', () => {
render()
expect(screen.getByText('Active')).toBeInTheDocument()
})
it('displays em-dash when value is empty', () => {
render()
expect(screen.getByText('\u2014')).toBeInTheDocument()
})
it('calls onStartEdit when clicked in view mode', () => {
render()
fireEvent.click(screen.getByText('Active'))
expect(onStartEdit).toHaveBeenCalled()
})
it('shows input in editing mode', () => {
render()
const input = screen.getByDisplayValue('Active')
expect(input).toBeInTheDocument()
expect(input.tagName).toBe('INPUT')
})
it('calls onSave when Enter is pressed', () => {
render()
const input = screen.getByDisplayValue('Active')
fireEvent.change(input, { target: { value: 'Done' } })
fireEvent.keyDown(input, { key: 'Enter' })
expect(onSave).toHaveBeenCalledWith('Done')
})
it('calls onCancel and resets when Escape is pressed', () => {
render()
const input = screen.getByDisplayValue('Active')
fireEvent.change(input, { target: { value: 'Changed' } })
fireEvent.keyDown(input, { key: 'Escape' })
expect(onCancel).toHaveBeenCalled()
})
it('calls onSave on blur', () => {
render()
const input = screen.getByDisplayValue('Active')
fireEvent.change(input, { target: { value: 'Paused' } })
fireEvent.blur(input)
expect(onSave).toHaveBeenCalledWith('Paused')
})
})
describe('TagPillList', () => {
const onSave = vi.fn()
beforeEach(() => {
vi.clearAllMocks()
})
it('renders all items as pills', () => {
render()
expect(screen.getByText('Tag1')).toBeInTheDocument()
expect(screen.getByText('Tag2')).toBeInTheDocument()
expect(screen.getByText('Tag3')).toBeInTheDocument()
})
it('shows add button with + text', () => {
render()
expect(screen.getByTitle('Add tags')).toBeInTheDocument()
})
it('shows add input when + button is clicked', () => {
render()
fireEvent.click(screen.getByTitle('Add tags'))
expect(screen.getByPlaceholderText('Tags...')).toBeInTheDocument()
})
it('adds new item on Enter in add input', () => {
render()
fireEvent.click(screen.getByTitle('Add tags'))
const input = screen.getByPlaceholderText('Tags...')
fireEvent.change(input, { target: { value: 'NewTag' } })
fireEvent.keyDown(input, { key: 'Enter' })
expect(onSave).toHaveBeenCalledWith(['A', 'NewTag'])
})
it('cancels add on Escape', () => {
render()
fireEvent.click(screen.getByTitle('Add tags'))
const input = screen.getByPlaceholderText('Tags...')
fireEvent.keyDown(input, { key: 'Escape' })
expect(onSave).not.toHaveBeenCalled()
// Add button should reappear
expect(screen.getByTitle('Add tags')).toBeInTheDocument()
})
it('deletes item when X button is clicked', () => {
render()
const removeButtons = screen.getAllByTitle('Remove')
fireEvent.click(removeButtons[1]) // Remove B
expect(onSave).toHaveBeenCalledWith(['A', 'C'])
})
it('enters edit mode when clicking a pill', () => {
render()
fireEvent.click(screen.getByText('Hello'))
expect(screen.getByDisplayValue('Hello')).toBeInTheDocument()
})
it('saves edit on Enter', () => {
render()
fireEvent.click(screen.getByText('Old'))
const input = screen.getByDisplayValue('Old')
fireEvent.change(input, { target: { value: 'New' } })
fireEvent.keyDown(input, { key: 'Enter' })
expect(onSave).toHaveBeenCalledWith(['New'])
})
it('removes item when edit value is cleared', () => {
render()
fireEvent.click(screen.getByText('Remove'))
const input = screen.getByDisplayValue('Remove')
fireEvent.change(input, { target: { value: '' } })
fireEvent.keyDown(input, { key: 'Enter' })
expect(onSave).toHaveBeenCalledWith(['Keep'])
})
it('cancels edit on Escape', () => {
render()
fireEvent.click(screen.getByText('Stay'))
const input = screen.getByDisplayValue('Stay')
fireEvent.keyDown(input, { key: 'Escape' })
expect(onSave).not.toHaveBeenCalled()
// Pill should reappear
expect(screen.getByText('Stay')).toBeInTheDocument()
})
it('adds item on blur when input has value', () => {
render()
fireEvent.click(screen.getByTitle('Add tags'))
const input = screen.getByPlaceholderText('Tags...')
fireEvent.change(input, { target: { value: 'BlurAdd' } })
fireEvent.blur(input)
expect(onSave).toHaveBeenCalledWith(['BlurAdd'])
})
it('cancels add on blur when input is empty', () => {
render()
fireEvent.click(screen.getByTitle('Add tags'))
const input = screen.getByPlaceholderText('Tags...')
fireEvent.blur(input)
expect(onSave).not.toHaveBeenCalled()
})
})