import { render, screen } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import React from 'react';
import { describe, expect, it, vi } from 'vitest';
import { MdCheckboxGroup } from '../MdCheckboxGroup';
const mockOptions = [
{ value: 'opt1', text: 'Option 1' },
{ value: 'opt2', text: 'Option 2' },
{ value: 'opt3', text: 'Option 3' },
];
describe('MdCheckboxGroup', () => {
describe('rendering', () => {
it('renders checkbox group', () => {
const { container } = render();
expect(container.querySelector('.md-checkboxgroup')).toBeInTheDocument();
});
it('renders all options', () => {
render();
expect(screen.getByText('Option 1')).toBeInTheDocument();
expect(screen.getByText('Option 2')).toBeInTheDocument();
expect(screen.getByText('Option 3')).toBeInTheDocument();
});
it('renders label', () => {
render();
expect(screen.getByText('Select options')).toBeInTheDocument();
});
it('renders all checkboxes', () => {
render();
const checkboxes = screen.getAllByRole('checkbox');
expect(checkboxes).toHaveLength(3);
});
it('does not render label section without label or helpText', () => {
const { container } = render();
expect(container.querySelector('.md-checkboxgroup__label-wrapper')).not.toBeInTheDocument();
});
});
describe('selected options', () => {
it('checks selected options', () => {
render();
const checkboxes = screen.getAllByRole('checkbox');
expect(checkboxes[0]).toBeChecked();
expect(checkboxes[1]).toBeChecked();
expect(checkboxes[2]).not.toBeChecked();
});
it('handles empty selectedOptions', () => {
render();
const checkboxes = screen.getAllByRole('checkbox');
checkboxes.forEach(checkbox => {
expect(checkbox).not.toBeChecked();
});
});
});
describe('directions', () => {
it('applies vertical direction', () => {
const { container } = render();
expect(container.querySelector('.md-checkboxgroup__options--vertical')).toBeInTheDocument();
});
it('applies grid direction', () => {
const { container } = render();
expect(container.querySelector('.md-checkboxgroup__options--grid')).toBeInTheDocument();
});
it('applies custom columns for grid', () => {
const { container } = render();
const optionsDiv = container.querySelector('.md-checkboxgroup__options');
expect(optionsDiv).toHaveStyle('grid-template-columns: repeat(3, minmax(max-content, 1fr))');
});
});
describe('states', () => {
it('disables all checkboxes when disabled', () => {
const { container } = render();
expect(container.querySelector('.md-checkboxgroup--disabled')).toBeInTheDocument();
});
it('disables individual options', () => {
const optionsWithDisabled = [
{ value: 'opt1', text: 'Option 1' },
{ value: 'opt2', text: 'Option 2', disabled: true },
];
render();
const checkboxes = screen.getAllByRole('checkbox');
expect(checkboxes[0]).not.toBeDisabled();
expect(checkboxes[1]).toBeDisabled();
});
it('shows error state', () => {
const { container } = render();
expect(container.querySelector('.md-checkboxgroup__error')).toBeInTheDocument();
});
it('displays error text', () => {
render();
expect(screen.getByText('Selection required')).toBeInTheDocument();
});
it('does not show error without errorText', () => {
const { container } = render();
expect(container.querySelector('.md-checkboxgroup__error')).not.toBeInTheDocument();
});
});
describe('help text', () => {
it('renders help button when helpText provided', () => {
const { container } = render(
,
);
expect(container.querySelector('.md-helpbutton')).toBeInTheDocument();
});
it('shows help text when help button is clicked', async () => {
const user = userEvent.setup();
const { container } = render(
,
);
const helpButton = container.querySelector('.md-helpbutton') as HTMLElement;
await user.click(helpButton);
expect(screen.getByText('Select at least one')).toBeInTheDocument();
});
it('uses custom helpTextFor label', () => {
const { container } = render(
,
);
const helpButton = container.querySelector('.md-helpbutton') as HTMLElement;
expect(helpButton).toHaveAttribute('aria-label', 'Help for Options');
});
});
describe('events', () => {
it('calls onChange when checkbox is clicked', async () => {
const user = userEvent.setup();
const onChange = vi.fn();
render();
const checkbox = screen.getAllByRole('checkbox')[0];
await user.click(checkbox);
expect(onChange).toHaveBeenCalled();
});
it('calls onFocus when checkbox receives focus', async () => {
const user = userEvent.setup();
const onFocus = vi.fn();
render();
const checkbox = screen.getAllByRole('checkbox')[0];
await user.click(checkbox);
expect(onFocus).toHaveBeenCalled();
});
it('calls onBlur when checkbox loses focus', async () => {
const user = userEvent.setup();
const onBlur = vi.fn();
render();
const checkboxes = screen.getAllByRole('checkbox');
await user.click(checkboxes[0]);
await user.click(checkboxes[1]);
expect(onBlur).toHaveBeenCalled();
});
it('handles missing onChange gracefully', async () => {
const user = userEvent.setup();
render();
const checkbox = screen.getAllByRole('checkbox')[0];
await user.click(checkbox);
// Should not throw
});
});
describe('accessibility', () => {
it('renders as fieldset', () => {
render();
expect(screen.getByRole('group')).toBeInTheDocument();
});
it('renders label as legend', () => {
render();
expect(screen.getByRole('group')).toContainElement(screen.getByText('Select options'));
});
it('associates help text with options', () => {
const { container } = render();
const optionsDiv = container.querySelector('.md-checkboxgroup__options');
expect(optionsDiv).toHaveAttribute('aria-describedby', expect.stringContaining('help-text'));
});
});
describe('props forwarding', () => {
it('applies custom className', () => {
const { container } = render();
expect(container.querySelector('.md-checkboxgroup')).toHaveClass('custom-class');
});
it('applies custom id', () => {
const { container } = render();
expect(container.querySelector('#custom-id')).toBeInTheDocument();
});
it('forwards other props to fieldset', () => {
render();
expect(screen.getByTestId('checkbox-group')).toBeInTheDocument();
});
});
});