934d114a29
- store/study 与 router 改为读取 /api-permissions/me 拉取当前用户在 当前项目的有效权限,菜单与路由守卫据此判定,并在 PM 项目缺省时 主动 ensureDefaultPmStudy。 - api/projectPermissions 暴露 fetchMyApiEndpointPermissions,并允许 调用方关闭统一错误提示,便于在批量项目权限拉取场景下静默失败。 - 项目管理:Projects 页根据每个项目的有效权限决定项目入口的可点击 状态,缺权限时禁用项目名跳转;ProjectDetail 据 setup_config 写权限 渲染保存/回填/清空/发布按钮,并在无读取权限时提示并退出页面。 - 权限管理:PermissionManagement 切换为按角色提交,Admin 视角才能 编辑 PM 行;按系统模块美化系统级权限展示并标注 PM 可访问项;项目 成员表的角色下拉受 PM 等级约束,自定义角色入口下线。 - 监控:PermissionMonitoring/PermissionAccessLogs 增加 isAdmin 与 showSecurityLog 入参,仅 ADMIN 才能看到底层安全日志与统计。 - ApiEndpointPermissions 表格根据当前用户角色禁用不可写行,避免 误操作 PM 行;MODULE_LABELS 增加 setup_config 的中文展示。 - 测试同步覆盖以上行为:新版 router/Layout/Projects/PermissionManagement /PermissionAccessLogs/PermissionMonitoring/PermissionIpLocations 等 组件均补足检测,并按需为相关组件提供 pinia + localStorage stub。 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
71 lines
2.3 KiB
TypeScript
71 lines
2.3 KiB
TypeScript
import { describe, it, expect, beforeEach, vi } from "vitest";
|
|
import { mount } from "@vue/test-utils";
|
|
import { readFileSync } from "node:fs";
|
|
import { resolve } from "node:path";
|
|
import ApiPermissions from "@/views/admin/ApiPermissions.vue";
|
|
import { createPinia, setActivePinia } from "pinia";
|
|
|
|
vi.mock("vue-router", async () => {
|
|
const actual = await vi.importActual<typeof import("vue-router")>("vue-router");
|
|
return {
|
|
...actual,
|
|
useRoute: () => ({ params: { id: "study-1" } }),
|
|
};
|
|
});
|
|
|
|
vi.mock("@/api/projectPermissions", () => ({
|
|
fetchApiEndpointPermissions: vi.fn().mockResolvedValue({ data: {} }),
|
|
updateApiEndpointPermissions: vi.fn().mockResolvedValue({ data: {} }),
|
|
fetchPermissionMetrics: vi.fn().mockResolvedValue({ data: {} }),
|
|
fetchCacheStats: vi.fn().mockResolvedValue({ data: {} }),
|
|
fetchPermissionAlerts: vi.fn().mockResolvedValue({ data: { total: 0, alerts: [] } }),
|
|
fetchPermissionHealth: vi.fn().mockResolvedValue({ data: {} }),
|
|
resetPermissionMetrics: vi.fn().mockResolvedValue({ data: undefined }),
|
|
}));
|
|
|
|
describe("ApiPermissions.vue", () => {
|
|
beforeEach(() => {
|
|
Object.defineProperty(window, "localStorage", {
|
|
value: {
|
|
getItem: vi.fn(() => null),
|
|
setItem: vi.fn(),
|
|
removeItem: vi.fn(),
|
|
clear: vi.fn(),
|
|
},
|
|
configurable: true,
|
|
});
|
|
Object.defineProperty(globalThis, "localStorage", {
|
|
value: window.localStorage,
|
|
configurable: true,
|
|
});
|
|
setActivePinia(createPinia());
|
|
});
|
|
|
|
it("renders permission management page", () => {
|
|
const wrapper = mount(ApiPermissions, {
|
|
global: {
|
|
stubs: {
|
|
ApiEndpointPermissions: true,
|
|
PermissionMonitoring: true,
|
|
},
|
|
},
|
|
});
|
|
|
|
expect(wrapper.find(".permission-page").exists()).toBe(true);
|
|
expect(wrapper.find(".permission-title h2").text()).toBe("权限管理");
|
|
});
|
|
|
|
it("renders tabs", () => {
|
|
const source = readFileSync(resolve(__dirname, "./ApiPermissions.vue"), "utf8");
|
|
|
|
expect(source).toContain('label="角色权限"');
|
|
expect(source).toContain('label="权限监控"');
|
|
});
|
|
|
|
it("has save button disabled when not dirty", () => {
|
|
const source = readFileSync(resolve(__dirname, "./ApiPermissions.vue"), "utf8");
|
|
|
|
expect(source).toContain(':disabled="!dirty"');
|
|
});
|
|
});
|