Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Choicegroup (v8): convert tests to use testing-library #22208

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
167 changes: 70 additions & 97 deletions packages/react/src/components/ChoiceGroup/ChoiceGroup.test.tsx
Original file line number Diff line number Diff line change
@@ -1,48 +1,34 @@
import * as React from 'react';
import { mount, ReactWrapper } from 'enzyme';
import * as ReactTestUtils from 'react-dom/test-utils';
import * as renderer from 'react-test-renderer';
import { render } from '@testing-library/react';
import userEvent from '@testing-library/user-event';

import { ChoiceGroup } from './ChoiceGroup';
import { merge, resetIds } from '../../Utilities';
import { safeMount } from '@fluentui/test-utilities';
import { isConformant } from '../../common/isConformant';
import type { IChoiceGroupOption, IChoiceGroup, IChoiceGroupProps } from './ChoiceGroup.types';
import type { IChoiceGroupOption, IChoiceGroup } from './ChoiceGroup.types';

const TEST_OPTIONS: IChoiceGroupOption[] = [
{ key: '1', text: '1', 'data-automation-id': 'auto1', autoFocus: true } as IChoiceGroupOption,
{ key: '2', text: '2' },
{ key: '3', text: '3' },
];
const CHOICE_QUERY_SELECTOR = '.ms-ChoiceField-input';

