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

refactor(react): update OverflowMenu and OverflowMenuItem tests to use RTL #11867

Merged
merged 5 commits into from
Aug 2, 2022
Merged
Show file tree
Hide file tree
Changes from 3 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
344 changes: 131 additions & 213 deletions packages/react/src/components/OverflowMenu/OverflowMenu-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,265 +6,183 @@
*/

import React from 'react';
import OverflowMenu from '../OverflowMenu';
import OverflowMenu from './OverflowMenu';
import OverflowMenuItem from '../OverflowMenuItem';
import { OverflowMenuVertical } from '@carbon/icons-react';
import { mount } from 'enzyme';

const prefix = 'cds';
import { Filter } from '@carbon/icons-react';
import userEvent from '@testing-library/user-event';
import { render, screen } from '@testing-library/react';

describe('OverflowMenu', () => {
describe('Renders as expected', () => {
const rootWrapper = mount(
<OverflowMenu className="extra-class">
<OverflowMenuItem className="test-child">one</OverflowMenuItem>
<OverflowMenuItem className="test-child">two</OverflowMenuItem>
</OverflowMenu>
);
const menu = rootWrapper.find(`button.${prefix}--overflow-menu`);
const icon = menu.find(OverflowMenuVertical);

it('should render an Icon', () => {
expect(icon.length).toBe(1);
expect(icon.hasClass(`${prefix}--overflow-menu__icon`)).toEqual(true);
});

it('has the expected classes', () => {
expect(menu.hasClass(`${prefix}--overflow-menu`)).toBe(true);
expect(menu.hasClass(`${prefix}--overflow-menu--open`)).not.toBe(true);
});
it('renders ariaLabel on the button', () => {
render(
<OverflowMenu open ariaLabel="Overflow menu" className="extra-class">
<OverflowMenuItem className="test-child">one</OverflowMenuItem>
<OverflowMenuItem className="test-child">two</OverflowMenuItem>
</OverflowMenu>
);

it('should not render a ul unless menu is open', () => {
const list = menu.find('ul');
expect(list.length).toEqual(0);
expect(screen.getByRole('button')).toHaveAttribute(
'aria-label',
'Overflow menu'
);
});

it('should add extra classes that are passed via className', () => {
expect(menu.hasClass('extra-class')).toEqual(true);
it('should support a custom `className` prop on the outermost element', () => {
const { container } = render(
<OverflowMenu open ariaLabel="Overflow menu" className="extra-class">
<OverflowMenuItem className="test-child" itemText="one" />
<OverflowMenuItem className="test-child" itemText="two" />
</OverflowMenu>
);
expect(container.firstChild).toHaveClass('extra-class');
});

it('should not render children unless the menu is open', () => {
expect(menu.find('.test-child').length).toEqual(0);
it('should spread extra props on the outermost element', () => {
const { container } = render(
<OverflowMenu
data-testid="test"
ariaLabel="Overflow menu"
className="extra-class">
<OverflowMenuItem className="test-child" itemText="one" />
<OverflowMenuItem className="test-child" itemText="two" />
</OverflowMenu>
);
expect(container.firstChild).toHaveAttribute('data-testid', 'test');
});

it('should set tabIndex if one is passed via props', () => {
rootWrapper.setProps({ tabIndex: 2 });

expect(
rootWrapper.find(`button.${prefix}--overflow-menu`).props().tabIndex
).toEqual(2);
});
it('should flip menu alignment', () => {
render(
<OverflowMenu
flipped={true}
ariaLabel="Overflow menu"
className="extra-class">
<OverflowMenuItem className="test-child" itemText="one" />
<OverflowMenuItem className="test-child" itemText="two" />
</OverflowMenu>
);

it('should set ariaLabel if one is passed via props', () => {
rootWrapper.setProps({ ariaLabel: 'test label' });
expect(
rootWrapper.find(`button.${prefix}--overflow-menu`).props()[
'aria-label'
]
).toEqual('test label');
});
userEvent.click(screen.getByRole('button'));

it('should set id if one is passed via props', () => {
rootWrapper.setProps({ id: 'uniqueId' });
expect(
rootWrapper.find(`button.${prefix}--overflow-menu`).props().id
).toEqual('uniqueId');
});

it('should specify light version as expected', () => {
rootWrapper.setProps({ light: true });
expect(rootWrapper.props().light).toEqual(true);
document.querySelector('.cds--overflow-menu--flip')
).toBeInTheDocument();
});
it('should add light modifier to overflow menu', () => {
// Enzyme doesn't seem to allow setState() in a forwardRef-wrapped class component
rootWrapper
.setProps({ light: true })
.find('OverflowMenu')
.instance()
.setState({ open: true });
rootWrapper.update();

const oMenu = rootWrapper.find(`.${prefix}--overflow-menu`);
const oMenuOptions = rootWrapper.find(
`.${prefix}--overflow-menu-options`
it('should call onClick', () => {
const onClick = jest.fn();
render(
<OverflowMenu
ariaLabel="Overflow menu"
className="extra-class"
onClick={onClick}>
<OverflowMenuItem className="test-child" itemText="one" />
<OverflowMenuItem className="test-child" itemText="two" />
</OverflowMenu>
);
expect(oMenu.hasClass(`${prefix}--overflow-menu--light`)).toEqual(true);
expect(
oMenuOptions.hasClass(`${prefix}--overflow-menu-options--light`)
).toEqual(true);
});
});

describe('open and closed states', () => {
it('open state should be false by default', () => {
const rootWrapper = mount(<OverflowMenu />);
// Enzyme doesn't seem to allow state() in a forwardRef-wrapped class component
expect(rootWrapper.find('OverflowMenu').instance().state.open).toEqual(
false
);
// Enzyme doesn't seem to allow props() in a forwardRef-wrapped class component
expect(rootWrapper.find('OverflowMenu').instance().props.open).toEqual(
false
);
userEvent.click(screen.getByRole('button'));
expect(onClick).toHaveBeenCalled();
});

it('should render a ul with the appropriate class', () => {
const rootWrapper = mount(
<OverflowMenu menuOptionsClass="extra-menu-class">
<OverflowMenuItem className="test-child">one</OverflowMenuItem>
<OverflowMenuItem className="test-child">two</OverflowMenuItem>
it('should call onClose', () => {
const onClose = jest.fn();
render(
<OverflowMenu
ariaLabel="Overflow menu"
className="extra-class"
onClose={onClose}>
<OverflowMenuItem className="test-child" itemText="one" />
<OverflowMenuItem className="test-child" itemText="two" />
</OverflowMenu>
);
// Enzyme doesn't seem to allow setState() in a forwardRef-wrapped class component
rootWrapper.find('OverflowMenu').instance().setState({ open: true });
rootWrapper.update();
const list = rootWrapper.find('ul');
expect(list.length).toEqual(1);
expect(list.hasClass(`${prefix}--overflow-menu-options`)).toEqual(true);
expect(list.hasClass('extra-menu-class')).toEqual(true);

userEvent.click(screen.getByRole('button'));
userEvent.click(screen.getByText('one'));
expect(onClose).toHaveBeenCalled();
});

it('should render children as expected', () => {
const rootWrapper = mount(
<OverflowMenu>
<OverflowMenuItem className="test-child">one</OverflowMenuItem>
<OverflowMenuItem className="test-child">two</OverflowMenuItem>
it('should call onFocus', () => {
const onFocus = jest.fn();
render(
<OverflowMenu
ariaLabel="Overflow menu"
className="extra-class"
onFocus={onFocus}>
<OverflowMenuItem className="test-child" itemText="one" />
<OverflowMenuItem className="test-child" itemText="two" />
</OverflowMenu>
);
// Enzyme doesn't seem to allow setState() in a forwardRef-wrapped class component
rootWrapper.find('OverflowMenu').instance().setState({ open: true });
rootWrapper.update();
expect(rootWrapper.find('button.test-child').length).toEqual(2);
});

it('should set expected class when state is open', () => {
const rootWrapper = mount(<OverflowMenu />);
const openClass = `${prefix}--overflow-menu-options--open`;
expect(rootWrapper.find('ul').length).toEqual(0);
// Enzyme doesn't seem to allow setState() in a forwardRef-wrapped class component
rootWrapper.find('OverflowMenu').instance().setState({ open: true });
rootWrapper.update();
expect(rootWrapper.find('ul').hasClass(openClass)).not.toEqual(false);
userEvent.click(screen.getByRole('button'));
expect(onFocus).toHaveBeenCalled();
});

it('should be in an open state after icon is clicked', () => {
const rootWrapper = mount(<OverflowMenu />);
const menu = rootWrapper.childAt(0);
const icon = menu.find(OverflowMenuVertical);

icon.simulate('click');
// Enzyme doesn't seem to allow state() in a forwardRef-wrapped class component
expect(rootWrapper.find('OverflowMenu').instance().state.open).toEqual(
true
it('should render open if open is true', () => {
render(
<OverflowMenu open ariaLabel="Overflow menu" className="extra-class">
<OverflowMenuItem className="test-child" itemText="one" />
<OverflowMenuItem className="test-child" itemText="two" />
</OverflowMenu>
);
});

it('fires onClick only once per button click', () => {
const mockOnClick = jest.fn();
const rootWrapper = mount(<OverflowMenu onClick={mockOnClick} />);

rootWrapper.find('button').simulate('click');

expect(mockOnClick).toHaveBeenCalledTimes(1);
});

it('should NOT toggle state in response to Enter or Space when the menu is open', () => {
const enterKey = 13;
const spaceKey = 32;
const rootWrapper = mount(<OverflowMenu />);
const menu = rootWrapper.childAt(0);

// Enzyme doesn't seem to allow setState() in a forwardRef-wrapped class component
rootWrapper.find('OverflowMenu').instance().setState({ open: true });

menu.simulate('keydown', { which: spaceKey });
// Enzyme doesn't seem to allow state() in a forwardRef-wrapped class component
expect(rootWrapper.find('OverflowMenu').instance().state.open).toEqual(
true
);
menu.simulate('keydown', { which: enterKey });
// Enzyme doesn't seem to allow state() in a forwardRef-wrapped class component
expect(rootWrapper.find('OverflowMenu').instance().state.open).toEqual(
true
expect(screen.getByRole('button')).toHaveAttribute(
'aria-expanded',
'true'
);
});

it('should be in a closed state after handleOutsideClick() is invoked', () => {
const rootWrapper = mount(<OverflowMenu />);

// Enzyme doesn't seem to allow state() in a forwardRef-wrapped class component
expect(
rootWrapper.find('OverflowMenu').instance().state.open
).not.toEqual(true);

// Enzyme doesn't seem to allow setState() in a forwardRef-wrapped class component
rootWrapper.find('OverflowMenu').instance().setState({ open: true });

rootWrapper
.find('ClickListener')
.props()
.onClickOutside({ target: document.body });

// Enzyme doesn't seem to allow state() in a forwardRef-wrapped class component
expect(
rootWrapper.find('OverflowMenu').instance().state.open
).not.toEqual(true);
});

it('open state should be controlled by open props', () => {
const rootWrapper = mount(<OverflowMenu />);

// Enzyme doesn't seem to allow setState() in a forwardRef-wrapped class component
rootWrapper.find('OverflowMenu').instance().setState({ open: true });
it('should render icon from renderIcon', () => {
render(
<OverflowMenu
ariaLabel="Overflow menu"
className="extra-class"
renderIcon={() => <Filter aria-label="filter icon" />}>
<OverflowMenuItem className="test-child" itemText="one" />
<OverflowMenuItem className="test-child" itemText="two" />
</OverflowMenu>
);

// Enzyme doesn't seem to allow state() in a forwardRef-wrapped class component
expect(rootWrapper.find('OverflowMenu').instance().state.open).toEqual(
true
expect(screen.getByRole('img')).toHaveAttribute(
'aria-label',
'filter icon'
);
});
});

describe('customized icon', () => {
it('renders', () => {
const rootWrapper = mount(
it('should change size based on size prop', () => {
render(
<OverflowMenu
open
ariaLabel="Overflow menu"
className="extra-class"
renderIcon={() => <div className="other">Other</div>}>
<OverflowMenuItem className="test-child">one</OverflowMenuItem>
<OverflowMenuItem className="test-child">two</OverflowMenuItem>
size="lg">
<OverflowMenuItem className="test-child" itemText="one" />
<OverflowMenuItem className="test-child" itemText="two" />
</OverflowMenu>
);
// renderIcon should be the only component where `${prefix}--overflow-menu__icon` class is applied,
// meaning no actual DOM node should have that class
const nodesWithIconClasses = rootWrapper.find(
`.${prefix}--overflow-menu__icon`

expect(screen.getByRole('button')).toHaveClass('cds--overflow-menu--lg');
});

it('should open on click', () => {
render(
<OverflowMenu ariaLabel="Overflow menu" className="extra-class">
<OverflowMenuItem className="test-child" itemText="one" />
<OverflowMenuItem className="test-child" itemText="two" />
</OverflowMenu>
);
expect(nodesWithIconClasses.length).toBe(
nodesWithIconClasses.filter('renderIcon').length

expect(screen.getByRole('button')).toHaveAttribute(
'aria-expanded',
'false'
);
expect(rootWrapper.find('.other')).toHaveLength(1);
});
});

describe('Getting derived state from props', () => {
it('should change the open state upon change in props', () => {
const wrapper = mount(<OverflowMenu open />);
// Enzyme doesn't seem to allow state() in a forwardRef-wrapped class component
expect(wrapper.find('OverflowMenu').instance().state.open).toEqual(true);
wrapper.setProps({ open: false });
// Enzyme doesn't seem to allow state() in a forwardRef-wrapped class component
expect(wrapper.find('OverflowMenu').instance().state.open).toEqual(false);
});
userEvent.click(screen.getByRole('button'));

it('should avoid change the open state upon setting props, unless there the value actually changes', () => {
const wrapper = mount(<OverflowMenu />);
wrapper.setProps({ open: true });
// Enzyme doesn't seem to allow setState() in a forwardRef-wrapped class component
wrapper.find('OverflowMenu').instance().setState({ open: false });
wrapper.update();
wrapper.setProps({ open: true });
// Enzyme doesn't seem to allow state() in a forwardRef-wrapped class component
expect(wrapper.find('OverflowMenu').instance().state.open).toEqual(false);
expect(screen.getByRole('button')).toHaveAttribute(
'aria-expanded',
'true'
);
});
});
});
Loading