import { render, screen, fireEvent, waitFor } from '@testing-library/react'
import { StageTransitionModal } from '../StageTransitionModal'
const stages = [
{ id: 'prospect', label: 'Prospect' },
{ id: 'qualified', label: 'Qualified' },
{ id: 'proposal', label: 'Proposal' },
{ id: 'closed', label: 'Closed Won' },
]
const baseProps = {
open: true,
onClose: jest.fn(),
onConfirm: jest.fn(),
currentStage: 'prospect',
stages,
}
describe('StageTransitionModal', () => {
beforeEach(() => {
jest.clearAllMocks()
})
it('renders nothing when open is false', () => {
render()
expect(screen.queryByText(/move/i)).not.toBeInTheDocument()
})
it('renders dialog when open is true', () => {
render()
expect(screen.getByText('Move Item to Stage')).toBeInTheDocument()
})
it('includes entityName in dialog title when provided', () => {
render()
expect(screen.getByText('Move "ACME Deal" to Stage')).toBeInTheDocument()
})
it('shows the current stage label', () => {
render()
expect(screen.getByText('Prospect')).toBeInTheDocument()
})
it('excludes the current stage from the new stage dropdown', () => {
render()
const options = screen.getAllByRole('option')
const optionValues = options.map((o) => (o as HTMLOptionElement).value)
expect(optionValues).not.toContain('prospect')
expect(optionValues).toContain('qualified')
expect(optionValues).toContain('proposal')
expect(optionValues).toContain('closed')
})
it('defaults to the first non-current stage', () => {
render()
const select = screen.getByLabelText('New Stage') as HTMLSelectElement
expect(select.value).toBe('qualified')
})
it('calls onConfirm with selected stage when Confirm is clicked', () => {
const onConfirm = jest.fn()
render()
fireEvent.click(screen.getByRole('button', { name: /confirm/i }))
expect(onConfirm).toHaveBeenCalledWith('qualified', undefined)
})
it('passes notes to onConfirm when notes are entered', () => {
const onConfirm = jest.fn()
render()
fireEvent.change(screen.getByLabelText(/notes/i), {
target: { value: 'Moving due to demo completed' },
})
fireEvent.click(screen.getByRole('button', { name: /confirm/i }))
expect(onConfirm).toHaveBeenCalledWith('qualified', 'Moving due to demo completed')
})
it('passes undefined for whitespace-only notes', () => {
const onConfirm = jest.fn()
render()
fireEvent.change(screen.getByLabelText(/notes/i), { target: { value: ' ' } })
fireEvent.click(screen.getByRole('button', { name: /confirm/i }))
expect(onConfirm).toHaveBeenCalledWith('qualified', undefined)
})
it('calls onClose when Cancel is clicked', () => {
const onClose = jest.fn()
render()
fireEvent.click(screen.getByRole('button', { name: /cancel/i }))
expect(onClose).toHaveBeenCalledTimes(1)
})
it('calls onClose when backdrop is clicked', () => {
const onClose = jest.fn()
const { container } = render(
)
const backdrop = container.querySelector('[aria-hidden="true"]')
fireEvent.click(backdrop!)
expect(onClose).toHaveBeenCalledTimes(1)
})
it('does not call onClose when backdrop clicked while submitting', () => {
const onClose = jest.fn()
const { container } = render(
)
const backdrop = container.querySelector('[aria-hidden="true"]')
fireEvent.click(backdrop!)
expect(onClose).not.toHaveBeenCalled()
})
it('shows Moving... when isSubmitting', () => {
render()
expect(screen.getByText('Moving...')).toBeInTheDocument()
})
it('disables select and textarea when isSubmitting', () => {
render()
expect(screen.getByLabelText('New Stage')).toBeDisabled()
expect(screen.getByLabelText(/notes/i)).toBeDisabled()
})
it('disables Cancel when isSubmitting', () => {
render()
expect(screen.getByRole('button', { name: /cancel/i })).toBeDisabled()
})
it('Confirm button is disabled while submitting', () => {
render()
expect(screen.getByText('Moving...').closest('button')).toBeDisabled()
})
it('resets notes when dialog reopens', () => {
const { rerender } = render(
)
rerender()
expect(screen.getByLabelText(/notes/i)).toHaveValue('')
})
it('allows changing the target stage via the select', () => {
render()
const select = screen.getByLabelText('New Stage') as HTMLSelectElement
fireEvent.change(select, { target: { value: 'closed' } })
expect(select.value).toBe('closed')
})
it('calls onConfirm with the newly selected stage', () => {
const onConfirm = jest.fn()
render()
fireEvent.change(screen.getByLabelText('New Stage'), { target: { value: 'closed' } })
fireEvent.click(screen.getByRole('button', { name: /confirm/i }))
expect(onConfirm).toHaveBeenCalledWith('closed', undefined)
})
it('renders Current Stage label', () => {
render()
expect(screen.getByText('Current Stage')).toBeInTheDocument()
})
it('renders New Stage label', () => {
render()
expect(screen.getByText('New Stage')).toBeInTheDocument()
})
})