describe('ChoiceGroup', () => {
let choiceGroup: ReactWrapper<IChoiceGroupProps> | undefined;

beforeEach(() => {
// Resetting ids to create predictability in generated ids.
resetIds();
});

afterEach(() => {
if (choiceGroup) {
choiceGroup.unmount();
choiceGroup = undefined;
}
});

it('renders ChoiceGroup correctly', () => {
const component = renderer.create(<ChoiceGroup className="testClassName" options={TEST_OPTIONS} required />);
const tree = component.toJSON();
expect(tree).toMatchSnapshot();
const { container } = render(<ChoiceGroup className="testClassName" options={TEST_OPTIONS} required />);
expect(container).toMatchSnapshot();
});

it('renders ChoiceGroup with label correctly', () => {
const component = renderer.create(
const { container } = render(
<ChoiceGroup className="testClassName" label="test label" options={TEST_OPTIONS} required />,
);
const tree = component.toJSON();
expect(tree).toMatchSnapshot();
expect(container).toMatchSnapshot();
});

isConformant({
Expand All @@ -51,39 +37,39 @@ describe('ChoiceGroup', () => {
});

it('does not use className prop from parent on label', () => {
choiceGroup = mount(<ChoiceGroup className="testClassName" label="test label" options={TEST_OPTIONS} required />);
const label = choiceGroup.getDOMNode().querySelector('label');
const { getByRole } = render(
<ChoiceGroup className="testClassName" label="test label" options={TEST_OPTIONS} required />,
);
const label = getByRole('radiogroup').firstElementChild;
expect(label).toBeTruthy();
expect(label!.textContent).toBe('test label');
expect(label!.className).not.toContain('testClassName');
});

it('can change options', () => {
choiceGroup = mount(<ChoiceGroup label="testgroup" options={TEST_OPTIONS} required={true} />);
const { getAllByRole } = render(<ChoiceGroup label="testgroup" options={TEST_OPTIONS} required={true} />);

const choiceOptions = choiceGroup
.getDOMNode()
.querySelectorAll(CHOICE_QUERY_SELECTOR) as NodeListOf<HTMLInputElement>;
const choiceOptions = getAllByRole('radio') as HTMLInputElement[];

expect(choiceOptions.length).toBe(3);

expect(choiceOptions[0].checked).toEqual(false);
expect(choiceOptions[1].checked).toEqual(false);
expect(choiceOptions[2].checked).toEqual(false);

ReactTestUtils.Simulate.change(choiceOptions[0]);
userEvent.click(choiceOptions[0]);

expect(choiceOptions[0].checked).toEqual(true);
expect(choiceOptions[1].checked).toEqual(false);
expect(choiceOptions[2].checked).toEqual(false);

ReactTestUtils.Simulate.change(choiceOptions[1]);
userEvent.click(choiceOptions[1]);

expect(choiceOptions[0].checked).toEqual(false);
expect(choiceOptions[1].checked).toEqual(true);
expect(choiceOptions[2].checked).toEqual(false);

ReactTestUtils.Simulate.change(choiceOptions[0]);
userEvent.click(choiceOptions[0]);

expect(choiceOptions[0].checked).toEqual(true);
expect(choiceOptions[1].checked).toEqual(false);
Expand All @@ -94,39 +80,36 @@ describe('ChoiceGroup', () => {
const options: IChoiceGroupOption[] = merge([], TEST_OPTIONS);
options[0].disabled = true;

choiceGroup = mount(<ChoiceGroup label="testgroup" options={options} required={true} />);
const { getAllByRole } = render(<ChoiceGroup label="testgroup" options={options} required={true} />);

const choiceOptions = choiceGroup
.getDOMNode()
.querySelectorAll(CHOICE_QUERY_SELECTOR) as NodeListOf<HTMLInputElement>;
const choiceOptions = getAllByRole('radio') as HTMLInputElement[];

expect(choiceOptions[0].disabled).toEqual(true);
expect(choiceOptions[1].disabled).toEqual(false);
expect(choiceOptions[2].disabled).toEqual(false);
});

it('renders all choice options as disabled when disabled', () => {
choiceGroup = mount(<ChoiceGroup label="testgroup" options={TEST_OPTIONS} required={true} disabled={true} />);
const { getAllByRole } = render(
<ChoiceGroup label="testgroup" options={TEST_OPTIONS} required={true} disabled={true} />,
);

const choiceOptions = choiceGroup
.getDOMNode()
.querySelectorAll(CHOICE_QUERY_SELECTOR) as NodeListOf<HTMLInputElement>;
const choiceOptions = getAllByRole('radio') as HTMLInputElement[];

expect(choiceOptions[0].disabled).toEqual(true);
expect(choiceOptions[1].disabled).toEqual(true);
expect(choiceOptions[2].disabled).toEqual(true);
});

/* Testing that the defaultSelectedKey is working correctly. */
it('can act as an uncontrolled component', () => {
choiceGroup = mount(<ChoiceGroup defaultSelectedKey="1" options={TEST_OPTIONS} />);
const { getAllByRole } = render(<ChoiceGroup defaultSelectedKey="1" options={TEST_OPTIONS} />);

const choiceOptions = choiceGroup
.getDOMNode()
.querySelectorAll(CHOICE_QUERY_SELECTOR) as NodeListOf<HTMLInputElement>;
const choiceOptions = getAllByRole('radio') as HTMLInputElement[];

expect(choiceOptions[0].checked).toEqual(true);

ReactTestUtils.Simulate.change(choiceOptions[1]);
userEvent.click(choiceOptions[1]);

expect(choiceOptions[1].checked).toEqual(true);
});
Expand All @@ -140,15 +123,13 @@ describe('ChoiceGroup', () => {
_selectedItem = item;
};

choiceGroup = mount(<ChoiceGroup selectedKey="1" options={TEST_OPTIONS} onChange={onChange} />);
const { getAllByRole } = render(<ChoiceGroup selectedKey="1" options={TEST_OPTIONS} onChange={onChange} />);

const choiceOptions = choiceGroup
.getDOMNode()
.querySelectorAll(CHOICE_QUERY_SELECTOR) as NodeListOf<HTMLInputElement>;
const choiceOptions = getAllByRole('radio') as HTMLInputElement[];

expect(choiceOptions[0].checked).toEqual(true);

ReactTestUtils.Simulate.change(choiceOptions[1]);
userEvent.click(choiceOptions[1]);

expect(choiceOptions[0].checked).toEqual(true);
expect(choiceOptions[1].checked).toEqual(false);
Expand All @@ -162,11 +143,9 @@ describe('ChoiceGroup', () => {
item: IChoiceGroupOption | undefined,
): void => undefined;

choiceGroup = mount(<ChoiceGroup options={TEST_OPTIONS} onChange={onChange} />);
const { getAllByRole } = render(<ChoiceGroup options={TEST_OPTIONS} onChange={onChange} />);

const choiceOptions = choiceGroup
.getDOMNode()
.querySelectorAll(CHOICE_QUERY_SELECTOR) as NodeListOf<HTMLInputElement>;
const choiceOptions = getAllByRole('radio') as HTMLInputElement[];

const extraAttributeGetter: (index: number) => string | null = (index: number): string | null => {
const input: HTMLInputElement = choiceOptions[index];
Expand All @@ -178,24 +157,24 @@ describe('ChoiceGroup', () => {
});

it('can set role attribute to empty string', () => {
choiceGroup = mount(<ChoiceGroup options={TEST_OPTIONS} role="" />);
const role = choiceGroup.getDOMNode().getAttribute('role');
const { container } = render(<ChoiceGroup options={TEST_OPTIONS} role="" />);
const role = container.firstElementChild!.getAttribute('role');
expect(role).toEqual('');
});

it('can set role attribute on the containing element', () => {
choiceGroup = mount(<ChoiceGroup options={TEST_OPTIONS} role="Test" />);
const role = choiceGroup.getDOMNode().getAttribute('role');
expect(role).toEqual('Test');
const { container } = render(<ChoiceGroup options={TEST_OPTIONS} role="radiogroup" />);
const role = container.firstElementChild!.getAttribute('role');
expect(role).toEqual('radiogroup');
});

it('can assign a custom aria label', () => {
const option4: IChoiceGroupOption[] = [{ key: '4', text: '4', ariaLabel: 'Custom aria label' }];
choiceGroup = mount(<ChoiceGroup label="testgroup" options={TEST_OPTIONS.concat(option4)} required={true} />);
const { getAllByRole } = render(
<ChoiceGroup label="testgroup" options={TEST_OPTIONS.concat(option4)} required={true} />,
);

const choiceOptions = choiceGroup
.getDOMNode()
.querySelectorAll(CHOICE_QUERY_SELECTOR) as NodeListOf<HTMLInputElement>;
const choiceOptions = getAllByRole('radio') as HTMLInputElement[];

expect(choiceOptions.length).toBe(4);

Expand All @@ -207,80 +186,74 @@ describe('ChoiceGroup', () => {

it('returns the current checked option with user interaction', () => {
const choiceGroupRef = React.createRef<IChoiceGroup>();
choiceGroup = mount(<ChoiceGroup options={TEST_OPTIONS} componentRef={choiceGroupRef} />);
const { getAllByRole } = render(<ChoiceGroup options={TEST_OPTIONS} componentRef={choiceGroupRef} />);

const choiceOptions = choiceGroup
.getDOMNode()
.querySelectorAll(CHOICE_QUERY_SELECTOR) as NodeListOf<HTMLInputElement>;
const choiceOptions = getAllByRole('radio') as HTMLInputElement[];

expect(choiceGroupRef.current!.checkedOption).toBeUndefined();
ReactTestUtils.Simulate.change(choiceOptions[0]);
userEvent.click(choiceOptions[0]);
expect(choiceGroupRef.current!.checkedOption).toEqual(TEST_OPTIONS[0]);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not a new issue but we really ought to verify that the checked state is updated in the DOM, not just in the component state

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've noticed that whenever the selected element is changed, the checked attribute in the DOM never actually changes; it stays on the element that was initially selected on page load. Codepen here

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I tried this too and it looks like what's checked in the actual DOM does get updated, though that may not happen until after a re-render. (The option object doesn't get updated, which is expected.)
https://codepen.io/ecraig12345/pen/mdpMyWO?editors=0011

});

it('returns the current checked option with defaultSelectedKey', () => {
const choiceGroupRef = React.createRef<IChoiceGroup>();
choiceGroup = mount(<ChoiceGroup options={TEST_OPTIONS} defaultSelectedKey="1" componentRef={choiceGroupRef} />);
const { getAllByRole } = render(
<ChoiceGroup options={TEST_OPTIONS} defaultSelectedKey="1" componentRef={choiceGroupRef} />,
);

const choiceOptions = choiceGroup
.getDOMNode()
.querySelectorAll(CHOICE_QUERY_SELECTOR) as NodeListOf<HTMLInputElement>;
const choiceOptions = getAllByRole('radio') as HTMLInputElement[];

expect(choiceGroupRef.current!.checkedOption).toEqual(TEST_OPTIONS[0]);
ReactTestUtils.Simulate.change(choiceOptions[1]);
userEvent.click(choiceOptions[1]);
expect(choiceGroupRef.current!.checkedOption).toEqual(TEST_OPTIONS[1]);
});

it('returns the current checked option with selectedKey', () => {
const choiceGroupRef = React.createRef<IChoiceGroup>();
choiceGroup = mount(<ChoiceGroup options={TEST_OPTIONS} selectedKey="1" componentRef={choiceGroupRef} />);
const { getAllByRole } = render(
<ChoiceGroup options={TEST_OPTIONS} selectedKey="1" componentRef={choiceGroupRef} />,
);

const choiceOptions = choiceGroup
.getDOMNode()
.querySelectorAll(CHOICE_QUERY_SELECTOR) as NodeListOf<HTMLInputElement>;
const choiceOptions = getAllByRole('radio') as HTMLInputElement[];

expect(choiceGroupRef.current!.checkedOption).toEqual(TEST_OPTIONS[0]);
ReactTestUtils.Simulate.change(choiceOptions[1]);
userEvent.click(choiceOptions[1]);
// selectedKey is still used even though it didn't get updated for latest user click
expect(choiceGroupRef.current!.checkedOption).toEqual(TEST_OPTIONS[0]);
});

it('can render element id', () => {
choiceGroup = mount(<ChoiceGroup defaultSelectedKey="1" id="foo" options={TEST_OPTIONS} />);
expect(choiceGroup.getDOMNode().getAttribute('id')).toBe('foo');
const { container } = render(<ChoiceGroup defaultSelectedKey="1" id="foo" options={TEST_OPTIONS} />);
const root = container.firstElementChild;
expect(root!.getAttribute('id')).toBe('foo');
});

it('can focus the checked option', () => {
// This test has to mount the element to the document since ChoiceGroup.focus() uses document.getElementById()
const choiceGroupRef = React.createRef<IChoiceGroup>();
safeMount(
const { getAllByRole } = render(
<ChoiceGroup options={TEST_OPTIONS} defaultSelectedKey="1" componentRef={choiceGroupRef} />,
choiceGroup2 => {
const option = choiceGroup2.getDOMNode().querySelector(CHOICE_QUERY_SELECTOR) as HTMLInputElement;
const focusSpy = jest.spyOn(option, 'focus');

choiceGroupRef.current!.focus();
expect(focusSpy).toHaveBeenCalled();
},
true /* attach */,
);

const option = getAllByRole('radio')[0] as HTMLInputElement;
const focusSpy = jest.spyOn(option, 'focus');

choiceGroupRef.current!.focus();
expect(focusSpy).toHaveBeenCalled();
});

it('can focus the first enabled option', () => {
const choiceGroupRef = React.createRef<IChoiceGroup>();
safeMount(
const { getAllByRole } = render(
<ChoiceGroup
options={[{ key: '0', text: 'disabled', disabled: true }, ...TEST_OPTIONS]}
componentRef={choiceGroupRef}
/>,
choiceGroup2 => {
const option = choiceGroup2.getDOMNode().querySelectorAll(CHOICE_QUERY_SELECTOR)![1] as HTMLInputElement;
const focusSpy = jest.spyOn(option, 'focus');

choiceGroupRef.current!.focus();
expect(focusSpy).toHaveBeenCalled();
},
true /* attach */,
);

const option = getAllByRole('radio')[1] as HTMLInputElement;
const focusSpy = jest.spyOn(option, 'focus');

choiceGroupRef.current!.focus();
expect(focusSpy).toHaveBeenCalled();
});
});
Loading