import React, { useState } from 'react'; import { render, screen, waitFor } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; import { getWrapper } from '../../testUtils'; import { RadioGroup, RadioOption, } from './RadioGroup.component'; const renderInWrapper = (ui: React.ReactElement) => { const { Wrapper } = getWrapper(); return render({ui}); }; const defaultOptions: RadioOption[] = [ { value: 'governance', label: 'Governance' }, { value: 'compliance', label: 'Compliance' }, { value: 'none', label: 'None' }, ]; const ControlledGroup = ({ initial = 'governance', options = defaultOptions, onChange, ...rest }: { initial?: string; options?: RadioOption[]; label?: string; 'aria-label'?: string; 'aria-labelledby'?: string; disabled?: boolean; direction?: 'vertical' | 'horizontal'; onChange?: (v: string) => void; }) => { const [value, setValue] = useState(initial); const handleChange = (v: string) => { setValue(v); onChange?.(v); }; return ( // @ts-expect-error — union narrowing at the test boundary ); }; describe('RadioGroup', () => { it('should show a visible group label when one is provided', () => { renderInWrapper(); const group = screen.getByRole('radiogroup', { name: 'Retention mode' }); expect(group.tagName).toBe('FIELDSET'); expect(screen.getByText('Retention mode').tagName).toBe('LEGEND'); }); it('should expose an accessible group name when no visible label is used', () => { renderInWrapper(); const group = screen.getByRole('radiogroup', { name: 'Inline selection' }); expect(group.tagName).toBe('DIV'); }); it('should inherit its accessible name from a label rendered outside the group', () => { renderInWrapper( <> Retention mode , ); expect( screen.getByRole('radiogroup', { name: 'Retention mode' }), ).toBeInTheDocument(); }); it('should show the selected option as checked', () => { renderInWrapper( , ); expect(screen.getByLabelText('Governance')).not.toBeChecked(); expect(screen.getByLabelText('Compliance')).toBeChecked(); expect(screen.getByLabelText('None')).not.toBeChecked(); }); it('should notify the parent when the user picks an option', async () => { const onChange = jest.fn(); renderInWrapper( , ); await userEvent.click(screen.getByLabelText('None')); expect(onChange).toHaveBeenCalledWith('none'); expect(screen.getByLabelText('None')).toBeChecked(); expect(screen.getByLabelText('Governance')).not.toBeChecked(); }); it('should let the user pick only one option at a time', async () => { renderInWrapper(); await userEvent.click(screen.getByLabelText('Compliance')); expect(screen.getByLabelText('Compliance')).toBeChecked(); expect(screen.getByLabelText('Governance')).not.toBeChecked(); expect(screen.getByLabelText('None')).not.toBeChecked(); }); it('should disable every option when the whole group is disabled', () => { renderInWrapper(); expect(screen.getByLabelText('Governance')).toBeDisabled(); expect(screen.getByLabelText('Compliance')).toBeDisabled(); expect(screen.getByLabelText('None')).toBeDisabled(); }); it('should disable a single option while leaving the others interactive', () => { renderInWrapper( , ); expect(screen.getByLabelText('Governance')).not.toBeDisabled(); expect(screen.getByLabelText('Compliance')).toBeDisabled(); expect(screen.getByLabelText('None')).not.toBeDisabled(); }); it('should group all options under the same name for keyboard navigation', () => { renderInWrapper(); expect(screen.getByLabelText('Governance')).toHaveAttribute( 'name', 'retention-mode', ); expect(screen.getByLabelText('Compliance')).toHaveAttribute( 'name', 'retention-mode', ); expect(screen.getByLabelText('None')).toHaveAttribute( 'name', 'retention-mode', ); }); it('should show a tooltip on a disabled option when a reason is provided', async () => { renderInWrapper( , ); await userEvent.hover(screen.getByLabelText('Compliance')); await waitFor(() => { expect( screen.getByText('Requires an upgraded license.'), ).toBeInTheDocument(); }); }); });