Files
workspace/code/fms/fms-vue/tests/components/tree.spec.js
T
2026-08-21 23:06:56 +08:00

91 lines
4.1 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { describe, it, expect, vi } from 'vitest'
import { mount } from '@vue/test-utils'
import Tree from '@/components/ui/tree/tree.vue'
const treeData = [{ b_id: '1', b_name: '根节点', children: [{ b_id: '2', b_name: '子节点' }] }]
const fieldNames = { key: 'b_id', title: 'b_name', children: 'children' }
describe('Tree 右键菜单', () => {
it('节点右键发出 contextmenu 事件(携带 node 与 key)', async () => {
const wrapper = mount(Tree, { props: { treeData, fieldNames } })
await wrapper.find('.tree-node-content').trigger('contextmenu')
const emitted = wrapper.emitted('contextmenu')
expect(emitted).toHaveLength(1)
expect(emitted[0][0].node.b_id).toBe('1')
expect(emitted[0][0].key).toBe('1')
expect(emitted[0][0].event).toBeTruthy()
})
it('节点右键不会重复触发树空白处右键逻辑', async () => {
const wrapper = mount(Tree, { props: { treeData, fieldNames } })
await wrapper.find('.tree-node-content').trigger('contextmenu')
expect(wrapper.emitted('contextmenu')).toHaveLength(1)
})
it('空白区域右键发出 contextmenu 事件(node 为 null)', async () => {
const wrapper = mount(Tree, { props: { treeData, fieldNames } })
await wrapper.find('.tree').trigger('contextmenu')
const emitted = wrapper.emitted('contextmenu')
expect(emitted).toHaveLength(1)
expect(emitted[0][0].node).toBeNull()
expect(emitted[0][0].key).toBeNull()
})
it('传入 contextMenu 时按节点渲染菜单(Teleport 到 body)并发出 contextMenuClick', async () => {
const contextMenu = vi.fn((node) =>
node
? [
{ key: 'add', label: '新增子项' },
{ divider: true },
{ key: 'delete', label: '删除', danger: true },
]
: [{ key: 'add-root', label: '新增顶级' }],
)
const wrapper = mount(Tree, { props: { treeData, fieldNames, contextMenu } })
// 节点右键:渲染节点菜单
await wrapper.find('.tree-node-content').trigger('contextmenu')
expect(contextMenu).toHaveBeenCalled()
expect(contextMenu.mock.calls[0][0].b_id).toBe('1')
let menu = document.body.querySelector('.tree-context-menu')
expect(menu).toBeTruthy()
expect(menu.textContent).toContain('新增子项')
const divider = menu.querySelector('.tree-context-menu__divider')
expect(divider).toBeTruthy()
menu.querySelector('.tree-context-menu__item').click()
expect(wrapper.emitted('contextMenuClick')).toHaveLength(1)
expect(wrapper.emitted('contextMenuClick')[0][0].item.key).toBe('add')
expect(wrapper.emitted('contextMenuClick')[0][0].node.b_id).toBe('1')
// 点击后菜单关闭(等待离开动画结束)
await new Promise((r) => setTimeout(r, 150))
expect(document.body.querySelector('.tree-context-menu')).toBeNull()
// 空白右键:渲染顶级菜单
await wrapper.find('.tree').trigger('contextmenu')
menu = document.body.querySelector('.tree-context-menu')
expect(menu.textContent).toContain('新增顶级')
})
it('disabled 菜单项点击不发出 contextMenuClick', async () => {
const contextMenu = () => [{ key: 'x', label: '禁用项', disabled: true }]
const wrapper = mount(Tree, { props: { treeData, fieldNames, contextMenu } })
await wrapper.find('.tree-node-content').trigger('contextmenu')
const menu = document.body.querySelector('.tree-context-menu')
menu.querySelector('.tree-context-menu__item').click()
expect(wrapper.emitted('contextMenuClick')).toBeUndefined()
})
it('点击菜单外部关闭菜单', async () => {
const contextMenu = () => [{ key: 'x', label: '项目' }]
const wrapper = mount(Tree, { props: { treeData, fieldNames, contextMenu } })
await wrapper.find('.tree-node-content').trigger('contextmenu')
expect(document.body.querySelector('.tree-context-menu')).toBeTruthy()
document.body.dispatchEvent(new MouseEvent('mousedown', { bubbles: true }))
await new Promise((r) => setTimeout(r, 0))
expect(document.body.querySelector('.tree-context-menu')).toBeNull()
wrapper.unmount()
})
})