Skip to content

Commit

Permalink
[icons] Expose a data-test-id attribute on all svg icons (#22634)
Browse files Browse the repository at this point in the history
  • Loading branch information
jaebradley authored Sep 18, 2020
1 parent 15af0bd commit 5a28de7
Show file tree
Hide file tree
Showing 16 changed files with 59 additions and 62 deletions.
14 changes: 14 additions & 0 deletions docs/src/pages/components/icons/icons.md
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,20 @@ Each icon also has a "theme": Filled (default), Outlined, Rounded, Two tone and
{{"demo": "pages/components/icons/SvgMaterialIcons.js"}}

### Testing

For testing purposes, each icon exposed from `@material-ui/icons` has a `data-testid` attribute with the name of the icon. For instance:

```jsx
import DeleteIcon from '@material-ui/icons/Delete';
```

has the following attribute once mounted:

```html
<svg data-testid="DeleteIcon"></svg>
```

## SvgIcon

If you need a custom SVG icon (not available in the Material Icons [default set](/components/material-icons/)) you can use the `SvgIcon` wrapper.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -386,7 +386,7 @@ describe('<Autocomplete />', () => {
it('should remove the last option', () => {
const handleChange = spy();
const options = ['one', 'two'];
const { container } = render(
const { getAllByTestId } = render(
<Autocomplete
{...defaultProps}
defaultValue={options}
Expand All @@ -396,7 +396,7 @@ describe('<Autocomplete />', () => {
multiple
/>,
);
fireEvent.click(container.querySelectorAll('svg[data-mui-test="CancelIcon"]')[1]);
fireEvent.click(getAllByTestId('CancelIcon')[1]);
expect(handleChange.callCount).to.equal(1);
expect(handleChange.args[0][1]).to.deep.equal([options[0]]);
});
Expand Down
18 changes: 6 additions & 12 deletions packages/material-ui-lab/src/Pagination/Pagination.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -63,15 +63,12 @@ describe('<Pagination />', () => {

const buttons = getAllByRole('button');

expect(buttons[0].querySelector('svg')).to.have.attribute('data-mui-test', 'LastPageIcon');
expect(buttons[1].querySelector('svg')).to.have.attribute('data-mui-test', 'NavigateNextIcon');
expect(buttons[0].querySelector('svg')).to.have.attribute('data-testid', 'LastPageIcon');
expect(buttons[1].querySelector('svg')).to.have.attribute('data-testid', 'NavigateNextIcon');
expect(buttons[2].textContent).to.equal('1');
expect(buttons[6].textContent).to.equal('5');
expect(buttons[7].querySelector('svg')).to.have.attribute(
'data-mui-test',
'NavigateBeforeIcon',
);
expect(buttons[8].querySelector('svg')).to.have.attribute('data-mui-test', 'FirstPageIcon');
expect(buttons[7].querySelector('svg')).to.have.attribute('data-testid', 'NavigateBeforeIcon');
expect(buttons[8].querySelector('svg')).to.have.attribute('data-testid', 'FirstPageIcon');
});

it('renders correct amount of buttons on correct order when boundaryCount is zero', () => {
Expand All @@ -86,13 +83,10 @@ describe('<Pagination />', () => {
);

const buttons = getAllByRole('button');
expect(buttons[4].querySelector('svg')).to.have.attribute(
'data-mui-test',
'NavigateBeforeIcon',
);
expect(buttons[4].querySelector('svg')).to.have.attribute('data-testid', 'NavigateBeforeIcon');
expect(buttons[1].textContent).to.equal('5');
expect(buttons[2].textContent).to.equal('6');
expect(buttons[3].textContent).to.equal('7');
expect(buttons[0].querySelector('svg')).to.have.attribute('data-mui-test', 'NavigateNextIcon');
expect(buttons[0].querySelector('svg')).to.have.attribute('data-testid', 'NavigateNextIcon');
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ describe('<SpeedDialIcon />', () => {

it('should render the Add icon by default', () => {
const wrapper = mount(<SpeedDialIcon />);
expect(findOutermostIntrinsic(wrapper).find('svg[data-mui-test="AddIcon"]').length).to.equal(1);
expect(findOutermostIntrinsic(wrapper).find('svg[data-testid="AddIcon"]').length).to.equal(1);
});

it('should render an Icon', () => {
Expand Down
2 changes: 1 addition & 1 deletion packages/material-ui/src/Avatar/Avatar.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,7 @@ describe('<Avatar />', () => {
it('should render a div containing an svg icon', () => {
expect(avatar.tagName).to.equal('DIV');
const cancelIcon = avatar.firstChild;
expect(cancelIcon).to.have.attribute('data-mui-test', 'CancelIcon');
expect(cancelIcon).to.have.attribute('data-testid', 'CancelIcon');
});

it('should merge user classes & spread custom props to the root node', () => {
Expand Down
2 changes: 1 addition & 1 deletion packages/material-ui/src/Breadcrumbs/Breadcrumbs.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ describe('<Breadcrumbs />', () => {

expect(listitems).to.have.length(3);
expect(getByRole('list')).to.have.text('first//ninth');
expect(getByRole('button').querySelector('[data-mui-test="MoreHorizIcon"]')).not.to.equal(null);
expect(getByRole('button').querySelector('[data-testid="MoreHorizIcon"]')).not.to.equal(null);
});

it('should expand when `BreadcrumbCollapsed` is clicked', () => {
Expand Down
6 changes: 2 additions & 4 deletions packages/material-ui/src/Checkbox/Checkbox.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -64,10 +64,8 @@ describe('<Checkbox />', () => {

describe('prop: indeterminate', () => {
it('should render an indeterminate icon', () => {
const { container } = render(<Checkbox indeterminate />);
expect(
container.querySelector('svg[data-mui-test="IndeterminateCheckBoxIcon"]'),
).not.to.equal(null);
const { getByTestId } = render(<Checkbox indeterminate />);
expect(getByTestId('IndeterminateCheckBoxIcon')).not.to.equal(null);
});
});

Expand Down
24 changes: 12 additions & 12 deletions packages/material-ui/src/Chip/Chip.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -253,60 +253,60 @@ describe('<Chip />', () => {

describe('prop: deleteIcon', () => {
it('should render a default icon with the root, deletable, deleteIcon and deleteIconOutlinedColorSecondary classes', () => {
const { container, getByRole } = render(
const { getByRole, getByTestId } = render(
<Chip label="Custom delete icon Chip" onDelete={() => {}} />,
);

const icon = container.querySelector('svg[data-mui-test="CancelIcon"]');
const icon = getByTestId('CancelIcon');
expect(getByRole('button')).to.contain(icon);
expect(icon).to.have.class(classes.deleteIcon);
});

it('should render a default icon with the root, deletable and deleteIcon classes', () => {
const { container, getByRole } = render(
const { getByRole, getByTestId } = render(
<Chip label="Custom delete icon Chip" onDelete={() => {}} />,
);

const icon = container.querySelector('svg[data-mui-test="CancelIcon"]');
const icon = getByTestId('CancelIcon');
expect(getByRole('button')).to.contain(icon);
expect(icon).to.have.class(classes.deleteIcon);
});

it('should render default icon with the root, deletable and deleteIcon primary class', () => {
const { container } = render(
const { container, getByTestId } = render(
<Chip label="Custom delete icon Chip" onDelete={() => {}} color="primary" />,
);

const chip = container.querySelector(`.${classes.root}`);
expect(chip).to.have.class(classes.colorPrimary);
expect(chip).to.have.class(classes.deletable);
expect(chip).to.have.class(classes.deletableColorPrimary);
const icon = chip.querySelector('svg[data-mui-test="CancelIcon"]');
const icon = getByTestId('CancelIcon');
expect(icon).to.have.class(classes.deleteIcon);
expect(icon).to.have.class(classes.deleteIconColorPrimary);
});

it('should render a default icon with the root, deletable, deleteIcon secondary class', () => {
const { container } = render(
const { container, getByTestId } = render(
<Chip label="Custom delete icon Chip" onDelete={() => {}} color="secondary" />,
);

const chip = container.querySelector(`.${classes.root}`);
expect(chip).to.have.class(classes.colorSecondary);
expect(chip).to.have.class(classes.deletable);
expect(chip).to.have.class(classes.deletableColorSecondary);
const icon = chip.querySelector('svg[data-mui-test="CancelIcon"]');
const icon = getByTestId('CancelIcon');
expect(icon).to.have.class(classes.deleteIcon);
expect(icon).to.have.class(classes.deleteIconColorSecondary);
});

it('accepts a custom icon', () => {
const handleDelete = spy();
const { container } = render(
const { getByTestId } = render(
<Chip label="Custom delete icon Chip" onDelete={handleDelete} deleteIcon={<CheckBox />} />,
);

fireEvent.click(container.querySelector('svg[data-mui-test="CheckBoxIcon"]'));
fireEvent.click(getByTestId('CheckBoxIcon'));

expect(handleDelete.callCount).to.equal(1);
});
Expand Down Expand Up @@ -532,9 +532,9 @@ describe('<Chip />', () => {
});

it('should render the delete icon with the deleteIcon and deleteIconSmall classes', () => {
const { container } = render(<Chip size="small" onDelete={() => {}} />);
const { getByTestId } = render(<Chip size="small" onDelete={() => {}} />);

const icon = container.querySelector('svg[data-mui-test="CancelIcon"]');
const icon = getByTestId('CancelIcon');
expect(icon).to.have.class(classes.deleteIcon);
expect(icon).to.have.class(classes.deleteIconSmall);
});
Expand Down
4 changes: 2 additions & 2 deletions packages/material-ui/src/MobileStepper/MobileStepper.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -63,14 +63,14 @@ describe('<MobileStepper />', () => {
const wrapper = mount(<MobileStepper {...defaultProps} />);
const backButton = wrapper.find('button[aria-label="back"]');
expect(backButton.exists()).to.equal(true);
expect(backButton.find('svg[data-mui-test="KeyboardArrowLeftIcon"]')).to.have.lengthOf(1);
expect(backButton.find('svg[data-testid="KeyboardArrowLeftIcon"]')).to.have.lengthOf(1);
});

it('should render next button', () => {
const wrapper = mount(<MobileStepper {...defaultProps} />);
const nextButton = wrapper.find('button[aria-label="next"]');
expect(nextButton.exists()).to.equal(true);
expect(nextButton.find('svg[data-mui-test="KeyboardArrowRightIcon"]')).to.have.lengthOf(1);
expect(nextButton.find('svg[data-testid="KeyboardArrowRightIcon"]')).to.have.lengthOf(1);
});

it('should render two buttons and text displaying progress when supplied with variant text', () => {
Expand Down
12 changes: 4 additions & 8 deletions packages/material-ui/src/Radio/Radio.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -32,19 +32,15 @@ describe('<Radio />', () => {

describe('prop: unchecked', () => {
it('should render an unchecked icon', () => {
const { container } = render(<Radio />);
expect(
container.querySelectorAll('svg[data-mui-test="RadioButtonUncheckedIcon"]').length,
).to.equal(1);
const { getAllByTestId } = render(<Radio />);
expect(getAllByTestId('RadioButtonUncheckedIcon').length).to.equal(1);
});
});

describe('prop: checked', () => {
it('should render a checked icon', () => {
const { container } = render(<Radio checked />);
expect(
container.querySelectorAll('svg[data-mui-test="RadioButtonCheckedIcon"]').length,
).to.equal(1);
const { getAllByTestId } = render(<Radio checked />);
expect(getAllByTestId('RadioButtonCheckedIcon').length).to.equal(1);
});
});

Expand Down
8 changes: 4 additions & 4 deletions packages/material-ui/src/StepIcon/StepIcon.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,14 +21,14 @@ describe('<StepIcon />', () => {
}));

it('renders <CheckCircle> when completed', () => {
const { container } = render(<StepIcon completed icon={1} />);
const { getAllByTestId } = render(<StepIcon completed icon={1} />);

expect(container.querySelectorAll('svg[data-mui-test="CheckCircleIcon"]')).to.have.length(1);
expect(getAllByTestId('CheckCircleIcon')).to.have.length(1);
});

it('renders <Warning> when error occurred', () => {
const { container } = render(<StepIcon icon={1} error />);
expect(container.querySelectorAll('svg[data-mui-test="WarningIcon"]')).to.have.length(1);
const { getAllByTestId } = render(<StepIcon icon={1} error />);
expect(getAllByTestId('WarningIcon')).to.have.length(1);
});

it('contains text "3" when position is "3"', () => {
Expand Down
4 changes: 2 additions & 2 deletions packages/material-ui/src/StepLabel/StepLabel.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ describe('<StepLabel />', () => {

const icon = container.querySelector(`.${iconClasses.root}`);
// Should render WarningIcon instead of CheckCircleIcon because of { error: true } props
expect(icon).to.have.attribute('data-mui-test').equal('WarningIcon');
expect(icon).to.have.attribute('data-testid').equal('WarningIcon');
});
});

Expand All @@ -65,7 +65,7 @@ describe('<StepLabel />', () => {

getByTestId('custom-icon');
expect(icon).to.not.equal(null);
expect(icon).to.not.have.attribute('data-mui-test').equal('CheckCircleIcon');
expect(icon).to.not.have.attribute('data-testid').equal('CheckCircleIcon');
expect(label).to.have.class(classes.active);
expect(label).to.have.class(classes.completed);
});
Expand Down
12 changes: 4 additions & 8 deletions packages/material-ui/src/TabScrollButton/TabScrollButton.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -39,21 +39,17 @@ describe('<TabScrollButton />', () => {

describe('prop: direction', () => {
it('should render with the left icon', () => {
const { container } = render(
const { getAllByTestId } = render(
<TabScrollButton {...defaultProps} {...defaultProps} direction="left" disabled />,
);
expect(
container.querySelectorAll('svg[data-mui-test="KeyboardArrowLeftIcon"]').length,
).to.equal(1);
expect(getAllByTestId('KeyboardArrowLeftIcon').length).to.equal(1);
});

it('should render with the right icon', () => {
const { container } = render(
const { getAllByTestId } = render(
<TabScrollButton {...defaultProps} {...defaultProps} direction="right" disabled />,
);
expect(
container.querySelectorAll('svg[data-mui-test="KeyboardArrowRightIcon"]').length,
).to.equal(1);
expect(getAllByTestId('KeyboardArrowRightIcon').length).to.equal(1);
});
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -57,9 +57,8 @@ describe('<TableSortLabel />', () => {
});

it('should accept a custom icon for the sort icon', () => {
const { container } = render(<TableSortLabel IconComponent={SortIcon} />);
const icon = container.querySelector(`svg.${classes.icon}[data-mui-test="SortIcon"]`);
expect(icon).to.not.equal(null);
const { getAllByTestId } = render(<TableSortLabel IconComponent={SortIcon} />);
expect(getAllByTestId('SortIcon')).to.not.equal(null);
});
});

Expand Down
2 changes: 1 addition & 1 deletion packages/material-ui/src/Tabs/Tabs.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ import Tabs from './Tabs';
import { createMuiTheme, ThemeProvider } from '../styles';

function findScrollButton(container, direction) {
return container.querySelector(`svg[data-mui-test="KeyboardArrow${capitalize(direction)}Icon"]`);
return container.querySelector(`svg[data-testid="KeyboardArrow${capitalize(direction)}Icon"]`);
}

function hasLeftScrollButton(container) {
Expand Down
2 changes: 1 addition & 1 deletion packages/material-ui/src/utils/createSvgIcon.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import SvgIcon from '../SvgIcon';
*/
export default function createSvgIcon(path, displayName) {
const Component = (props, ref) => (
<SvgIcon data-mui-test={`${displayName}Icon`} ref={ref} {...props}>
<SvgIcon data-testid={`${displayName}Icon`} ref={ref} {...props}>
{path}
</SvgIcon>
);
Expand Down

0 comments on commit 5a28de7

Please sign in to comment.