20260915170028

This commit is contained in:
oneao committed 2026-09-15 17:00:29 +08:00
1 parent d26afb5fec
commit 1f56cc3efb
89 files changed
+1135 -397

No files matched your search

+151 -1
View File
@@ -1,5 +1,15 @@
import { describe, it, expect, beforeEach, vi } from 'vitest'
import { buildTree, sortTree } from '@/utils/tree'
import {
addRowUpdate,
applyTreePosition,
buildSortedTree,
buildTree,
decorateTreeNodes,
findNodePosition,
rebuildTreePositions,
renumberSiblings,
sortTree,
} from '@/utils/tree'
describe('utils/tree buildTree', () => {
it('空数组返回空树', () => {
@@ -91,6 +101,146 @@ describe('utils/tree sortTree', () => {
})
})
describe('utils/tree 可编辑树:建树与节点装饰', () => {
it('buildSortedTree 建树并排序,且不污染原始行(重复构建不累积子节点)', () => {
const rows = [
{ b_id: 'a', b_parent_id: null, b_xh: 20 },
{ b_id: 'b', b_parent_id: null, b_xh: 10 },
{ b_id: 'a1', b_parent_id: 'a', b_xh: 10 },
]
const tree = buildSortedTree(rows)
expect(tree.map((node) => node.b_id)).toEqual(['b', 'a'])
expect(tree[1].children.map((node) => node.b_id)).toEqual(['a1'])
// children 挂在浅拷贝上,原始行保持干净
expect(rows[0].children).toBeUndefined()
expect(buildSortedTree(rows)[1].children).toHaveLength(1)
})
it('decorateTreeNodes 预载类型图标,并强制指定类型非叶子', () => {
const Folder = () => null
const tree = [
{
b_id: 'a',
b_module_type: 'module',
children: [{ b_id: 'a1', b_module_type: 'data', children: [] }],
},
]
decorateTreeNodes(tree, {
typeField: 'b_module_type',
nonLeafValues: ['module'],
iconMap: { module: Folder },
})
expect(tree[0].isLeaf).toBe(false)
expect(tree[0].icon).toBe(Folder)
// 非 module 类型不改 isLeaf,图标表里没有的类型不写 icon
expect(tree[0].children[0].isLeaf).toBeUndefined()
expect(tree[0].children[0].icon).toBeUndefined()
})
})
describe('utils/tree 可编辑树:位置派生与就地变更', () => {
it('applyTreePosition 根节点的 b_depth 为 0、b_path 为 /编码/', () => {
const row = { b_id: 'a' }
applyTreePosition(row, null)
expect(row).toMatchObject({ b_depth: 0, b_path: '/a/' })
})
it('applyTreePosition 子节点继承父级路径并递增层级', () => {
const row = { b_id: 'a1' }
applyTreePosition(row, { b_depth: 2, b_path: '/a/b/' })
expect(row).toMatchObject({ b_depth: 3, b_path: '/a/b/a1/' })
})
it('findNodePosition 返回所在父数组与索引', () => {
const tree = [{ b_id: 'a', children: [{ b_id: 'a1' }, { b_id: 'a2' }] }]
expect(findNodePosition(tree, 'a')).toMatchObject({ index: 0 })
const found = findNodePosition(tree, 'a2')
expect(found.index).toBe(1)
expect(found.list.map((node) => node.b_id)).toEqual(['a1', 'a2'])
expect(findNodePosition(tree, 'not-exist')).toBe(null)
})
it('addRowUpdate 按 b_id 合并字段,避免同节点多条 update', () => {
const updated = []
addRowUpdate(updated, { b_id: 'a', b_xh: 20 })
addRowUpdate(updated, { b_id: 'a', b_parent_id: 'p' })
addRowUpdate(updated, { b_id: 'b', b_xh: 10 })
expect(updated).toEqual([
{ b_id: 'a', b_xh: 20, b_parent_id: 'p' },
{ b_id: 'b', b_xh: 10 },
])
})
it('renumberSiblings 按 10 步长重排并只收集变化的行', () => {
const a = { b_id: 'a', b_xh: 30 }
const b = { b_id: 'b', b_xh: 10 }
const updated = []
renumberSiblings([a, b], updated)
expect([a.b_xh, b.b_xh]).toEqual([10, 20])
expect(updated).toEqual([
{ b_id: 'a', b_xh: 10 },
{ b_id: 'b', b_xh: 20 },
])
})
it('rebuildTreePositions 整树收敛 b_depth / b_path,未变化的不重复收集', () => {
const tree = [
{
b_id: 'a',
b_depth: 0,
b_path: '/a/',
children: [{ b_id: 'a1', b_depth: 9, b_path: '/stale/', children: [] }],
},
]
const updated = []
rebuildTreePositions(tree, updated)
expect(tree[0].children[0]).toMatchObject({ b_depth: 1, b_path: '/a/a1/' })
// 根节点本来就是 0 / '/a/',不该产生一条无意义的 update
expect(updated).toEqual([{ b_id: 'a1', b_depth: 1, b_path: '/a/a1/' }])
})
it('rebuildTreePositions 根节点路径保留前导斜杠(回归:三份实现曾漂移)', () => {
const tree = [
{
b_id: 'base',
b_depth: 0,
b_path: '/base/',
children: [
{ b_id: 'base_otherdata', b_depth: 1, b_path: '/base/base_otherdata/', children: [] },
],
},
]
const updated = []
rebuildTreePositions(tree, updated)
expect(tree[0].b_path).toBe('/base/')
expect(tree[0].children[0].b_path).toBe('/base/base_otherdata/')
// 与库中既有格式一致,整棵树都不该被改动
expect(updated).toEqual([])
})
it('rebuildTreePositions 修正过期路径时补全前导斜杠', () => {
const tree = [{ b_id: 'base', b_depth: 3, b_path: 'stale/', children: [] }]
const updated = []
rebuildTreePositions(tree, updated)
expect(tree[0]).toMatchObject({ b_depth: 0, b_path: '/base/' })
expect(updated).toEqual([{ b_id: 'base', b_depth: 0, b_path: '/base/' }])
})
})
describe('utils/theme', () => {
beforeEach(() => {
document.documentElement.style.cssText = ''