task-56(账号/权限页面): 实现菜单编辑表单

menu-manage-model.ts 增 menuFormFromNode 回填 + menuParentChangeReason 防环(自身/子孙不可为父);
menu-manage-api.ts 增 updateMenu PUT /api/admin/permission-menus/{id};MenusPage 行内编辑复用
Dialog(新增/编辑切换)校验后提交并刷新。8 用例全过,411 单测 + build 绿。
This commit is contained in:
2026-09-05 15:19:37 +08:00
parent aaa943a86d
commit 45a8161bb2
4 changed files with 149 additions and 10 deletions
+88
View File
@@ -0,0 +1,88 @@
import test from 'node:test'
import assert from 'node:assert/strict'
import { readSource } from './helpers.ts'
import {
buildMenuManageTree,
menuFormFromNode,
menuParentChangeReason,
parseMenuManageList,
toMenuUpdateRequest,
} from '../src/pages/account/menu-manage-model.ts'
test('test_task_056_menu_edit_form_normal_primary_path', () => {
// 正常主路径:从节点回填可编辑表单(含 parentId/routePath/sort)。
const node = parseMenuManageList([
{ id: 4, name: '用户管理', column_key: 'account_users', parent_id: 1, route_path: '/account/users', sort_order: 2, menu_type: 'admin' },
])[0]
const form = menuFormFromNode(node)
assert.equal(form.name, '用户管理')
assert.equal(form.columnKey, 'account_users')
assert.equal(form.parentId, 1)
assert.equal(form.routePath, '/account/users')
assert.equal(form.sortOrder, 2)
})
test('test_task_056_menu_edit_form_normal_variant_input', () => {
// 正常变体:根节点回填 parentId=null;序列化到 update 请求体字段对齐。
const node = parseMenuManageList([{ id: 1, name: '账号', column_key: 'account', parent_id: null, sort_order: 0 }])[0]
const form = menuFormFromNode(node)
assert.equal(form.parentId, null)
assert.deepEqual(toMenuUpdateRequest(form).columnKey, 'account')
})
test('test_task_056_menu_edit_form_normal_repeated_operation_is_idempotent', () => {
// 正常重复:回填不改节点、结果稳定。
const node = parseMenuManageList([{ id: 5, name: 'A', column_key: 'a', parent_id: null, sort_order: 1 }])[0]
assert.deepEqual(menuFormFromNode(node), menuFormFromNode(node))
assert.equal(node.name, 'A')
})
test('test_task_056_menu_edit_form_boundary_empty_input', () => {
// 边界空值:仅 id+name 节点回填,其余字段给默认,不崩溃。
const node = parseMenuManageList([{ id: 7, name: 'Only' }])[0]
const form = menuFormFromNode(node)
assert.equal(form.columnKey, '')
assert.equal(form.routePath, '')
assert.equal(form.sortOrder, 0)
})
test('test_task_056_menu_edit_form_boundary_single_item', () => {
// 边界单元素:单根节点可安全改父为根(null)。
const node = parseMenuManageList([{ id: 9, name: 'Only', column_key: 'only', parent_id: null }])[0]
assert.equal(menuParentChangeReason(node, null), undefined)
})
test('test_task_056_menu_edit_form_boundary_limit_or_missing_field', () => {
// 边界上限/越权父:不能把自身/子孙设为父(防环)。
const tree = buildMenuManageTree(
parseMenuManageList([
{ id: 1, name: '根', column_key: 'root', parent_id: null },
{ id: 2, name: '子', column_key: 'sub', parent_id: 1 },
{ id: 3, name: '孙', column_key: 'leaf', parent_id: 2 },
]),
)
const root = tree[0]
const sub = root.children?.[0]!
const leaf = sub.children?.[0]!
assert.match(menuParentChangeReason(sub, sub.id) || '', /自身/)
assert.match(menuParentChangeReason(root, leaf.id) || '', /子菜单/)
})
test('test_task_056_menu_edit_form_invalid_input_rejected', () => {
// 异常输入:父选自身/子孙被拒;换父到合法节点(自身父=1)允许。
const node = parseMenuManageList([{ id: 1, name: 'A', column_key: 'a', parent_id: null }])[0]
assert.ok(menuParentChangeReason(node, 1))
assert.equal(menuParentChangeReason(node, 99), undefined)
})
test('test_task_056_menu_edit_form_dependency_failure_returns_actionable_message', () => {
// 依赖失败/编辑走 adapterPUT permission-menus/{id},页面用 updateMenu + 回填 + 防环校验。
const api = readSource('src/pages/account/menu-manage-api.ts')
assert.match(api, /updateMenu/)
assert.match(api, /http\.put/)
assert.match(api, /permission-menus/)
const page = readSource('src/pages/account/MenusPage.vue')
assert.match(page, /updateMenu/)
assert.match(page, /menuFormFromNode|menuParentChangeReason/)
assert.equal(/http\.(get|post|put)|unwrap\(/.test(page), false, '页面不内联请求')
})