diff --git a/components/menu/__tests__/demo.test.js b/components/menu/__tests__/demo.test.js new file mode 100644 index 000000000..ad45d6588 --- /dev/null +++ b/components/menu/__tests__/demo.test.js @@ -0,0 +1,3 @@ +import demoTest from '../../../tests/shared/demoTest'; + +demoTest('menu'); diff --git a/components/menu/__tests__/index.test.js b/components/menu/__tests__/index.test.js new file mode 100644 index 000000000..ed370bc10 --- /dev/null +++ b/components/menu/__tests__/index.test.js @@ -0,0 +1,542 @@ +import { mount } from '@vue/test-utils'; +import { asyncExpect } from '@/tests/utils'; +import Menu from '..'; +import { InboxOutlined, PieChartOutlined } from '@ant-design/icons-vue'; +import mountTest from '../../../tests/shared/mountTest'; + +const { SubMenu } = Menu; +function $$(className) { + return document.body.querySelectorAll(className); +} +describe('Menu', () => { + mountTest({ + render() { + return ( + <div> + <Menu> + <Menu.Item /> + <Menu.ItemGroup /> + <Menu.SubMenu /> + </Menu> + </div> + ); + }, + }); + beforeEach(() => { + document.body.innerHTML = ''; + // jest.useFakeTimers() + }); + + afterEach(() => { + // jest.useRealTimers() + }); + it('If has select nested submenu item ,the menu items on the grandfather level should be highlight', async () => { + mount( + { + render() { + return ( + <Menu defaultSelectedKeys={['1-3-2']} mode="vertical"> + <SubMenu key="1" title="submenu1"> + <Menu.Item key="1-1">Option 1</Menu.Item> + <Menu.Item key="1-2">Option 2</Menu.Item> + <SubMenu key="1-3" title="submenu1-3"> + <Menu.Item key="1-3-1">Option 3</Menu.Item> + <Menu.Item key="1-3-2">Option 4</Menu.Item> + </SubMenu> + </SubMenu> + <Menu.Item key="2">menu2</Menu.Item> + </Menu> + ); + }, + }, + { attachTo: 'body', sync: false }, + ); + await asyncExpect(() => { + expect($$('.ant-menu-submenu-selected').length).toBe(1); + }); + }); + it('should accept defaultOpenKeys in mode horizontal', async () => { + mount( + { + render() { + return ( + <Menu defaultOpenKeys={['1']} mode="horizontal"> + <SubMenu key="1" title="submenu1"> + <Menu.Item key="submenu1">Option 1</Menu.Item> + <Menu.Item key="submenu2">Option 2</Menu.Item> + </SubMenu> + <Menu.Item key="2">menu2</Menu.Item> + </Menu> + ); + }, + }, + { attachTo: 'body', sync: false }, + ); + await asyncExpect(() => { + expect($$('.ant-menu-sub')[0].parentElement.style.display).not.toBe('none'); + }); + }); + + it('should accept defaultOpenKeys in mode inline', async () => { + mount( + { + render() { + return ( + <Menu defaultOpenKeys={['1']} mode="inline"> + <SubMenu key="1" title="submenu1"> + <Menu.Item key="submenu1">Option 1</Menu.Item> + <Menu.Item key="submenu2">Option 2</Menu.Item> + </SubMenu> + <Menu.Item key="2">menu2</Menu.Item> + </Menu> + ); + }, + }, + { attachTo: 'body', sync: false }, + ); + await asyncExpect(() => { + expect($$('.ant-menu-sub')[0].parentElement.style.display).not.toBe('none'); + }); + }); + + it('should accept defaultOpenKeys in mode vertical', async () => { + mount( + { + render() { + return ( + <Menu defaultOpenKeys={['1']} mode="vertical"> + <SubMenu key="1" title="submenu1"> + <Menu.Item key="submenu1">Option 1</Menu.Item> + <Menu.Item key="submenu2">Option 2</Menu.Item> + </SubMenu> + <Menu.Item key="2">menu2</Menu.Item> + </Menu> + ); + }, + }, + { attachTo: 'body', sync: false }, + ); + await asyncExpect(() => { + expect($$('.ant-menu-sub')[0].parentElement.style.display).not.toBe('none'); + }); + }); + + it('horizontal', async () => { + const wrapper = mount( + { + props: { + openKeys: { + type: Array, + default() { + return ['1']; + }, + }, + }, + render() { + return ( + <Menu openKeys={this.openKeys} mode="horizontal" openTransitionName=""> + <SubMenu key="1" title="submenu1"> + <Menu.Item key="submenu1">Option 1</Menu.Item> + <Menu.Item key="submenu2">Option 2</Menu.Item> + </SubMenu> + <Menu.Item key="2">menu2</Menu.Item> + </Menu> + ); + }, + }, + { attachTo: 'body', sync: false }, + ); + await asyncExpect(() => { + expect($$('.ant-menu-sub')[0].parentElement.style.display).not.toBe('none'); + }); + wrapper.setProps({ openKeys: [] }); + await asyncExpect(() => { + expect($$('.ant-menu-sub')[0].parentElement.style.display).toBe('none'); + }, 500); + + wrapper.setProps({ openKeys: ['1'] }); + await asyncExpect(() => { + expect($$('.ant-menu-sub')[0].parentElement.style.display).not.toBe('none'); + }, 0); + }); + + it('inline', async () => { + const wrapper = mount( + { + props: { + openKeys: { + type: Array, + default() { + return ['1']; + }, + }, + }, + render() { + return ( + <Menu openKeys={this.openKeys} mode="inline" openAnimation=""> + <SubMenu key="1" title="submenu1"> + <Menu.Item key="submenu1">Option 1</Menu.Item> + <Menu.Item key="submenu2">Option 2</Menu.Item> + </SubMenu> + <Menu.Item key="2">menu2</Menu.Item> + </Menu> + ); + }, + }, + { attachTo: 'body', sync: false }, + ); + await asyncExpect(() => { + expect($$('.ant-menu-sub')[0].style.display).not.toBe('none'); + }); + wrapper.setProps({ openKeys: [] }); + await asyncExpect(() => { + expect($$('.ant-menu-sub')[0].style.display).toBe('none'); + }, 0); + wrapper.setProps({ openKeys: ['1'] }); + await asyncExpect(() => { + expect($$('.ant-menu-sub')[0].style.display).not.toBe('none'); + }, 0); + }); + + it('vertical', async () => { + const wrapper = mount( + { + props: { + openKeys: { + type: Array, + default() { + return ['1']; + }, + }, + }, + render() { + return ( + <Menu openKeys={this.openKeys} mode="vertical" openTransitionName=""> + <SubMenu key="1" title="submenu1"> + <Menu.Item key="submenu1">Option 1</Menu.Item> + <Menu.Item key="submenu2">Option 2</Menu.Item> + </SubMenu> + <Menu.Item key="2">menu2</Menu.Item> + </Menu> + ); + }, + }, + { attachTo: 'body', sync: false }, + ); + await asyncExpect(() => { + expect($$('.ant-menu-sub')[0].parentElement.style.display).not.toBe('none'); + }); + wrapper.setProps({ openKeys: [] }); + await asyncExpect(() => { + expect($$('.ant-menu-sub')[0].parentElement.style.display).toBe('none'); + }, 500); + wrapper.setProps({ openKeys: ['1'] }); + await asyncExpect(() => { + expect($$('.ant-menu-sub')[0].parentElement.style.display).not.toBe('none'); + }, 0); + }); + + // https://github.com/ant-design/ant-design/pulls/4677 + // https://github.com/ant-design/ant-design/issues/4692 + // TypeError: Cannot read property 'indexOf' of undefined + it('pr #4677 and issue #4692', () => { + const wrapper = mount( + { + render() { + return ( + <Menu mode="horizontal"> + <SubMenu title="submenu"> + <Menu.Item key="1">menu1</Menu.Item> + <Menu.Item key="2">menu2</Menu.Item> + </SubMenu> + </Menu> + ); + }, + }, + { attachTo: 'body', sync: false }, + ); + wrapper.vm.$forceUpdate(); + // just expect no error emit + }); + + it('should always follow openKeys when mode is switched', async () => { + const wrapper = mount( + { + props: { + mode: { + type: String, + default: 'inline', + }, + }, + render() { + return ( + <Menu openKeys={['1']} mode={this.mode}> + <SubMenu key="1" title="submenu1"> + <Menu.Item key="submenu1">Option 1</Menu.Item> + <Menu.Item key="submenu2">Option 2</Menu.Item> + </SubMenu> + <Menu.Item key="2">menu2</Menu.Item> + </Menu> + ); + }, + }, + { attachTo: 'body', sync: false }, + ); + await asyncExpect(() => { + expect($$('ul.ant-menu-sub')[0].style.display).not.toBe('none'); + }); + wrapper.setProps({ mode: 'vertical' }); + await asyncExpect(() => { + expect($$('ul.ant-menu-sub')[0].parentElement.style.display).not.toBe('none'); + }, 0); + wrapper.setProps({ mode: 'inline' }); + await asyncExpect(() => { + expect($$('ul.ant-menu-sub')[0].style.display).not.toBe('none'); + }, 0); + }); + + it('should always follow openKeys when inlineCollapsed is switched', async () => { + const wrapper = mount( + { + props: { + inlineCollapsed: { + type: Boolean, + default: false, + }, + }, + render() { + return ( + <Menu + ref="menu" + defaultOpenKeys={['1']} + mode="inline" + inlineCollapsed={this.inlineCollapsed} + > + <Menu.Item key="menu1"> + <InboxOutlined /> + <span>Option</span> + </Menu.Item> + <SubMenu key="1" title="submenu1"> + <Menu.Item key="submenu1">Option</Menu.Item> + <Menu.Item key="submenu2">Option</Menu.Item> + </SubMenu> + </Menu> + ); + }, + }, + { attachTo: 'body', sync: false }, + ); + await asyncExpect(() => { + expect(wrapper.findAll('ul.ant-menu-sub')[0].classes()).toContain('ant-menu-inline'); + expect($$('ul.ant-menu-sub')[0].style.display).not.toBe('none'); + }, 0); + wrapper.setProps({ inlineCollapsed: true }); + await asyncExpect(() => { + // 动画完成后的回调 + wrapper.vm.$refs.menu.switchModeFromInline = false; + wrapper.vm.$forceUpdate(); + }); + // await asyncExpect(() => { + // wrapper.trigger('transitionend', { propertyName: 'width' }); + // }); + // await asyncExpect(() => { + // expect(wrapper.findAll('ul.ant-menu-root')[0].classes()).toContain('ant-menu-vertical'); + // expect(wrapper.findAll('ul.ant-menu-sub').length).toBe(0); + // }, 500); + wrapper.setProps({ inlineCollapsed: false }); + await asyncExpect(() => { + expect(wrapper.findAll('ul.ant-menu-sub')[0].classes()).toContain('ant-menu-inline'); + expect($$('ul.ant-menu-sub')[0].style.display).not.toBe('none'); + }, 0); + }); + + it('inlineCollapsed should works well when specify a not existed default openKeys', async () => { + const wrapper = mount( + { + props: { + inlineCollapsed: { + type: Boolean, + default: false, + }, + }, + render() { + return ( + <Menu + ref="menu" + defaultOpenKeys={['not-existed']} + mode="inline" + inlineCollapsed={this.inlineCollapsed} + > + <Menu.Item key="menu1"> + <InboxOutlined /> + <span>Option</span> + </Menu.Item> + <SubMenu key="1" title="submenu1"> + <Menu.Item key="submenu1">Option</Menu.Item> + <Menu.Item key="submenu2">Option</Menu.Item> + </SubMenu> + </Menu> + ); + }, + }, + { attachTo: 'body', sync: false }, + ); + await asyncExpect(() => { + expect(wrapper.findAll('.ant-menu-sub').length).toBe(0); + }); + wrapper.setProps({ inlineCollapsed: true }); + await asyncExpect(() => { + // 动画完成后的回调 + wrapper.vm.$refs.menu.switchModeFromInline = false; + wrapper.vm.$forceUpdate(); + }); + // await asyncExpect(() => { + // wrapper.trigger('transitionend', { propertyName: 'width' }); + // }); + // await asyncExpect(() => { + // $$('.ant-menu-submenu-title')[0].dispatchEvent(new MouseEvent('mouseenter')); + // }); + // await asyncExpect(() => { + // expect(wrapper.findAll('.ant-menu-submenu')[0].classes()).toContain( + // 'ant-menu-submenu-vertical', + // ); + // expect(wrapper.findAll('.ant-menu-submenu')[0].classes()).toContain('ant-menu-submenu-open'); + // expect($$('ul.ant-menu-sub')[0].className).toContain('ant-menu-vertical'); + // expect($$('ul.ant-menu-sub')[0].style.display).not.toBe('none'); + // }, 500); + }); + + describe('open submenu when click submenu title', () => { + beforeEach(() => { + document.body.innerHTML = ''; + }); + + const toggleMenu = (wrapper, index, event) => { + wrapper.findAll('.ant-menu-submenu-title')[index].trigger(event); + }; + + it('inline', async () => { + const wrapper = mount( + { + render() { + return ( + <Menu mode="inline"> + <SubMenu key="1" title="submenu1"> + <Menu.Item key="submenu1">Option 1</Menu.Item> + <Menu.Item key="submenu2">Option 2</Menu.Item> + </SubMenu> + <Menu.Item key="2">menu2</Menu.Item> + </Menu> + ); + }, + }, + { attachTo: 'body', sync: false }, + ); + await asyncExpect(() => { + expect($$('.ant-menu-sub').length).toBe(0); + toggleMenu(wrapper, 0, 'click'); + }, 0); + await asyncExpect(() => { + expect($$('.ant-menu-sub').length).toBe(1); + expect($$('.ant-menu-sub')[0].style.display).not.toBe('none'); + toggleMenu(wrapper, 0, 'click'); + }, 500); + await asyncExpect(() => { + expect($$('.ant-menu-sub')[0].style.display).toBe('none'); + }, 500); + }); + + it('vertical', async () => { + const wrapper = mount( + { + render() { + return ( + <Menu mode="vertical"> + <SubMenu key="1" title="submenu1"> + <Menu.Item key="submenu1">Option 1</Menu.Item> + <Menu.Item key="submenu2">Option 2</Menu.Item> + </SubMenu> + <Menu.Item key="2">menu2</Menu.Item> + </Menu> + ); + }, + }, + { attachTo: 'body', sync: false }, + ); + await asyncExpect(() => { + expect($$('.ant-menu-sub').length).toBe(0); + toggleMenu(wrapper, 0, 'mouseenter'); + }, 0); + await asyncExpect(() => { + expect($$('.ant-menu-sub').length).toBe(1); + expect($$('.ant-menu-sub')[0].parentElement.style.display).not.toBe('none'); + toggleMenu(wrapper, 0, 'mouseleave'); + }, 500); + await asyncExpect(() => { + expect($$('.ant-menu-sub')[0].parentElement.style.display).toBe('none'); + }, 500); + }); + + it('horizontal', async () => { + const wrapper = mount( + { + render() { + return ( + <Menu mode="horizontal"> + <SubMenu key="1" title="submenu1"> + <Menu.Item key="submenu1">Option 1</Menu.Item> + <Menu.Item key="submenu2">Option 2</Menu.Item> + </SubMenu> + <Menu.Item key="2">menu2</Menu.Item> + </Menu> + ); + }, + }, + { attachTo: 'body', sync: false }, + ); + await asyncExpect(() => { + expect($$('.ant-menu-sub').length).toBe(0); + toggleMenu(wrapper, 1, 'mouseenter'); + }, 100); + await asyncExpect(() => { + expect($$('.ant-menu-sub').length).toBe(1); + expect($$('.ant-menu-sub')[0].parentElement.style.display).not.toBe('none'); + toggleMenu(wrapper, 1, 'mouseleave'); + }, 500); + await asyncExpect(() => { + expect($$('.ant-menu-sub')[0].parentElement.style.display).toBe('none'); + }, 500); + }); + }); + + it('inline title', async () => { + const wrapper = mount( + { + render() { + return ( + <Menu mode="inline" inlineCollapsed> + <Menu.Item key="1" title="bamboo lucky"> + <PieChartOutlined /> + <span> + Option 1 + <img + style={{ width: 20 }} + alt="test" + src="https://gw.alipayobjects.com/zos/rmsportal/KDpgvguMpGfqaHPjicRK.svg" + /> + </span> + </Menu.Item> + </Menu> + ); + }, + }, + { sync: false, attachTo: 'body' }, + ); + + wrapper.find('.ant-menu-item').trigger('mouseenter'); + await asyncExpect(() => { + const text = $$('.ant-tooltip-inner')[0].textContent; + expect(text).toBe('bamboo lucky'); + }, 500); + }); +});