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

[test] Convert Menu tests to testing-library #26915

Merged
merged 3 commits into from
Jun 24, 2021
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
228 changes: 131 additions & 97 deletions packages/material-ui/src/Menu/Menu.test.js
Original file line number Diff line number Diff line change
@@ -1,23 +1,32 @@
import * as React from 'react';
import { spy } from 'sinon';
import { spy, useFakeTimers } from 'sinon';
import { expect } from 'chai';
import { createMount, createClientRender, describeConformanceV5 } from 'test/utils';
import {
createMount,
createClientRender,
describeConformanceV5,
screen,
fireEvent,
} from 'test/utils';
import Menu, { menuClasses as classes } from '@material-ui/core/Menu';
import MenuList from '@material-ui/core/MenuList';
import Popover from '@material-ui/core/Popover';

const MENU_LIST_HEIGHT = 100;

describe('<Menu />', () => {
// StrictModeViolation: Not using act(), prefer using createClientRender from test/utils
const mount = createMount({ strict: false });
/**
* @type {ReturnType<typeof useFakeTimers>}
*/
let clock;
beforeEach(() => {
clock = useFakeTimers();
});
afterEach(() => {
clock.restore();
});

const mount = createMount();
const render = createClientRender();
const defaultProps = {
open: false,
anchorEl: () => document.createElement('div'),
};

describeConformanceV5(<Menu {...defaultProps} open />, () => ({
describeConformanceV5(<Menu anchorEl={() => document.createElement('div')} open />, () => ({
classes,
inheritComponent: Popover,
render,
Expand All @@ -38,176 +47,201 @@ describe('<Menu />', () => {

describe('event callbacks', () => {
describe('entering', () => {
it('should fire callbacks', (done) => {
it('should fire callbacks', () => {
const handleEnter = spy();
const handleEntering = spy();

const wrapper = mount(
render(
<Menu
anchorEl={document.createElement('div')}
open
TransitionProps={{
onEnter: handleEnter,
onEntering: handleEntering,
onEntered: () => {
expect(handleEnter.callCount).to.equal(1);
expect(handleEnter.args[0].length).to.equal(2);
expect(handleEntering.callCount).to.equal(1);
expect(handleEntering.args[0].length).to.equal(2);
done();
},
}}
{...defaultProps}
/>,
);

wrapper.setProps({
open: true,
});
expect(handleEnter.callCount).to.equal(1);
expect(handleEnter.args[0].length).to.equal(2);
expect(handleEntering.callCount).to.equal(1);
expect(handleEntering.args[0].length).to.equal(2);
});
});

describe('exiting', () => {
it('should fire callbacks', (done) => {
it('should fire callbacks', () => {
const handleExit = spy();
const handleExiting = spy();

const wrapper = mount(
const { setProps } = render(
<Menu
TransitionProps={{
onExit: handleExit,
onExiting: handleExiting,
onExited: () => {
expect(handleExit.callCount).to.equal(1);
expect(handleExit.args[0].length).to.equal(1);
expect(handleExiting.callCount).to.equal(1);
expect(handleExiting.args[0].length).to.equal(1);
done();
},
}}
{...defaultProps}
anchorEl={document.createElement('div')}
open
/>,
);

wrapper.setProps({
setProps({
open: false,
});

expect(handleExit.callCount).to.equal(1);
expect(handleExit.args[0].length).to.equal(1);
expect(handleExiting.callCount).to.equal(1);
expect(handleExiting.args[0].length).to.equal(1);
});
});
});

it('should pass `classes.paper` to the Popover', () => {
const wrapper = mount(<Menu {...defaultProps} />);
expect(wrapper.find(Popover).props().PaperProps.classes.root).to.equal(classes.paper);
it('should pass `classes.paper` to the Paper', () => {
render(
<Menu
anchorEl={document.createElement('div')}
open
PaperProps={{ 'data-testid': 'paper' }}
/>,
);

expect(screen.getByTestId('paper')).to.have.class(classes.paper);
});

describe('prop: PopoverClasses', () => {
it('should be able to change the Popover style', () => {
const wrapper = mount(<Menu {...defaultProps} PopoverClasses={{ paper: 'bar' }} />);
expect(wrapper.find(Popover).props().classes.paper).to.equal('bar');
render(
<Menu
anchorEl={document.createElement('div')}
open
PaperProps={{ 'data-testid': 'paper' }}
PopoverClasses={{ paper: 'bar' }}
/>,
);

expect(screen.getByTestId('paper')).to.have.class('bar');
});
});

it('should pass onClose prop to Popover', () => {
const fn = () => {};
const wrapper = mount(<Menu {...defaultProps} onClose={fn} />);
expect(wrapper.find(Popover).props().onClose).to.equal(fn);
});
const handleClose = spy();
render(<Menu anchorEl={document.createElement('div')} open onClose={handleClose} />);

fireEvent.keyDown(screen.getByRole('menu'), { key: 'Escape' });

it('should pass anchorEl prop to Popover', () => {
const el = document.createElement('div');
const wrapper = mount(<Menu {...defaultProps} anchorEl={el} />);
expect(wrapper.find(Popover).props().anchorEl).to.equal(el);
expect(handleClose.callCount).to.equal(1);
});

it('should pass through the `open` prop to Popover', () => {
const wrapper = mount(<Menu {...defaultProps} />);
expect(wrapper.find(Popover).props().open).to.equal(false);
wrapper.setProps({ open: true });
expect(wrapper.find(Popover).props().open).to.equal(true);
it('renders its children only when open', () => {
const { setProps } = render(
<Menu anchorEl={document.createElement('div')} open={false}>
<div data-testid="children" />
</Menu>,
);

expect(screen.queryByTestId('children')).to.equal(null);

setProps({ open: true });

expect(screen.getByTestId('children')).not.to.equal(null);
});

describe('list node', () => {
it('should render a MenuList inside the Popover', () => {
const wrapper = mount(<Menu {...defaultProps} className="test-class" data-test="hi" open />);
expect(wrapper.find(Popover).find(MenuList).exists()).to.equal(true);
it('should render a menu inside the Popover', () => {
render(<Menu anchorEl={document.createElement('div')} open data-testid="popover" />);

expect(screen.getByTestId('popover').querySelector('[role="menu"]')).not.to.equal(null);
});
});

it('should open during the initial mount', () => {
const wrapper = mount(
<Menu {...defaultProps} open>
<div role="menuitem" tabIndex={-1}>
one
function MenuItem(props) {
const { autoFocus, children } = props;
return (
<div role="menuitem" tabIndex={-1} data-autofocus={autoFocus}>
{children}
</div>
);
}
render(
<Menu anchorEl={document.createElement('div')} open>
<MenuItem>one</MenuItem>
</Menu>,
);
const popover = wrapper.find(Popover);
expect(popover.props().open).to.equal(true);
expect(wrapper.find('[role="menuitem"]').props().autoFocus).to.equal(true);

expect(screen.getByRole('menuitem')).to.have.attribute('data-autofocus', 'true');
});

it('should not focus list if autoFocus=false', () => {
const wrapper = mount(
<Menu {...defaultProps} autoFocus={false} open>
render(
<Menu anchorEl={document.createElement('div')} autoFocus={false} open>
<div tabIndex={-1} />
</Menu>,
);
const popover = wrapper.find(Popover);
expect(popover.props().open).to.equal(true);
const menuEl = document.querySelector('[role="menu"]');
expect(document.activeElement).not.to.equal(menuEl);
expect(false).to.equal(menuEl.contains(document.activeElement));

expect(screen.getByRole('menu')).not.toHaveFocus();
});

it('should call TransitionProps.onEntering with element if exists', () => {
it('should call TransitionProps.onEntering', () => {
const onEnteringSpy = spy();
const wrapper = mount(
<Menu {...defaultProps} TransitionProps={{ onEntering: onEnteringSpy }} />,
render(
<Menu
anchorEl={document.createElement('div')}
open
TransitionProps={{ onEntering: onEnteringSpy }}
/>,
);
const popover = wrapper.find(Popover);

const elementForHandleEnter = { clientHeight: MENU_LIST_HEIGHT };

popover.props().TransitionProps.onEntering(elementForHandleEnter);
expect(onEnteringSpy.callCount).to.equal(1);
expect(onEnteringSpy.args[0][0]).to.equal(elementForHandleEnter);
});

it('should call TransitionProps.onEntering, disableAutoFocusItem', () => {
const onEnteringSpy = spy();
const wrapper = mount(
render(
<Menu
anchorEl={document.createElement('div')}
disableAutoFocusItem
{...defaultProps}
open
TransitionProps={{ onEntering: onEnteringSpy }}
/>,
);
const popover = wrapper.find(Popover);

const elementForHandleEnter = { clientHeight: MENU_LIST_HEIGHT };

popover.props().TransitionProps.onEntering(elementForHandleEnter);
expect(onEnteringSpy.callCount).to.equal(1);
expect(onEnteringSpy.args[0][0]).to.equal(elementForHandleEnter);
});

it('should call onClose on tab', () => {
function MenuItem(props) {
const { autoFocus, children } = props;

const ref = React.useRef(null);
React.useEffect(() => {
if (autoFocus) {
ref.current.focus();
}
}, [autoFocus]);

return (
<div ref={ref} role="menuitem" tabIndex={-1}>
{children}
</div>
);
}
const onCloseSpy = spy();
const wrapper = mount(
<Menu {...defaultProps} open onClose={onCloseSpy}>
<span>hello</span>
render(
<Menu anchorEl={document.createElement('div')} open onClose={onCloseSpy}>
<MenuItem>hello</MenuItem>
</Menu>,
);
wrapper.find('span').simulate('keyDown', {
key: 'Tab',
});

fireEvent.keyDown(screen.getByRole('menuitem'), { key: 'Tab' });

expect(onCloseSpy.callCount).to.equal(1);
expect(onCloseSpy.args[0][1]).to.equal('tabKeyDown');
});

it('ignores invalid children', () => {
const wrapper = mount(
<Menu {...defaultProps} open>
render(
<Menu anchorEl={document.createElement('div')} open>
{null}
<span role="menuitem">hello</span>
{/* testing conditional rendering */}
Expand All @@ -217,13 +251,13 @@ describe('<Menu />', () => {
</Menu>,
);

expect(wrapper.find('span[role="menuitem"]')).to.have.length(1);
expect(screen.getAllByRole('menuitem')).to.have.length(1);
});

describe('warnings', () => {
it('warns a Fragment is passed as a child', () => {
expect(() => {
mount(
render(
<Menu anchorEl={document.createElement('div')} open>
<React.Fragment />
</Menu>,
Expand Down