Files
2026-09-15 22:10:13 +08:00

180 lines
5.3 KiB
JavaScript

/**
* 其他数据页:左侧模块树的子树裁剪。
*
* 本页只显示挂在根模块 `base_otherdata` 之下的子孙,不显示 s_module 里的其他模块。
* 这段裁剪原先按 b_path 前缀匹配实现,现已统一到 @/utils/tree 的 collectDescendantIds
* (按 b_parent_id 展开)—— 与同一函数里向上回溯祖先所用的权威字段保持一致。
* 用例按「行为」而非「实现」编写,因此两种实现都应通过。
*/
import { describe, it, expect, beforeEach, vi } from 'vitest'
import { flushPromises } from '@vue/test-utils'
import Otherdata from '@/views/base/otherdata/index.vue'
import { mountPage } from '../helpers/app.js'
vi.mock('@/services/api', () => ({
loadDataApi: vi.fn(),
pageDataApi: vi.fn(() => Promise.resolve({ data: { rows: [], total: 0 } })),
saveObjectApi: vi.fn(() => Promise.resolve({ code: 0, success: true })),
nextIdApi: vi.fn(() => Promise.resolve({ data: 9001, code: 0, success: true })),
describeApi: vi.fn(() => Promise.resolve({ data: [], code: 0, success: true })),
}))
// 权限:分类维护权限开启 → 不做可见性裁剪,只验证子树范围本身
vi.mock('@/stores/permissions', () => ({
usePermissionStore: () => ({
load: () => Promise.resolve(),
canAccess: () => true,
canPower: () => true,
}),
}))
import { loadDataApi } from '@/services/api'
/** 造一条 s_module 行;默认启用、非 virtual */
function makeModule(over = {}) {
return {
b_id: 'x',
b_parent_id: null,
b_depth: 0,
b_path: '/x/',
b_name: 'X',
b_module_type: 'data',
b_xh: 10,
b_canuse: 1,
...over,
}
}
/**
* 固定夹具:
* base_otherdata(根)
* ├─ odc_1(分类)
* │ └─ od_1(数据模块)
* └─ od_2(数据模块)
* other_root(完全无关的模块,不应出现在本页)
*/
const FIXTURE = [
makeModule({
b_id: 'base_otherdata',
b_parent_id: null,
b_depth: 0,
b_path: '/base_otherdata/',
b_module_type: 'module',
}),
makeModule({
b_id: 'odc_1',
b_parent_id: 'base_otherdata',
b_depth: 1,
b_path: '/base_otherdata/odc_1/',
b_module_type: 'module',
}),
makeModule({
b_id: 'od_1',
b_parent_id: 'odc_1',
b_depth: 2,
b_path: '/base_otherdata/odc_1/od_1/',
b_module_type: 'data',
}),
makeModule({
b_id: 'od_2',
b_parent_id: 'base_otherdata',
b_depth: 1,
b_path: '/base_otherdata/od_2/',
b_module_type: 'data',
}),
makeModule({
b_id: 'other_root',
b_parent_id: null,
b_depth: 0,
b_path: '/other_root/',
b_module_type: 'module',
}),
]
beforeEach(() => {
localStorage.clear()
sessionStorage.clear()
loadDataApi.mockReset()
loadDataApi.mockImplementation(() =>
Promise.resolve({ data: FIXTURE.map((row) => ({ ...row })) }),
)
})
async function mountOtherdata() {
const { wrapper } = await mountPage(Otherdata)
await flushPromises()
return wrapper
}
describe('其他数据:只显示根模块之下的子孙', () => {
it('包含根的各层级子孙,排除根自身', async () => {
const wrapper = await mountOtherdata()
const ids = wrapper.vm.modules.map((item) => String(item.b_id)).toSorted()
// 根自身不进树(它是本页入口,不是可导航节点)
expect(ids).toEqual(['od_1', 'od_2', 'odc_1'])
})
it('排除与根无关的其他模块', async () => {
const wrapper = await mountOtherdata()
const ids = wrapper.vm.modules.map((item) => String(item.b_id))
expect(ids).not.toContain('other_root')
})
it('virtual 类型即使挂在根下也不进树', async () => {
loadDataApi.mockImplementation(() =>
Promise.resolve({
data: [
...FIXTURE.map((row) => ({ ...row })),
makeModule({
b_id: 'od_virtual',
b_parent_id: 'base_otherdata',
b_depth: 1,
b_path: '/base_otherdata/od_virtual/',
b_module_type: 'virtual',
}),
],
}),
)
const wrapper = await mountOtherdata()
const ids = wrapper.vm.modules.map((item) => String(item.b_id))
expect(ids).not.toContain('od_virtual')
})
it('b_path 与 b_parent_id 不一致时以 b_parent_id 为准(b_path 只是派生字段)', async () => {
// 回归用例:裁剪原先按 b_path 前缀匹配,而同一函数里向上回溯祖先用 b_parent_id。
// 这条数据的 b_path 是过期的(写着挂在别的根下),但 b_parent_id 正确指向本页根,
// 应当被算作根的子孙 —— 否则会和祖先回溯算出矛盾结果。
loadDataApi.mockImplementation(() =>
Promise.resolve({
data: [
...FIXTURE.map((row) => ({ ...row })),
makeModule({
b_id: 'od_moved',
b_parent_id: 'base_otherdata',
b_path: '/some_stale_path/od_moved/',
}),
],
}),
)
const wrapper = await mountOtherdata()
const ids = wrapper.vm.modules.map((item) => String(item.b_id))
expect(ids).toContain('od_moved')
})
it('根不存在时树为空,不退化成显示全部模块', async () => {
loadDataApi.mockImplementation(() =>
Promise.resolve({ data: [makeModule({ b_id: 'other_root', b_path: '/other_root/' })] }),
)
const wrapper = await mountOtherdata()
expect(wrapper.vm.modules).toEqual([])
})
})