flowLayout.test.ts 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130
  1. import { describe, it, expect } from 'vitest'
  2. import { buildFlowLayout } from '@/utils/flowLayout'
  3. const diamond = {
  4. nodes: [
  5. { id: 'start', type: 'start' },
  6. { id: 'A', type: 'approval', name: '审批A' },
  7. { id: 'cond', type: 'condition', name: '条件' },
  8. { id: 'B1', type: 'approval', name: '大额审批' },
  9. { id: 'B2', type: 'approval', name: '默认审批' },
  10. { id: 'C', type: 'approval', name: '审批C' },
  11. { id: 'end', type: 'end' },
  12. ],
  13. edges: [
  14. { sourceNodeId: 'start', targetNodeId: 'A' },
  15. { sourceNodeId: 'A', targetNodeId: 'cond' },
  16. { sourceNodeId: 'cond', targetNodeId: 'B1', condition: { condition: 'amount>=1000', isDefault: false } },
  17. { sourceNodeId: 'cond', targetNodeId: 'B2', condition: { condition: '', isDefault: true } },
  18. { sourceNodeId: 'B1', targetNodeId: 'C' },
  19. { sourceNodeId: 'B2', targetNodeId: 'C' },
  20. { sourceNodeId: 'C', targetNodeId: 'end' },
  21. ],
  22. }
  23. const allIds = (rows: any[]) =>
  24. rows.flatMap((r) => {
  25. const main = (r.nodes || []).map((n: any) => n.id)
  26. const branch = (r.branches || []).flatMap((b: any) => b.nodes.map((n: any) => n.id))
  27. return main.concat(branch).concat(r.kind === 'branch' ? [`${r.title}`] : [])
  28. })
  29. describe('buildFlowLayout', () => {
  30. it('线性流程全部展示且不重复', () => {
  31. const linear = {
  32. nodes: [
  33. { id: 'start', type: 'start' },
  34. { id: 'A', type: 'approval', name: 'A' },
  35. { id: 'B', type: 'approval', name: 'B' },
  36. { id: 'end', type: 'end' },
  37. ],
  38. edges: [
  39. { sourceNodeId: 'start', targetNodeId: 'A' },
  40. { sourceNodeId: 'A', targetNodeId: 'B' },
  41. { sourceNodeId: 'B', targetNodeId: 'end' },
  42. ],
  43. }
  44. const rows = buildFlowLayout(linear.nodes, linear.edges, [
  45. { nodeId: 'A', status: 'completed' },
  46. { nodeId: 'B', status: 'current' },
  47. ], [])
  48. const ids = allIds(rows)
  49. expect(ids.filter((x) => x === 'A')).toHaveLength(1)
  50. expect(ids.filter((x) => x === 'B')).toHaveLength(1)
  51. expect(rows.filter((r) => r.kind === 'branch')).toHaveLength(0)
  52. })
  53. it('菱形分支:两条分支都展示,后续节点也展示,无重复', () => {
  54. const rows = buildFlowLayout(diamond.nodes, diamond.edges, [
  55. { nodeId: 'A', status: 'completed' },
  56. { nodeId: 'B1', status: 'completed' },
  57. { nodeId: 'C', status: 'current' },
  58. ], [{ nodeId: 'B1' }], 'C')
  59. const branchRow = rows.find((r) => r.kind === 'branch')
  60. expect(branchRow).toBeTruthy()
  61. expect(branchRow.branches).toHaveLength(2)
  62. expect(branchRow.branches[0].nodes[0].id).toBe('B1')
  63. expect(branchRow.branches[1].nodes[0].id).toBe('B2')
  64. // 后续节点 C、end 在主线上
  65. expect(rows.some((r) => r.kind === 'node' && r.nodes?.[0]?.id === 'C')).toBe(true)
  66. expect(rows.some((r) => r.kind === 'end')).toBe(true)
  67. // 无重复
  68. const ids = allIds(rows)
  69. for (const id of ['A', 'B1', 'B2', 'C']) {
  70. expect(ids.filter((x) => x === id)).toHaveLength(1)
  71. }
  72. // 已选择分支标记
  73. expect(branchRow.branches[0].isChosen).toBe(true)
  74. expect(branchRow.branches[1].isChosen).toBe(false)
  75. })
  76. it('直连合并点:默认分支直连下一节点,后续仍展示', () => {
  77. const direct = {
  78. nodes: [
  79. { id: 'start', type: 'start' },
  80. { id: 'A', type: 'approval', name: 'A' },
  81. { id: 'cond', type: 'condition', name: '条件' },
  82. { id: 'B1', type: 'approval', name: '大额' },
  83. { id: 'C', type: 'approval', name: 'C' },
  84. { id: 'end', type: 'end' },
  85. ],
  86. edges: [
  87. { sourceNodeId: 'start', targetNodeId: 'A' },
  88. { sourceNodeId: 'A', targetNodeId: 'cond' },
  89. { sourceNodeId: 'cond', targetNodeId: 'B1', condition: { condition: 'x', isDefault: false } },
  90. { sourceNodeId: 'cond', targetNodeId: 'C', condition: { condition: '', isDefault: true } },
  91. { sourceNodeId: 'B1', targetNodeId: 'C' },
  92. { sourceNodeId: 'C', targetNodeId: 'end' },
  93. ],
  94. }
  95. const rows = buildFlowLayout(direct.nodes, direct.edges, [
  96. { nodeId: 'A', status: 'completed' },
  97. { nodeId: 'C', status: 'current' },
  98. ], [], 'C')
  99. const branchRow = rows.find((r) => r.kind === 'branch')
  100. expect(branchRow).toBeTruthy()
  101. expect(branchRow.branches).toHaveLength(2)
  102. // C 在分支后主线上出现一次
  103. expect(rows.filter((r) => r.nodes?.some((n: any) => n.id === 'C'))).toHaveLength(1)
  104. expect(rows.some((r) => r.kind === 'end')).toBe(true)
  105. })
  106. it('导入型:前面主线节点标记为已走过(passed)', () => {
  107. const rows = buildFlowLayout(diamond.nodes, diamond.edges, [
  108. { nodeId: 'C', status: 'current' },
  109. ], [], 'C')
  110. const aRow = rows.find((r) => r.nodes?.[0]?.id === 'A')
  111. expect(aRow?.nodes?.[0]?.status).toBe('passed')
  112. })
  113. it('分支内节点状态:未选择分支为 pending', () => {
  114. const rows = buildFlowLayout(diamond.nodes, diamond.edges, [
  115. { nodeId: 'A', status: 'completed' },
  116. { nodeId: 'B1', status: 'completed' },
  117. { nodeId: 'C', status: 'current' },
  118. ], [], 'C')
  119. const branchRow = rows.find((r) => r.kind === 'branch')
  120. expect(branchRow.branches[0].nodes[0].status).toBe('completed')
  121. expect(branchRow.branches[1].nodes[0].status).toBe('pending')
  122. })
  123. })