// @ts-nocheck import { nextTick } from 'vue' import { mount } from '@vue/test-utils' import { afterEach, describe, expect, test, vi } from 'vitest' import { rAF } from '@element-plus/test-utils/tick' import { EVENT_CODE } from '@element-plus/constants' import { ElTooltip } from '@element-plus/components/tooltip' import Button from '@element-plus/components/button' import { usePopperContainerId } from '@element-plus/hooks' import Dropdown from '../src/dropdown.vue' import DropdownItem from '../src/dropdown-item.vue' import DropdownMenu from '../src/dropdown-menu.vue' const MOUSE_ENTER_EVENT = 'mouseenter' const MOUSE_LEAVE_EVENT = 'mouseleave' const CONTEXTMENU = 'contextmenu' const _mount = (template: string, data, otherObj?) => mount({ components: { [Button.name]: Button, [Dropdown.name]: Dropdown, [DropdownItem.name]: DropdownItem, [DropdownMenu.name]: DropdownMenu, }, template, data, ...otherObj, }) describe('Dropdown', () => { afterEach(() => { document.body.innerHTML = '' }) test('create', async () => { const wrapper = _mount( ` dropdown `, () => ({}) ) await nextTick() const content = wrapper.findComponent(ElTooltip).vm as InstanceType< typeof ElTooltip > vi.useFakeTimers() const triggerElm = wrapper.find('.el-tooltip__trigger') expect(content.open).toBe(false) await triggerElm.trigger(MOUSE_ENTER_EVENT) vi.runAllTimers() expect(content.open).toBe(true) await triggerElm.trigger(MOUSE_LEAVE_EVENT) vi.runAllTimers() expect(content.open).toBe(false) vi.useRealTimers() }) test('menu click', async () => { const commandHandler = vi.fn() const wrapper = _mount( ` dropdown `, () => ({ myCommandObject: { name: 'CommandC' }, name: '', }), { methods: { commandHandler, }, } ) await nextTick() // const content = wrapper.findComponent({ ref: 'b' }).vm as any const triggerElm = wrapper.find('.el-tooltip__trigger') await triggerElm.trigger(MOUSE_ENTER_EVENT) await nextTick() await wrapper .findComponent({ ref: 'c' }) .findComponent({ name: 'DropdownItemImpl', }) .find('.el-dropdown-menu__item') .trigger('click') await nextTick() expect(commandHandler).toHaveBeenCalled() }) test('trigger', async () => { const wrapper = _mount( ` dropdown `, () => ({ myCommandObject: { name: 'CommandC' }, name: '', }) ) await nextTick() const content = wrapper.findComponent(ElTooltip).vm as InstanceType< typeof ElTooltip > const triggerElm = wrapper.find('.el-dropdown-link') expect(content.open).toBe(false) await triggerElm.trigger(MOUSE_ENTER_EVENT) expect(content.open).toBe(false) await triggerElm.trigger('click', { button: 0, }) await rAF() expect(content.open).toBe(true) }) test('trigger contextmenu', async () => { const wrapper = _mount( ` dropdown `, () => ({ myCommandObject: { name: 'CommandC' }, name: '', }) ) await nextTick() const content = wrapper.findComponent(ElTooltip).vm as InstanceType< typeof ElTooltip > const triggerElm = wrapper.find('.el-dropdown-link') expect(content.open).toBe(false) await triggerElm.trigger(CONTEXTMENU) await rAF() expect(content.open).toBe(true) }) test('virtual ref', async () => { const wrapper = _mount( ` Right click `, function () { return { dropdownRef: null, popperOptions: { modifiers: [ { name: 'offset', options: { offset: [0, 0] } }, { name: 'flip', options: { fallbackPlacements: ['bottom'] } }, ], }, position: { top: 0, left: 0, bottom: 0, right: 0, x: 0, y: 0, width: 0, height: 0, toJSON: () => {}, }, triggerRef: { getBoundingClientRect: () => this.position, }, } }, { methods: { handleClick() { this.$refs.dropdownRef?.handleClose() }, handleContextmenu(event: MouseEvent) { event.preventDefault() const { clientX, clientY } = event this.position = { ...this.position, top: clientY, left: clientX, x: clientX, y: clientY, right: clientX, bottom: clientY, } this.$refs.dropdownRef?.handleOpen() }, }, } ) await nextTick() const content = wrapper.findComponent(ElTooltip).vm as InstanceType< typeof ElTooltip > const triggerElm = wrapper.find('.el-button') expect(content.open).toBe(false) vi.useFakeTimers() await triggerElm.trigger('contextmenu', { clientX: 100, clientY: 100, }) vi.runAllTimers() expect(content.open).toBe(true) const dropdownEl = document.querySelector('.virtual-ref-cls') const dropdownStyle = getComputedStyle(dropdownEl) expect(dropdownStyle.left).toBe('100px') expect(dropdownStyle.top).toBe('100px') await triggerElm.trigger('click') vi.runAllTimers() expect(content.open).toBe(false) vi.useRealTimers() }) test('handleOpen and handleClose', async () => { const wrapper = _mount( ` dropdown `, () => ({ name: '', }) ) await nextTick() const dropdown = wrapper.vm const content = wrapper.findComponent(ElTooltip).vm as InstanceType< typeof ElTooltip > expect(content.open).toBe(false) await dropdown.$refs.refDropdown.handleOpen() await rAF() expect(content.open).toBe(true) await dropdown.$refs.refDropdown.handleClose() await rAF() expect(content.open).toBe(false) }) test('split button', async () => { const handleClick = vi.fn() const wrapper = _mount( ` dropdown `, () => ({ myCommandObject: { name: 'CommandC' }, name: '', }), { methods: { handleClick, }, } ) await nextTick() const content = wrapper.findComponent(ElTooltip).vm as InstanceType< typeof ElTooltip > const triggerElm = wrapper.find('.el-dropdown__caret-button') const button = wrapper.find('.el-button') expect(content.open).toBe(false) await button.trigger('click') expect(handleClick).toHaveBeenCalled() vi.useFakeTimers() await triggerElm.trigger(MOUSE_ENTER_EVENT) vi.runAllTimers() vi.useRealTimers() expect(content.open).toBe(true) }) test('hide on click', async () => { const wrapper = _mount( ` dropdown `, () => ({}) ) await nextTick() const content = wrapper.findComponent(ElTooltip).vm as InstanceType< typeof ElTooltip > expect(content.open).toBe(false) const triggerElm = wrapper.find('.el-tooltip__trigger') vi.useFakeTimers() await triggerElm.trigger(MOUSE_ENTER_EVENT) vi.runAllTimers() expect(content.open).toBe(true) await wrapper .findComponent({ ref: 'c' }) .findComponent({ name: 'DropdownItemImpl', }) .trigger('click') vi.runAllTimers() expect(content.open).toBe(true) vi.useRealTimers() }) test('triggerElm keydown', async () => { const wrapper = _mount( ` dropdown `, () => ({}) ) await nextTick() const content = wrapper.findComponent(ElTooltip).vm as InstanceType< typeof ElTooltip > const triggerElm = wrapper.find('.el-tooltip__trigger') vi.useFakeTimers() await triggerElm.trigger(MOUSE_ENTER_EVENT) vi.runAllTimers() await triggerElm.trigger('keydown', { code: EVENT_CODE.enter, }) vi.runAllTimers() expect(content.open).toBe(false) await triggerElm.trigger(MOUSE_ENTER_EVENT) vi.runAllTimers() await triggerElm.trigger('keydown', { code: EVENT_CODE.tab, }) vi.runAllTimers() expect(content.open).toBe(true) vi.useRealTimers() }) test('max height', async () => { const wrapper = _mount( ` dropdown `, () => ({}) ) await nextTick() const scrollbar = wrapper .findComponent({ ref: 'b', }) .findComponent({ ref: 'scrollbar' }) expect(scrollbar.find('.el-scrollbar__wrap').attributes('style')).toContain( 'max-height: 60px;' ) }) test('tooltip debounce', async () => { const wrapper = _mount( ` dropdown `, () => ({}) ) const content = wrapper.findComponent(ElTooltip).vm as InstanceType< typeof ElTooltip > const triggerElm = wrapper.find('.el-tooltip__trigger') expect(content.open).toBe(false) vi.useFakeTimers() await triggerElm.trigger(MOUSE_ENTER_EVENT) await triggerElm.trigger(MOUSE_LEAVE_EVENT) await triggerElm.trigger(MOUSE_ENTER_EVENT) vi.runAllTimers() vi.useRealTimers() expect(content.open).toBe(true) }) test('popperClass', async () => { const wrapper = await _mount( ` dropdown `, () => ({}) ) const popperElement = wrapper.findComponent({ name: 'ElPopperContent', }).element expect(popperElement.classList.contains('custom-popper-class')).toBe(true) }) test('custom attributes for dropdown items', async () => { const wrapper = _mount( ` Custom Attributes `, () => ({}) ) await nextTick() expect( wrapper .findComponent({ name: 'DropdownItemImpl', }) .find('.el-dropdown-menu__item').element.dataset.customAttribute ).toBe('hello') }) test('disable normal dropdown', async () => { const wrapper = _mount( ` Dropdown List `, () => ({}) ) await nextTick() expect( wrapper .findComponent({ name: 'ElDropdown', }) .classes() ).toContain('is-disabled') }) test('disable dropdown with split button', async () => { const wrapper = _mount( ` Dropdown List `, () => ({}) ) await nextTick() expect( wrapper .findAllComponents({ name: 'ElButton', })[0] .classes() ).toContain('is-disabled') expect( wrapper .findAllComponents({ name: 'ElButton', })[1] .classes() ).toContain('is-disabled') }) test('set show-timeout/hide-timeout when trigger is hover', async () => { const wrapper = _mount( ` Dropdown List `, () => ({}) ) const tooltipElement = wrapper.getComponent({ name: 'ElTooltip', }) expect(tooltipElement.vm.showAfter).toBe(200) expect(tooltipElement.vm.hideAfter).toBe(300) }) test('ignore show-timeout/hide-timeout when trigger is not hover', async () => { const wrapper = _mount( ` Dropdown List `, () => ({}) ) const tooltipElement = wrapper.getComponent({ name: 'ElTooltip', }) expect(tooltipElement.vm.showAfter).toBe(0) expect(tooltipElement.vm.hideAfter).toBe(0) }) describe('accessibility', () => { test('Custom span trigger has proper attributes', async () => { const wrapper = _mount( ` Dropdown List `, () => ({}) ) await nextTick() const trigger = wrapper.find('[data-test-ref="trigger"]') const menu = wrapper.findComponent({ ref: 'menu' }) expect(trigger.attributes()['role']).toBe('button') expect(trigger.attributes()['tabindex']).toBe('0') expect(trigger.attributes()['aria-haspopup']).toBe('menu') expect(trigger.attributes()['id']).toBe( menu.attributes()['aria-labelledby'] ) expect(trigger.attributes()['aria-controls']).toBe( menu.attributes()['id'] ) }) test('ElButton trigger has proper attributes', async () => { const wrapper = _mount( ` Dropdown List `, () => ({}) ) await nextTick() const trigger = wrapper.findComponent({ ref: 'trigger' }) const menu = wrapper.findComponent({ ref: 'menu' }) expect(trigger.attributes()['role']).toBe('button') expect(trigger.attributes()['tabindex']).toBe('0') expect(trigger.attributes()['aria-haspopup']).toBe('menu') expect(trigger.attributes()['id']).toBe( menu.attributes()['aria-labelledby'] ) expect(trigger.attributes()['aria-controls']).toBe( menu.attributes()['id'] ) }) test('Split button trigger has proper attributes', async () => { const wrapper = _mount( ` `, () => ({}) ) await nextTick() const trigger = wrapper.find('.el-dropdown__caret-button') const menu = wrapper.findComponent({ ref: 'menu' }) expect(trigger.attributes()['role']).toBe('button') expect(trigger.attributes()['tabindex']).toBe('0') expect(trigger.attributes()['aria-haspopup']).toBe('menu') expect(trigger.attributes()['id']).toBe( menu.attributes()['aria-labelledby'] ) expect(trigger.attributes()['aria-controls']).toBe( menu.attributes()['id'] ) }) test('Menu items with "menu" role', async () => { const wrapper = _mount( ` `, () => ({}) ) const menu = wrapper.findComponent({ ref: 'menu' }) const menuItem = menu.find('.el-dropdown-menu__item') expect(menu.attributes()['role']).toBe('menu') expect(menuItem.attributes()['role']).toBe('menuitem') }) test('Menu items with "navigation" role', async () => { const wrapper = _mount( ` `, () => ({}) ) const menu = wrapper.findComponent({ ref: 'menu' }) const menuItem = menu.find('.el-dropdown-menu__item') expect(menu.attributes()['role']).toBe('navigation') expect(menuItem.attributes()['role']).toBe('link') }) test('Menu items with "group" role', async () => { const wrapper = _mount( ` `, () => ({}) ) const menu = wrapper.findComponent({ ref: 'menu' }) const menuItem = menu.find('.el-dropdown-menu__item') expect(menu.attributes()['role']).toBe('group') expect(menuItem.attributes()['role']).toBe('button') }) test('Trigger dropdown via hover', async () => { const wrapper = _mount( ` Dropdown List `, () => ({}) ) await nextTick() const trigger = wrapper.find('.el-tooltip__trigger') await trigger.trigger(MOUSE_ENTER_EVENT) await rAF() const menuItem = wrapper.findComponent({ ref: 'menu' }) await menuItem.trigger('keydown', { code: EVENT_CODE.down }) await rAF() const item2 = menuItem.find('.item-2') expect(item2.element.getAttribute('tabindex')).toBe('0') await item2.trigger('keydown', { code: EVENT_CODE.down }) await rAF() const item3 = menuItem.find('.item-3') expect(item3.element.getAttribute('tabindex')).toBe('0') }) test('Trigger dropdown via click', async () => { const wrapper = _mount( ` Dropdown List `, () => ({}) ) await nextTick() const trigger = wrapper.find('.el-tooltip__trigger') await trigger.trigger('click') await rAF() const menuItem = wrapper.findComponent({ ref: 'menu' }) expect(document.activeElement).toBe(menuItem.element) await menuItem.trigger('keydown', { code: EVENT_CODE.down }) await rAF() const item2 = menuItem.find('.item-2') expect(item2.element.getAttribute('tabindex')).toBe('0') await item2.trigger('keydown', { code: EVENT_CODE.down }) await rAF() const item3 = menuItem.find('.item-3') expect(item3.element.getAttribute('tabindex')).toBe('0') }) test('Trigger dropdown via focus', async () => { const wrapper = _mount( ` Dropdown List `, () => ({}) ) await nextTick() const trigger = wrapper.find('.el-tooltip__trigger') await trigger.trigger('focus') await trigger.trigger('keydown', { code: EVENT_CODE.down }) await rAF() const menuItem = wrapper.findComponent({ ref: 'menu' }) const item2 = menuItem.find('.item-2') expect(document.activeElement).toBe(item2.element) expect(item2.element.getAttribute('tabindex')).toBe('0') }) }) describe('teleported API', () => { test('should mount on popper container', async () => { expect(document.body.innerHTML).toBe('') _mount( ` dropdown `, () => ({}) ) await nextTick() const { selector } = usePopperContainerId() expect(document.body.querySelector(selector.value).innerHTML).not.toBe('') }) test('should not mount on the popper container', async () => { expect(document.body.innerHTML).toBe('') _mount( ` dropdown `, () => ({}) ) await nextTick() const { selector } = usePopperContainerId() expect(document.body.querySelector(selector.value).innerHTML).toBe('') }) }) test('hover trigger: click dropdown-item should close immediately', async () => { const wrapper = _mount( ` dropdown `, () => ({}) ) await nextTick() const content = wrapper.findComponent(ElTooltip).vm as InstanceType< typeof ElTooltip > vi.useFakeTimers() const triggerElm = wrapper.find('.el-tooltip__trigger') expect(content.open).toBe(false) await triggerElm.trigger(MOUSE_ENTER_EVENT) vi.runAllTimers() await nextTick() expect(content.open).toBe(true) const menu = wrapper.findComponent({ ref: 'menu' }) await menu.trigger(MOUSE_ENTER_EVENT) const item = wrapper .findComponent({ ref: 'item' }) .findComponent({ name: 'DropdownItemImpl' }) .find('.el-dropdown-menu__item') await item.trigger('click') await nextTick() vi.advanceTimersByTime(100) expect(content.open).toBe(false) await triggerElm.trigger(MOUSE_ENTER_EVENT) vi.runAllTimers() await nextTick() expect(content.open).toBe(true) vi.useRealTimers() }) })