wix-style-react
Version:
509 lines (421 loc) • 17.2 kB
JavaScript
import React from 'react';
import More from 'wix-ui-icons-common/More';
import { cleanup, createRendererWithUniDriver } from '../../../test/utils/unit';
import PopoverMenu from '../PopoverMenu';
import { PopoverMenuPrivateDriver } from './PopoverMenu.private.uni.driver';
import IconButton from '../../IconButton';
import { iconButtonDriverFactory } from '../../IconButton/IconButton.uni.driver';
import eventually from 'wix-eventually';
describe('PopoverMenu', () => {
const render = createRendererWithUniDriver(PopoverMenuPrivateDriver);
afterEach(() => {
cleanup();
});
const renderPopoverMenu = (props = {}) => (
<PopoverMenu
dataHook="random"
triggerElement={({ toggle }) => (
<IconButton dataHook="iconbutton" onClick={toggle}>
<More />
</IconButton>
)}
children={<PopoverMenu.MenuItem text="dark option" skin="dark" />}
{...props}
/>
);
const renderPopoverMenuItem = (props = {}) => (
<PopoverMenu.MenuItem text="dark option" skin="dark" {...props} />
);
const renderPopoverDivider = (props = {}) => (
<PopoverMenu.Divider {...props} />
);
describe('`triggerElement` prop', () => {
it('should open menu [when] direct React element is passed', async () => {
const { driver } = render(
renderPopoverMenu({
triggerElement: <IconButton dataHook="iconbutton-custom" />,
}),
);
const iconButton = await driver.getTriggerElement('iconbutton-custom');
const iconButtonTestkit = iconButtonDriverFactory(iconButton);
await iconButtonTestkit.click();
expect(await driver.isMenuOpen()).toBe(true);
});
it('should open menu [when] render props function is used', async () => {
const { driver } = render(
renderPopoverMenu({
triggerElement: ({ toggle }) => (
<IconButton onClick={toggle} dataHook="iconbutton-custom" />
),
}),
);
const iconButton = await driver.getTriggerElement('iconbutton-custom');
const iconButtonTestkit = iconButtonDriverFactory(iconButton);
await iconButtonTestkit.click();
expect(await driver.isMenuOpen()).toBe(true);
});
});
describe('`zIndex` prop', () => {
it('should set z-index value', async () => {
const { driver } = render(renderPopoverMenu({ zIndex: 123456 }));
const iconButton = await driver.getTriggerElement('iconbutton');
const iconButtonTestkit = iconButtonDriverFactory(iconButton);
await iconButtonTestkit.click();
expect(await driver.getZIndex()).toBe('123456');
});
});
describe('`children` prop', () => {
describe('PopoverMenu.Item', () => {
describe('`onClick` prop', () => {
it('should be called [when] clicked inside PopoverMenu', async () => {
const onClick = jest.fn();
const { driver } = render(
renderPopoverMenu({
children: renderPopoverMenuItem({ onClick }),
}),
);
const iconButton = await driver.getTriggerElement('iconbutton');
const iconButtonTestkit = iconButtonDriverFactory(iconButton);
await iconButtonTestkit.click();
expect(await driver.isMenuOpen()).toBe(true);
await driver.clickAtChild(0);
expect(onClick).toHaveBeenCalled();
});
it('should be called [when] clicked inside PopoverMenu using dataHook', async () => {
const onClick = jest.fn();
const { driver } = render(
renderPopoverMenu({
children: renderPopoverMenuItem({
onClick,
dataHook: 'testDataHook',
}),
}),
);
const iconButton = await driver.getTriggerElement('iconbutton');
const iconButtonTestkit = iconButtonDriverFactory(iconButton);
await iconButtonTestkit.click();
expect(await driver.isMenuOpen()).toBe(true);
await driver.clickAtChildByDataHook('testDataHook');
expect(onClick).toHaveBeenCalled();
});
it('should be called only once', async () => {
const onClick = jest.fn();
const { driver } = render(
renderPopoverMenu({
children: renderPopoverMenuItem({ onClick }),
}),
);
const iconButton = await driver.getTriggerElement('iconbutton');
const iconButtonTestkit = iconButtonDriverFactory(iconButton);
await iconButtonTestkit.click();
expect(await driver.isMenuOpen()).toBe(true);
const option = await driver.getMenuItem(0);
await option.click(); // Imitates real user click; Using driver.clickAtChild(0) does not simulate real click
expect(onClick).toHaveBeenCalledTimes(1);
});
it('should allow passing `dataHook` prop', async () => {
const { driver } = render(
renderPopoverMenu({
children: [renderPopoverMenuItem({ dataHook: 'random' })],
}),
);
await driver.keyDownTrigger('Enter');
expect(await driver.isMenuOpen()).toBe(true);
expect(
(await driver.getMenuItem(0, 'random')).getAttribute('data-hook'),
).toBe('random');
});
});
describe('`text` prop', () => {
it('should render the given text', async () => {
const text = 'First Menu Item';
const { driver } = render(
renderPopoverMenu({
children: renderPopoverMenuItem({ text }),
}),
);
const iconButton = await driver.getTriggerElement('iconbutton');
const iconButtonTestkit = iconButtonDriverFactory(iconButton);
await iconButtonTestkit.click();
expect(await driver.isMenuOpen()).toBe(true);
expect(await driver.itemContentAt(0)).toBe(text);
});
});
});
describe('PopoverMenu.Divider', () => {
it('should not throw errors [when] clicked', async () => {
const onClick = jest.fn();
const { driver } = render(
renderPopoverMenu({
children: renderPopoverDivider({ onClick }),
}),
);
const iconButton = await driver.getTriggerElement('iconbutton');
const iconButtonTestkit = iconButtonDriverFactory(iconButton);
await iconButtonTestkit.click();
expect(await driver.isMenuOpen()).toBe(true);
await driver.clickAtChild(0);
expect(onClick).not.toHaveBeenCalled();
});
it('should allow passing `dataHook` prop', async () => {
const { driver } = render(
renderPopoverMenu({
children: [renderPopoverDivider({ dataHook: 'random' })],
}),
);
await driver.keyDownTrigger('Enter');
expect(await driver.isMenuOpen()).toBe(true);
expect(
(await driver.getDivider('random')).getAttribute('data-hook'),
).toBe('random');
});
});
describe('String', () => {
it('should be filtered out from rendering', async () => {
const { driver } = render(
renderPopoverMenu({
children: [
renderPopoverMenuItem(),
renderPopoverMenuItem(),
'string',
],
}),
);
const iconButton = await driver.getTriggerElement('iconbutton');
const iconButtonTestkit = iconButtonDriverFactory(iconButton);
await iconButtonTestkit.click();
expect(await driver.isMenuOpen()).toBe(true);
expect(await driver.childrenCount()).toBe(2);
});
});
describe('React element', () => {
it('should render node based items', async () => {
const { driver } = render(
renderPopoverMenu({
children: [
renderPopoverMenuItem(),
renderPopoverMenuItem(),
<div>custom</div>,
],
}),
);
const iconButton = await driver.getTriggerElement('iconbutton');
const iconButtonTestkit = iconButtonDriverFactory(iconButton);
await iconButtonTestkit.click();
expect(await driver.isMenuOpen()).toBe(true);
expect(await driver.childrenCount()).toBe(3);
});
});
describe('Controlled behaviour', () => {
it('should allow controlling the behaviour using the `toggle` render prop', async () => {
let _args;
const { driver } = render(
renderPopoverMenu({
triggerElement: args => {
_args = args;
return <IconButton dataHook="iconbutton" />;
},
}),
);
_args.open();
expect(await driver.isMenuOpen()).toBe(true);
});
it('should allow controlling the behaviour using the `open` render prop', async () => {
let _args;
const { driver } = render(
renderPopoverMenu({
triggerElement: args => {
_args = args;
return <IconButton dataHook="iconbutton" />;
},
}),
);
_args.open();
expect(await driver.isMenuOpen()).toBe(true);
});
it('should allow controlling the behaviour using the `close` render prop', async () => {
let _args;
const { driver } = render(
renderPopoverMenu({
triggerElement: args => {
_args = args;
return <IconButton dataHook="iconbutton" />;
},
}),
);
_args.close();
expect(await driver.isMenuOpen()).toBe(false);
});
});
describe('`onShow` prop', () => {
it('should call `onShow` with when menu is opened', async () => {
const onShow = jest.fn();
const { driver } = render(renderPopoverMenu({ onShow }));
const iconButton = await driver.getTriggerElement('iconbutton');
const iconButtonTestkit = iconButtonDriverFactory(iconButton);
await iconButtonTestkit.click();
expect(await driver.isMenuOpen()).toBe(true);
expect(onShow).toBeCalled();
});
});
describe('`onHide` prop', () => {
it('should call `onHide` with when menu is closed', async () => {
const onHide = jest.fn();
const { driver } = render(renderPopoverMenu({ onHide }));
const iconButton = await driver.getTriggerElement('iconbutton');
const iconButtonTestkit = iconButtonDriverFactory(iconButton);
await iconButtonTestkit.click();
expect(await driver.isMenuOpen()).toBe(true);
await iconButtonTestkit.click();
await eventually(async () =>
expect(await driver.isMenuOpen()).toBe(false),
);
expect(onHide).toBeCalled();
});
});
describe('Accessibility', () => {
describe('Menu', () => {
it('should open [when] when triggered with key Enter', async () => {
const dataHook = 'iconbutton-custom';
const { driver } = render(
renderPopoverMenu({
triggerElement: <IconButton dataHook={dataHook} />,
}),
);
await driver.keyDownTrigger('Enter');
expect(await driver.isMenuOpen()).toBe(true);
});
it('should be opened [when] when triggered with key Space', async () => {
const dataHook = 'iconbutton-custom';
const { driver } = render(
renderPopoverMenu({
triggerElement: <IconButton dataHook={dataHook} />,
}),
);
await driver.keyDownTrigger(' ');
expect(await driver.isMenuOpen()).toBe(true);
});
it('should be closed [when] one of the menu items triggered with key Escape', async () => {
const dataHook = 'iconbutton-custom';
const { driver } = render(
renderPopoverMenu({
triggerElement: <IconButton dataHook={dataHook} />,
}),
);
await driver.keyDownTrigger('Enter');
expect(await driver.isMenuOpen()).toBe(true);
await driver.keyDownTrigger('Escape');
await eventually(async () =>
expect(await driver.isMenuOpen()).toBe(false),
);
});
});
describe('Roving TabIndex', () => {
it('focused item should have tabIndex set to 0', async () => {
const { driver } = render(
renderPopoverMenu({ children: renderPopoverMenuItem() }),
);
await driver.keyDownTrigger('Enter');
expect(await driver.isMenuOpen()).toBe(true);
expect((await driver.getMenuItem(0)).tabIndex).toBe(0);
});
it('not focused items should have tabIndex -1', async () => {
const { driver } = render(
renderPopoverMenu({
children: [renderPopoverMenuItem(), renderPopoverMenuItem()],
}),
);
await driver.keyDownTrigger('Enter');
expect(await driver.isMenuOpen()).toBe(true);
expect((await driver.getMenuItem(1)).tabIndex).toBe(-1);
});
it('navigating to next item should set next item with tabIndex to 0', async () => {
const { driver } = render(
renderPopoverMenu({
children: [renderPopoverMenuItem(), renderPopoverMenuItem()],
}),
);
await driver.keyDownTrigger('Enter');
expect(await driver.isMenuOpen()).toBe(true);
await driver.keyDownOption(0, 'ArrowDown');
expect((await driver.getMenuItem(0)).tabIndex).toBe(-1);
expect((await driver.getMenuItem(1)).tabIndex).toBe(0);
});
it('navigating to next item [when] item in front is divider should ignore it and and jump to next item ', async () => {
const { driver } = render(
renderPopoverMenu({
children: [
renderPopoverMenuItem(),
renderPopoverDivider(),
renderPopoverMenuItem(),
],
}),
);
await driver.keyDownTrigger('Enter');
expect(await driver.isMenuOpen()).toBe(true);
await driver.keyDownOption(0, 'ArrowDown');
expect((await driver.getMenuItem(0)).tabIndex).toBe(-1);
expect((await driver.getMenuItem(2)).tabIndex).toBe(0);
});
it('navigating to next item [when] item in front is disabled should ignore it and jump to next item', async () => {
const { driver } = render(
renderPopoverMenu({
children: [
renderPopoverMenuItem(),
renderPopoverMenuItem({ disabled: true }),
renderPopoverMenuItem(),
],
}),
);
await driver.keyDownTrigger('Enter');
expect(await driver.isMenuOpen()).toBe(true);
await driver.keyDownOption(0, 'ArrowDown');
expect((await driver.getMenuItem(0)).tabIndex).toBe(-1);
expect((await driver.getMenuItem(1)).tabIndex).toBe(-1);
expect((await driver.getMenuItem(2)).tabIndex).toBe(0);
});
it('navigating to next item [when] there is no options left should go back to first ', async () => {
const { driver } = render(
renderPopoverMenu({
children: [
renderPopoverMenuItem(),
renderPopoverMenuItem(),
renderPopoverMenuItem(),
],
}),
);
await driver.keyDownTrigger('Enter');
expect(await driver.isMenuOpen()).toBe(true);
await driver.keyDownOption(0, 'ArrowDown');
expect((await driver.getMenuItem(0)).tabIndex).toBe(-1);
expect((await driver.getMenuItem(1)).tabIndex).toBe(0);
await driver.keyDownOption(1, 'ArrowDown');
expect((await driver.getMenuItem(0)).tabIndex).toBe(-1);
expect((await driver.getMenuItem(1)).tabIndex).toBe(-1);
expect((await driver.getMenuItem(2)).tabIndex).toBe(0);
await driver.keyDownOption(2, 'ArrowDown');
expect((await driver.getMenuItem(0)).tabIndex).toBe(0);
expect((await driver.getMenuItem(1)).tabIndex).toBe(-1);
expect((await driver.getMenuItem(2)).tabIndex).toBe(-1);
});
it('navigating to previous item [when] there is no options in front should jump to last item', async () => {
const { driver } = render(
renderPopoverMenu({
children: [
renderPopoverMenuItem(),
renderPopoverMenuItem(),
renderPopoverMenuItem(),
],
}),
);
await driver.keyDownTrigger('Enter');
expect(await driver.isMenuOpen()).toBe(true);
await driver.keyDownOption(0, 'ArrowUp');
expect((await driver.getMenuItem(0)).tabIndex).toBe(-1);
expect((await driver.getMenuItem(1)).tabIndex).toBe(-1);
expect((await driver.getMenuItem(2)).tabIndex).toBe(0);
});
});
});
});
});