ForgeFlow-ERP/frontend/scripts/test-system-permission-management-ui.mjs
2026-06-12 16:29:05 +08:00

355 lines
21 KiB
JavaScript

import assert from "node:assert/strict";
import { readFileSync } from "node:fs";
const appSource = readFileSync(new URL("../src/App.vue", import.meta.url), "utf8");
const routerSource = readFileSync(new URL("../src/router/index.js", import.meta.url), "utf8");
const systemPermissionSource = readFileSync(new URL("../src/views/SystemPermissionView.vue", import.meta.url), "utf8");
const orgMindMapSource = readFileSync(new URL("../src/components/OrgMindMap.vue", import.meta.url), "utf8");
const orgPermissionTreeSource = readFileSync(new URL("../src/components/OrgPermissionTree.vue", import.meta.url), "utf8");
assert.match(appSource, /系统管理/, "App.vue should show the system-management parent menu");
assert.match(appSource, /系统权限管理/, "App.vue should show the system permission child menu");
assert.match(appSource, /系统拓展/, "App.vue should keep the system extension child menu");
assert.match(
appSource,
/permissionsAny:\s*\[[\s\S]*"MENU_SYSTEM_PERMISSION"[\s\S]*"MENU_SYSTEM_EXTENSION"[\s\S]*\]/,
"App.vue system menu should allow either system permission"
);
assert.match(
routerSource,
/component:\s*view\("SystemPermissionView"\)/,
"router should lazy load SystemPermissionView"
);
assert.match(routerSource, /path:\s*"\/system-management"[\s\S]*redirect:/, "router should add /system-management redirect");
assert.match(routerSource, /path:\s*"\/system-permissions"/, "router should add /system-permissions route");
assert.match(
routerSource,
/name:\s*"system-permissions"[\s\S]*permission:\s*"MENU_SYSTEM_PERMISSION"/,
"system-permissions route should require MENU_SYSTEM_PERMISSION"
);
assert.match(systemPermissionSource, /系统权限管理/, "SystemPermissionView should show page title");
assert.match(systemPermissionSource, /组织架构/, "SystemPermissionView should expose organization structure tab");
assert.match(systemPermissionSource, /角色维护/, "SystemPermissionView should expose role-only maintenance tab");
assert.doesNotMatch(systemPermissionSource, /人员与角色维护/, "personnel and role maintenance should be renamed");
assert.doesNotMatch(systemPermissionSource, /maintenanceSubTab/, "role maintenance should not keep nested personnel tabs");
assert.doesNotMatch(systemPermissionSource, /system-permission-sub-tabs/, "role maintenance should not render nested sub tabs");
assert.doesNotMatch(
systemPermissionSource,
/v-if="maintenanceSubTab === 'users'"/,
"standalone system users table should be removed from maintenance page"
);
assert.doesNotMatch(
systemPermissionSource,
/v-if="maintenanceSubTab === 'roles'"/,
"roles should render directly in the role maintenance tab"
);
assert.match(
systemPermissionSource,
/import PaginationBar from "\.\.\/components\/PaginationBar\.vue";/,
"page should use global PaginationBar"
);
assert.match(
systemPermissionSource,
/import \{ usePagination \} from "\.\.\/utils\/pagination";/,
"page should use global pagination helper"
);
assert.match(systemPermissionSource, /paginatedRoles/, "roles table should use paginated rows");
assert.match(systemPermissionSource, /<table class="data-table compact-table">/, "role maintenance should use global table styling");
assert.doesNotMatch(
systemPermissionSource,
/class="maintenance-grid"/,
"maintenance page should not use the old side-by-side card grid"
);
assert.doesNotMatch(
systemPermissionSource,
/<ul class="permission-list">[\s\S]*v-for="role in roles"/,
"roles should not render as old card list"
);
assert.doesNotMatch(
systemPermissionSource,
/<ul class="permission-list">[\s\S]*v-for="user in users"/,
"users should not render as old card list"
);
assert.match(systemPermissionSource, /permissionMainTab/, "SystemPermissionView should keep top-level tabs separate");
assert.match(
systemPermissionSource,
/v-if="permissionMainTab === 'org'"/,
"organization mind map should live in its own top-level tab"
);
assert.match(
systemPermissionSource,
/v-if="permissionMainTab === 'maintenance'"/,
"role maintenance should live in a separate top-level tab"
);
assert.match(systemPermissionSource, /orgViewMode/, "organization tab should keep organization view mode state");
assert.match(systemPermissionSource, /脑图模式/, "organization tab should expose mind-map mode switch");
assert.match(systemPermissionSource, /组织树模式/, "organization tab should expose organization-tree mode switch");
assert.match(
systemPermissionSource,
/<OrgPermissionTree/,
"SystemPermissionView should render organization tree mode through OrgPermissionTree"
);
assert.match(
systemPermissionSource,
/showAuthorizeDrawer/,
"SystemPermissionView should expose a personnel authorization drawer"
);
assert.match(systemPermissionSource, /openAuthorizeDrawer/, "personnel authorization should open from org personnel nodes");
assert.match(systemPermissionSource, /submitAuthorizeUser/, "authorization drawer should save account and roles");
assert.match(
systemPermissionSource,
/putResource\(`\/system-permissions\/users\/\$\{[^`]+\.user_id\}\/roles`/,
"existing users should update roles"
);
assert.match(systemPermissionSource, /postResource\("\/system-permissions\/users"/, "missing users should be created from authorization drawer");
assert.doesNotMatch(systemPermissionSource, /function openUserDrawer/, "standalone add-user drawer should be removed");
assert.doesNotMatch(systemPermissionSource, /function submitCreateUser/, "standalone add-user submit should be removed");
assert.doesNotMatch(systemPermissionSource, /paginatedUsers/, "standalone users table pagination should be removed");
assert.doesNotMatch(systemPermissionSource, /reset-password/, "standalone password reset action should not be exposed on this page");
assert.match(systemPermissionSource, /角色维护/, "SystemPermissionView should expose role maintenance");
assert.match(systemPermissionSource, /人员[\s\S]*电话号码[\s\S]*账号/, "SystemPermissionView should explain employee phone username mapping");
assert.match(systemPermissionSource, /密码/, "SystemPermissionView should mention password");
assert.match(systemPermissionSource, /最少6位/, "SystemPermissionView should mention the password length rule");
assert.match(systemPermissionSource, /employee-options/, "page must load employee options from backend");
assert.match(systemPermissionSource, /role_ids/, "page must submit role ids");
assert.match(systemPermissionSource, /\/system-permissions\/users/, "page must create system users through backend");
assert.match(systemPermissionSource, /menu-permission-tree/, "page must load menu permission tree");
assert.match(systemPermissionSource, /permission_codes/, "role form must submit permission codes");
assert.match(systemPermissionSource, /function collectPermissionCodes/, "role form must collect parent and child permission codes");
assert.match(systemPermissionSource, /function collectPersistablePermissionCodes/, "role form must collect only persistable menu permission codes");
assert.match(systemPermissionSource, /function togglePermissionNode/, "role form must toggle permission tree nodes");
assert.match(systemPermissionSource, /function isPermissionIndeterminate/, "role form must show partial permission tree selection");
assert.match(systemPermissionSource, /openOrgDrawer/, "page must support adding or editing org nodes");
assert.match(systemPermissionSource, /\/system-permissions\/org-nodes/, "page must persist org nodes through backend");
assert.match(systemPermissionSource, /node_type/, "org form must capture node type");
assert.match(systemPermissionSource, /主管/, "org form must support manager binding");
assert.match(systemPermissionSource, /manager_employee_ids/, "org form must submit multiple manager ids");
assert.match(systemPermissionSource, /manager-option-grid/, "manager selector should use clickable cards instead of a native multi select");
assert.match(systemPermissionSource, /selected-manager-tags/, "manager selector should show selected managers as tags");
assert.match(systemPermissionSource, /function toggleOrgManager/, "manager selector should toggle managers without keyboard modifiers");
assert.match(systemPermissionSource, /managerSearchKeyword/, "manager selector should keep a search keyword");
assert.match(systemPermissionSource, /filteredManagerOptions/, "manager selector should filter employees before rendering options");
assert.match(systemPermissionSource, /搜索主管姓名或手机号/, "manager selector should expose a Chinese search placeholder");
assert.match(
systemPermissionSource,
/v-for="employee in filteredManagerOptions"/,
"manager selector cards should render filtered manager options"
);
assert.doesNotMatch(
systemPermissionSource,
/<select v-model="orgForm\.manager_employee_ids" multiple>/,
"manager selector should not rely on native multi-select keyboard behavior"
);
assert.match(systemPermissionSource, /多位主管/, "manager drawer should explain multiple manager binding");
assert.match(systemPermissionSource, /contextMenu/, "page must expose a right-click context menu for mind-map nodes");
assert.match(systemPermissionSource, /@node-contextmenu/, "page must listen for node context menu events");
assert.match(systemPermissionSource, /重命名/, "context menu should support renaming organization nodes");
assert.match(systemPermissionSource, /新增分公司节点/, "company context menu should support adding branch nodes");
assert.match(systemPermissionSource, /新增部门节点/, "branch context menu should support adding department nodes");
assert.match(systemPermissionSource, /新增小组节点/, "department context menu should support adding group nodes");
assert.match(systemPermissionSource, /新增人员/, "department and group context menu should support adding employee leaves");
assert.match(systemPermissionSource, /添加该节点的主管/, "context menu should support manager binding");
assert.match(systemPermissionSource, /deleteResource/, "page must call backend delete endpoints for org actions");
assert.match(systemPermissionSource, /\/employees/, "page must call backend employee binding endpoints");
assert.match(systemPermissionSource, /员工节点默认收拢/, "page should document collapsed employee leaves");
assert.doesNotMatch(systemPermissionSource, /class="org-node-detail"/, "mind-map node operations must not stay in a persistent bottom bar");
assert.match(systemPermissionSource, /mindmap-toolbar/, "mind-map should expose a top-right toolbar");
assert.match(systemPermissionSource, /放大/, "mind-map toolbar should support zooming in");
assert.match(systemPermissionSource, /缩小/, "mind-map toolbar should support zooming out");
assert.match(systemPermissionSource, /刷新/, "mind-map toolbar should support refreshing data");
assert.match(systemPermissionSource, /一屏展示/, "mind-map toolbar should expose a separate one-screen fit button");
assert.match(systemPermissionSource, /mindMapScale/, "page should keep mind-map zoom state");
assert.match(systemPermissionSource, /refreshMindMap/, "page should refresh mind-map data from toolbar");
assert.match(systemPermissionSource, /fitMindMapToScreen/, "page should separate one-screen fitting from refreshing");
assert.match(systemPermissionSource, /mindMapFitRequestKey/, "page should request one-screen mind-map fitting");
assert.match(
systemPermissionSource,
/:fit-request-key="mindMapFitRequestKey"/,
"SystemPermissionView should pass fit request key to OrgMindMap"
);
assert.doesNotMatch(
systemPermissionSource,
/@zoom-scale=/,
"SystemPermissionView should not accept mouse-wheel zoom from OrgMindMap"
);
assert.match(
systemPermissionSource,
/@fit-scale="applyMindMapFitScale"/,
"SystemPermissionView should accept fit scale from OrgMindMap"
);
assert.match(
systemPermissionSource,
/function applyMindMapFitScale/,
"SystemPermissionView should apply one-screen fit scale"
);
assert.doesNotMatch(
systemPermissionSource,
/:key="mindMapRenderKey"/,
"mind-map refresh must not recreate OrgMindMap because that collapses expanded employees"
);
const refreshMindMapMatch = systemPermissionSource.match(/async function refreshMindMap\(\)\s*\{([\s\S]*?)\n\}/);
assert.ok(refreshMindMapMatch, "refreshMindMap function block must be parseable");
assert.doesNotMatch(
refreshMindMapMatch[1],
/mindMapFitRequestKey\.value \+= 1/,
"refresh must not request one-screen fit; one-screen fit is a separate user action"
);
assert.doesNotMatch(
refreshMindMapMatch[1],
/mindMapRenderKey\.value \+= 1/,
"refresh must not recreate mind-map component"
);
assert.match(
systemPermissionSource,
/async function requestMindMapFitToScreen[\s\S]*mindMapFitRequestKey\.value \+= 1[\s\S]*function fitMindMapToScreen\(\)\s*\{\s*requestMindMapFitToScreen\(\{ showToast: true, closeMenu: true \}\);/m,
"one-screen fit action should request a viewport fit explicitly"
);
assert.match(systemPermissionSource, /const updatePayload\s*=/, "org edit must build an update-only payload");
assert.match(systemPermissionSource, /const createPayload\s*=/, "org create must build a create payload");
const updatePayloadMatch = systemPermissionSource.match(/const updatePayload\s*=\s*\{([\s\S]*?)\n\s*\};/);
assert.ok(updatePayloadMatch, "org edit updatePayload block must be parseable");
assert.doesNotMatch(
updatePayloadMatch[1],
/parent_id|node_type|dept_code/,
"org edit updatePayload must exclude create-only fields"
);
assert.match(
systemPermissionSource,
/putResource\(`\/system-permissions\/org-nodes\/\$\{editingOrgNode\.value\.node_id\}`,\s*updatePayload\)/,
"org edit PUT must only send the update payload"
);
assert.match(
systemPermissionSource,
/postResource\("\/system-permissions\/org-nodes",\s*createPayload\)/,
"org create POST must send the create payload"
);
assert.match(
systemPermissionSource,
/@change="togglePermissionNode\(group, \$event\.target\.checked\)"/,
"parent permission checkbox must toggle descendants"
);
const persistablePermissionCodesMatch = systemPermissionSource.match(/function collectPersistablePermissionCodes\(node\)\s*\{([\s\S]*?)\n\}/);
assert.ok(persistablePermissionCodesMatch, "collectPersistablePermissionCodes function block must be parseable");
assert.match(
persistablePermissionCodesMatch[1],
/if \(children\.length\)[\s\S]*return children\.flatMap\(\(child\) => collectPersistablePermissionCodes\(child\)\)/,
"group-only parent permission codes must be skipped in favor of child menu permissions"
);
assert.match(
systemPermissionSource,
/for \(const code of collectPersistablePermissionCodes\(node\)\)/,
"permission toggling must persist only real menu permission codes"
);
assert.match(
systemPermissionSource,
/if \(!authorizingEmployee\.value\?\.employee_id\)[\s\S]*notifyError\("请选择有效人员"\)/,
"authorization submit must reject missing employee context"
);
assert.match(
systemPermissionSource,
/if \(!authorizingEmployeePhone\.value\)[\s\S]*notifyError\("该人员没有电话号码,不能作为账号"\)/,
"authorization submit must reject employees without a phone account"
);
assert.doesNotMatch(
systemPermissionSource,
/window\.alert\("新增系统使用人员抽屉将在实现任务中接入/,
"temporary user alert must be removed"
);
assert.doesNotMatch(
systemPermissionSource,
/window\.alert\("新增角色抽屉将在实现任务中接入/,
"temporary role alert must be removed"
);
assert.match(orgMindMapSource, /defineProps/, "OrgMindMap should use defineProps");
assert.match(orgMindMapSource, /orthogonal/i, "OrgMindMap should name orthogonal connector logic");
assert.match(orgMindMapSource, /collapsedEmployeeNodeIds/, "OrgMindMap should default employee leaves to collapsed");
assert.match(orgMindMapSource, /toggle-employee-leaves/, "OrgMindMap should emit employee leaf expand or collapse events");
assert.match(orgMindMapSource, /employee_count/, "OrgMindMap should display employee count on parent org cards");
assert.match(orgMindMapSource, /node-contextmenu/, "OrgMindMap should emit right-click context menu events");
assert.match(orgMindMapSource, /manager_names/, "OrgMindMap should display multiple manager names");
assert.match(orgMindMapSource, /zoomScale/, "OrgMindMap should accept a zoom scale prop");
assert.match(orgMindMapSource, /fitRequestKey/, "OrgMindMap should accept fit request key");
assert.match(orgMindMapSource, /fit-scale/, "OrgMindMap should emit fit scale");
assert.match(orgMindMapSource, /mindMapViewport/, "OrgMindMap should keep a viewport ref for canvas panning");
assert.match(orgMindMapSource, /isCanvasPanning/, "OrgMindMap should track canvas panning state");
assert.match(orgMindMapSource, /startCanvasPan/, "OrgMindMap should start canvas panning from empty canvas space");
assert.match(orgMindMapSource, /moveCanvasPan/, "OrgMindMap should move the viewport while panning");
assert.match(orgMindMapSource, /endCanvasPan/, "OrgMindMap should stop canvas panning");
assert.doesNotMatch(orgMindMapSource, /@wheel/, "OrgMindMap should not bind mouse-wheel zooming");
assert.doesNotMatch(orgMindMapSource, /function handleCanvasWheel/, "OrgMindMap should remove mouse-wheel zoom handling");
assert.doesNotMatch(orgMindMapSource, /WHEEL_ZOOM_STEP/, "OrgMindMap should not keep mouse-wheel zoom step configuration");
assert.doesNotMatch(orgMindMapSource, /emit\("zoom-scale"/, "OrgMindMap should not emit wheel zoom scale to parent");
assert.match(orgMindMapSource, /fitCanvasToViewport/, "OrgMindMap should fit canvas to visible viewport");
assert.match(
orgMindMapSource,
/window\.addEventListener\("mousemove", moveCanvasPan\)/,
"OrgMindMap should pan canvas through window mousemove listener"
);
assert.match(
orgMindMapSource,
/viewport\.scrollTop = canvasPan\.value\.scrollTop - deltaY/,
"OrgMindMap should support vertical canvas panning in addition to horizontal panning"
);
assert.match(
orgMindMapSource,
/window\.removeEventListener\("mousemove", moveCanvasPan\)/,
"OrgMindMap should clean up canvas pan listener"
);
assert.match(
orgMindMapSource,
/closest\?\.\("\.org-mind-map-node"\)/,
"canvas panning should not start from node cards"
);
assert.match(
orgMindMapSource,
/defineExpose/,
"OrgMindMap should expose fit helper for explicit refresh handling if needed"
);
assert.match(orgMindMapSource, /scaledCanvasStyle/, "OrgMindMap should size the canvas according to zoom scale");
assert.match(orgMindMapSource, /overscroll-behavior: contain;/, "OrgMindMap should keep drag interaction inside the canvas viewport");
assert.match(orgMindMapSource, /NODE_MIN_WIDTH/, "OrgMindMap should have a minimum node width");
assert.match(orgMindMapSource, /NODE_MAX_WIDTH/, "OrgMindMap should have a maximum node width");
assert.match(orgMindMapSource, /function estimateTextWidth/, "OrgMindMap should estimate text width before laying out cards");
assert.match(orgMindMapSource, /function computeNodeWidth/, "OrgMindMap should compute node width from text length");
assert.match(orgMindMapSource, /function fitTextToWidth/, "OrgMindMap should safely fit very long text into cards");
assert.match(orgMindMapSource, /:width="node\.width"/, "OrgMindMap node card width should use computed per-node width");
assert.match(
orgMindMapSource,
/parent\.x \+ parent\.width/,
"connector layout should use each node's computed width"
);
assert.match(orgMindMapSource, /function measureSubtree/, "mind map must measure subtree leaf spans before layout");
assert.match(orgMindMapSource, /function layoutTree/, "mind map must use deterministic tree layout");
assert.match(orgMindMapSource, /connectorSegments/, "mind map must deduplicate connector segments");
assert.match(orgMindMapSource, /COLUMN_GAP/, "mind map must reserve a column gutter for connector lines");
assert.match(orgMindMapSource, /NODE_MAX_WIDTH/, "mind map must know maximum node width for line avoidance");
assert.match(
orgMindMapSource,
/H \$\{connector\.midX\} V \$\{connector\.toY\} H \$\{connector\.toX\}/,
"OrgMindMap should render H/V/H orthogonal connector paths"
);
assert.doesNotMatch(orgMindMapSource, /draggable="true"/, "OrgMindMap should not mark cards draggable");
assert.doesNotMatch(
orgMindMapSource,
/@(drag|dragstart|dragend|pointermove|mousemove)\b/,
"OrgMindMap should not bind free-drag handlers"
);
assert.doesNotMatch(
orgMindMapSource,
/<path[^>]*\sd="[^"]*[CQ]/i,
"OrgMindMap should not render curved SVG path commands"
);
assert.match(orgPermissionTreeSource, /组织树/, "OrgPermissionTree should render organization tree UI");
assert.match(orgPermissionTreeSource, /onContextmenu/, "OrgPermissionTree should support right-click org node actions");
assert.match(orgPermissionTreeSource, /授权/, "OrgPermissionTree should expose personnel authorization action");
assert.match(orgPermissionTreeSource, /移除/, "OrgPermissionTree should expose personnel removal action");
assert.match(orgPermissionTreeSource, /新增人员/, "OrgPermissionTree should expose add-person action for selected org node");
assert.match(orgPermissionTreeSource, /PaginationBar/, "OrgPermissionTree should use global pagination bar");
assert.match(orgPermissionTreeSource, /搜索人员姓名或手机号/, "OrgPermissionTree should support personnel search");
console.log("system permission management UI static assertions passed");