feat: 完善前端页面与用户引导
This commit is contained in:
116
frontend/scripts/regression-user-settings.mjs
Normal file
116
frontend/scripts/regression-user-settings.mjs
Normal file
@@ -0,0 +1,116 @@
|
||||
import assert from 'node:assert/strict'
|
||||
import { readFile } from 'node:fs/promises'
|
||||
import { fileURLToPath } from 'node:url'
|
||||
import path from 'node:path'
|
||||
import ts from 'typescript'
|
||||
import { parse as parseSfc } from '@vue/compiler-sfc'
|
||||
|
||||
const scriptDir = path.dirname(fileURLToPath(import.meta.url))
|
||||
const sourceRoot = path.resolve(scriptDir, '../src')
|
||||
const [viewSource, createSource, permissionSource, sidebarSource, routerSource, apiSource, adapterSource, authSource, usersSource] = await Promise.all([
|
||||
readFile(path.join(sourceRoot, 'views/system/UserSettingsView.vue'), 'utf8'),
|
||||
readFile(path.join(sourceRoot, 'views/system/UserCreateView.vue'), 'utf8'),
|
||||
readFile(path.join(sourceRoot, 'views/system/UserPermissionView.vue'), 'utf8'),
|
||||
readFile(path.join(sourceRoot, 'components/AppSidebar.vue'), 'utf8'),
|
||||
readFile(path.join(sourceRoot, 'router/index.ts'), 'utf8'),
|
||||
readFile(path.join(sourceRoot, 'api/modules/system.ts'), 'utf8'),
|
||||
readFile(path.join(sourceRoot, 'mock/adapter.ts'), 'utf8'),
|
||||
readFile(path.join(sourceRoot, 'stores/auth.ts'), 'utf8'),
|
||||
readFile(path.join(sourceRoot, 'mock/users.ts'), 'utf8'),
|
||||
])
|
||||
const userFeatureSource = [viewSource, createSource, permissionSource].join('\n')
|
||||
|
||||
const descriptor = parseSfc(viewSource).descriptor
|
||||
assert.ok(descriptor.template?.content, '用户设置页面缺少模板')
|
||||
assert.ok(descriptor.scriptSetup?.content, '用户设置页面缺少 script setup')
|
||||
|
||||
assert.match(sidebarSource, /label:\s*'用户设置'/, '系统设置菜单缺少用户设置入口')
|
||||
assert.match(sidebarSource, /to:\s*'\/user-settings'/, '用户设置菜单路由不正确')
|
||||
assert.match(sidebarSource, /visibleMenuGroups/, '侧边栏没有根据权限过滤菜单')
|
||||
assert.match(routerSource, /path:\s*'user-settings'/, '缺少用户设置路由')
|
||||
assert.match(routerSource, /permission:\s*'user-settings'/, '用户设置路由缺少权限声明')
|
||||
assert.match(routerSource, /permission-denied/, '缺少无权限访问反馈页面')
|
||||
assert.match(authSource, /hasPermission/, '认证状态缺少权限检查能力')
|
||||
|
||||
for (const marker of [
|
||||
'创建用户',
|
||||
'用户权限设置',
|
||||
'功能权限',
|
||||
'权限设置',
|
||||
'删除',
|
||||
'AppConfirmDialog',
|
||||
"tone: 'danger'",
|
||||
]) {
|
||||
assert.match(userFeatureSource, new RegExp(marker), `用户设置页面缺少关键交互:${marker}`)
|
||||
}
|
||||
assert.match(createSource, /createRules/, '创建用户缺少表单校验')
|
||||
assert.match(viewSource, /deleteDisabledReason/, '删除操作缺少当前用户和内置用户保护')
|
||||
assert.match(viewSource, /prefers-reduced-motion/, '页面未适配减少动态效果偏好')
|
||||
|
||||
for (const apiName of ['getUsers', 'createUser', 'deleteUser', 'updateUserAccess']) {
|
||||
assert.match(apiSource, new RegExp(`export const ${apiName}`), `用户 API 缺少 ${apiName}`)
|
||||
}
|
||||
assert.match(adapterSource, /url === '\/users' && method === 'get'/, 'Mock 缺少用户列表路由')
|
||||
assert.match(adapterSource, /url === '\/users' && method === 'post'/, 'Mock 缺少创建用户路由')
|
||||
assert.match(adapterSource, /userMatch && method === 'put'/, 'Mock 缺少权限更新路由')
|
||||
assert.match(adapterSource, /userMatch && method === 'delete'/, 'Mock 缺少删除用户路由')
|
||||
assert.match(usersSource, /mock:system-users:v1/, 'Mock 用户没有持久化存储')
|
||||
|
||||
const usersModuleCode = ts.transpileModule(usersSource, {
|
||||
compilerOptions: { module: ts.ModuleKind.ESNext, target: ts.ScriptTarget.ES2022 },
|
||||
}).outputText
|
||||
const usersModule = await import(
|
||||
`data:text/javascript;base64,${Buffer.from(usersModuleCode).toString('base64')}`
|
||||
)
|
||||
|
||||
const initialUsers = usersModule.listMockUsers()
|
||||
assert.equal(initialUsers.length, 2, '初始状态应包含内置管理员和操作员')
|
||||
assert.equal(initialUsers[0].username, 'admin', '默认管理员账号应为 admin')
|
||||
assert.equal(initialUsers[0].permissions.length, 12, '默认管理员应拥有全部模块权限')
|
||||
assert.equal(initialUsers[1].username, 'operator', '默认操作员账号应为 operator')
|
||||
assert.ok(initialUsers[1].permissions.includes('compute'), '默认操作员应拥有算力节点权限')
|
||||
assert.ok(!initialUsers[1].permissions.includes('user-settings'), '默认操作员不应拥有用户管理权限')
|
||||
assert.ok(!('password' in initialUsers[0]), '用户列表不能向页面返回密码')
|
||||
|
||||
const operator = usersModule.createMockUser({
|
||||
username: 'operator01',
|
||||
display_name: '测试操作员',
|
||||
password: '123456',
|
||||
role: 'operator',
|
||||
status: 'active',
|
||||
})
|
||||
assert.equal(usersModule.listMockUsers().length, 3, '创建用户后列表数量应增加')
|
||||
assert.ok(!operator.permissions.includes('user-settings'), '操作员默认不应拥有用户管理权限')
|
||||
|
||||
assert.throws(
|
||||
() => usersModule.createMockUser({ username: 'OPERATOR01', display_name: '重复账号', password: '123456', role: 'viewer' }),
|
||||
/用户名已存在/,
|
||||
'用户名唯一校验应忽略大小写',
|
||||
)
|
||||
|
||||
const updated = usersModule.updateMockUserAccess(operator.id, {
|
||||
role: 'viewer',
|
||||
permissions: ['dashboard', 'dataset'],
|
||||
})
|
||||
assert.deepEqual(updated.permissions, ['dashboard', 'dataset'], '权限更新应准确保存勾选结果')
|
||||
assert.equal(updated.role, 'viewer', '权限设置应同步保存用户角色')
|
||||
|
||||
const loginResult = usersModule.authenticateMockUser('operator01', '123456')
|
||||
assert.equal(loginResult.user.id, operator.id, '新建用户应可使用初始密码登录')
|
||||
assert.ok(loginResult.user.last_login, '登录后应记录最近登录时间')
|
||||
|
||||
assert.throws(
|
||||
() => usersModule.deleteMockUser(operator.id, 'operator01'),
|
||||
/不能删除当前登录用户/,
|
||||
'不能删除当前登录账号',
|
||||
)
|
||||
assert.throws(
|
||||
() => usersModule.deleteMockUser(initialUsers[0].id, 'other-user'),
|
||||
/系统内置管理员不能删除/,
|
||||
'不能删除系统内置管理员',
|
||||
)
|
||||
|
||||
usersModule.deleteMockUser(operator.id, 'admin')
|
||||
assert.equal(usersModule.listMockUsers().length, 2, '确认删除后用户应从列表移除')
|
||||
|
||||
console.log('用户设置页回归检查通过')
|
||||
Reference in New Issue
Block a user