Files
workspace/code/fms/fms-vue/tests/unit/workflow-designer-utils.spec.js
T
2026-09-27 21:59:13 +08:00

257 lines
8.9 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, expect, it } from 'vitest'
import { spreadBranchNodes } from '@/views/system/workflow/designer/utils'
/** 构造一个画布节点 */
function node(id, type, x, y, width, height, text) {
return { id, type, x, y, width, height, text: text ?? { value: id, x, y } }
}
function edge(id, sourceNodeId, targetNodeId) {
return { id, sourceNodeId, targetNodeId, offset: 15 }
}
/** 默认出口表:source → 出线目标 */
function defaultExits(entries) {
return new Map(Object.entries(entries).map(([source, targets]) => [source, new Set(targets)]))
}
describe('一键整理的布局后处理', () => {
it('旁支与默认出口排在同一列(连线从它正中穿过)时,推到该连线的外侧', () => {
const graph = {
nodes: [
node('amount_gateway', 'fms-exclusive-gateway', 200, 300, 68, 68),
node('gm_approve', 'fms-user-task', 200, 400, 160, 48),
node('finance_review', 'fms-user-task', 200, 500, 160, 48),
],
edges: [
edge('e3', 'amount_gateway', 'gm_approve'),
edge('e4', 'amount_gateway', 'finance_review'),
edge('e5', 'gm_approve', 'finance_review'),
],
}
const moved = spreadBranchNodes(graph, {
defaultExitTargets: defaultExits({ amount_gateway: ['finance_review'] }),
})
expect(moved.has('gm_approve')).toBe(true)
// 线在 x=200(金额判断与财务复核同列),旁支被推到线右侧、留白 120
expect(graph.nodes[1].x).toBe(400)
// 源、目标本身不动
expect(graph.nodes[0].x).toBe(200)
expect(graph.nodes[2].x).toBe(200)
})
it('文本坐标跟着节点一起搬,否则框动了字还留在原地', () => {
const graph = {
nodes: [
node('amount_gateway', 'fms-exclusive-gateway', 200, 300, 68, 68, { value: '金额判断', x: 200, y: 300 }),
node('gm_approve', 'fms-user-task', 200, 400, 160, 48, { value: '总经理审批', x: 200, y: 400 }),
node('finance_review', 'fms-user-task', 200, 500, 160, 48, { value: '财务复核', x: 200, y: 500 }),
],
edges: [
edge('e3', 'amount_gateway', 'gm_approve'),
edge('e4', 'amount_gateway', 'finance_review'),
edge('e5', 'gm_approve', 'finance_review'),
],
}
spreadBranchNodes(graph, {
defaultExitTargets: defaultExits({ amount_gateway: ['finance_review'] }),
})
expect(graph.nodes[1].text.x).toBe(graph.nodes[1].x)
expect(graph.nodes[0].text.x).toBe(200)
})
it('网关旁支与主干贴得太近时,推到固定留白处(含旁支下游整条)', () => {
const graph = {
nodes: [
node('split', 'fms-exclusive-gateway', 0, 0, 68, 68),
node('branch', 'fms-user-task', 120, 200, 160, 48),
node('branch_2', 'fms-user-task', 120, 320, 160, 48),
node('join', 'fms-user-task', 0, 440, 160, 48),
],
edges: [
edge('e1', 'split', 'branch'),
edge('e2', 'branch', 'branch_2'),
edge('e3', 'branch_2', 'join'),
edge('e4', 'split', 'join'),
],
}
const moved = spreadBranchNodes(graph, { defaultExitTargets: defaultExits({ split: ['join'] }) })
// 间距只有 6px,补到 120:120 + (120 - 6) = 234
expect(moved.has('branch')).toBe(true)
expect(moved.has('branch_2')).toBe(true)
expect(graph.nodes[1].x).toBe(234)
expect(graph.nodes[2].x).toBe(234)
// 与网关同列的默认出口不动
expect(graph.nodes[3].x).toBe(0)
})
it('旁支已经让开足够距离时不动它', () => {
const graph = {
nodes: [
node('split', 'fms-exclusive-gateway', 0, 0, 68, 68),
node('branch', 'fms-user-task', 300, 200, 160, 48),
node('join', 'fms-user-task', 0, 400, 160, 48),
],
edges: [
edge('e1', 'split', 'branch'),
edge('e2', 'branch', 'join'),
edge('e3', 'split', 'join'),
],
}
const moved = spreadBranchNodes(graph, { defaultExitTargets: defaultExits({ split: ['join'] }) })
expect(moved.size).toBe(0)
expect(graph.nodes[1].x).toBe(300)
})
it('并行网关对齐到三条分支的水平中心,中间那条直上直下', () => {
const graph = {
nodes: [
node('split', 'fms-parallel-gateway', 0, 0, 68, 68),
node('a', 'fms-user-task', 0, 200, 160, 48),
node('b', 'fms-user-task', 260, 200, 160, 48),
node('c', 'fms-user-task', 520, 200, 160, 48),
node('join', 'fms-parallel-gateway', 260, 400, 68, 68),
],
edges: [
edge('e1', 'split', 'a'),
edge('e2', 'split', 'b'),
edge('e3', 'split', 'c'),
edge('e4', 'a', 'join'),
edge('e5', 'b', 'join'),
edge('e6', 'c', 'join'),
],
}
const moved = spreadBranchNodes(graph)
expect(moved.has('split')).toBe(true)
// 三条分支的中间那条在 260,分叉点与汇合点都对齐到这里
expect(graph.nodes[0].x).toBe(260)
expect(graph.nodes[4].x).toBe(260)
// 分支节点自己不动
expect(graph.nodes[1].x).toBe(0)
expect(graph.nodes[3].x).toBe(520)
})
it('并行网关下面两条分支时,网关落在两条分支中间', () => {
const graph = {
nodes: [
node('split', 'fms-parallel-gateway', 0, 0, 68, 68),
node('a', 'fms-user-task', 0, 200, 160, 48),
node('b', 'fms-user-task', 260, 200, 160, 48),
node('join', 'fms-parallel-gateway', 0, 400, 68, 68),
],
edges: [edge('e1', 'split', 'a'), edge('e2', 'split', 'b'), edge('e3', 'a', 'join'), edge('e4', 'b', 'join')],
}
spreadBranchNodes(graph)
expect(graph.nodes[0].x).toBe(130)
expect(graph.nodes[3].x).toBe(130)
})
it('主干排成两列时拉直成一条竖线,挂在主干上的旁支跟着搬', () => {
// dagre 的常见输出:并行段前面那段主干在 0,并行段之后在 260
const graph = {
nodes: [
node('start', 'fms-start', 0, 0, 52, 52),
node('check', 'fms-user-task', 0, 200, 160, 48),
node('gm', 'fms-user-task', 234, 300, 160, 48),
node('split', 'fms-parallel-gateway', 260, 400, 68, 68),
node('a', 'fms-user-task', 0, 600, 160, 48),
node('b', 'fms-user-task', 260, 600, 160, 48),
node('c', 'fms-user-task', 520, 600, 160, 48),
node('join', 'fms-parallel-gateway', 260, 800, 68, 68),
node('end', 'fms-end', 260, 1000, 52, 52),
],
edges: [
edge('e1', 'start', 'check'),
edge('e2', 'check', 'split'),
edge('e3', 'check', 'gm'),
edge('e4', 'gm', 'split'),
edge('e5', 'split', 'a'),
edge('e6', 'split', 'b'),
edge('e7', 'split', 'c'),
edge('e8', 'a', 'join'),
edge('e9', 'b', 'join'),
edge('e10', 'c', 'join'),
edge('e11', 'join', 'end'),
],
}
spreadBranchNodes(graph, { defaultExitTargets: defaultExits({ check: ['split'] }) })
// 主轴取主干 x 的中位数 = 260(并行段中心),前半段整段拉过来
expect(graph.nodes.map((item) => [item.id, item.x])).toEqual([
['start', 260],
['check', 260],
// 旁支跟着主干走,保持它相对主干的偏移(234 - 0)
['gm', 494],
['split', 260],
['a', 0],
['b', 260],
['c', 520],
['join', 260],
['end', 260],
])
})
it('条件网关的出线落在不同层时不居中(默认出口继续直下)', () => {
const graph = {
nodes: [
node('gateway', 'fms-exclusive-gateway', 0, 0, 68, 68),
node('branch', 'fms-user-task', 300, 200, 160, 48),
node('next', 'fms-user-task', 0, 400, 160, 48),
],
edges: [
edge('e1', 'gateway', 'branch'),
edge('e2', 'gateway', 'next'),
edge('e3', 'branch', 'next'),
],
}
spreadBranchNodes(graph, { defaultExitTargets: defaultExits({ gateway: ['next'] }) })
expect(graph.nodes[0].x).toBe(0)
})
it('条件网关两条都是显式条件、没有兜底时,两条支线左右分开、主干走到汇合点', () => {
const graph = {
nodes: [
node('start', 'fms-start', 0, 0, 52, 52),
node('gateway', 'fms-exclusive-gateway', 0, 100, 68, 68),
node('left', 'fms-user-task', -200, 200, 160, 48),
node('right', 'fms-user-task', 200, 200, 160, 48),
node('join', 'fms-user-task', 0, 300, 160, 48),
],
edges: [
edge('e1', 'start', 'gateway'),
edge('e2', 'gateway', 'left'),
edge('e3', 'gateway', 'right'),
edge('e4', 'left', 'join'),
edge('e5', 'right', 'join'),
],
}
const moved = spreadBranchNodes(graph)
// 没有默认出口,主干跳过两条支线直接走到汇合点,两条支线都算旁支
expect(moved.has('left')).toBe(true)
expect(moved.has('right')).toBe(true)
expect(graph.nodes[1].x).toBe(0)
// 左右各留白 120:86 + (120 - 86)
expect(graph.nodes[2].x).toBe(-234)
expect(graph.nodes[3].x).toBe(234)
// 汇合点在主轴上,不动
expect(graph.nodes[4].x).toBe(0)
})
})