2026-07-30 17:59:43 +08:00
|
|
|
import { describe, expect, it } from 'vitest'
|
|
|
|
|
import {
|
|
|
|
|
adminNavigation,
|
|
|
|
|
filterAdminNavigation,
|
|
|
|
|
getFirstNavigationLeaf,
|
|
|
|
|
getNavigationLeaves,
|
|
|
|
|
type AdminNavigationNode,
|
|
|
|
|
} from './adminNavigation'
|
|
|
|
|
|
|
|
|
|
describe('adminNavigation', () => {
|
2026-08-04 21:01:39 +08:00
|
|
|
it('exposes implemented phase-one and phase-two menus while retaining future prototype nodes as comments', () => {
|
2026-07-30 17:59:43 +08:00
|
|
|
expect(adminNavigation.map((item) => item.title)).toEqual([
|
2026-08-03 11:45:45 +08:00
|
|
|
'客户管理',
|
|
|
|
|
'国内仓',
|
|
|
|
|
'头程订单',
|
2026-08-04 21:01:39 +08:00
|
|
|
'头程计费',
|
2026-08-03 11:45:45 +08:00
|
|
|
'通用',
|
2026-07-30 17:59:43 +08:00
|
|
|
'系统管理',
|
|
|
|
|
])
|
2026-08-03 20:50:18 +08:00
|
|
|
expect(adminNavigation[0].children?.map((item) => item.title)).toEqual(['客户列表'])
|
2026-08-04 21:01:39 +08:00
|
|
|
expect(adminNavigation.find((item) => item.key === 'domestic-warehouse')?.children?.map((item) => item.title)).toEqual([
|
|
|
|
|
'仓库管理',
|
|
|
|
|
'入库单',
|
|
|
|
|
'库位管理',
|
|
|
|
|
'托盘管理',
|
|
|
|
|
])
|
|
|
|
|
expect(adminNavigation.find((item) => item.key === 'first-leg-pricing')?.children?.map((item) => item.title)).toEqual([
|
|
|
|
|
'客户计费规则',
|
|
|
|
|
'船司 FBA 价格',
|
|
|
|
|
])
|
2026-08-03 11:45:45 +08:00
|
|
|
expect(adminNavigation.find((item) => item.key === 'general')?.children?.map((item) => item.title)).toContain('快递公司')
|
2026-08-03 18:15:41 +08:00
|
|
|
expect(adminNavigation.find((item) => item.key === 'general')?.children?.map((item) => item.title)).toContain('下载中心')
|
2026-08-03 11:45:45 +08:00
|
|
|
expect(adminNavigation.find((item) => item.key === 'system')?.children?.map((item) => item.title)).toEqual([
|
|
|
|
|
'用户管理',
|
|
|
|
|
'角色权限',
|
2026-07-30 17:59:43 +08:00
|
|
|
'菜单权限',
|
|
|
|
|
])
|
|
|
|
|
})
|
|
|
|
|
|
2026-08-03 11:45:45 +08:00
|
|
|
it('assigns every page a unique path and keeps real permissions unique', () => {
|
2026-07-30 17:59:43 +08:00
|
|
|
const leaves = getNavigationLeaves(adminNavigation)
|
|
|
|
|
const paths = leaves.map((item) => item.path)
|
2026-08-03 11:45:45 +08:00
|
|
|
const permissions = leaves.flatMap((item) => item.permission ? [item.permission] : [])
|
2026-07-30 17:59:43 +08:00
|
|
|
|
2026-08-03 11:45:45 +08:00
|
|
|
expect(paths).not.toContain('/dashboard')
|
|
|
|
|
expect(paths).not.toContain('/prototype/finance/profit-report')
|
|
|
|
|
expect(paths).not.toContain('/prototype/system-management/brand-settings')
|
|
|
|
|
expect(paths).toContain('/system/permissions')
|
2026-07-30 17:59:43 +08:00
|
|
|
expect(new Set(paths).size).toBe(paths.length)
|
|
|
|
|
expect(new Set(permissions).size).toBe(permissions.length)
|
|
|
|
|
expect(leaves.every((item) => Boolean(item.icon))).toBe(true)
|
|
|
|
|
})
|
|
|
|
|
|
2026-08-03 11:45:45 +08:00
|
|
|
it('filters protected pages without mutating the source tree', () => {
|
2026-07-30 17:59:43 +08:00
|
|
|
const sourceSnapshot = JSON.stringify(adminNavigation, (key, value) => key === 'icon' ? '[icon]' : value)
|
|
|
|
|
const filtered = filterAdminNavigation(adminNavigation, new Set(['platform:permission:list']))
|
|
|
|
|
|
2026-08-03 11:45:45 +08:00
|
|
|
const paths = getNavigationLeaves(filtered).map((item) => item.path)
|
|
|
|
|
expect(paths).toContain('/system/permissions')
|
|
|
|
|
expect(paths).not.toContain('/prototype/finance/profit-report')
|
|
|
|
|
expect(paths).not.toContain('/customers')
|
2026-07-30 17:59:43 +08:00
|
|
|
expect(filtered).not.toBe(adminNavigation)
|
2026-08-03 11:45:45 +08:00
|
|
|
expect(filtered.find((item) => item.key === 'system')).not.toBe(adminNavigation.find((item) => item.key === 'system'))
|
2026-07-30 17:59:43 +08:00
|
|
|
expect(JSON.stringify(adminNavigation, (key, value) => key === 'icon' ? '[icon]' : value)).toBe(sourceSnapshot)
|
|
|
|
|
})
|
|
|
|
|
|
2026-08-03 20:50:18 +08:00
|
|
|
it('keeps the merged customer entry for existing review-only roles', () => {
|
|
|
|
|
const filtered = filterAdminNavigation(adminNavigation, new Set(['platform:customer:review']))
|
|
|
|
|
|
|
|
|
|
expect(getNavigationLeaves(filtered).map((item) => item.path)).toContain('/customers')
|
|
|
|
|
})
|
|
|
|
|
|
2026-08-03 11:45:45 +08:00
|
|
|
it('supports manually hiding a module without removing its route definition', () => {
|
|
|
|
|
const filtered = filterAdminNavigation([{ key: 'hidden', title: '隐藏模块', icon: adminNavigation[0].icon, path: '/hidden', hidden: true }], new Set())
|
|
|
|
|
|
|
|
|
|
expect(filtered).toEqual([])
|
|
|
|
|
})
|
|
|
|
|
|
2026-07-30 17:59:43 +08:00
|
|
|
it('keeps directories based on accessible descendants instead of a directory permission', () => {
|
|
|
|
|
const nodes: AdminNavigationNode[] = [{
|
|
|
|
|
key: 'operations',
|
|
|
|
|
title: '运营管理',
|
|
|
|
|
icon: adminNavigation[0].icon,
|
|
|
|
|
permission: 'directory:denied',
|
|
|
|
|
children: [{
|
|
|
|
|
key: 'tracking',
|
|
|
|
|
title: '轨迹管理',
|
|
|
|
|
icon: adminNavigation[0].icon,
|
|
|
|
|
path: '/tracking',
|
|
|
|
|
permission: 'tracking:list',
|
|
|
|
|
}],
|
|
|
|
|
}]
|
|
|
|
|
|
|
|
|
|
const filtered = filterAdminNavigation(nodes, new Set(['tracking:list']))
|
|
|
|
|
|
|
|
|
|
expect(filtered).toHaveLength(1)
|
|
|
|
|
expect(filtered[0].children?.[0].path).toBe('/tracking')
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
it('selects the first fallback leaf in strict depth-first configuration order', () => {
|
|
|
|
|
const nodes: AdminNavigationNode[] = [{
|
|
|
|
|
key: 'level-one',
|
|
|
|
|
title: '一级目录',
|
|
|
|
|
icon: adminNavigation[0].icon,
|
|
|
|
|
children: [{
|
|
|
|
|
key: 'level-two',
|
|
|
|
|
title: '二级目录',
|
|
|
|
|
icon: adminNavigation[0].icon,
|
|
|
|
|
children: [{
|
|
|
|
|
key: 'tracking',
|
|
|
|
|
title: '轨迹管理',
|
|
|
|
|
icon: adminNavigation[0].icon,
|
|
|
|
|
path: '/tracking',
|
|
|
|
|
permission: 'tracking:list',
|
|
|
|
|
}],
|
|
|
|
|
}, {
|
|
|
|
|
key: 'reports',
|
|
|
|
|
title: '报表管理',
|
|
|
|
|
icon: adminNavigation[0].icon,
|
|
|
|
|
path: '/reports',
|
|
|
|
|
permission: 'reports:list',
|
|
|
|
|
}],
|
|
|
|
|
}]
|
|
|
|
|
|
|
|
|
|
expect(getNavigationLeaves(nodes).map((item) => item.path)).toEqual(['/tracking', '/reports'])
|
|
|
|
|
expect(getFirstNavigationLeaf(nodes)?.path).toBe('/tracking')
|
|
|
|
|
})
|
|
|
|
|
})
|