import React from 'react';
import { render, screen, fireEvent, waitFor } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
jest.mock('lucide-react', () => ({
CheckIcon: () =>
,
ChevronDownIcon: () => ,
ChevronUpIcon: () => ,
}));
jest.mock('@radix-ui/react-select', () => ({
Root: ({ children, ...props }: any) => (
{children}
),
Trigger: ({ children, className, ...props }: any) => (
),
Content: ({ children, ...props }: any) => (
{children}
),
Viewport: ({ children, ...props }: any) => (
{children}
),
Item: ({ children, ...props }: any) => (
{children}
),
ItemText: ({ children, ...props }: any) => (
{children}
),
ItemIndicator: ({ children, ...props }: any) => (
{children}
),
Group: ({ children, ...props }: any) => (
{children}
),
Label: ({ children, ...props }: any) => (
{children}
),
Separator: ({ ...props }: any) => (
),
ScrollUpButton: ({ children, ...props }: any) => (
),
ScrollDownButton: ({ children, ...props }: any) => (
),
Icon: ({ children, ...props }: any) => (
{children}
),
Value: ({ placeholder, children, ...props }: any) => (
{children || placeholder}
),
Portal: ({ children, ...props }: any) => (
{children}
),
__esModule: true,
}));
import {
Select,
SelectContent,
SelectGroup,
SelectItem,
SelectLabel,
SelectScrollDownButton,
SelectScrollUpButton,
SelectSeparator,
SelectTrigger,
SelectValue,
} from '../../../../src/renderer/components/ui/select';
describe('Select Components', () => {
const mockOnValueChange = jest.fn();
const mockOnOpenChange = jest.fn();
describe('Select Root', () => {
test('should render with default props', () => {
render(
);
expect(screen.getByTestId('radix-select-root')).toBeInTheDocument();
expect(screen.getByTestId('radix-select-trigger')).toBeInTheDocument();
expect(screen.getByTestId('radix-select-content')).toBeInTheDocument();
});
test('should handle value changes', async () => {
const user = userEvent.setup();
render(
);
const trigger = screen.getByTestId('radix-select-trigger');
await user.click(trigger);
const item = screen.getByTestId('radix-select-item');
await user.click(item);
expect(screen.getByTestId('radix-select-root')).toHaveAttribute(
'value',
'option1'
);
});
test('should handle open state changes', () => {
render(
);
expect(screen.getByTestId('radix-select-root')).toHaveAttribute(
'open',
'true'
);
});
});
describe('SelectTrigger', () => {
test('should render with default size', () => {
render(
);
const trigger = screen.getByTestId('radix-select-trigger');
expect(trigger).toHaveAttribute('data-size', 'default');
expect(trigger).toHaveClass('data-[size=default]:h-9');
});
test('should render with small size', () => {
render(
);
const trigger = screen.getByTestId('radix-select-trigger');
expect(trigger).toHaveAttribute('data-size', 'sm');
expect(trigger).toHaveClass('data-[size=sm]:h-8');
});
test('should apply custom className', () => {
render(
);
const trigger = screen.getByTestId('radix-select-trigger');
expect(trigger).toHaveClass('custom-class');
});
test('should render chevron icon', () => {
render(
);
expect(screen.getByTestId('chevron-down-icon')).toBeInTheDocument();
});
test('should forward additional props', () => {
render(
);
const trigger = screen.getByTestId('custom-trigger');
expect(trigger).toBeDisabled();
});
});
describe('SelectValue', () => {
test('should render placeholder when no value is selected', () => {
render(
);
expect(screen.getByTestId('radix-select-value')).toBeInTheDocument();
});
test('should render children when provided', () => {
render(
);
expect(screen.getByText('Custom content')).toBeInTheDocument();
});
test('should handle placeholder attribute', () => {
render(
);
const trigger = screen.getByTestId('radix-select-trigger');
expect(trigger).toHaveAttribute('data-placeholder', 'true');
});
});
describe('SelectContent', () => {
test('should render content when open', () => {
render(
);
expect(screen.getByTestId('radix-select-content')).toBeInTheDocument();
});
test('should not render content when closed', () => {
render(
);
expect(
screen.queryByTestId('radix-select-content')
).not.toBeInTheDocument();
});
test('should forward props to content', () => {
render(
);
const content = screen.getByTestId('radix-select-content');
expect(content).toHaveClass('custom-content');
expect(content).toHaveAttribute('position', 'popper');
});
});
describe('SelectItem', () => {
test('should render item with value and text', () => {
render(
);
expect(screen.getByTestId('radix-select-item')).toHaveAttribute(
'value',
'option1'
);
expect(screen.getByTestId('radix-select-item-text')).toHaveTextContent(
'Option 1'
);
});
test('should render check icon for selected item', () => {
render(
);
expect(screen.getByTestId('check-icon')).toBeInTheDocument();
});
test('should handle disabled state', () => {
render(
);
const item = screen.getByTestId('radix-select-item');
expect(item).toHaveAttribute('disabled', '');
});
});
describe('SelectGroup', () => {
test('should render group with items', () => {
render(
);
expect(screen.getByTestId('radix-select-group')).toBeInTheDocument();
expect(screen.getByTestId('radix-select-label')).toHaveTextContent(
'Group 1'
);
expect(screen.getAllByTestId('radix-select-item')).toHaveLength(2);
});
});
describe('SelectLabel', () => {
test('should render label', () => {
render(
);
expect(screen.getByTestId('radix-select-label')).toHaveTextContent(
'Choose an option'
);
});
});
describe('SelectSeparator', () => {
test('should render separator', () => {
render(
);
expect(screen.getByTestId('radix-select-separator')).toBeInTheDocument();
});
});
describe('SelectScrollButtons', () => {
test('should render scroll buttons', () => {
render(
);
expect(screen.getByTestId('radix-select-scroll-up')).toBeInTheDocument();
expect(
screen.getByTestId('radix-select-scroll-down')
).toBeInTheDocument();
});
test('should render chevron icons in scroll buttons', () => {
render(
);
expect(screen.getByTestId('chevron-up-icon')).toBeInTheDocument();
expect(screen.getByTestId('chevron-down-icon')).toBeInTheDocument();
});
});
describe('Integration Tests', () => {
test('should handle complete select workflow', async () => {
const user = userEvent.setup();
const onValueChange = jest.fn();
render(
);
expect(screen.getByTestId('radix-select-trigger')).toBeInTheDocument();
expect(
screen.queryByTestId('radix-select-content')
).not.toBeInTheDocument();
expect(screen.getByTestId('radix-select-root')).toBeInTheDocument();
});
test('should handle complex select with multiple groups and separators', () => {
render(
);
expect(screen.getAllByTestId('radix-select-group')).toHaveLength(3);
expect(screen.getAllByTestId('radix-select-separator')).toHaveLength(2);
expect(screen.getAllByTestId('radix-select-label')).toHaveLength(3);
expect(screen.getAllByTestId('radix-select-item')).toHaveLength(6);
});
});
describe('Accessibility', () => {
test('should have proper ARIA attributes', () => {
render(
);
const trigger = screen.getByTestId('radix-select-trigger');
expect(trigger).toHaveAttribute('aria-label', 'Select option');
});
test('should support disabled state', () => {
render(
);
const trigger = screen.getByTestId('radix-select-trigger');
expect(trigger).toBeDisabled();
});
test('should handle required state', () => {
render(
);
const trigger = screen.getByTestId('radix-select-trigger');
expect(trigger).toHaveAttribute('aria-required', 'true');
});
});
describe('Styling and Classes', () => {
test('should apply default styling classes', () => {
render(
);
const trigger = screen.getByTestId('radix-select-trigger');
expect(mockCn).toHaveBeenCalledWith(
expect.stringContaining('border-input'),
expect.stringContaining('focus-visible:border-ring'),
expect.stringContaining('data-[size=default]:h-9'),
expect.anything()
);
});
test('should apply size-specific classes', () => {
render(
);
const trigger = screen.getByTestId('radix-select-trigger');
expect(mockCn).toHaveBeenCalledWith(
expect.stringContaining('data-[size=sm]:h-8'),
expect.anything()
);
});
test('should merge custom classes with default classes', () => {
mockCn.mockClear();
render(
);
expect(mockCn).toHaveBeenCalled();
const callArgs = mockCn.mock.calls[0];
expect(callArgs).toContain('custom-class');
expect(callArgs.some((arg) => arg && arg.includes('border-input'))).toBe(
true
);
});
});
describe('Error States', () => {
test('should handle invalid aria state', () => {
render(
);
const trigger = screen.getByTestId('radix-select-trigger');
expect(trigger).toHaveAttribute('aria-invalid', 'true');
});
test('should apply error styling for invalid state', () => {
mockCn.mockClear();
render(
);
expect(mockCn).toHaveBeenCalledWith(
expect.stringContaining('aria-invalid:ring-destructive/20'),
expect.stringContaining('dark:aria-invalid:ring-destructive/40'),
expect.stringContaining('aria-invalid:border-destructive'),
expect.anything()
);
});
});
describe('Data Attributes', () => {
test('should apply proper data attributes', () => {
render(
);
const trigger = screen.getByTestId('radix-select-trigger');
expect(trigger).toHaveAttribute('data-slot', 'select-trigger');
expect(trigger).toHaveAttribute('data-size', 'default');
});
test('should handle placeholder data attribute', () => {
render(
);
const trigger = screen.getByTestId('radix-select-trigger');
expect(trigger).toHaveAttribute('data-placeholder', '');
});
});
});