mirror of
https://github.com/Powerful-517/yys-editor.git
synced 2026-03-05 23:15:26 +00:00
fix: 修复保存后刷新网页图层全变成1的问题
问题原因: 1. LogicFlow 的 render() 方法不会自动应用节点的 zIndex 属性 2. 切换标签时,LogicFlow Label 插件对空 _label 数组处理有误导致渲染失败 3. 渲染失败后节点 zIndex 被重置为默认值 1 解决方案: 1. 在 App.vue 中,render() 后立即从保存的数据中恢复每个节点的 zIndex 2. 在 normalizeGraphData() 中清理空的 _label 数组,避免 Label 插件报错 3. 简化 FlowEditor.vue 中的 normalizeAllNodes(),移除不必要的重新分配逻辑 4. 清理调试日志,保持代码整洁 测试: - 添加节点并调整图层顺序 - 切换标签页 - 刷新浏览器 - 确认图层顺序保持不变
This commit is contained in:
94
src/__tests__/layer-management/README.md
Normal file
94
src/__tests__/layer-management/README.md
Normal file
@@ -0,0 +1,94 @@
|
||||
# 图层管理测试
|
||||
|
||||
## 📁 文件说明
|
||||
|
||||
### ✅ 活跃的测试
|
||||
|
||||
- **`real-scenario.spec.ts`** - 真实场景测试(推荐使用)
|
||||
- 使用真实的 LogicFlow 实例
|
||||
- 模拟真实的用户操作流程
|
||||
- 能够发现真实的代码问题
|
||||
|
||||
### 📦 已废弃的测试(仅供参考)
|
||||
|
||||
- **`mock-test.spec.ts.bak`** - Mock 测试
|
||||
- 使用模拟类(MockLogicFlow)
|
||||
- 只能验证理想逻辑
|
||||
- 无法发现真实代码的问题
|
||||
|
||||
- **`integration-test.spec.ts.bak`** - 组件集成测试
|
||||
- 尝试挂载 Vue 组件
|
||||
- 因为缺少浏览器 API 而失败
|
||||
|
||||
- **`unit-test.spec.ts.bak`** - 单元测试
|
||||
- 测试单个函数
|
||||
- 覆盖范围有限
|
||||
|
||||
---
|
||||
|
||||
## 🚀 运行测试
|
||||
|
||||
```bash
|
||||
# 运行图层管理测试
|
||||
npm test -- layer-management
|
||||
|
||||
# 只运行真实场景测试
|
||||
npm test -- real-scenario
|
||||
|
||||
# 监听模式
|
||||
npm run test:watch -- layer-management
|
||||
|
||||
# 查看详细输出
|
||||
npm test -- layer-management --reporter=verbose
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📊 测试结果
|
||||
|
||||
### 当前状态(9 个测试)
|
||||
|
||||
- ✅ **通过**: 5/9
|
||||
- ❌ **失败**: 4/9
|
||||
|
||||
### 发现的问题
|
||||
|
||||
1. **zIndex 不会保存到数据中**
|
||||
- `getGraphRawData()` 返回的数据中没有 zIndex
|
||||
- 导致导出/导入数据会丢失图层信息
|
||||
|
||||
2. **置底操作逻辑错误**
|
||||
- 置底后 zIndex 变成 998/996
|
||||
- 应该是比所有节点都小的值
|
||||
|
||||
---
|
||||
|
||||
## 📝 测试场景
|
||||
|
||||
### ✅ 通过的测试
|
||||
|
||||
1. **场景1**: 创建节点并验证 zIndex 分配
|
||||
2. **场景2**: 置顶操作(模拟右键菜单)
|
||||
3. **场景4**: 上移一层操作
|
||||
4. **场景5**: 下移一层操作
|
||||
5. **场景8**: 边界情况 - 最顶层节点继续置顶
|
||||
|
||||
### ❌ 失败的测试
|
||||
|
||||
1. **场景3**: 置底操作(模拟右键菜单)
|
||||
2. **场景6**: 数据预览验证(模拟 Toolbar.handlePreviewData)
|
||||
3. **场景7**: 完整用户流程测试
|
||||
4. **场景9**: 边界情况 - 最底层节点继续置底
|
||||
|
||||
---
|
||||
|
||||
## 🔧 如何修复
|
||||
|
||||
参考 `../README-测试报告.md` 中的解决方案。
|
||||
|
||||
---
|
||||
|
||||
## 📚 相关文档
|
||||
|
||||
- [测试规范文档](../TEST-RULES.md)
|
||||
- [测试报告](../README-测试报告.md)
|
||||
483
src/__tests__/layer-management/integration-test.spec.ts.bak
Normal file
483
src/__tests__/layer-management/integration-test.spec.ts.bak
Normal file
@@ -0,0 +1,483 @@
|
||||
import { describe, it, expect, beforeEach, vi } from 'vitest'
|
||||
import { mount, flushPromises } from '@vue/test-utils'
|
||||
import { createPinia, setActivePinia } from 'pinia'
|
||||
import FlowEditor from '@/components/flow/FlowEditor.vue'
|
||||
import ComponentsPanel from '@/components/flow/ComponentsPanel.vue'
|
||||
import { getLogicFlowInstance, setLogicFlowInstance } from '@/ts/useLogicFlow'
|
||||
|
||||
/**
|
||||
* 图层管理集成测试 - 真实组件交互
|
||||
*
|
||||
* 这个测试模拟真实的用户操作流程:
|
||||
* 1. 从 ComponentsPanel 拖拽创建节点
|
||||
* 2. 在 FlowEditor 中执行图层操作(置顶、置底、上移、下移)
|
||||
* 3. 验证 zIndex 的变化
|
||||
*/
|
||||
|
||||
describe('图层管理集成测试 - 真实组件交互', () => {
|
||||
let pinia: ReturnType<typeof createPinia>
|
||||
|
||||
beforeEach(() => {
|
||||
// 创建新的 Pinia 实例
|
||||
pinia = createPinia()
|
||||
setActivePinia(pinia)
|
||||
|
||||
// 清理 LogicFlow 实例
|
||||
vi.clearAllMocks()
|
||||
})
|
||||
|
||||
it('应该能够创建节点并验证 zIndex', async () => {
|
||||
console.log('\n=== 测试:创建节点并验证 zIndex ===')
|
||||
|
||||
// 挂载 FlowEditor 组件
|
||||
const wrapper = mount(FlowEditor, {
|
||||
global: {
|
||||
plugins: [pinia],
|
||||
stubs: {
|
||||
PropertyPanel: true, // 暂时 stub 掉属性面板
|
||||
}
|
||||
},
|
||||
props: {
|
||||
height: '600px'
|
||||
}
|
||||
})
|
||||
|
||||
await flushPromises()
|
||||
|
||||
// 获取 LogicFlow 实例
|
||||
const lf = getLogicFlowInstance()
|
||||
expect(lf).toBeTruthy()
|
||||
|
||||
if (!lf) return
|
||||
|
||||
// 模拟创建 3 个节点
|
||||
console.log('创建节点...')
|
||||
const node1 = lf.addNode({
|
||||
type: 'rect',
|
||||
x: 100,
|
||||
y: 100,
|
||||
properties: {}
|
||||
})
|
||||
|
||||
const node2 = lf.addNode({
|
||||
type: 'rect',
|
||||
x: 200,
|
||||
y: 200,
|
||||
properties: {}
|
||||
})
|
||||
|
||||
const node3 = lf.addNode({
|
||||
type: 'rect',
|
||||
x: 300,
|
||||
y: 300,
|
||||
properties: {}
|
||||
})
|
||||
|
||||
await flushPromises()
|
||||
|
||||
// 获取节点模型
|
||||
const model1 = lf.getNodeModelById(node1.id)
|
||||
const model2 = lf.getNodeModelById(node2.id)
|
||||
const model3 = lf.getNodeModelById(node3.id)
|
||||
|
||||
console.log('节点创建后的 zIndex:')
|
||||
console.log(` node1: ${model1?.zIndex}`)
|
||||
console.log(` node2: ${model2?.zIndex}`)
|
||||
console.log(` node3: ${model3?.zIndex}`)
|
||||
|
||||
// 验证节点都有 zIndex
|
||||
expect(model1?.zIndex).toBeDefined()
|
||||
expect(model2?.zIndex).toBeDefined()
|
||||
expect(model3?.zIndex).toBeDefined()
|
||||
|
||||
wrapper.unmount()
|
||||
})
|
||||
|
||||
it('应该能够执行置顶操作', async () => {
|
||||
console.log('\n=== 测试:置顶操作 ===')
|
||||
|
||||
const wrapper = mount(FlowEditor, {
|
||||
global: {
|
||||
plugins: [pinia],
|
||||
stubs: {
|
||||
PropertyPanel: true,
|
||||
}
|
||||
},
|
||||
props: {
|
||||
height: '600px'
|
||||
}
|
||||
})
|
||||
|
||||
await flushPromises()
|
||||
|
||||
const lf = getLogicFlowInstance()
|
||||
if (!lf) return
|
||||
|
||||
// 创建 3 个节点,手动设置不同的 zIndex
|
||||
const node1 = lf.addNode({ type: 'rect', x: 100, y: 100 })
|
||||
const node2 = lf.addNode({ type: 'rect', x: 200, y: 200 })
|
||||
const node3 = lf.addNode({ type: 'rect', x: 300, y: 300 })
|
||||
|
||||
await flushPromises()
|
||||
|
||||
const model1 = lf.getNodeModelById(node1.id)
|
||||
const model2 = lf.getNodeModelById(node2.id)
|
||||
const model3 = lf.getNodeModelById(node3.id)
|
||||
|
||||
// 手动设置初始 zIndex
|
||||
model1?.setZIndex(1)
|
||||
model2?.setZIndex(2)
|
||||
model3?.setZIndex(3)
|
||||
|
||||
console.log('初始 zIndex:')
|
||||
console.log(` node1: ${model1?.zIndex}`)
|
||||
console.log(` node2: ${model2?.zIndex}`)
|
||||
console.log(` node3: ${model3?.zIndex}`)
|
||||
|
||||
// 执行置顶操作:将 node1 置顶
|
||||
lf.setElementZIndex(node1.id, 'top')
|
||||
|
||||
await flushPromises()
|
||||
|
||||
console.log('置顶后 zIndex:')
|
||||
console.log(` node1: ${model1?.zIndex}`)
|
||||
console.log(` node2: ${model2?.zIndex}`)
|
||||
console.log(` node3: ${model3?.zIndex}`)
|
||||
|
||||
// 验证 node1 的 zIndex 最大
|
||||
const allZIndexes = [model1?.zIndex, model2?.zIndex, model3?.zIndex].filter(z => z !== undefined) as number[]
|
||||
expect(model1?.zIndex).toBe(Math.max(...allZIndexes))
|
||||
expect(model1?.zIndex).toBeGreaterThan(model2?.zIndex || 0)
|
||||
expect(model1?.zIndex).toBeGreaterThan(model3?.zIndex || 0)
|
||||
|
||||
wrapper.unmount()
|
||||
})
|
||||
|
||||
it('应该能够执行置底操作', async () => {
|
||||
console.log('\n=== 测试:置底操作 ===')
|
||||
|
||||
const wrapper = mount(FlowEditor, {
|
||||
global: {
|
||||
plugins: [pinia],
|
||||
stubs: {
|
||||
PropertyPanel: true,
|
||||
}
|
||||
},
|
||||
props: {
|
||||
height: '600px'
|
||||
}
|
||||
})
|
||||
|
||||
await flushPromises()
|
||||
|
||||
const lf = getLogicFlowInstance()
|
||||
if (!lf) return
|
||||
|
||||
// 创建 3 个节点
|
||||
const node1 = lf.addNode({ type: 'rect', x: 100, y: 100 })
|
||||
const node2 = lf.addNode({ type: 'rect', x: 200, y: 200 })
|
||||
const node3 = lf.addNode({ type: 'rect', x: 300, y: 300 })
|
||||
|
||||
await flushPromises()
|
||||
|
||||
const model1 = lf.getNodeModelById(node1.id)
|
||||
const model2 = lf.getNodeModelById(node2.id)
|
||||
const model3 = lf.getNodeModelById(node3.id)
|
||||
|
||||
// 手动设置初始 zIndex
|
||||
model1?.setZIndex(1)
|
||||
model2?.setZIndex(2)
|
||||
model3?.setZIndex(3)
|
||||
|
||||
console.log('初始 zIndex:')
|
||||
console.log(` node1: ${model1?.zIndex}`)
|
||||
console.log(` node2: ${model2?.zIndex}`)
|
||||
console.log(` node3: ${model3?.zIndex}`)
|
||||
|
||||
// 执行置底操作:将 node3 置底
|
||||
lf.setElementZIndex(node3.id, 'bottom')
|
||||
|
||||
await flushPromises()
|
||||
|
||||
console.log('置底后 zIndex:')
|
||||
console.log(` node1: ${model1?.zIndex}`)
|
||||
console.log(` node2: ${model2?.zIndex}`)
|
||||
console.log(` node3: ${model3?.zIndex}`)
|
||||
|
||||
// 验证 node3 的 zIndex 最小
|
||||
const allZIndexes = [model1?.zIndex, model2?.zIndex, model3?.zIndex].filter(z => z !== undefined) as number[]
|
||||
expect(model3?.zIndex).toBe(Math.min(...allZIndexes))
|
||||
expect(model3?.zIndex).toBeLessThan(model1?.zIndex || Infinity)
|
||||
expect(model3?.zIndex).toBeLessThan(model2?.zIndex || Infinity)
|
||||
|
||||
wrapper.unmount()
|
||||
})
|
||||
|
||||
it('应该能够执行上移和下移操作', async () => {
|
||||
console.log('\n=== 测试:上移和下移操作 ===')
|
||||
|
||||
const wrapper = mount(FlowEditor, {
|
||||
global: {
|
||||
plugins: [pinia],
|
||||
stubs: {
|
||||
PropertyPanel: true,
|
||||
}
|
||||
},
|
||||
props: {
|
||||
height: '600px'
|
||||
}
|
||||
})
|
||||
|
||||
await flushPromises()
|
||||
|
||||
const lf = getLogicFlowInstance()
|
||||
if (!lf) return
|
||||
|
||||
// 创建 3 个节点
|
||||
const node1 = lf.addNode({ type: 'rect', x: 100, y: 100 })
|
||||
const node2 = lf.addNode({ type: 'rect', x: 200, y: 200 })
|
||||
const node3 = lf.addNode({ type: 'rect', x: 300, y: 300 })
|
||||
|
||||
await flushPromises()
|
||||
|
||||
const model1 = lf.getNodeModelById(node1.id)
|
||||
const model2 = lf.getNodeModelById(node2.id)
|
||||
const model3 = lf.getNodeModelById(node3.id)
|
||||
|
||||
// 手动设置初始 zIndex
|
||||
model1?.setZIndex(1)
|
||||
model2?.setZIndex(2)
|
||||
model3?.setZIndex(3)
|
||||
|
||||
console.log('初始 zIndex:')
|
||||
console.log(` node1: ${model1?.zIndex}`)
|
||||
console.log(` node2: ${model2?.zIndex}`)
|
||||
console.log(` node3: ${model3?.zIndex}`)
|
||||
|
||||
const originalZIndex1 = model1?.zIndex
|
||||
const originalZIndex2 = model2?.zIndex
|
||||
|
||||
// 测试上移:node1 上移一层(应该与 node2 交换)
|
||||
// 需要调用 FlowEditor 中的 bringForward 方法
|
||||
// 由于方法不是暴露的,我们直接操作 LogicFlow
|
||||
const allNodes = lf.graphModel.nodes
|
||||
const currentNode = model1
|
||||
if (currentNode) {
|
||||
const currentZIndex = currentNode.zIndex
|
||||
const nodesAbove = allNodes
|
||||
.filter((node) => node.zIndex > currentZIndex)
|
||||
.sort((a, b) => a.zIndex - b.zIndex)
|
||||
|
||||
if (nodesAbove.length > 0) {
|
||||
const nextNode = nodesAbove[0]
|
||||
currentNode.setZIndex(nextNode.zIndex)
|
||||
nextNode.setZIndex(currentZIndex)
|
||||
}
|
||||
}
|
||||
|
||||
await flushPromises()
|
||||
|
||||
console.log('上移后 zIndex:')
|
||||
console.log(` node1: ${model1?.zIndex}`)
|
||||
console.log(` node2: ${model2?.zIndex}`)
|
||||
console.log(` node3: ${model3?.zIndex}`)
|
||||
|
||||
// 验证 zIndex 已交换
|
||||
expect(model1?.zIndex).toBe(originalZIndex2)
|
||||
expect(model2?.zIndex).toBe(originalZIndex1)
|
||||
|
||||
wrapper.unmount()
|
||||
})
|
||||
|
||||
it('应该能够通过右键菜单执行图层操作', async () => {
|
||||
console.log('\n=== 测试:右键菜单图层操作 ===')
|
||||
|
||||
const wrapper = mount(FlowEditor, {
|
||||
global: {
|
||||
plugins: [pinia],
|
||||
stubs: {
|
||||
PropertyPanel: true,
|
||||
}
|
||||
},
|
||||
props: {
|
||||
height: '600px'
|
||||
}
|
||||
})
|
||||
|
||||
await flushPromises()
|
||||
|
||||
const lf = getLogicFlowInstance()
|
||||
if (!lf) return
|
||||
|
||||
// 创建节点
|
||||
const node1 = lf.addNode({ type: 'rect', x: 100, y: 100 })
|
||||
const node2 = lf.addNode({ type: 'rect', x: 200, y: 200 })
|
||||
|
||||
await flushPromises()
|
||||
|
||||
const model1 = lf.getNodeModelById(node1.id)
|
||||
const model2 = lf.getNodeModelById(node2.id)
|
||||
|
||||
model1?.setZIndex(1)
|
||||
model2?.setZIndex(2)
|
||||
|
||||
console.log('初始 zIndex:')
|
||||
console.log(` node1: ${model1?.zIndex}`)
|
||||
console.log(` node2: ${model2?.zIndex}`)
|
||||
|
||||
// 模拟右键菜单的"置于顶层"操作
|
||||
// 在真实场景中,这会通过菜单回调触发
|
||||
const menuConfig = (lf.extension as any).menu.menuConfig
|
||||
const bringToFrontMenuItem = menuConfig.nodeMenu.find((item: any) => item.text === '置于顶层')
|
||||
|
||||
if (bringToFrontMenuItem) {
|
||||
// 执行菜单回调
|
||||
bringToFrontMenuItem.callback({ id: node1.id })
|
||||
await flushPromises()
|
||||
|
||||
console.log('执行"置于顶层"后 zIndex:')
|
||||
console.log(` node1: ${model1?.zIndex}`)
|
||||
console.log(` node2: ${model2?.zIndex}`)
|
||||
|
||||
// 验证 node1 现在在最上层
|
||||
expect(model1?.zIndex).toBeGreaterThan(model2?.zIndex || 0)
|
||||
}
|
||||
|
||||
wrapper.unmount()
|
||||
})
|
||||
|
||||
it('应该能够验证数据预览中的 zIndex', async () => {
|
||||
console.log('\n=== 测试:数据预览中的 zIndex ===')
|
||||
|
||||
const wrapper = mount(FlowEditor, {
|
||||
global: {
|
||||
plugins: [pinia],
|
||||
stubs: {
|
||||
PropertyPanel: true,
|
||||
}
|
||||
},
|
||||
props: {
|
||||
height: '600px'
|
||||
}
|
||||
})
|
||||
|
||||
await flushPromises()
|
||||
|
||||
const lf = getLogicFlowInstance()
|
||||
if (!lf) return
|
||||
|
||||
// 创建节点
|
||||
const node1 = lf.addNode({ type: 'rect', x: 100, y: 100 })
|
||||
const node2 = lf.addNode({ type: 'rect', x: 200, y: 200 })
|
||||
const node3 = lf.addNode({ type: 'rect', x: 300, y: 300 })
|
||||
|
||||
await flushPromises()
|
||||
|
||||
const model1 = lf.getNodeModelById(node1.id)
|
||||
const model2 = lf.getNodeModelById(node2.id)
|
||||
const model3 = lf.getNodeModelById(node3.id)
|
||||
|
||||
model1?.setZIndex(1)
|
||||
model2?.setZIndex(2)
|
||||
model3?.setZIndex(3)
|
||||
|
||||
// 执行置顶操作
|
||||
lf.setElementZIndex(node1.id, 'top')
|
||||
await flushPromises()
|
||||
|
||||
// 获取图数据(模拟 Toolbar 的 handlePreviewData)
|
||||
const graphData = lf.getGraphRawData()
|
||||
|
||||
console.log('数据预览:')
|
||||
console.log(JSON.stringify(graphData, null, 2))
|
||||
|
||||
// 验证数据中包含 zIndex
|
||||
expect(graphData.nodes).toBeDefined()
|
||||
expect(graphData.nodes.length).toBe(3)
|
||||
|
||||
graphData.nodes.forEach((node: any) => {
|
||||
expect(node.zIndex).toBeDefined()
|
||||
expect(typeof node.zIndex).toBe('number')
|
||||
console.log(`节点 ${node.id} 的 zIndex: ${node.zIndex}`)
|
||||
})
|
||||
|
||||
// 验证 node1 的 zIndex 最大
|
||||
const node1Data = graphData.nodes.find((n: any) => n.id === node1.id)
|
||||
const allZIndexes = graphData.nodes.map((n: any) => n.zIndex)
|
||||
expect(node1Data?.zIndex).toBe(Math.max(...allZIndexes))
|
||||
|
||||
wrapper.unmount()
|
||||
})
|
||||
|
||||
it('完整流程:创建节点 -> 图层操作 -> 验证数据', async () => {
|
||||
console.log('\n=== 测试:完整流程 ===')
|
||||
|
||||
const wrapper = mount(FlowEditor, {
|
||||
global: {
|
||||
plugins: [pinia],
|
||||
stubs: {
|
||||
PropertyPanel: true,
|
||||
}
|
||||
},
|
||||
props: {
|
||||
height: '600px'
|
||||
}
|
||||
})
|
||||
|
||||
await flushPromises()
|
||||
|
||||
const lf = getLogicFlowInstance()
|
||||
if (!lf) return
|
||||
|
||||
// 步骤 1: 创建 4 个节点(模拟从 ComponentsPanel 拖拽)
|
||||
console.log('步骤 1: 创建节点')
|
||||
const nodes = [
|
||||
lf.addNode({ type: 'rect', x: 100, y: 100 }),
|
||||
lf.addNode({ type: 'rect', x: 200, y: 200 }),
|
||||
lf.addNode({ type: 'rect', x: 300, y: 300 }),
|
||||
lf.addNode({ type: 'rect', x: 400, y: 400 })
|
||||
]
|
||||
|
||||
await flushPromises()
|
||||
|
||||
const models = nodes.map(n => lf.getNodeModelById(n.id))
|
||||
models.forEach((m, i) => m?.setZIndex(i + 1))
|
||||
|
||||
console.log('初始状态:', models.map((m, i) => ({ id: nodes[i].id, zIndex: m?.zIndex })))
|
||||
|
||||
// 步骤 2: 执行图层操作
|
||||
console.log('\n步骤 2: 执行图层操作')
|
||||
|
||||
// node1 置顶
|
||||
lf.setElementZIndex(nodes[0].id, 'top')
|
||||
await flushPromises()
|
||||
console.log('node1 置顶后:', models.map((m, i) => ({ id: nodes[i].id, zIndex: m?.zIndex })))
|
||||
|
||||
// node4 置底
|
||||
lf.setElementZIndex(nodes[3].id, 'bottom')
|
||||
await flushPromises()
|
||||
console.log('node4 置底后:', models.map((m, i) => ({ id: nodes[i].id, zIndex: m?.zIndex })))
|
||||
|
||||
// 步骤 3: 验证数据(模拟 Toolbar 的 handlePreviewData)
|
||||
console.log('\n步骤 3: 验证数据')
|
||||
const graphData = lf.getGraphRawData()
|
||||
|
||||
console.log('最终数据预览:')
|
||||
graphData.nodes.forEach((node: any) => {
|
||||
console.log(` 节点 ${node.id}: zIndex = ${node.zIndex}`)
|
||||
})
|
||||
|
||||
// 验证最终顺序
|
||||
const sortedNodes = [...graphData.nodes].sort((a: any, b: any) => a.zIndex - b.zIndex)
|
||||
|
||||
// node4 应该在最底层
|
||||
expect(sortedNodes[0].id).toBe(nodes[3].id)
|
||||
|
||||
// node1 应该在最顶层
|
||||
expect(sortedNodes[sortedNodes.length - 1].id).toBe(nodes[0].id)
|
||||
|
||||
console.log('\n✅ 完整流程测试通过!')
|
||||
|
||||
wrapper.unmount()
|
||||
})
|
||||
})
|
||||
443
src/__tests__/layer-management/mock-test.spec.ts.bak
Normal file
443
src/__tests__/layer-management/mock-test.spec.ts.bak
Normal file
@@ -0,0 +1,443 @@
|
||||
import { describe, it, expect, beforeEach, vi } from 'vitest'
|
||||
|
||||
/**
|
||||
* 图层管理集成测试
|
||||
*
|
||||
* 这个测试模拟真实的图层操作场景,验证:
|
||||
* 1. 节点创建时的 zIndex 分配
|
||||
* 2. 上移、下移、置顶、置底操作的正确性
|
||||
* 3. 数据持久化后 zIndex 的保存
|
||||
*/
|
||||
|
||||
// 模拟 LogicFlow 节点模型
|
||||
class MockNodeModel {
|
||||
id: string
|
||||
zIndex: number
|
||||
x: number
|
||||
y: number
|
||||
|
||||
constructor(id: string, x: number, y: number, zIndex: number = 1) {
|
||||
this.id = id
|
||||
this.x = x
|
||||
this.y = y
|
||||
this.zIndex = zIndex
|
||||
}
|
||||
|
||||
setZIndex(zIndex: number) {
|
||||
this.zIndex = zIndex
|
||||
}
|
||||
}
|
||||
|
||||
// 模拟 LogicFlow 实例
|
||||
class MockLogicFlow {
|
||||
nodes: MockNodeModel[] = []
|
||||
private nodeIdCounter = 0
|
||||
|
||||
addNode(config: { x: number; y: number; zIndex?: number }) {
|
||||
const id = `node_${++this.nodeIdCounter}`
|
||||
const zIndex = config.zIndex ?? 1000 // 新节点默认 zIndex 为 1000
|
||||
const node = new MockNodeModel(id, config.x, config.y, zIndex)
|
||||
this.nodes.push(node)
|
||||
console.log(`[NODE_ADD] 创建节点 ${id}, zIndex: ${zIndex}`)
|
||||
return node
|
||||
}
|
||||
|
||||
getNodeModelById(id: string) {
|
||||
return this.nodes.find(n => n.id === id)
|
||||
}
|
||||
|
||||
setElementZIndex(id: string, zIndexOrPosition: number | 'top' | 'bottom') {
|
||||
const node = this.getNodeModelById(id)
|
||||
if (!node) return
|
||||
|
||||
if (zIndexOrPosition === 'top') {
|
||||
const maxZIndex = Math.max(...this.nodes.map(n => n.zIndex))
|
||||
node.setZIndex(maxZIndex + 1)
|
||||
console.log(`[置于顶层] ${id}: ${node.zIndex}`)
|
||||
} else if (zIndexOrPosition === 'bottom') {
|
||||
const minZIndex = Math.min(...this.nodes.map(n => n.zIndex))
|
||||
node.setZIndex(minZIndex - 1)
|
||||
console.log(`[置于底层] ${id}: ${node.zIndex}`)
|
||||
} else {
|
||||
node.setZIndex(zIndexOrPosition)
|
||||
}
|
||||
}
|
||||
|
||||
// 模拟上移一层
|
||||
bringForward(id: string) {
|
||||
const currentNode = this.getNodeModelById(id)
|
||||
if (!currentNode) return
|
||||
|
||||
const currentZIndex = currentNode.zIndex
|
||||
const nodesAbove = this.nodes
|
||||
.filter(node => node.zIndex > currentZIndex)
|
||||
.sort((a, b) => a.zIndex - b.zIndex)
|
||||
|
||||
if (nodesAbove.length > 0) {
|
||||
const nextNode = nodesAbove[0]
|
||||
const tempZIndex = currentNode.zIndex
|
||||
currentNode.setZIndex(nextNode.zIndex)
|
||||
nextNode.setZIndex(tempZIndex)
|
||||
console.log(`[上移一层] ${id}: ${tempZIndex} -> ${currentNode.zIndex}`)
|
||||
}
|
||||
}
|
||||
|
||||
// 模拟下移一层
|
||||
sendBackward(id: string) {
|
||||
const currentNode = this.getNodeModelById(id)
|
||||
if (!currentNode) return
|
||||
|
||||
const currentZIndex = currentNode.zIndex
|
||||
const nodesBelow = this.nodes
|
||||
.filter(node => node.zIndex < currentZIndex)
|
||||
.sort((a, b) => b.zIndex - a.zIndex)
|
||||
|
||||
if (nodesBelow.length > 0) {
|
||||
const prevNode = nodesBelow[0]
|
||||
const tempZIndex = currentNode.zIndex
|
||||
currentNode.setZIndex(prevNode.zIndex)
|
||||
prevNode.setZIndex(tempZIndex)
|
||||
console.log(`[下移一层] ${id}: ${tempZIndex} -> ${currentNode.zIndex}`)
|
||||
}
|
||||
}
|
||||
|
||||
// 获取图数据(模拟数据预览)
|
||||
getGraphRawData() {
|
||||
return {
|
||||
nodes: this.nodes.map(n => ({
|
||||
id: n.id,
|
||||
x: n.x,
|
||||
y: n.y,
|
||||
zIndex: n.zIndex
|
||||
})),
|
||||
edges: []
|
||||
}
|
||||
}
|
||||
|
||||
printAllNodes() {
|
||||
console.log('所有节点 zIndex:', this.nodes.map(n => ({ id: n.id, zIndex: n.zIndex })))
|
||||
}
|
||||
}
|
||||
|
||||
describe('图层管理集成测试 - 真实场景模拟', () => {
|
||||
let lf: MockLogicFlow
|
||||
|
||||
beforeEach(() => {
|
||||
lf = new MockLogicFlow()
|
||||
})
|
||||
|
||||
describe('1. 创建多个节点并检查zIndex', () => {
|
||||
it('应该为新创建的节点分配正确的zIndex', () => {
|
||||
console.log('\n=== 测试:创建节点并检查 zIndex ===')
|
||||
|
||||
// 创建3个节点
|
||||
const node1 = lf.addNode({ x: 100, y: 100 })
|
||||
const node2 = lf.addNode({ x: 200, y: 200 })
|
||||
const node3 = lf.addNode({ x: 300, y: 300 })
|
||||
|
||||
lf.printAllNodes()
|
||||
|
||||
// 验证每个节点都有zIndex
|
||||
expect(node1.zIndex).toBeDefined()
|
||||
expect(node2.zIndex).toBeDefined()
|
||||
expect(node3.zIndex).toBeDefined()
|
||||
|
||||
// 验证zIndex是数字
|
||||
expect(typeof node1.zIndex).toBe('number')
|
||||
expect(typeof node2.zIndex).toBe('number')
|
||||
expect(typeof node3.zIndex).toBe('number')
|
||||
|
||||
// 新节点应该有较高的zIndex(默认1000)
|
||||
expect(node1.zIndex).toBe(1000)
|
||||
expect(node2.zIndex).toBe(1000)
|
||||
expect(node3.zIndex).toBe(1000)
|
||||
})
|
||||
|
||||
it('多个节点的zIndex应该各不相同', () => {
|
||||
console.log('\n=== 测试:多个节点 zIndex 唯一性 ===')
|
||||
|
||||
// 创建4个节点,手动设置不同的zIndex
|
||||
const node1 = lf.addNode({ x: 100, y: 100, zIndex: 1 })
|
||||
const node2 = lf.addNode({ x: 200, y: 200, zIndex: 2 })
|
||||
const node3 = lf.addNode({ x: 300, y: 300, zIndex: 3 })
|
||||
const node4 = lf.addNode({ x: 400, y: 400, zIndex: 4 })
|
||||
|
||||
lf.printAllNodes()
|
||||
|
||||
const zIndexes = [node1.zIndex, node2.zIndex, node3.zIndex, node4.zIndex]
|
||||
const uniqueZIndexes = new Set(zIndexes)
|
||||
|
||||
// 所有zIndex应该是唯一的
|
||||
expect(uniqueZIndexes.size).toBe(4)
|
||||
})
|
||||
})
|
||||
|
||||
describe('2. 节点上移操作', () => {
|
||||
it('上移一层应该与上方节点交换zIndex', () => {
|
||||
console.log('\n=== 测试:上移一层 ===')
|
||||
|
||||
const node1 = lf.addNode({ x: 100, y: 100, zIndex: 1 })
|
||||
const node2 = lf.addNode({ x: 200, y: 200, zIndex: 2 })
|
||||
const node3 = lf.addNode({ x: 300, y: 300, zIndex: 3 })
|
||||
|
||||
console.log('上移前:')
|
||||
lf.printAllNodes()
|
||||
|
||||
const originalZIndex1 = node1.zIndex
|
||||
const originalZIndex2 = node2.zIndex
|
||||
|
||||
// 对node1执行上移操作
|
||||
lf.bringForward(node1.id)
|
||||
|
||||
console.log('上移后:')
|
||||
lf.printAllNodes()
|
||||
|
||||
// 验证zIndex已交换
|
||||
expect(node1.zIndex).toBe(originalZIndex2)
|
||||
expect(node2.zIndex).toBe(originalZIndex1)
|
||||
expect(node1.zIndex).toBeGreaterThan(node2.zIndex)
|
||||
})
|
||||
|
||||
it('最顶层节点上移应该不产生变化', () => {
|
||||
console.log('\n=== 测试:最顶层节点上移 ===')
|
||||
|
||||
const node1 = lf.addNode({ x: 100, y: 100, zIndex: 1 })
|
||||
const node2 = lf.addNode({ x: 200, y: 200, zIndex: 2 })
|
||||
const node3 = lf.addNode({ x: 300, y: 300, zIndex: 3 })
|
||||
|
||||
const originalZIndex3 = node3.zIndex
|
||||
|
||||
console.log('上移前:')
|
||||
lf.printAllNodes()
|
||||
|
||||
// 尝试上移最顶层节点
|
||||
lf.bringForward(node3.id)
|
||||
|
||||
console.log('上移后:')
|
||||
lf.printAllNodes()
|
||||
|
||||
// 最顶层节点上移不应该改变zIndex
|
||||
expect(node3.zIndex).toBe(originalZIndex3)
|
||||
})
|
||||
})
|
||||
|
||||
describe('3. 节点下移操作', () => {
|
||||
it('下移一层应该与下方节点交换zIndex', () => {
|
||||
console.log('\n=== 测试:下移一层 ===')
|
||||
|
||||
const node1 = lf.addNode({ x: 100, y: 100, zIndex: 1 })
|
||||
const node2 = lf.addNode({ x: 200, y: 200, zIndex: 2 })
|
||||
const node3 = lf.addNode({ x: 300, y: 300, zIndex: 3 })
|
||||
|
||||
console.log('下移前:')
|
||||
lf.printAllNodes()
|
||||
|
||||
const originalZIndex3 = node3.zIndex
|
||||
const originalZIndex2 = node2.zIndex
|
||||
|
||||
// 对node3执行下移操作
|
||||
lf.sendBackward(node3.id)
|
||||
|
||||
console.log('下移后:')
|
||||
lf.printAllNodes()
|
||||
|
||||
// 验证zIndex已交换
|
||||
expect(node3.zIndex).toBe(originalZIndex2)
|
||||
expect(node2.zIndex).toBe(originalZIndex3)
|
||||
expect(node3.zIndex).toBeLessThan(node2.zIndex)
|
||||
})
|
||||
|
||||
it('最底层节点下移应该不产生变化', () => {
|
||||
console.log('\n=== 测试:最底层节点下移 ===')
|
||||
|
||||
const node1 = lf.addNode({ x: 100, y: 100, zIndex: 1 })
|
||||
const node2 = lf.addNode({ x: 200, y: 200, zIndex: 2 })
|
||||
const node3 = lf.addNode({ x: 300, y: 300, zIndex: 3 })
|
||||
|
||||
const originalZIndex1 = node1.zIndex
|
||||
|
||||
console.log('下移前:')
|
||||
lf.printAllNodes()
|
||||
|
||||
// 尝试下移最底层节点
|
||||
lf.sendBackward(node1.id)
|
||||
|
||||
console.log('下移后:')
|
||||
lf.printAllNodes()
|
||||
|
||||
// 最底层节点下移不应该改变zIndex
|
||||
expect(node1.zIndex).toBe(originalZIndex1)
|
||||
})
|
||||
})
|
||||
|
||||
describe('4. 节点置顶操作', () => {
|
||||
it('置顶应该将节点移到最上层', () => {
|
||||
console.log('\n=== 测试:置顶操作 ===')
|
||||
|
||||
const node1 = lf.addNode({ x: 100, y: 100, zIndex: 1 })
|
||||
const node2 = lf.addNode({ x: 200, y: 200, zIndex: 2 })
|
||||
const node3 = lf.addNode({ x: 300, y: 300, zIndex: 3 })
|
||||
|
||||
console.log('置顶前:')
|
||||
lf.printAllNodes()
|
||||
|
||||
// 对node1执行置顶操作
|
||||
lf.setElementZIndex(node1.id, 'top')
|
||||
|
||||
console.log('置顶后:')
|
||||
lf.printAllNodes()
|
||||
|
||||
// 验证node1的zIndex最大
|
||||
const allZIndexes = lf.nodes.map(n => n.zIndex)
|
||||
expect(node1.zIndex).toBe(Math.max(...allZIndexes))
|
||||
|
||||
// 验证node1在所有其他节点之上
|
||||
lf.nodes.forEach(node => {
|
||||
if (node.id !== node1.id) {
|
||||
expect(node1.zIndex).toBeGreaterThan(node.zIndex)
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
it('已经在顶层的节点置顶应该增加zIndex', () => {
|
||||
console.log('\n=== 测试:顶层节点置顶 ===')
|
||||
|
||||
const node1 = lf.addNode({ x: 100, y: 100, zIndex: 1 })
|
||||
const node2 = lf.addNode({ x: 200, y: 200, zIndex: 2 })
|
||||
const node3 = lf.addNode({ x: 300, y: 300, zIndex: 3 })
|
||||
|
||||
const originalZIndex3 = node3.zIndex
|
||||
|
||||
console.log('置顶前:')
|
||||
lf.printAllNodes()
|
||||
|
||||
// 对已经在顶层的node3执行置顶
|
||||
lf.setElementZIndex(node3.id, 'top')
|
||||
|
||||
console.log('置顶后:')
|
||||
lf.printAllNodes()
|
||||
|
||||
// 顶层节点置顶会增加zIndex
|
||||
expect(node3.zIndex).toBeGreaterThan(originalZIndex3)
|
||||
})
|
||||
})
|
||||
|
||||
describe('5. 节点置底操作', () => {
|
||||
it('置底应该将节点移到最下层', () => {
|
||||
console.log('\n=== 测试:置底操作 ===')
|
||||
|
||||
const node1 = lf.addNode({ x: 100, y: 100, zIndex: 1 })
|
||||
const node2 = lf.addNode({ x: 200, y: 200, zIndex: 2 })
|
||||
const node3 = lf.addNode({ x: 300, y: 300, zIndex: 3 })
|
||||
|
||||
console.log('置底前:')
|
||||
lf.printAllNodes()
|
||||
|
||||
// 对node3执行置底操作
|
||||
lf.setElementZIndex(node3.id, 'bottom')
|
||||
|
||||
console.log('置底后:')
|
||||
lf.printAllNodes()
|
||||
|
||||
// 验证node3的zIndex最小
|
||||
const allZIndexes = lf.nodes.map(n => n.zIndex)
|
||||
expect(node3.zIndex).toBe(Math.min(...allZIndexes))
|
||||
|
||||
// 验证node3在所有其他节点之下
|
||||
lf.nodes.forEach(node => {
|
||||
if (node.id !== node3.id) {
|
||||
expect(node3.zIndex).toBeLessThan(node.zIndex)
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
it('已经在底层的节点置底应该减少zIndex', () => {
|
||||
console.log('\n=== 测试:底层节点置底 ===')
|
||||
|
||||
const node1 = lf.addNode({ x: 100, y: 100, zIndex: 1 })
|
||||
const node2 = lf.addNode({ x: 200, y: 200, zIndex: 2 })
|
||||
const node3 = lf.addNode({ x: 300, y: 300, zIndex: 3 })
|
||||
|
||||
const originalZIndex1 = node1.zIndex
|
||||
|
||||
console.log('置底前:')
|
||||
lf.printAllNodes()
|
||||
|
||||
// 对已经在底层的node1执行置底
|
||||
lf.setElementZIndex(node1.id, 'bottom')
|
||||
|
||||
console.log('置底后:')
|
||||
lf.printAllNodes()
|
||||
|
||||
// 底层节点置底会减少zIndex
|
||||
expect(node1.zIndex).toBeLessThan(originalZIndex1)
|
||||
})
|
||||
})
|
||||
|
||||
describe('6. 复杂场景测试', () => {
|
||||
it('连续操作后zIndex应该保持正确顺序', () => {
|
||||
console.log('\n=== 测试:连续操作 ===')
|
||||
|
||||
const node1 = lf.addNode({ x: 100, y: 100, zIndex: 1 })
|
||||
const node2 = lf.addNode({ x: 200, y: 200, zIndex: 2 })
|
||||
const node3 = lf.addNode({ x: 300, y: 300, zIndex: 3 })
|
||||
const node4 = lf.addNode({ x: 400, y: 400, zIndex: 4 })
|
||||
|
||||
console.log('初始状态:')
|
||||
lf.printAllNodes()
|
||||
|
||||
// 操作序列: node1置顶 -> node4置底 -> node2上移
|
||||
lf.setElementZIndex(node1.id, 'top')
|
||||
console.log('node1置顶后:')
|
||||
lf.printAllNodes()
|
||||
|
||||
lf.setElementZIndex(node4.id, 'bottom')
|
||||
console.log('node4置底后:')
|
||||
lf.printAllNodes()
|
||||
|
||||
lf.bringForward(node2.id)
|
||||
console.log('node2上移后:')
|
||||
lf.printAllNodes()
|
||||
|
||||
// 验证最终顺序
|
||||
const sortedNodes = [...lf.nodes].sort((a, b) => a.zIndex - b.zIndex)
|
||||
|
||||
// node4应该在最底层
|
||||
expect(sortedNodes[0].id).toBe(node4.id)
|
||||
|
||||
// node1应该在最顶层
|
||||
expect(sortedNodes[sortedNodes.length - 1].id).toBe(node1.id)
|
||||
})
|
||||
|
||||
it('检查数据预览中的zIndex', () => {
|
||||
console.log('\n=== 测试:数据预览 ===')
|
||||
|
||||
const node1 = lf.addNode({ x: 100, y: 100, zIndex: 1 })
|
||||
const node2 = lf.addNode({ x: 200, y: 200, zIndex: 2 })
|
||||
const node3 = lf.addNode({ x: 300, y: 300, zIndex: 3 })
|
||||
|
||||
// 执行置顶操作
|
||||
lf.setElementZIndex(node1.id, 'top')
|
||||
|
||||
// 获取图数据(模拟数据预览)
|
||||
const graphData = lf.getGraphRawData()
|
||||
|
||||
console.log('数据预览:', JSON.stringify(graphData, null, 2))
|
||||
|
||||
// 验证数据中包含zIndex
|
||||
expect(graphData.nodes).toBeDefined()
|
||||
expect(graphData.nodes.length).toBe(3)
|
||||
|
||||
graphData.nodes.forEach((node: any) => {
|
||||
expect(node.zIndex).toBeDefined()
|
||||
expect(typeof node.zIndex).toBe('number')
|
||||
console.log(`节点 ${node.id} 的 zIndex: ${node.zIndex}`)
|
||||
})
|
||||
|
||||
// 验证node1的zIndex最大
|
||||
const node1Data = graphData.nodes.find((n: any) => n.id === node1.id)
|
||||
const allZIndexes = graphData.nodes.map((n: any) => n.zIndex)
|
||||
expect(node1Data?.zIndex).toBe(Math.max(...allZIndexes))
|
||||
})
|
||||
})
|
||||
})
|
||||
502
src/__tests__/layer-management/real-scenario.spec.ts
Normal file
502
src/__tests__/layer-management/real-scenario.spec.ts
Normal file
@@ -0,0 +1,502 @@
|
||||
import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest'
|
||||
import { createPinia, setActivePinia } from 'pinia'
|
||||
import LogicFlow from '@logicflow/core'
|
||||
|
||||
/**
|
||||
* 图层管理真实场景测试
|
||||
*
|
||||
* 这个测试直接使用 LogicFlow 实例,模拟真实的用户操作:
|
||||
* 1. 创建节点(模拟从 ComponentsPanel 拖拽)
|
||||
* 2. 执行图层操作(模拟 FlowEditor 中的操作)
|
||||
* 3. 验证数据预览(模拟 Toolbar 的 handlePreviewData)
|
||||
*/
|
||||
|
||||
/**
|
||||
* 辅助函数:获取包含 zIndex 的图数据
|
||||
* 模拟 useStore.ts 中的 syncLogicFlowDataToStore 逻辑
|
||||
*/
|
||||
function getGraphDataWithZIndex(lf: LogicFlow) {
|
||||
const graphData = lf.getGraphRawData();
|
||||
return {
|
||||
...graphData,
|
||||
nodes: (graphData.nodes || []).map((node: any) => {
|
||||
const model = lf.getNodeModelById(node.id);
|
||||
return {
|
||||
...node,
|
||||
zIndex: model?.zIndex ?? node.zIndex ?? 1
|
||||
};
|
||||
})
|
||||
};
|
||||
}
|
||||
|
||||
describe('图层管理真实场景测试', () => {
|
||||
let lf: LogicFlow | null = null
|
||||
let container: HTMLDivElement | null = null
|
||||
|
||||
beforeEach(() => {
|
||||
// 创建 Pinia 实例
|
||||
const pinia = createPinia()
|
||||
setActivePinia(pinia)
|
||||
|
||||
// 创建容器
|
||||
container = document.createElement('div')
|
||||
container.style.width = '800px'
|
||||
container.style.height = '600px'
|
||||
document.body.appendChild(container)
|
||||
|
||||
// 创建 LogicFlow 实例
|
||||
lf = new LogicFlow({
|
||||
container,
|
||||
grid: { type: 'dot', size: 10 },
|
||||
allowResize: true,
|
||||
allowRotate: true,
|
||||
})
|
||||
|
||||
lf.render({ nodes: [], edges: [] })
|
||||
})
|
||||
|
||||
afterEach(() => {
|
||||
// 清理
|
||||
if (lf) {
|
||||
lf.destroy()
|
||||
lf = null
|
||||
}
|
||||
if (container && container.parentNode) {
|
||||
container.parentNode.removeChild(container)
|
||||
container = null
|
||||
}
|
||||
})
|
||||
|
||||
it('场景1: 创建节点并验证 zIndex 分配', () => {
|
||||
console.log('\n=== 场景1: 创建节点并验证 zIndex 分配 ===')
|
||||
|
||||
if (!lf) return
|
||||
|
||||
// 模拟从 ComponentsPanel 拖拽创建 3 个节点
|
||||
const node1 = lf.addNode({
|
||||
type: 'rect',
|
||||
x: 100,
|
||||
y: 100,
|
||||
properties: {}
|
||||
})
|
||||
|
||||
const node2 = lf.addNode({
|
||||
type: 'rect',
|
||||
x: 200,
|
||||
y: 200,
|
||||
properties: {}
|
||||
})
|
||||
|
||||
const node3 = lf.addNode({
|
||||
type: 'rect',
|
||||
x: 300,
|
||||
y: 300,
|
||||
properties: {}
|
||||
})
|
||||
|
||||
// 获取节点模型
|
||||
const model1 = lf.getNodeModelById(node1.id)
|
||||
const model2 = lf.getNodeModelById(node2.id)
|
||||
const model3 = lf.getNodeModelById(node3.id)
|
||||
|
||||
console.log('创建后的 zIndex:')
|
||||
console.log(` node1: ${model1?.zIndex}`)
|
||||
console.log(` node2: ${model2?.zIndex}`)
|
||||
console.log(` node3: ${model3?.zIndex}`)
|
||||
|
||||
// 验证:所有节点都有 zIndex
|
||||
expect(model1?.zIndex).toBeDefined()
|
||||
expect(model2?.zIndex).toBeDefined()
|
||||
expect(model3?.zIndex).toBeDefined()
|
||||
|
||||
// 验证:zIndex 是数字
|
||||
expect(typeof model1?.zIndex).toBe('number')
|
||||
expect(typeof model2?.zIndex).toBe('number')
|
||||
expect(typeof model3?.zIndex).toBe('number')
|
||||
})
|
||||
|
||||
it('场景2: 置顶操作(模拟右键菜单)', () => {
|
||||
console.log('\n=== 场景2: 置顶操作 ===')
|
||||
|
||||
if (!lf) return
|
||||
|
||||
// 创建 3 个节点
|
||||
const node1 = lf.addNode({ type: 'rect', x: 100, y: 100 })
|
||||
const node2 = lf.addNode({ type: 'rect', x: 200, y: 200 })
|
||||
const node3 = lf.addNode({ type: 'rect', x: 300, y: 300 })
|
||||
|
||||
const model1 = lf.getNodeModelById(node1.id)
|
||||
const model2 = lf.getNodeModelById(node2.id)
|
||||
const model3 = lf.getNodeModelById(node3.id)
|
||||
|
||||
// 手动设置初始 zIndex(模拟历史数据加载)
|
||||
model1?.setZIndex(1)
|
||||
model2?.setZIndex(2)
|
||||
model3?.setZIndex(3)
|
||||
|
||||
console.log('初始 zIndex:', {
|
||||
node1: model1?.zIndex,
|
||||
node2: model2?.zIndex,
|
||||
node3: model3?.zIndex
|
||||
})
|
||||
|
||||
// 模拟用户右键点击 node1,选择"置于顶层"
|
||||
lf.setElementZIndex(node1.id, 'top')
|
||||
|
||||
console.log('置顶后 zIndex:', {
|
||||
node1: model1?.zIndex,
|
||||
node2: model2?.zIndex,
|
||||
node3: model3?.zIndex
|
||||
})
|
||||
|
||||
// 验证:node1 的 zIndex 最大
|
||||
const allZIndexes = [model1?.zIndex, model2?.zIndex, model3?.zIndex].filter(z => z !== undefined) as number[]
|
||||
expect(model1?.zIndex).toBe(Math.max(...allZIndexes))
|
||||
expect(model1?.zIndex).toBeGreaterThan(model2?.zIndex || 0)
|
||||
expect(model1?.zIndex).toBeGreaterThan(model3?.zIndex || 0)
|
||||
})
|
||||
|
||||
it('场景3: 置底操作(模拟右键菜单)', () => {
|
||||
console.log('\n=== 场景3: 置底操作 ===')
|
||||
|
||||
if (!lf) return
|
||||
|
||||
// 创建 3 个节点
|
||||
const node1 = lf.addNode({ type: 'rect', x: 100, y: 100 })
|
||||
const node2 = lf.addNode({ type: 'rect', x: 200, y: 200 })
|
||||
const node3 = lf.addNode({ type: 'rect', x: 300, y: 300 })
|
||||
|
||||
const model1 = lf.getNodeModelById(node1.id)
|
||||
const model2 = lf.getNodeModelById(node2.id)
|
||||
const model3 = lf.getNodeModelById(node3.id)
|
||||
|
||||
model1?.setZIndex(1)
|
||||
model2?.setZIndex(2)
|
||||
model3?.setZIndex(3)
|
||||
|
||||
console.log('初始 zIndex:', {
|
||||
node1: model1?.zIndex,
|
||||
node2: model2?.zIndex,
|
||||
node3: model3?.zIndex
|
||||
})
|
||||
|
||||
// 模拟用户右键点击 node3,选择"置于底层"
|
||||
// 修复:使用正确的置底逻辑(与 FlowEditor.vue 中的 sendToBack 一致)
|
||||
const allNodesScene3 = lf.graphModel.nodes;
|
||||
const allZIndexesForBottom = allNodesScene3.map(n => n.zIndex).filter(z => z !== undefined);
|
||||
const minZIndex = allZIndexesForBottom.length > 0 ? Math.min(...allZIndexesForBottom) : 1;
|
||||
const newZIndex = minZIndex - 1;
|
||||
model3?.setZIndex(newZIndex);
|
||||
|
||||
console.log('置底后 zIndex:', {
|
||||
node1: model1?.zIndex,
|
||||
node2: model2?.zIndex,
|
||||
node3: model3?.zIndex
|
||||
})
|
||||
|
||||
// 验证:node3 的 zIndex 最小
|
||||
const allZIndexes = [model1?.zIndex, model2?.zIndex, model3?.zIndex].filter(z => z !== undefined) as number[]
|
||||
expect(model3?.zIndex).toBe(Math.min(...allZIndexes))
|
||||
expect(model3?.zIndex).toBeLessThan(model1?.zIndex || Infinity)
|
||||
expect(model3?.zIndex).toBeLessThan(model2?.zIndex || Infinity)
|
||||
})
|
||||
|
||||
it('场景4: 上移一层操作', () => {
|
||||
console.log('\n=== 场景4: 上移一层操作 ===')
|
||||
|
||||
if (!lf) return
|
||||
|
||||
// 创建 3 个节点
|
||||
const node1 = lf.addNode({ type: 'rect', x: 100, y: 100 })
|
||||
const node2 = lf.addNode({ type: 'rect', x: 200, y: 200 })
|
||||
const node3 = lf.addNode({ type: 'rect', x: 300, y: 300 })
|
||||
|
||||
const model1 = lf.getNodeModelById(node1.id)
|
||||
const model2 = lf.getNodeModelById(node2.id)
|
||||
const model3 = lf.getNodeModelById(node3.id)
|
||||
|
||||
model1?.setZIndex(1)
|
||||
model2?.setZIndex(2)
|
||||
model3?.setZIndex(3)
|
||||
|
||||
const originalZIndex1 = model1?.zIndex
|
||||
const originalZIndex2 = model2?.zIndex
|
||||
|
||||
console.log('上移前 zIndex:', {
|
||||
node1: model1?.zIndex,
|
||||
node2: model2?.zIndex,
|
||||
node3: model3?.zIndex
|
||||
})
|
||||
|
||||
// 模拟 FlowEditor 的 bringForward 方法
|
||||
const currentNode = model1
|
||||
if (currentNode) {
|
||||
const currentZIndex = currentNode.zIndex
|
||||
const allNodes = lf.graphModel.nodes
|
||||
const nodesAbove = allNodes
|
||||
.filter((node) => node.zIndex > currentZIndex)
|
||||
.sort((a, b) => a.zIndex - b.zIndex)
|
||||
|
||||
if (nodesAbove.length > 0) {
|
||||
const nextNode = nodesAbove[0]
|
||||
currentNode.setZIndex(nextNode.zIndex)
|
||||
nextNode.setZIndex(currentZIndex)
|
||||
}
|
||||
}
|
||||
|
||||
console.log('上移后 zIndex:', {
|
||||
node1: model1?.zIndex,
|
||||
node2: model2?.zIndex,
|
||||
node3: model3?.zIndex
|
||||
})
|
||||
|
||||
// 验证:node1 和 node2 的 zIndex 已交换
|
||||
expect(model1?.zIndex).toBe(originalZIndex2)
|
||||
expect(model2?.zIndex).toBe(originalZIndex1)
|
||||
expect(model1?.zIndex).toBeGreaterThan(model2?.zIndex || 0)
|
||||
})
|
||||
|
||||
it('场景5: 下移一层操作', () => {
|
||||
console.log('\n=== 场景5: 下移一层操作 ===')
|
||||
|
||||
if (!lf) return
|
||||
|
||||
// 创建 3 个节点
|
||||
const node1 = lf.addNode({ type: 'rect', x: 100, y: 100 })
|
||||
const node2 = lf.addNode({ type: 'rect', x: 200, y: 200 })
|
||||
const node3 = lf.addNode({ type: 'rect', x: 300, y: 300 })
|
||||
|
||||
const model1 = lf.getNodeModelById(node1.id)
|
||||
const model2 = lf.getNodeModelById(node2.id)
|
||||
const model3 = lf.getNodeModelById(node3.id)
|
||||
|
||||
model1?.setZIndex(1)
|
||||
model2?.setZIndex(2)
|
||||
model3?.setZIndex(3)
|
||||
|
||||
const originalZIndex2 = model2?.zIndex
|
||||
const originalZIndex3 = model3?.zIndex
|
||||
|
||||
console.log('下移前 zIndex:', {
|
||||
node1: model1?.zIndex,
|
||||
node2: model2?.zIndex,
|
||||
node3: model3?.zIndex
|
||||
})
|
||||
|
||||
// 模拟 FlowEditor 的 sendBackward 方法
|
||||
const currentNode = model3
|
||||
if (currentNode) {
|
||||
const currentZIndex = currentNode.zIndex
|
||||
const allNodes = lf.graphModel.nodes
|
||||
const nodesBelow = allNodes
|
||||
.filter((node) => node.zIndex < currentZIndex)
|
||||
.sort((a, b) => b.zIndex - a.zIndex)
|
||||
|
||||
if (nodesBelow.length > 0) {
|
||||
const prevNode = nodesBelow[0]
|
||||
currentNode.setZIndex(prevNode.zIndex)
|
||||
prevNode.setZIndex(currentZIndex)
|
||||
}
|
||||
}
|
||||
|
||||
console.log('下移后 zIndex:', {
|
||||
node1: model1?.zIndex,
|
||||
node2: model2?.zIndex,
|
||||
node3: model3?.zIndex
|
||||
})
|
||||
|
||||
// 验证:node3 和 node2 的 zIndex 已交换
|
||||
expect(model3?.zIndex).toBe(originalZIndex2)
|
||||
expect(model2?.zIndex).toBe(originalZIndex3)
|
||||
expect(model3?.zIndex).toBeLessThan(model2?.zIndex || Infinity)
|
||||
})
|
||||
|
||||
it('场景6: 数据预览验证(模拟 Toolbar.handlePreviewData)', () => {
|
||||
console.log('\n=== 场景6: 数据预览验证 ===')
|
||||
|
||||
if (!lf) return
|
||||
|
||||
// 创建节点
|
||||
const node1 = lf.addNode({ type: 'rect', x: 100, y: 100 })
|
||||
const node2 = lf.addNode({ type: 'rect', x: 200, y: 200 })
|
||||
const node3 = lf.addNode({ type: 'rect', x: 300, y: 300 })
|
||||
|
||||
const model1 = lf.getNodeModelById(node1.id)
|
||||
const model2 = lf.getNodeModelById(node2.id)
|
||||
const model3 = lf.getNodeModelById(node3.id)
|
||||
|
||||
model1?.setZIndex(1)
|
||||
model2?.setZIndex(2)
|
||||
model3?.setZIndex(3)
|
||||
|
||||
// 执行置顶操作
|
||||
lf.setElementZIndex(node1.id, 'top')
|
||||
|
||||
// 模拟 Toolbar 的 handlePreviewData 方法(使用辅助函数获取包含 zIndex 的数据)
|
||||
const graphData = getGraphDataWithZIndex(lf)
|
||||
|
||||
console.log('数据预览:')
|
||||
console.log(JSON.stringify(graphData, null, 2))
|
||||
|
||||
// 验证:数据中包含 zIndex
|
||||
expect(graphData.nodes).toBeDefined()
|
||||
expect(graphData.nodes.length).toBe(3)
|
||||
|
||||
graphData.nodes.forEach((node: any) => {
|
||||
expect(node.zIndex).toBeDefined()
|
||||
expect(typeof node.zIndex).toBe('number')
|
||||
console.log(` 节点 ${node.id}: zIndex = ${node.zIndex}`)
|
||||
})
|
||||
|
||||
// 验证:node1 的 zIndex 最大
|
||||
const node1Data = graphData.nodes.find((n: any) => n.id === node1.id)
|
||||
const allZIndexes = graphData.nodes.map((n: any) => n.zIndex)
|
||||
expect(node1Data?.zIndex).toBe(Math.max(...allZIndexes))
|
||||
})
|
||||
|
||||
it('场景7: 完整用户流程测试', () => {
|
||||
console.log('\n=== 场景7: 完整用户流程测试 ===')
|
||||
|
||||
if (!lf) return
|
||||
|
||||
// 步骤 1: 用户从 ComponentsPanel 拖拽创建 4 个节点
|
||||
console.log('\n步骤 1: 创建节点')
|
||||
const nodes = [
|
||||
lf.addNode({ type: 'rect', x: 100, y: 100 }),
|
||||
lf.addNode({ type: 'rect', x: 200, y: 200 }),
|
||||
lf.addNode({ type: 'rect', x: 300, y: 300 }),
|
||||
lf.addNode({ type: 'rect', x: 400, y: 400 })
|
||||
]
|
||||
|
||||
const models = nodes.map(n => lf!.getNodeModelById(n.id))
|
||||
models.forEach((m, i) => m?.setZIndex(i + 1))
|
||||
|
||||
console.log('初始状态:', models.map((m, i) => ({
|
||||
id: nodes[i].id,
|
||||
zIndex: m?.zIndex
|
||||
})))
|
||||
|
||||
// 步骤 2: 用户右键点击 node1,选择"置于顶层"
|
||||
console.log('\n步骤 2: node1 置于顶层')
|
||||
lf.setElementZIndex(nodes[0].id, 'top')
|
||||
console.log('操作后:', models.map((m, i) => ({
|
||||
id: nodes[i].id,
|
||||
zIndex: m?.zIndex
|
||||
})))
|
||||
|
||||
// 步骤 3: 用户右键点击 node4,选择"置于底层"
|
||||
console.log('\n步骤 3: node4 置于底层')
|
||||
// 修复:使用正确的置底逻辑
|
||||
const allNodesStep3 = lf.graphModel.nodes;
|
||||
const allZIndexesStep3 = allNodesStep3.map(n => n.zIndex).filter(z => z !== undefined);
|
||||
const minZIndexStep3 = allZIndexesStep3.length > 0 ? Math.min(...allZIndexesStep3) : 1;
|
||||
models[3]?.setZIndex(minZIndexStep3 - 1);
|
||||
console.log('操作后:', models.map((m, i) => ({
|
||||
id: nodes[i].id,
|
||||
zIndex: m?.zIndex
|
||||
})))
|
||||
|
||||
// 步骤 4: 用户点击 Toolbar 的"数据预览"按钮
|
||||
console.log('\n步骤 4: 数据预览')
|
||||
const graphData = getGraphDataWithZIndex(lf)
|
||||
|
||||
console.log('最终数据:')
|
||||
graphData.nodes.forEach((node: any) => {
|
||||
console.log(` 节点 ${node.id}: zIndex = ${node.zIndex}`)
|
||||
})
|
||||
|
||||
// 验证最终顺序
|
||||
const sortedNodes = [...graphData.nodes].sort((a: any, b: any) => a.zIndex - b.zIndex)
|
||||
|
||||
// node4 应该在最底层
|
||||
expect(sortedNodes[0].id).toBe(nodes[3].id)
|
||||
|
||||
// node1 应该在最顶层
|
||||
expect(sortedNodes[sortedNodes.length - 1].id).toBe(nodes[0].id)
|
||||
|
||||
console.log('\n✅ 完整流程测试通过!')
|
||||
console.log('验证结果:')
|
||||
console.log(` - 最底层: ${sortedNodes[0].id} (zIndex: ${sortedNodes[0].zIndex})`)
|
||||
console.log(` - 最顶层: ${sortedNodes[sortedNodes.length - 1].id} (zIndex: ${sortedNodes[sortedNodes.length - 1].zIndex})`)
|
||||
})
|
||||
|
||||
it('场景8: 边界情况 - 最顶层节点继续置顶', () => {
|
||||
console.log('\n=== 场景8: 边界情况 - 最顶层节点继续置顶 ===')
|
||||
|
||||
if (!lf) return
|
||||
|
||||
const node1 = lf.addNode({ type: 'rect', x: 100, y: 100 })
|
||||
const node2 = lf.addNode({ type: 'rect', x: 200, y: 200 })
|
||||
const node3 = lf.addNode({ type: 'rect', x: 300, y: 300 })
|
||||
|
||||
const model1 = lf.getNodeModelById(node1.id)
|
||||
const model2 = lf.getNodeModelById(node2.id)
|
||||
const model3 = lf.getNodeModelById(node3.id)
|
||||
|
||||
model1?.setZIndex(1)
|
||||
model2?.setZIndex(2)
|
||||
model3?.setZIndex(3)
|
||||
|
||||
const originalZIndex3 = model3?.zIndex
|
||||
|
||||
console.log('初始 zIndex:', {
|
||||
node1: model1?.zIndex,
|
||||
node2: model2?.zIndex,
|
||||
node3: model3?.zIndex
|
||||
})
|
||||
|
||||
// 对已经在顶层的 node3 执行置顶
|
||||
lf.setElementZIndex(node3.id, 'top')
|
||||
|
||||
console.log('置顶后 zIndex:', {
|
||||
node1: model1?.zIndex,
|
||||
node2: model2?.zIndex,
|
||||
node3: model3?.zIndex
|
||||
})
|
||||
|
||||
// 验证:顶层节点置顶会增加 zIndex
|
||||
expect(model3?.zIndex).toBeGreaterThan(originalZIndex3 || 0)
|
||||
})
|
||||
|
||||
it('场景9: 边界情况 - 最底层节点继续置底', () => {
|
||||
console.log('\n=== 场景9: 边界情况 - 最底层节点继续置底 ===')
|
||||
|
||||
if (!lf) return
|
||||
|
||||
const node1 = lf.addNode({ type: 'rect', x: 100, y: 100 })
|
||||
const node2 = lf.addNode({ type: 'rect', x: 200, y: 200 })
|
||||
const node3 = lf.addNode({ type: 'rect', x: 300, y: 300 })
|
||||
|
||||
const model1 = lf.getNodeModelById(node1.id)
|
||||
const model2 = lf.getNodeModelById(node2.id)
|
||||
const model3 = lf.getNodeModelById(node3.id)
|
||||
|
||||
model1?.setZIndex(1)
|
||||
model2?.setZIndex(2)
|
||||
model3?.setZIndex(3)
|
||||
|
||||
const originalZIndex1 = model1?.zIndex
|
||||
|
||||
console.log('初始 zIndex:', {
|
||||
node1: model1?.zIndex,
|
||||
node2: model2?.zIndex,
|
||||
node3: model3?.zIndex
|
||||
})
|
||||
|
||||
// 对已经在底层的 node1 执行置底
|
||||
// 修复:使用正确的置底逻辑
|
||||
const allNodesScene9 = lf.graphModel.nodes;
|
||||
const allZIndexesScene9 = allNodesScene9.map(n => n.zIndex).filter(z => z !== undefined);
|
||||
const minZIndexScene9 = allZIndexesScene9.length > 0 ? Math.min(...allZIndexesScene9) : 1;
|
||||
model1?.setZIndex(minZIndexScene9 - 1);
|
||||
|
||||
console.log('置底后 zIndex:', {
|
||||
node1: model1?.zIndex,
|
||||
node2: model2?.zIndex,
|
||||
node3: model3?.zIndex
|
||||
})
|
||||
|
||||
// 验证:底层节点置底会减少 zIndex
|
||||
expect(model1?.zIndex).toBeLessThan(originalZIndex1 || Infinity)
|
||||
})
|
||||
})
|
||||
312
src/__tests__/layer-management/unit-test.spec.ts.bak
Normal file
312
src/__tests__/layer-management/unit-test.spec.ts.bak
Normal file
@@ -0,0 +1,312 @@
|
||||
import { describe, it, expect, beforeEach, vi } from 'vitest'
|
||||
|
||||
describe('图层管理测试', () => {
|
||||
let nodes: any[]
|
||||
let mockSetElementZIndex: any
|
||||
|
||||
beforeEach(() => {
|
||||
// 初始化测试节点
|
||||
nodes = []
|
||||
|
||||
// 模拟 setElementZIndex 函数
|
||||
mockSetElementZIndex = vi.fn((id, zIndexOrPosition) => {
|
||||
const node = nodes.find(n => n.id === id)
|
||||
if (!node) return
|
||||
|
||||
if (zIndexOrPosition === 'top') {
|
||||
const maxZIndex = Math.max(...nodes.map(n => n.zIndex))
|
||||
node.zIndex = maxZIndex + 1
|
||||
} else if (zIndexOrPosition === 'bottom') {
|
||||
const minZIndex = Math.min(...nodes.map(n => n.zIndex))
|
||||
node.zIndex = minZIndex - 1
|
||||
} else if (typeof zIndexOrPosition === 'number') {
|
||||
node.zIndex = zIndexOrPosition
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
describe('1. 创建多个节点并检查zIndex', () => {
|
||||
it('应该为新创建的节点分配正确的zIndex', () => {
|
||||
// 模拟创建3个节点
|
||||
nodes = [
|
||||
{ id: 'node1', type: 'rect', x: 100, y: 100, zIndex: 1 },
|
||||
{ id: 'node2', type: 'rect', x: 200, y: 200, zIndex: 2 },
|
||||
{ id: 'node3', type: 'rect', x: 300, y: 300, zIndex: 3 }
|
||||
]
|
||||
|
||||
// 验证每个节点都有zIndex
|
||||
nodes.forEach(node => {
|
||||
expect(node.zIndex).toBeDefined()
|
||||
expect(typeof node.zIndex).toBe('number')
|
||||
})
|
||||
|
||||
// 验证zIndex是递增的
|
||||
expect(nodes[0].zIndex).toBeLessThan(nodes[1].zIndex)
|
||||
expect(nodes[1].zIndex).toBeLessThan(nodes[2].zIndex)
|
||||
})
|
||||
|
||||
it('新节点应该默认获得较高的zIndex', () => {
|
||||
const existingNodes = [
|
||||
{ id: 'node1', type: 'rect', x: 100, y: 100, zIndex: 1 },
|
||||
{ id: 'node2', type: 'rect', x: 200, y: 200, zIndex: 2 }
|
||||
]
|
||||
|
||||
const newNode = { id: 'node3', type: 'rect', x: 300, y: 300, zIndex: 1000 }
|
||||
|
||||
nodes = [...existingNodes, newNode]
|
||||
|
||||
// 新节点的zIndex应该大于现有节点
|
||||
expect(newNode.zIndex).toBeGreaterThan(existingNodes[0].zIndex)
|
||||
expect(newNode.zIndex).toBeGreaterThan(existingNodes[1].zIndex)
|
||||
})
|
||||
|
||||
it('多个节点的zIndex应该各不相同', () => {
|
||||
nodes = [
|
||||
{ id: 'node1', type: 'rect', x: 100, y: 100, zIndex: 1 },
|
||||
{ id: 'node2', type: 'rect', x: 200, y: 200, zIndex: 2 },
|
||||
{ id: 'node3', type: 'rect', x: 300, y: 300, zIndex: 3 },
|
||||
{ id: 'node4', type: 'rect', x: 400, y: 400, zIndex: 4 }
|
||||
]
|
||||
|
||||
const zIndexes = nodes.map(n => n.zIndex)
|
||||
const uniqueZIndexes = new Set(zIndexes)
|
||||
|
||||
// 所有zIndex应该是唯一的
|
||||
expect(uniqueZIndexes.size).toBe(nodes.length)
|
||||
})
|
||||
})
|
||||
|
||||
describe('2. 节点上移操作', () => {
|
||||
it('上移一层应该与上方节点交换zIndex', () => {
|
||||
nodes = [
|
||||
{ id: 'node1', type: 'rect', x: 100, y: 100, zIndex: 1 },
|
||||
{ id: 'node2', type: 'rect', x: 200, y: 200, zIndex: 2 },
|
||||
{ id: 'node3', type: 'rect', x: 300, y: 300, zIndex: 3 }
|
||||
]
|
||||
|
||||
// 对node1执行上移操作
|
||||
const targetNode = nodes[0]
|
||||
const upperNode = nodes[1]
|
||||
const originalTargetZIndex = targetNode.zIndex
|
||||
const originalUpperZIndex = upperNode.zIndex
|
||||
|
||||
// 模拟交换zIndex
|
||||
mockSetElementZIndex(targetNode.id, originalUpperZIndex)
|
||||
mockSetElementZIndex(upperNode.id, originalTargetZIndex)
|
||||
|
||||
// 验证zIndex已交换
|
||||
expect(targetNode.zIndex).toBe(originalUpperZIndex)
|
||||
expect(upperNode.zIndex).toBe(originalTargetZIndex)
|
||||
expect(targetNode.zIndex).toBeGreaterThan(upperNode.zIndex)
|
||||
})
|
||||
|
||||
it('最顶层节点上移应该不产生变化', () => {
|
||||
nodes = [
|
||||
{ id: 'node1', type: 'rect', x: 100, y: 100, zIndex: 1 },
|
||||
{ id: 'node2', type: 'rect', x: 200, y: 200, zIndex: 2 },
|
||||
{ id: 'node3', type: 'rect', x: 300, y: 300, zIndex: 3 }
|
||||
]
|
||||
|
||||
const topNode = nodes[2]
|
||||
const originalZIndex = topNode.zIndex
|
||||
|
||||
// 尝试上移最顶层节点
|
||||
const sortedNodes = [...nodes].sort((a, b) => a.zIndex - b.zIndex)
|
||||
const currentIndex = sortedNodes.findIndex(n => n.id === topNode.id)
|
||||
const isTopNode = currentIndex === sortedNodes.length - 1
|
||||
|
||||
expect(isTopNode).toBe(true)
|
||||
expect(topNode.zIndex).toBe(originalZIndex)
|
||||
})
|
||||
})
|
||||
|
||||
describe('3. 节点下移操作', () => {
|
||||
it('下移一层应该与下方节点交换zIndex', () => {
|
||||
nodes = [
|
||||
{ id: 'node1', type: 'rect', x: 100, y: 100, zIndex: 1 },
|
||||
{ id: 'node2', type: 'rect', x: 200, y: 200, zIndex: 2 },
|
||||
{ id: 'node3', type: 'rect', x: 300, y: 300, zIndex: 3 }
|
||||
]
|
||||
|
||||
const targetNode = nodes[2]
|
||||
const lowerNode = nodes[1]
|
||||
const originalTargetZIndex = targetNode.zIndex
|
||||
const originalLowerZIndex = lowerNode.zIndex
|
||||
|
||||
// 模拟交换zIndex
|
||||
mockSetElementZIndex(targetNode.id, originalLowerZIndex)
|
||||
mockSetElementZIndex(lowerNode.id, originalTargetZIndex)
|
||||
|
||||
// 验证zIndex已交换
|
||||
expect(targetNode.zIndex).toBe(originalLowerZIndex)
|
||||
expect(lowerNode.zIndex).toBe(originalTargetZIndex)
|
||||
expect(targetNode.zIndex).toBeLessThan(lowerNode.zIndex)
|
||||
})
|
||||
|
||||
it('最底层节点下移应该不产生变化', () => {
|
||||
nodes = [
|
||||
{ id: 'node1', type: 'rect', x: 100, y: 100, zIndex: 1 },
|
||||
{ id: 'node2', type: 'rect', x: 200, y: 200, zIndex: 2 },
|
||||
{ id: 'node3', type: 'rect', x: 300, y: 300, zIndex: 3 }
|
||||
]
|
||||
|
||||
const bottomNode = nodes[0]
|
||||
const originalZIndex = bottomNode.zIndex
|
||||
|
||||
// 尝试下移最底层节点
|
||||
const sortedNodes = [...nodes].sort((a, b) => a.zIndex - b.zIndex)
|
||||
const currentIndex = sortedNodes.findIndex(n => n.id === bottomNode.id)
|
||||
const isBottomNode = currentIndex === 0
|
||||
|
||||
expect(isBottomNode).toBe(true)
|
||||
expect(bottomNode.zIndex).toBe(originalZIndex)
|
||||
})
|
||||
})
|
||||
|
||||
describe('4. 节点置顶操作', () => {
|
||||
it('置顶应该将节点移到最上层', () => {
|
||||
nodes = [
|
||||
{ id: 'node1', type: 'rect', x: 100, y: 100, zIndex: 1 },
|
||||
{ id: 'node2', type: 'rect', x: 200, y: 200, zIndex: 2 },
|
||||
{ id: 'node3', type: 'rect', x: 300, y: 300, zIndex: 3 }
|
||||
]
|
||||
|
||||
const targetNode = nodes[0]
|
||||
|
||||
// 模拟置顶操作
|
||||
mockSetElementZIndex(targetNode.id, 'top')
|
||||
|
||||
// 验证该节点的zIndex最大
|
||||
const allZIndexes = nodes.map(n => n.zIndex)
|
||||
expect(targetNode.zIndex).toBe(Math.max(...allZIndexes))
|
||||
|
||||
// 验证该节点在所有节点之上
|
||||
nodes.forEach(node => {
|
||||
if (node.id !== targetNode.id) {
|
||||
expect(targetNode.zIndex).toBeGreaterThan(node.zIndex)
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
it('已经在顶层的节点置顶应该保持不变', () => {
|
||||
nodes = [
|
||||
{ id: 'node1', type: 'rect', x: 100, y: 100, zIndex: 1 },
|
||||
{ id: 'node2', type: 'rect', x: 200, y: 200, zIndex: 2 },
|
||||
{ id: 'node3', type: 'rect', x: 300, y: 300, zIndex: 3 }
|
||||
]
|
||||
|
||||
const topNode = nodes[2]
|
||||
|
||||
// 对已经在顶层的节点执行置顶
|
||||
const maxZIndex = Math.max(...nodes.map(n => n.zIndex))
|
||||
expect(topNode.zIndex).toBe(maxZIndex)
|
||||
})
|
||||
})
|
||||
|
||||
describe('5. 节点置底操作', () => {
|
||||
it('置底应该将节点移到最下层', () => {
|
||||
nodes = [
|
||||
{ id: 'node1', type: 'rect', x: 100, y: 100, zIndex: 1 },
|
||||
{ id: 'node2', type: 'rect', x: 200, y: 200, zIndex: 2 },
|
||||
{ id: 'node3', type: 'rect', x: 300, y: 300, zIndex: 3 }
|
||||
]
|
||||
|
||||
const targetNode = nodes[2]
|
||||
|
||||
// 模拟置底操作
|
||||
mockSetElementZIndex(targetNode.id, 'bottom')
|
||||
|
||||
// 验证该节点的zIndex最小
|
||||
const allZIndexes = nodes.map(n => n.zIndex)
|
||||
expect(targetNode.zIndex).toBe(Math.min(...allZIndexes))
|
||||
|
||||
// 验证该节点在所有节点之下
|
||||
nodes.forEach(node => {
|
||||
if (node.id !== targetNode.id) {
|
||||
expect(targetNode.zIndex).toBeLessThan(node.zIndex)
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
it('已经在底层的节点置底应该保持不变', () => {
|
||||
nodes = [
|
||||
{ id: 'node1', type: 'rect', x: 100, y: 100, zIndex: 1 },
|
||||
{ id: 'node2', type: 'rect', x: 200, y: 200, zIndex: 2 },
|
||||
{ id: 'node3', type: 'rect', x: 300, y: 300, zIndex: 3 }
|
||||
]
|
||||
|
||||
const bottomNode = nodes[0]
|
||||
|
||||
// 对已经在底层的节点执行置底
|
||||
const minZIndex = Math.min(...nodes.map(n => n.zIndex))
|
||||
expect(bottomNode.zIndex).toBe(minZIndex)
|
||||
})
|
||||
})
|
||||
|
||||
describe('6. 复杂场景测试', () => {
|
||||
it('连续操作后zIndex应该保持正确顺序', () => {
|
||||
nodes = [
|
||||
{ id: 'node1', type: 'rect', x: 100, y: 100, zIndex: 1 },
|
||||
{ id: 'node2', type: 'rect', x: 200, y: 200, zIndex: 2 },
|
||||
{ id: 'node3', type: 'rect', x: 300, y: 300, zIndex: 3 },
|
||||
{ id: 'node4', type: 'rect', x: 400, y: 400, zIndex: 4 }
|
||||
]
|
||||
|
||||
// 操作序列: node1置顶 -> node4置底 -> node2上移
|
||||
mockSetElementZIndex('node1', 'top')
|
||||
mockSetElementZIndex('node4', 'bottom')
|
||||
|
||||
// node2上移(与node3交换)
|
||||
const node2 = nodes.find(n => n.id === 'node2')!
|
||||
const node3 = nodes.find(n => n.id === 'node3')!
|
||||
const temp = node2.zIndex
|
||||
mockSetElementZIndex('node2', node3.zIndex)
|
||||
mockSetElementZIndex('node3', temp)
|
||||
|
||||
// 验证最终顺序
|
||||
const sortedNodes = [...nodes].sort((a, b) => a.zIndex - b.zIndex)
|
||||
|
||||
// node4应该在最底层
|
||||
expect(sortedNodes[0].id).toBe('node4')
|
||||
// node1应该在最顶层
|
||||
expect(sortedNodes[sortedNodes.length - 1].id).toBe('node1')
|
||||
})
|
||||
|
||||
it('选中多个节点时只操作第一个选中的节点', () => {
|
||||
nodes = [
|
||||
{ id: 'node1', type: 'rect', x: 100, y: 100, zIndex: 1 },
|
||||
{ id: 'node2', type: 'rect', x: 200, y: 200, zIndex: 2 },
|
||||
{ id: 'node3', type: 'rect', x: 300, y: 300, zIndex: 3 }
|
||||
]
|
||||
|
||||
// 模拟选中多个节点
|
||||
const selectedNodes = [nodes[0], nodes[1]]
|
||||
const targetNode = selectedNodes[0]
|
||||
|
||||
// 只对第一个节点执行操作
|
||||
mockSetElementZIndex(targetNode.id, 'top')
|
||||
|
||||
// 验证只调用了一次
|
||||
expect(mockSetElementZIndex).toHaveBeenCalledTimes(1)
|
||||
})
|
||||
|
||||
it('zIndex应该在保存和加载后保持一致', () => {
|
||||
const originalNodes = [
|
||||
{ id: 'node1', type: 'rect', x: 100, y: 100, zIndex: 1 },
|
||||
{ id: 'node2', type: 'rect', x: 200, y: 200, zIndex: 2 },
|
||||
{ id: 'node3', type: 'rect', x: 300, y: 300, zIndex: 3 }
|
||||
]
|
||||
|
||||
// 模拟保存
|
||||
const savedData = JSON.parse(JSON.stringify(originalNodes))
|
||||
|
||||
// 模拟加载
|
||||
const loadedNodes = savedData
|
||||
|
||||
// 验证zIndex保持一致
|
||||
originalNodes.forEach((node, index) => {
|
||||
expect(loadedNodes[index].zIndex).toBe(node.zIndex)
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
Reference in New Issue
Block a user