import * as React from 'react';
import { render, screen } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import { Radio } from '../../Radio';
import { RadioGroupField } from '../RadioGroupField';
import { RadioGroupFieldProps } from '../../types/radioGroupField';
import { ComponentClassNames } from '../../shared';
import {
testFlexProps,
expectFlexContainerStyleProps,
} from '../../Flex/__tests__/Flex.test';
const basicProps = { label: 'testLabel', name: 'testName', testId: 'testId' };
const radioGroupTestId = `${basicProps.testId}-${ComponentClassNames.RadioGroup}`;
const RadioFieldGroup = ({ label, name, ...props }: RadioGroupFieldProps) => {
return (
html
css
javascript
);
};
describe('RadioFieldGroup', () => {
it('should render default and custom classname', async () => {
const className = 'class-test';
render(RadioFieldGroup({ ...basicProps, className }));
const radioField = await screen.findByTestId(basicProps.testId);
expect(radioField).toHaveClass(
ComponentClassNames.Field,
ComponentClassNames.RadioGroupField,
className
);
});
it('should forward ref to DOM element', async () => {
const ref = React.createRef();
render();
await screen.findByTestId(basicProps.testId);
expect(ref.current?.nodeName).toBe('FIELDSET');
});
it('should render all flex style props', async () => {
render(RadioFieldGroup({ ...basicProps, ...testFlexProps }));
const radioField = await screen.findByTestId(basicProps.testId);
expectFlexContainerStyleProps(radioField);
});
it('should have no default labelPosition', async () => {
render(RadioFieldGroup({ ...basicProps }));
const radioField = await screen.findByTestId(basicProps.testId);
expect(radioField.querySelector('.amplify-radio')).not.toHaveAttribute(
'data-label-position'
);
});
it('should work with labelPosition', async () => {
render(RadioFieldGroup({ ...basicProps, labelPosition: 'end' }));
const radioField = await screen.findByTestId(basicProps.testId);
expect(radioField.querySelector('.amplify-radio')).toHaveAttribute(
'data-label-position',
'end'
);
});
describe('Label', () => {
it('should render visually-hidden legend element with label name', async () => {
render(RadioFieldGroup({ ...basicProps }));
const labelElement = await screen.findAllByText(basicProps.label);
expect(labelElement[0].nodeName).toBe('LEGEND');
});
it('should render expected label classname', async () => {
render(RadioFieldGroup({ ...basicProps }));
const labelElement = await screen.findAllByText(basicProps.label);
expect(labelElement[1]).toHaveClass(ComponentClassNames.Label);
});
it('should have `amplify-visually-hidden` class when labelHidden is true', async () => {
render(RadioFieldGroup({ ...basicProps, labelHidden: true }));
const labelElement = await screen.findAllByText(basicProps.label);
expect(labelElement[1]).toHaveClass('amplify-visually-hidden');
});
});
describe('RadioGroup', () => {
it('should render default classname', async () => {
render(RadioFieldGroup({ ...basicProps }));
const radioGroup = await screen.findByTestId(radioGroupTestId);
expect(radioGroup).toHaveClass(ComponentClassNames.RadioGroup);
});
it('should work in uncontrolled way', async () => {
render(RadioFieldGroup({ ...basicProps, defaultValue: 'html' }));
const radios = await screen.findAllByRole('radio');
const html = radios[0];
const css = radios[1];
const javascript = radios[2];
expect(html).toBeChecked();
expect(css).not.toBeChecked();
expect(javascript).not.toBeChecked();
userEvent.click(css);
expect(html).not.toBeChecked();
expect(css).toBeChecked();
expect(javascript).not.toBeChecked();
userEvent.click(javascript);
expect(html).not.toBeChecked();
expect(css).not.toBeChecked();
expect(javascript).toBeChecked();
});
it('should work in controlled way', async () => {
const { rerender } = render(
html
css
javascript
);
const radios = await screen.findAllByRole('radio');
const html = radios[0];
const css = radios[1];
const javascript = radios[2];
expect(html).toBeChecked();
expect(css).not.toBeChecked();
expect(javascript).not.toBeChecked();
rerender(
html
css
javascript
);
// userEvent.click(css);
expect(html).not.toBeChecked();
expect(css).toBeChecked();
expect(javascript).not.toBeChecked();
rerender(
html
css
javascript
);
expect(html).not.toBeChecked();
expect(css).not.toBeChecked();
expect(javascript).toBeChecked();
});
it('should set size attribute', async () => {
render(RadioFieldGroup({ ...basicProps, size: 'large' }));
const radioField = await screen.findByTestId(basicProps.testId);
expect(radioField).toHaveAttribute('data-size', 'large');
const radioButtons = await screen.findAllByTestId('radio-button');
expect(radioButtons[0]).toHaveAttribute('data-size', 'large');
expect(radioButtons[1]).toHaveAttribute('data-size', 'large');
expect(radioButtons[2]).toHaveAttribute('data-size', 'large');
});
it('should render size classes for RadioGroupField', async () => {
render(
<>
>
);
const small = await screen.findByTestId('small');
const large = await screen.findByTestId('large');
expect(small.classList).toContain(
`${ComponentClassNames['Field']}--small`
);
expect(large.classList).toContain(
`${ComponentClassNames['Field']}--large`
);
});
it('should be disabled if isDisabled is passed', async () => {
render(RadioFieldGroup({ ...basicProps, isDisabled: true }));
const radios = await screen.findAllByRole('radio');
const html = radios[0];
const css = radios[1];
const javascript = radios[2];
expect(html).toBeDisabled();
expect(css).toBeDisabled();
expect(javascript).toBeDisabled();
});
it('should be read-only if isReadOnly is passed and defaultValue is provided', async () => {
render(
RadioFieldGroup({
...basicProps,
defaultValue: 'html',
isReadOnly: true,
})
);
const radios = await screen.findAllByRole('radio');
const html = radios[0];
const css = radios[1];
const javascript = radios[2];
expect(html).toBeChecked();
expect(css).toBeDisabled();
expect(javascript).toBeDisabled();
});
it('should be required if isRequired is passed', async () => {
render(RadioFieldGroup({ ...basicProps, isRequired: true }));
const radios = await screen.findAllByRole('radio');
const html = radios[0];
const css = radios[1];
const javascript = radios[2];
expect(html).toBeRequired();
expect(css).toBeRequired();
expect(javascript).toBeRequired();
});
it('should set aria-hidden to be true on custom radio buttons', async () => {
render(RadioFieldGroup({ ...basicProps }));
const radioButtons = await screen.findAllByTestId('radio-button');
expect(radioButtons[0]).toHaveAttribute('aria-hidden', 'true');
expect(radioButtons[1]).toHaveAttribute('aria-hidden', 'true');
expect(radioButtons[2]).toHaveAttribute('aria-hidden', 'true');
});
it('should set aria-invalid to be true on radio control when hasError is true', async () => {
render(RadioFieldGroup({ ...basicProps, hasError: true }));
const radios = await screen.findAllByRole('radio');
expect(radios[0]).toHaveAttribute('aria-invalid', 'true');
expect(radios[1]).toHaveAttribute('aria-invalid', 'true');
expect(radios[2]).toHaveAttribute('aria-invalid', 'true');
});
});
describe('Descriptive message', () => {
const descriptiveText = 'This is a descriptive text.';
it('should render when descriptiveText is provided', () => {
render(RadioFieldGroup({ ...basicProps, descriptiveText }));
const descriptiveField = screen.queryByText(descriptiveText);
expect(descriptiveField).toContainHTML(descriptiveText);
});
it('should map to descriptive text correctly', async () => {
const descriptiveText = 'Description';
render(RadioFieldGroup({ ...basicProps, descriptiveText }));
const radioGroup = await screen.findByTestId(radioGroupTestId);
expect(radioGroup).toHaveAccessibleDescription(descriptiveText);
});
});
describe('Error messages', () => {
const errorMessage = 'This is an error message';
it('should not show when hasError is false', () => {
render(RadioFieldGroup({ ...basicProps }));
const errorText = screen.queryByText(errorMessage);
expect(errorText).not.toBeInTheDocument();
});
it('should show when hasError and errorMessage', () => {
render(RadioFieldGroup({ ...basicProps, hasError: true, errorMessage }));
const errorText = screen.queryByText(errorMessage);
expect(errorText).toContainHTML(errorMessage);
});
});
});