20260915221013
This commit is contained in:
1 parent
1f56cc3efb
commit
dce29f250e
54 files changed
+3732
-574
No files matched your search
@@ -0,0 +1,179 @@
|
||||
/**
|
||||
* 其他数据页:左侧模块树的子树裁剪。
|
||||
*
|
||||
* 本页只显示挂在根模块 `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([])
|
||||
})
|
||||
})
|
||||
Reference in new issue
Block a user