20260827230636

This commit is contained in:
oneao committed 2026-08-27 23:06:36 +08:00
1 parent 3824af0f12
commit 35ca496b5f
15 files changed
+2313 -321

No files matched your search

@@ -0,0 +1,97 @@
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' })
})
})