6a2e43f829
1、将药物发货新建和编辑从独立页面迁移到抽屉,详情页支持权限控制和停用中心只读状态。 2、补充药物发货状态字段校验,统一 PENDING、IN_TRANSIT、SIGNED、EXCEPTION 状态及演示数据。 3、为物资设备增加详情页和编辑抽屉,复用通用附件上传并按项目权限控制操作入口。 4、将启动会和培训授权编辑迁移到抽屉,详情页按中心过滤培训记录并移除旧独立编辑路由。
100 lines
4.7 KiB
TypeScript
100 lines
4.7 KiB
TypeScript
import { describe, expect, it } from "vitest";
|
|
import { readFileSync } from "node:fs";
|
|
import { resolve } from "node:path";
|
|
|
|
const readRouter = () => readFileSync(resolve(__dirname, "./router/index.ts"), "utf8");
|
|
|
|
describe("admin project route permissions", () => {
|
|
it("lets any authorized project role open the project management detail page", () => {
|
|
const source = readRouter();
|
|
|
|
expect(source).toContain('name: "AdminProjectDetail"');
|
|
expect(source).toContain("requiresProjectMember: true");
|
|
expect(source).not.toContain("meta: { title: TEXT.modules.adminProjects.detailTitle, requiresAdmin: true }");
|
|
expect(source).toContain("ensureRouteProjectMember(to, studyStore)");
|
|
});
|
|
|
|
it("allows project admin pages through matrix-authorized PMs instead of all project roles", () => {
|
|
const source = readRouter();
|
|
|
|
expect(source).toContain('adminProjectPermission: { module: "sites", action: "read" }');
|
|
expect(source).toContain('adminProjectPermission: { module: "audit_export", action: "read" }');
|
|
expect(source).toContain("meta: { title: TEXT.menu.projectManagement }");
|
|
expect(source).toContain("ADMIN_PROJECT_OPERATION_KEYS");
|
|
expect(source).toContain('if (role !== "PM") return false;');
|
|
expect(source).toContain("isApiPermissionAllowed");
|
|
expect(source).toContain("studyStore.currentPermissions?.[role]?.[operationKey]");
|
|
});
|
|
|
|
it("guards project permission configuration with the system-level project config permission", () => {
|
|
const source = readRouter();
|
|
const projectPermissionRouteStart = source.indexOf('name: "AdminPermissionsProject"');
|
|
const projectPermissionRouteEnd = source.indexOf('name: "AdminPermissionsMonitoring"', projectPermissionRouteStart);
|
|
const projectPermissionRoute = source.slice(projectPermissionRouteStart, projectPermissionRouteEnd);
|
|
|
|
expect(source).toContain('const SYSTEM_PERMISSION_PROJECT_CONFIG = "system:permissions:project_config";');
|
|
expect(projectPermissionRoute).toContain("systemPermission: SYSTEM_PERMISSION_PROJECT_CONFIG");
|
|
expect(projectPermissionRoute).not.toContain("requiresProjectPm: true");
|
|
expect(source).toContain("to.meta.systemPermission");
|
|
expect(source).toContain("hasSystemPermissionAccess(to.meta.systemPermission as string, isAdmin)");
|
|
});
|
|
|
|
it("keeps login landing independent from PM management backend access", () => {
|
|
const source = readRouter();
|
|
|
|
expect(source).not.toContain("findPmAdminLandingPath");
|
|
expect(source).not.toContain("pmAdminLandingModules");
|
|
expect(source).toContain(' : studyStore.currentStudy\n ? "/project/overview"\n : "/admin/projects",');
|
|
});
|
|
|
|
it("does not register the removed non-admin personal dashboard route", () => {
|
|
const source = readRouter();
|
|
const removedComponent = ["My", "Work", "bench"].join("");
|
|
const removedPath = `/${["work", "bench"].join("")}`;
|
|
|
|
expect(source).not.toContain(removedComponent);
|
|
expect(source).not.toContain(`path: "${removedPath.slice(1)}"`);
|
|
expect(source).not.toContain(`"${removedPath}"`);
|
|
});
|
|
|
|
it("does not register legacy finance contract routes", () => {
|
|
const source = readRouter();
|
|
|
|
expect(source).not.toContain("FinanceContracts");
|
|
expect(source).not.toContain("FinanceContract");
|
|
expect(source).not.toContain("finance/contracts");
|
|
});
|
|
|
|
it("does not register standalone contract fee create or edit pages", () => {
|
|
const source = readRouter();
|
|
|
|
expect(source).not.toContain("FeeContractForm");
|
|
expect(source).not.toContain('name: "FeeContractNew"');
|
|
expect(source).not.toContain('name: "FeeContractEdit"');
|
|
expect(source).not.toContain('path: "fees/contracts/new"');
|
|
expect(source).not.toContain('path: "fees/contracts/:contractId/edit"');
|
|
});
|
|
|
|
it("does not register standalone startup auth edit pages", () => {
|
|
const source = readRouter();
|
|
|
|
expect(source).not.toContain('name: "StartupKickoffEdit"');
|
|
expect(source).not.toContain('name: "StartupTrainingEdit"');
|
|
expect(source).not.toContain('name: "StartupTrainingNew"');
|
|
expect(source).not.toContain('path: "startup/kickoff/:meetingId/edit"');
|
|
expect(source).not.toContain('path: "startup/training/new"');
|
|
expect(source).not.toContain('path: "startup/training/:recordId/edit"');
|
|
});
|
|
|
|
it("uses precautions routes instead of knowledge note routes", () => {
|
|
const source = readRouter();
|
|
|
|
expect(source).toContain('path: "knowledge/precautions"');
|
|
expect(source).toContain('path: "knowledge/precautions/new"');
|
|
expect(source).toContain('path: "knowledge/precautions/:id"');
|
|
expect(source).toContain('path: "knowledge/precautions/:id/edit"');
|
|
expect(source).not.toContain("KnowledgeNote");
|
|
expect(source).not.toContain("/knowledge/notes");
|
|
});
|
|
});
|