Files
Neo-ZQYY/apps/admin-web/src/__tests__/sidebarHighlight.property.test.ts
Neo 6f8f12314f feat: 累积功能变更 — 聊天集成、租户管理、小程序更新、ETL 增强、迁移脚本
包含多个会话的累积代码变更:
- backend: AI 聊天服务、触发器调度、认证增强、WebSocket、调度器最小间隔
- admin-web: ETL 状态页、任务管理、调度配置、登录优化
- miniprogram: 看板页面、聊天集成、UI 组件、导航更新
- etl: DWS 新任务(finance_area_daily/board_cache)、连接器增强
- tenant-admin: 项目初始化
- db: 19 个迁移脚本(etl_feiqiu 11 + zqyy_app 8)
- packages/shared: 枚举和工具函数更新
- tools: 数据库工具、报表生成、健康检查
- docs: PRD/架构/部署/合约文档更新

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-06 00:03:48 +08:00

161 lines
7.2 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* 属性测试:侧边栏高亮与当前路由一致
*
* Feature: admin-web-restructure, Property 7: 侧边栏高亮与当前路由一致
* **Validates: Requirements 10.3**
*
* 对于任意有效的应用路由路径侧边栏中被高亮selectedKeys的菜单项
* 应对应该路由所属的一级模块。
*/
import { describe, it, expect } from "vitest";
import * as fc from "fast-check";
import { getSelectedKeys, NAV_ITEMS } from "../App";
/* ------------------------------------------------------------------ */
/* 路由 → 一级模块映射(从 NAV_ITEMS 和路由配置提取) */
/* ------------------------------------------------------------------ */
/**
* 所有有效路由及其所属一级模块 key 的映射。
* 一级模块 key 定义:
* - 无子菜单的项:直接用 item.key如 "/dashboard"
* - 有子菜单的项:用 group key如 "task-engine-group"
*/
const ROUTE_TO_MODULE: Array<{
pathname: string;
search: string;
moduleKey: string;
/** 该路由在菜单中对应的 selectedKey */
expectedSelectedKey: string;
}> = [
// 运行状态
{ pathname: "/dashboard", search: "", moduleKey: "/dashboard", expectedSelectedKey: "/dashboard" },
// ETL 任务管理
{ pathname: "/etl-tasks", search: "", moduleKey: "/etl-tasks", expectedSelectedKey: "/etl-tasks" },
{ pathname: "/etl-tasks", search: "?tab=config", moduleKey: "/etl-tasks", expectedSelectedKey: "/etl-tasks" },
{ pathname: "/etl-tasks", search: "?tab=queue", moduleKey: "/etl-tasks", expectedSelectedKey: "/etl-tasks" },
{ pathname: "/etl-tasks", search: "?tab=schedule", moduleKey: "/etl-tasks", expectedSelectedKey: "/etl-tasks" },
{ pathname: "/etl-tasks", search: "?tab=history", moduleKey: "/etl-tasks", expectedSelectedKey: "/etl-tasks" },
{ pathname: "/etl-tasks", search: "?tab=status", moduleKey: "/etl-tasks", expectedSelectedKey: "/etl-tasks" },
// 小程序任务管理
{ pathname: "/task-engine/trigger-jobs", search: "", moduleKey: "task-engine-group", expectedSelectedKey: "/task-engine/trigger-jobs" },
{ pathname: "/task-engine/transfer-log", search: "", moduleKey: "task-engine-group", expectedSelectedKey: "/task-engine/transfer-log" },
{ pathname: "/task-engine/pending-review", search: "", moduleKey: "task-engine-group", expectedSelectedKey: "/task-engine/pending-review" },
{ pathname: "/task-engine/config", search: "", moduleKey: "task-engine-group", expectedSelectedKey: "/task-engine/config" },
// 触发器管理
{ pathname: "/triggers", search: "", moduleKey: "/triggers", expectedSelectedKey: "/triggers" },
{ pathname: "/triggers", search: "?tab=all", moduleKey: "/triggers", expectedSelectedKey: "/triggers" },
// 特殊情况:/triggers?tab=biz 同时是"系统设置 > 触发器配置"的快捷入口,
// getSelectedKeys 会精确匹配到 settings-group 下的子项
{ pathname: "/triggers", search: "?tab=biz", moduleKey: "settings-group", expectedSelectedKey: "/triggers?tab=biz" },
{ pathname: "/triggers", search: "?tab=ai", moduleKey: "/triggers", expectedSelectedKey: "/triggers" },
{ pathname: "/triggers", search: "?tab=etl", moduleKey: "/triggers", expectedSelectedKey: "/triggers" },
// 租户管理员
{ pathname: "/tenant-admins", search: "", moduleKey: "/tenant-admins", expectedSelectedKey: "/tenant-admins" },
// 系统设置
{ pathname: "/settings/env-config", search: "", moduleKey: "settings-group", expectedSelectedKey: "/settings/env-config" },
// 日志调试
{ pathname: "/logs/dev-trace", search: "", moduleKey: "logs-group", expectedSelectedKey: "/logs/dev-trace" },
{ pathname: "/logs/ai-run-logs", search: "", moduleKey: "logs-group", expectedSelectedKey: "/logs/ai-run-logs" },
{ pathname: "/logs/db-viewer", search: "", moduleKey: "logs-group", expectedSelectedKey: "/logs/db-viewer" },
];
/* ------------------------------------------------------------------ */
/* 辅助:从 NAV_ITEMS 构建 selectedKey → moduleKey 的反查表 */
/* ------------------------------------------------------------------ */
/** 收集所有菜单叶子节点的 key映射到其所属一级模块 key */
function buildKeyToModuleMap(): Map<string, string> {
const map = new Map<string, string>();
for (const item of NAV_ITEMS ?? []) {
if (!item || !("key" in item)) continue;
const topKey = item.key as string;
if ("children" in item && item.children) {
// 有子菜单:子项 key → group key
for (const child of item.children) {
if (child && "key" in child) {
map.set(child.key as string, topKey);
}
}
} else {
// 无子菜单:自身 key → 自身 key
map.set(topKey, topKey);
}
}
return map;
}
const KEY_TO_MODULE = buildKeyToModuleMap();
/* ------------------------------------------------------------------ */
/* 属性测试 */
/* ------------------------------------------------------------------ */
describe("Property 7: 侧边栏高亮与当前路由一致", () => {
// 生成器:从所有有效路由中随机选取
const routeArb = fc.constantFrom(...ROUTE_TO_MODULE);
it("对任意有效路由selectedKeys 应包含该路由对应的菜单项 key", () => {
fc.assert(
fc.property(routeArb, (route) => {
const selectedKeys = getSelectedKeys(route.pathname, route.search);
// selectedKeys 不应为空
expect(selectedKeys.length).toBeGreaterThan(0);
// selectedKeys 中应包含预期的 key
expect(selectedKeys).toContain(route.expectedSelectedKey);
}),
{ numRuns: 100 },
);
});
it("对任意有效路由selectedKeys 对应的一级模块应与路由所属模块一致", () => {
fc.assert(
fc.property(routeArb, (route) => {
const selectedKeys = getSelectedKeys(route.pathname, route.search);
const selectedKey = selectedKeys[0];
// 查找 selectedKey 所属的一级模块
const actualModule = KEY_TO_MODULE.get(selectedKey);
// 如果 selectedKey 不在菜单叶子节点中(如一级路由直接匹配),
// 则 selectedKey 本身就是模块 key
const resolvedModule = actualModule ?? selectedKey;
expect(resolvedModule).toBe(route.moduleKey);
}),
{ numRuns: 100 },
);
});
it("NAV_ITEMS 应包含 7 个一级菜单项", () => {
expect(NAV_ITEMS).toHaveLength(7);
});
it("所有有效路由的 selectedKey 都能在 NAV_ITEMS 中找到对应菜单项", () => {
fc.assert(
fc.property(routeArb, (route) => {
const selectedKeys = getSelectedKeys(route.pathname, route.search);
const selectedKey = selectedKeys[0];
// selectedKey 必须存在于菜单的叶子节点或一级节点中
const allMenuKeys = new Set<string>();
for (const item of NAV_ITEMS ?? []) {
if (!item || !("key" in item)) continue;
allMenuKeys.add(item.key as string);
if ("children" in item && item.children) {
for (const child of item.children) {
if (child && "key" in child) allMenuKeys.add(child.key as string);
}
}
}
expect(allMenuKeys.has(selectedKey)).toBe(true);
}),
{ numRuns: 100 },
);
});
});