task-49(账号/权限页面): 实现用户菜单授权数据加载

新增 user-menu-auth.ts 纯解析:parseMenuOptionList 归一 permission-menus 扁平项、
buildMenuOptionTree 按 parent_id 组装并按 (sort,key) 排序、parseUserGrantedColumnIds/
normalizeMenuIds 解包用户已授权 id 去重升序。user-menu-auth-api.ts 走 GET
/api/admin/permission-menus(menuType=admin) 与 /permission-users/{id}/columns 加载授权树与已勾选。
8 用例全过,355 单测 + build 绿。
This commit is contained in:
2026-09-05 15:06:58 +08:00
parent fba8e35118
commit 0fcee02189
3 changed files with 221 additions and 0 deletions
+87
View File
@@ -0,0 +1,87 @@
import test from 'node:test'
import assert from 'node:assert/strict'
import { readSource } from './helpers.ts'
import {
ADMIN_MENU_TYPE,
buildMenuOptionTree,
normalizeMenuIds,
parseMenuOptionList,
parsePermissionMenuItem,
parseUserGrantedColumnIds,
} from '../src/pages/account/user-menu-auth.ts'
test('test_task_049_user_menu_auth_load_normal_primary_path', () => {
// 正常主路径:扁平 permission-menus 归一后按 parent_id 组装出嵌套、按 sort 排序的树。
const items = parseMenuOptionList({
success: true,
data: [
{ id: 1, name: '账号权限', column_key: 'account', parent_id: null, sort_order: 1, menu_type: 'admin' },
{ id: 2, name: '用户管理', column_key: 'account_users', parent_id: 1, sort_order: 1, menu_type: 'admin' },
{ id: 3, name: '角色组', column_key: 'account_groups', parent_id: 1, sort_order: 2, menu_type: 'admin' },
],
})
assert.equal(items.length, 3)
const tree = buildMenuOptionTree(items)
assert.equal(tree.length, 1)
assert.equal(tree[0].id, 1)
assert.equal(tree[0].children?.length, 2)
assert.equal(tree[0].children?.[0].key, 'account_users')
assert.deepEqual(parseUserGrantedColumnIds({ success: true, data: { columnIds: [3, 2] } }), [2, 3])
})
test('test_task_049_user_menu_auth_load_normal_variant_input', () => {
// 正常变体:缺 column_key 用 id 兜底;父节点缺失的子节点按根处理。
const orphan = parsePermissionMenuItem({ id: 9, name: '孤儿', parent_id: 99, sort_order: 0 })
assert.equal(orphan?.parentId, 99)
assert.equal(buildMenuOptionTree([orphan as never])[0].id, 9)
const node = parsePermissionMenuItem({ id: 5, name: 'x', sort_order: 3 })
assert.equal(node?.key, '5')
assert.equal(node?.name, 'x')
})
test('test_task_049_user_menu_auth_load_normal_repeated_operation_is_idempotent', () => {
// 正常重复:组装不改输入、结果稳定。
const items = parseMenuOptionList([{ id: 1, name: 'A', parent_id: null, sort_order: 0 }])
assert.deepEqual(buildMenuOptionTree(items), buildMenuOptionTree(items))
assert.equal(items[0].id, 1)
})
test('test_task_049_user_menu_auth_load_boundary_empty_input', () => {
// 边界空值:空负载/空数组/缺 granted 字段均回默认,不崩溃。
assert.deepEqual(parseMenuOptionList({}), [])
assert.deepEqual(buildMenuOptionTree([]), [])
assert.deepEqual(parseUserGrantedColumnIds({ success: true, data: {} }), [])
assert.deepEqual(normalizeMenuIds(undefined), [])
})
test('test_task_049_user_menu_auth_load_boundary_single_item', () => {
// 边界单元素:单根节点成树。
const items = parseMenuOptionList([{ id: 7, name: 'Only', column_key: 'only', parent_id: null, sort_order: 0 }])
const tree = buildMenuOptionTree(items)
assert.equal(tree.length, 1)
assert.equal(tree[0].key, 'only')
})
test('test_task_049_user_menu_auth_load_boundary_limit_or_missing_field', () => {
// 边界上限/缺字段:缺 id 的行被过滤;非法/重复 granted 去重并升序。
assert.equal(parsePermissionMenuItem({ name: 'no-id' }), null)
assert.deepEqual(normalizeMenuIds([2, 1, 2, -1, 3.7, 'x' as never]), [1, 2, 3])
})
test('test_task_049_user_menu_auth_load_invalid_input_rejected', () => {
// 异常输入:success=false 抛后端 messagegranted 无 columnIds 字段回默认。
assert.throws(() => parseMenuOptionList({ success: false, message: '无权限查看菜单' }), /无权限查看菜单/)
assert.deepEqual(parseUserGrantedColumnIds({ success: true, data: { nothing: 1 } }), [])
})
test('test_task_049_user_menu_auth_load_dependency_failure_returns_actionable_message', () => {
// 依赖失败/加载走 adapterGET permission-menus(menuType=admin)+permission-users/{id}/columns,页面不内联。
const mod = readSource('src/pages/account/user-menu-auth.ts')
assert.equal(/axios|http\./.test(mod), false, '解析模型保持纯逻辑')
const api = readSource('src/pages/account/user-menu-auth-api.ts')
assert.match(api, /permission-menus/)
assert.match(api, /permission-users\/\$?\{?userId|permission-users/)
assert.match(api, new RegExp(`menuType.*${ADMIN_MENU_TYPE}|${ADMIN_MENU_TYPE}`))
assert.match(api, /parseMenuOptionList/)
assert.match(api, /parseUserGrantedColumnIds/)
})