import { describe, test, expect, vi } from 'vitest'
import { render, screen, fireEvent } from '@testing-library/react'
import { Options } from './options'
describe('Options', () => {
test('renders options button with default label', () => {
const options = [
{
label: 'Option 1',
onClick: vi.fn(),
},
]
render()
expect(screen.getByLabelText('Options')).toBeTruthy()
})
test('renders options button with custom label', () => {
const options = [
{
label: 'Option 1',
onClick: vi.fn(),
},
]
const labels = {
title: 'Custom Options Label',
}
render()
expect(screen.getByLabelText('Custom Options Label')).toBeTruthy()
})
test('renders when options array is empty', () => {
render()
// Component still renders button even with empty options
// (In practice, WrapperUI doesn't render Options when options.length === 0)
expect(screen.getByLabelText('Options')).toBeTruthy()
})
test('renders when options is undefined', () => {
render()
// Component still renders button even with undefined options
// (In practice, WrapperUI doesn't render Options when options.length === 0)
expect(screen.getByLabelText('Options')).toBeTruthy()
})
test('opens menu when button is clicked', () => {
const options = [
{
label: 'Option 1',
onClick: vi.fn(),
},
]
render()
const button = screen.getByLabelText('Options')
fireEvent.click(button)
expect(screen.getByText('Option 1')).toBeTruthy()
})
test('closes menu when option is clicked', () => {
const onClick = vi.fn()
const options = [
{
label: 'Option 1',
onClick,
},
]
render()
const button = screen.getByLabelText('Options')
fireEvent.click(button)
const menuItem = screen.getByText('Option 1')
fireEvent.click(menuItem)
expect(onClick).toHaveBeenCalledTimes(1)
// Menu should be closed after clicking
expect(screen.queryByRole('menu')).toBeNull()
})
test('stops event propagation when opening menu', () => {
const parentOnClick = vi.fn()
const options = [
{
label: 'Option 1',
onClick: vi.fn(),
},
]
render(
,
)
const button = screen.getByLabelText('Options')
fireEvent.click(button)
// Parent onClick should not be called
expect(parentOnClick).not.toHaveBeenCalled()
})
test('stops event propagation when clicking option', () => {
const parentOnClick = vi.fn()
const onClick = vi.fn()
const options = [
{
label: 'Option 1',
onClick,
},
]
render(
,
)
const button = screen.getByLabelText('Options')
fireEvent.click(button)
const menuItem = screen.getByText('Option 1')
fireEvent.click(menuItem)
expect(onClick).toHaveBeenCalledTimes(1)
expect(parentOnClick).not.toHaveBeenCalled()
})
test('renders multiple options', () => {
const options = [
{
label: 'Option 1',
onClick: vi.fn(),
},
{
label: 'Option 2',
onClick: vi.fn(),
},
{
label: 'Option 3',
onClick: vi.fn(),
},
]
render()
const button = screen.getByLabelText('Options')
fireEvent.click(button)
expect(screen.getByText('Option 1')).toBeTruthy()
expect(screen.getByText('Option 2')).toBeTruthy()
expect(screen.getByText('Option 3')).toBeTruthy()
})
test('renders disabled option', () => {
const options = [
{
label: 'Disabled Option',
disabled: true,
onClick: vi.fn(),
},
]
render()
const button = screen.getByLabelText('Options')
fireEvent.click(button)
const menuItem = screen.getByText('Disabled Option').closest('li')
expect(menuItem?.classList.contains('Mui-disabled')).toBe(true)
})
test('does not call onClick for disabled option', () => {
const onClick = vi.fn()
const options = [
{
label: 'Enabled Option',
onClick,
},
{
label: 'Disabled Option',
disabled: true,
onClick,
},
]
render()
const button = screen.getByLabelText('Options')
fireEvent.click(button)
// Click on enabled option first
const enabled = screen.getByText('Enabled Option')
fireEvent.click(enabled)
// onClick should be called once for enabled
expect(onClick).toHaveBeenCalledTimes(1)
// Open menu again and try clicking disabled
fireEvent.click(button)
const disabled = screen.getByText('Disabled Option').closest('li')
// Disabled should have the class
expect(disabled?.classList.contains('Mui-disabled')).toBe(true)
})
test('renders option with icon', () => {
const icon = Icon
const options = [
{
label: 'Option with Icon',
icon,
onClick: vi.fn(),
},
]
render()
const button = screen.getByLabelText('Options')
fireEvent.click(button)
expect(screen.getByTestId('test-icon')).toBeTruthy()
expect(screen.getByText('Option with Icon')).toBeTruthy()
})
test('renders option without icon', () => {
const options = [
{
label: 'Option without Icon',
onClick: vi.fn(),
},
]
render()
const button = screen.getByLabelText('Options')
fireEvent.click(button)
expect(screen.getByText('Option without Icon')).toBeTruthy()
})
test('closes menu when clicking outside', () => {
const options = [
{
label: 'Option 1',
onClick: vi.fn(),
},
]
render()
const button = screen.getByLabelText('Options')
fireEvent.click(button)
// Menu should be open
expect(screen.getByText('Option 1')).toBeTruthy()
// Click outside (on the backdrop)
const backdrop = document.querySelector('.MuiBackdrop-root')
if (backdrop) {
fireEvent.click(backdrop)
}
// Menu should be closed
expect(screen.queryByRole('menu')).toBeNull()
})
test('handles multiple option clicks correctly', () => {
const onClick1 = vi.fn()
const onClick2 = vi.fn()
const options = [
{
label: 'Option 1',
onClick: onClick1,
},
{
label: 'Option 2',
onClick: onClick2,
},
]
render()
// Open menu and click first option
const button = screen.getByLabelText('Options')
fireEvent.click(button)
fireEvent.click(screen.getByText('Option 1'))
expect(onClick1).toHaveBeenCalledTimes(1)
expect(onClick2).not.toHaveBeenCalled()
// Open menu again and click second option
fireEvent.click(button)
fireEvent.click(screen.getByText('Option 2'))
expect(onClick1).toHaveBeenCalledTimes(1)
expect(onClick2).toHaveBeenCalledTimes(1)
})
test('renders MoreVert icon', () => {
const options = [
{
label: 'Option 1',
onClick: vi.fn(),
},
]
render()
// Check that the button is rendered
const button = screen.getByLabelText('Options')
expect(button).toBeTruthy()
})
test('menu has correct attributes', () => {
const options = [
{
label: 'Option 1',
onClick: vi.fn(),
},
]
render()
const button = screen.getByLabelText('Options')
expect(button.getAttribute('aria-label')).toBe('Options')
expect(button.getAttribute('aria-controls')).toBe('options-menu')
expect(button.getAttribute('aria-haspopup')).toBe('true')
})
})