98 lines
4.0 KiB
JavaScript
98 lines
4.0 KiB
JavaScript
import { mount } from '@vue/test-utils'
|
||
import { describe, expect, it, vi, beforeEach } from 'vitest'
|
||
import Tree from '@/components/ui/tree/tree.vue'
|
||
|
||
/**
|
||
* 模拟 Chrome 的 DataTransfer 行为:
|
||
* - dragover / dragenter 阶段:getData() 对所有类型返回空字符串(隐私保护),但 types / effectAllowed 可用
|
||
* - drop 阶段:getData() 正常返回 dragstart 中写入的数据
|
||
*/
|
||
function makeDataTransfer({ types = [], get = () => '' } = {}) {
|
||
return {
|
||
effectAllowed: 'copy',
|
||
dropEffect: 'none',
|
||
types,
|
||
getData: vi.fn((t) => get(t)),
|
||
setData: vi.fn(),
|
||
}
|
||
}
|
||
|
||
const treeData = [
|
||
{
|
||
key: 'g:1',
|
||
title: '分组1',
|
||
isLeaf: false,
|
||
children: [{ key: 'c:1', title: '列1', isLeaf: true }],
|
||
},
|
||
]
|
||
|
||
describe('Tree 外部拖拽(字段库 → 分组)', () => {
|
||
beforeEach(() => {
|
||
// 让 calcDropPosition 能算出 'inside'(行高 40,鼠标落在中点)
|
||
Element.prototype.getBoundingClientRect = () => ({
|
||
top: 0,
|
||
bottom: 40,
|
||
left: 0,
|
||
right: 200,
|
||
width: 200,
|
||
height: 40,
|
||
x: 0,
|
||
y: 0,
|
||
toJSON: () => ({}),
|
||
})
|
||
})
|
||
|
||
it('dragover 阶段 getData 为空(Chrome 限制)时,基于 types 识别外部源并设置拖放指示', async () => {
|
||
const externalDragKey = vi.fn((dt) => {
|
||
const types = Array.from(dt?.types || [])
|
||
if (!types.includes('application/fms-field')) return null
|
||
const fid = dt?.getData?.('application/fms-field')
|
||
return fid ? `external-field:${fid}` : `external-field:__pending__`
|
||
})
|
||
const wrapper = mount(Tree, { props: { treeData, draggable: true, externalDragKey } })
|
||
const nodeRow = wrapper.find('.tree-node')
|
||
// dragover:getData 读不到值(模拟 Chrome),仅 types 可用
|
||
const dt = makeDataTransfer({ types: ['application/fms-field'], get: () => '' })
|
||
await nodeRow.trigger('dragover', { dataTransfer: dt, clientY: 20 })
|
||
expect(externalDragKey).toHaveBeenCalled()
|
||
// dragKey 已被解析 → 节点行出现放置指示(说明拖拽已被识别,可正常落放)
|
||
expect(nodeRow.classes()).toContain('drop-inside')
|
||
})
|
||
|
||
it('dragover 阶段仅用 getData 判断时(修复前实现),拖放指示不出现', async () => {
|
||
// 旧实现:只靠 getData 判断拖拽源 → Chrome 下 dragover 读不到 → dragKey 不设置
|
||
const externalDragKey = vi.fn((dt) => {
|
||
const fid = dt?.getData?.('application/fms-field')
|
||
return fid ? `external-field:${String(fid)}` : null
|
||
})
|
||
const wrapper = mount(Tree, { props: { treeData, draggable: true, externalDragKey } })
|
||
const nodeRow = wrapper.find('.tree-node')
|
||
const dt = makeDataTransfer({ types: ['application/fms-field'], get: () => '' })
|
||
await nodeRow.trigger('dragover', { dataTransfer: dt, clientY: 20 })
|
||
// 没有任何 drop-* 指示类
|
||
expect(nodeRow.classes().some((c) => c.startsWith('drop-'))).toBe(false)
|
||
})
|
||
|
||
it('drop 阶段 getData 可用时,发出正确的 external-field 拖拽键', async () => {
|
||
const externalDragKey = (dt) => {
|
||
const types = Array.from(dt?.types || [])
|
||
if (!types.includes('application/fms-field')) return null
|
||
const fid = dt?.getData?.('application/fms-field')
|
||
return fid ? `external-field:${fid}` : `external-field:__pending__`
|
||
}
|
||
const wrapper = mount(Tree, { props: { treeData, draggable: true, externalDragKey } })
|
||
const nodeRow = wrapper.find('.tree-node')
|
||
// dragover(读不到)→ drop(能读到 '42')
|
||
const overDT = makeDataTransfer({ types: ['application/fms-field'], get: () => '' })
|
||
await nodeRow.trigger('dragover', { dataTransfer: overDT, clientY: 20 })
|
||
const dropDT = makeDataTransfer({
|
||
types: ['application/fms-field'],
|
||
get: (t) => (t === 'application/fms-field' ? '42' : ''),
|
||
})
|
||
await nodeRow.trigger('drop', { dataTransfer: dropDT })
|
||
const drops = wrapper.emitted('drop')
|
||
expect(drops).toBeTruthy()
|
||
expect(drops[0][0]).toMatchObject({ dragKey: 'external-field:42', dropKey: 'g:1' })
|
||
})
|
||
})
|