import { fireEvent, render, screen } from '@testing-library/react';
import { describe, expect, it } from 'vitest';
import { RadioGroup } from './RadioGroup';
describe('RadioGroup', () => {
it('renders radio items', () => {
render(
,
);
expect(screen.getByRole('radio', { name: 'Option A' })).toBeInTheDocument();
expect(screen.getByRole('radio', { name: 'Option B' })).toBeInTheDocument();
});
it('supports compact item sizing', () => {
render(
,
);
expect(screen.getByRole('radio', { name: 'Compact' })).toHaveAttribute(
'data-size',
'sm',
);
});
it('selects defaultValue on mount', () => {
render(
,
);
expect(screen.getByRole('radio', { name: 'Option B' })).toHaveAttribute(
'aria-checked',
'true',
);
expect(screen.getByRole('radio', { name: 'Option A' })).toHaveAttribute(
'aria-checked',
'false',
);
});
it('selecting one item deselects the others', () => {
render(
,
);
const radioB = screen.getByRole('radio', { name: 'Option B' });
fireEvent.click(radioB);
expect(radioB).toHaveAttribute('aria-checked', 'true');
expect(screen.getByRole('radio', { name: 'Option A' })).toHaveAttribute(
'aria-checked',
'false',
);
});
it('clicking the label selects its item', () => {
render(
,
);
const label = screen.getByText('Option A');
const radio = screen.getByRole('radio', { name: 'Option A' });
expect(radio).toHaveAttribute('aria-checked', 'false');
fireEvent.click(label);
expect(radio).toHaveAttribute('aria-checked', 'true');
});
it('disabled item is not interactive', () => {
render(
,
);
const radio = screen.getByRole('radio', { name: 'Disabled' });
expect(radio).toHaveAttribute('aria-disabled', 'true');
fireEvent.click(radio);
expect(radio).toHaveAttribute('aria-checked', 'false');
});
it('mutes the label when its item is disabled', () => {
render(
,
);
expect(
screen.getByText('Disabled').closest('[data-slot="radio-label"]'),
).toHaveClass('text-muted-foreground');
expect(
screen.getByText('Enabled').closest('[data-slot="radio-label"]'),
).not.toHaveClass('text-muted-foreground');
});
it('supports arrow-key roving focus between items', () => {
render(
,
);
// All radios in a group are reachable via keyboard.
const radios = screen.getAllByRole('radio');
expect(radios).toHaveLength(3);
// The group element exists and has roving-tabindex structure (tabIndex 0 on selected).
const radioA = screen.getByRole('radio', { name: 'Option A' });
expect(radioA).toHaveAttribute('aria-checked', 'true');
// Click directly to simulate selection change (roving focus is a browser behavior).
fireEvent.click(screen.getByRole('radio', { name: 'Option C' }));
expect(screen.getByRole('radio', { name: 'Option C' })).toHaveAttribute(
'aria-checked',
'true',
);
expect(radioA).toHaveAttribute('aria-checked', 'false');
});
it('disabled unlabeled item is not interactive', () => {
render(
,
);
const radio = screen.getByRole('radio', { name: 'Hidden' });
expect(radio).toHaveAttribute('aria-disabled', 'true');
fireEvent.click(radio);
expect(radio).toHaveAttribute('aria-checked', 'false');
});
it('renders compound RadioGroup.Item.Label', () => {
render(
Custom label
,
);
expect(screen.getByText('Custom label')).toHaveAttribute(
'data-slot',
'radio-label',
);
});
});