import { describe, expect, test, vi, beforeEach } from 'vitest'; import { render, screen, fireEvent } from '@testing-library/react'; import { MantineProvider } from '@mantine/core'; import ButtonSelector from './ButtonSelector'; // Wrapper component to provide Mantine context const TestWrapper = ({ children }: { children: React.ReactNode }) => ( {children} ); describe('ButtonSelector', () => { const mockOnChange = vi.fn(); beforeEach(() => { vi.clearAllMocks(); }); test('should render all options as buttons', () => { const options = [ { value: 'option1', label: 'Option 1' }, { value: 'option2', label: 'Option 2' }, ]; render( ); expect(screen.getByText('Test Label')).toBeInTheDocument(); expect(screen.getByText('Option 1')).toBeInTheDocument(); expect(screen.getByText('Option 2')).toBeInTheDocument(); }); test('should highlight selected button with filled variant', () => { const options = [ { value: 'option1', label: 'Option 1' }, { value: 'option2', label: 'Option 2' }, ]; render( ); const selectedButton = screen.getByRole('button', { name: 'Option 1' }); const unselectedButton = screen.getByRole('button', { name: 'Option 2' }); // Check data-variant attribute for filled/outline expect(selectedButton).toHaveAttribute('data-variant', 'filled'); expect(unselectedButton).toHaveAttribute('data-variant', 'outline'); expect(screen.getByText('Selection Label')).toBeInTheDocument(); }); test('should call onChange when button is clicked', () => { const options = [ { value: 'option1', label: 'Option 1' }, { value: 'option2', label: 'Option 2' }, ]; render( ); fireEvent.click(screen.getByRole('button', { name: 'Option 2' })); expect(mockOnChange).toHaveBeenCalledWith('option2'); }); test('should handle undefined value (no selection)', () => { const options = [ { value: 'option1', label: 'Option 1' }, { value: 'option2', label: 'Option 2' }, ]; render( ); // Both buttons should be outlined when no value is selected const button1 = screen.getByRole('button', { name: 'Option 1' }); const button2 = screen.getByRole('button', { name: 'Option 2' }); expect(button1).toHaveAttribute('data-variant', 'outline'); expect(button2).toHaveAttribute('data-variant', 'outline'); }); test.each([ { description: 'disable buttons when disabled prop is true', options: [ { value: 'option1', label: 'Option 1' }, { value: 'option2', label: 'Option 2' }, ], globalDisabled: true, expectedStates: [true, true], }, { description: 'disable individual options when option.disabled is true', options: [ { value: 'option1', label: 'Option 1' }, { value: 'option2', label: 'Option 2', disabled: true }, ], globalDisabled: false, expectedStates: [false, true], }, ])('should $description', ({ options, globalDisabled, expectedStates }) => { render( ); options.forEach((option, index) => { const button = screen.getByRole('button', { name: option.label }); expect(button).toHaveProperty('disabled', expectedStates[index]); }); }); test('should not call onChange when disabled button is clicked', () => { const options = [ { value: 'option1', label: 'Option 1' }, { value: 'option2', label: 'Option 2', disabled: true }, ]; render( ); fireEvent.click(screen.getByRole('button', { name: 'Option 2' })); expect(mockOnChange).not.toHaveBeenCalled(); }); test('should not apply fullWidth styling when fullWidth is false', () => { const options = [ { value: 'option1', label: 'Option 1' }, { value: 'option2', label: 'Option 2' }, ]; render( ); const button = screen.getByRole('button', { name: 'Option 1' }); expect(button).not.toHaveStyle({ flex: '1' }); expect(screen.getByText('Layout Label')).toBeInTheDocument(); }); test('should not render label element when not provided', () => { const options = [ { value: 'option1', label: 'Option 1' }, { value: 'option2', label: 'Option 2' }, ]; const { container } = render( ); // Should render buttons expect(screen.getByText('Option 1')).toBeInTheDocument(); expect(screen.getByText('Option 2')).toBeInTheDocument(); // Stack should only contain the Group (buttons), no Text element for label const stackElement = container.querySelector('[class*="mantine-Stack-root"]'); expect(stackElement?.children).toHaveLength(1); // Only the Group, no label Text }); });