import { describe, expect, test } from "vitest"; import { fallbackNavigation, filterNavigationByPermission, mapBackendMenus, mergeNavigationItems } from "./menu.js"; describe("navigation menu helpers", () => { test("keeps requested parent menu order in fallback navigation", () => { expect(fallbackNavigation.map((item) => item.code)).toEqual([ "overview", "app-users", "host-org", "rooms", "app-config", "resources", "operations", "payment", "activities", "games", "geo", "system", ]); }); test("normalizes requested parent menu order from backend menus", () => { const mapped = mapBackendMenus([ { children: [], code: "system", icon: "settings", id: "system", label: "后台设置" }, { children: [], code: "geo", icon: "map", id: "geo", label: "地区管理" }, { children: [], code: "host-org", icon: "network", id: "host-org", label: "团队管理" }, { children: [], code: "app-users", icon: "users", id: "app-users", label: "用户管理" }, { children: [], code: "overview", icon: "dashboard", id: "overview", label: "总览" }, ]); expect(mapped.map((item) => item.code)).toEqual(["overview", "app-users", "host-org", "geo", "system"]); }); test("keeps only fallback menu items allowed by permissions", () => { const items = filterNavigationByPermission( fallbackNavigation, (code) => code === "agency:view" || code === "host:view", ); expect(flattenCodes(items)).not.toContain("geo"); expect(flattenCodes(items)).toContain("host-org"); expect(flattenCodes(items)).toContain("host-org-managers"); expect(flattenCodes(items)).toContain("host-org-agencies"); expect(flattenCodes(items)).toContain("host-org-hosts"); expect(flattenCodes(items)).not.toContain("host-org-bds"); }); test("places country and region pages under geo menu", () => { const items = filterNavigationByPermission( fallbackNavigation, (code) => code === "country:view" || code === "region:view", ); const geo = items.find((item) => item.code === "geo"); expect(geo).toBeTruthy(); expect(geo.children.map((item) => item.code)).toEqual(["host-org-countries", "host-org-regions"]); expect(flattenCodes(items)).not.toContain("host-org"); }); test("keeps the app user login log entry routable", () => { const items = filterNavigationByPermission(fallbackNavigation, (code) => code === "app-user:view"); const appUsers = items.find((item) => item.code === "app-users"); expect(appUsers.children.map((item) => item.code)).toContain("app-user-login-logs"); expect(appUsers.children.find((item) => item.code === "app-user-login-logs").path).toBe( "/app/users/login-logs", ); }); test("adds missing permitted fallback children to backend menu groups", () => { const backendMenus = [ { children: [], code: "host-org", icon: fallbackNavigation.find((item) => item.code === "host-org").icon, id: "host-org", label: "团队管理", }, ]; const fallbackMenus = filterNavigationByPermission(fallbackNavigation, (code) => code === "agency:view"); const merged = mergeNavigationItems(backendMenus, fallbackMenus); expect(flattenCodes(merged)).toContain("host-org-agencies"); expect(flattenCodes(merged)).toContain("host-org-managers"); }); test("keeps payment children under payment menu when backend menu is current", () => { const mapped = mapBackendMenus([ { children: [], code: "app-config", icon: "settings", id: "app-config", label: "APP配置" }, { children: [ { code: "payment-recharge-products", icon: "wallet", id: "payment-recharge-products", label: "内购配置", path: "/payment/recharge-products", permissionCode: "payment-product:view", }, { code: "payment-third-party", icon: "wallet", id: "payment-third-party", label: "第三方支付", path: "/payment/third-party", permissionCode: "payment-third-party:view", }, ], code: "payment", icon: "wallet", id: "payment", label: "支付管理", }, ]); const appConfig = mapped.find((item) => item.code === "app-config"); const payment = mapped.find((item) => item.code === "payment"); expect(appConfig.children || []).toEqual([]); expect(payment.children.map((item) => item.code)).toEqual(["payment-recharge-products", "payment-third-party"]); expect(payment.children.find((item) => item.code === "payment-recharge-products").path).toBe( "/payment/recharge-products", ); }); test("places splash screens after banners when backend menu is stale", () => { const backendMenus = [ { children: [ { code: "app-config-h5", id: "app-config-h5", label: "H5配置", path: "/app-config/h5" }, { code: "app-config-banners", id: "app-config-banners", label: "BANNER配置", path: "/app-config/banners", }, { code: "app-config-explore", id: "app-config-explore", label: "Explore配置", path: "/app-config/explore", }, { code: "app-config-popups", id: "app-config-popups", label: "弹窗配置", path: "/app-config/popups", }, { code: "app-config-versions", id: "app-config-versions", label: "版本管理", path: "/app-config/versions", }, ], code: "app-config", icon: "settings", id: "app-config", label: "APP配置", }, ]; const fallbackMenus = filterNavigationByPermission(fallbackNavigation, (code) => code === "app-config:view"); const merged = mergeNavigationItems(mapBackendMenus(backendMenus), fallbackMenus); const appConfig = merged.find((item) => item.code === "app-config"); expect(appConfig.children.map((item) => item.code)).toEqual([ "app-config-h5", "app-config-banners", "app-config-splash-screens", "app-config-popups", "app-config-explore", "app-config-versions", ]); }); }); function flattenCodes(items) { return items.flatMap((item) => [item.code, ...flattenCodes(item.children || [])]); }