utils.test.ts 8.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305
  1. import { describe, it, expect, vi } from "vitest";
  2. import {
  3. ascending,
  4. filterTree,
  5. filterNoPermissionTree,
  6. isOneOfArray,
  7. getParentPaths,
  8. findRouteByPath,
  9. handleAliveRoute,
  10. formatFlatteningRoutes,
  11. formatTwoStageRoutes,
  12. initRouter,
  13. getTopMenu,
  14. addPathMatch,
  15. getHistoryMode,
  16. addAsyncRoutes,
  17. hasAuth,
  18. getAuths
  19. } from "../utils";
  20. import { RouteRecordRaw } from "vue-router";
  21. import { storageLocal } from "@pureadmin/utils";
  22. import { usePermissionStoreHook } from "@/store/modules/permission";
  23. import { useMultiTagsStoreHook } from "@/store/modules/multiTags";
  24. describe("Router Utils", () => {
  25. /**
  26. * @description 测试 ascending 函数
  27. * @function ascending
  28. * @test 验证路由按 meta.rank 升序排序
  29. */
  30. describe("ascending", () => {
  31. it("should sort routes by meta.rank in ascending order", () => {
  32. const routes = [
  33. { meta: { rank: 3 } },
  34. { meta: { rank: 1 } },
  35. { meta: { rank: 2 } }
  36. ];
  37. const result = ascending(routes);
  38. expect(result[0].meta.rank).toBe(1);
  39. expect(result[1].meta.rank).toBe(2);
  40. expect(result[2].meta.rank).toBe(3);
  41. });
  42. it("should assign rank if not provided", () => {
  43. const routes = [{ name: "Home", path: "/" }, { name: "About" }];
  44. const result = ascending(routes);
  45. expect(result[0].meta.rank).toBe(2);
  46. expect(result[1].meta.rank).toBe(3);
  47. });
  48. });
  49. /**
  50. * @description 测试 filterTree 函数
  51. * @function filterTree
  52. * @test 验证过滤掉 meta.showLink 为 false 的路由
  53. */
  54. describe("filterTree", () => {
  55. it("should filter out routes with meta.showLink set to false", () => {
  56. const routes = [
  57. { meta: { showLink: true } },
  58. { meta: { showLink: false } },
  59. { meta: { showLink: true } }
  60. ];
  61. const result = filterTree(routes);
  62. expect(result.length).toBe(2);
  63. expect(result[0].meta.showLink).toBe(true);
  64. expect(result[1].meta.showLink).toBe(true);
  65. });
  66. });
  67. /**
  68. * @description 测试 filterNoPermissionTree 函数
  69. * @function filterNoPermissionTree
  70. * @test 验证过滤掉无权限的路由
  71. */
  72. describe("filterNoPermissionTree", () => {
  73. it("should filter out routes without permission", () => {
  74. const routes = [
  75. { meta: { roles: ["admin"] } },
  76. { meta: { roles: ["user"] } },
  77. { meta: { roles: [] } }
  78. ];
  79. vi.spyOn(storageLocal(), "getItem").mockReturnValue({ roles: ["admin"] });
  80. const result = filterNoPermissionTree(routes);
  81. expect(result.length).toBe(1);
  82. expect(result[0].meta.roles).toEqual(["admin"]);
  83. });
  84. });
  85. /**
  86. * @description 测试 isOneOfArray 函数
  87. * @function isOneOfArray
  88. * @test 验证两个数组是否存在交集
  89. */
  90. describe("isOneOfArray", () => {
  91. it("should return true if arrays have common elements", () => {
  92. expect(isOneOfArray(["admin", "user"], ["user", "guest"])).toBe(true);
  93. });
  94. it("should return false if arrays have no common elements", () => {
  95. expect(isOneOfArray(["admin"], ["user"])).toBe(false);
  96. });
  97. });
  98. /**
  99. * @description 测试 getParentPaths 函数
  100. * @function getParentPaths
  101. * @test 验证获取父级路径集合
  102. */
  103. describe("getParentPaths", () => {
  104. it("should return parent paths for a given route path", () => {
  105. const routes = [
  106. {
  107. path: "/parent",
  108. children: [{ path: "/child" }]
  109. }
  110. ];
  111. const result = getParentPaths("/child", routes);
  112. expect(result).toEqual(["/parent"]);
  113. });
  114. });
  115. /**
  116. * @description 测试 findRouteByPath 函数
  117. * @function findRouteByPath
  118. * @test 验证根据路径查找路由
  119. */
  120. describe("findRouteByPath", () => {
  121. it("should find route by path", () => {
  122. const routes = [
  123. {
  124. path: "/parent",
  125. children: [{ path: "/child" }]
  126. }
  127. ];
  128. const result = findRouteByPath("/child", routes);
  129. expect(result.path).toBe("/child");
  130. });
  131. });
  132. /**
  133. * @description 测试 handleAliveRoute 函数
  134. * @function handleAliveRoute
  135. * @test 验证处理缓存路由的逻辑
  136. */
  137. describe("handleAliveRoute", () => {
  138. it("should handle cache route operations", () => {
  139. const mockCacheOperate = vi.fn();
  140. vi.spyOn(usePermissionStoreHook(), "cacheOperate").mockImplementation(
  141. mockCacheOperate
  142. );
  143. handleAliveRoute({ name: "test" }, "add");
  144. expect(mockCacheOperate).toHaveBeenCalledWith({
  145. mode: "add",
  146. name: "test"
  147. });
  148. });
  149. });
  150. /**
  151. * @description 测试 formatFlatteningRoutes 函数
  152. * @function formatFlatteningRoutes
  153. * @test 验证将多级嵌套路由处理成一维数组
  154. */
  155. describe("formatFlatteningRoutes", () => {
  156. it("should flatten nested routes into a one-dimensional array", () => {
  157. const routes = [
  158. {
  159. path: "/parent",
  160. children: [{ path: "/child" }]
  161. }
  162. ];
  163. const result = formatFlatteningRoutes(routes);
  164. expect(result.length).toBe(2);
  165. expect(result[0].path).toBe("/parent");
  166. expect(result[1].path).toBe("/child");
  167. });
  168. });
  169. /**
  170. * @description 测试 formatTwoStageRoutes 函数
  171. * @function formatTwoStageRoutes
  172. * @test 验证将一维数组处理成多级嵌套数组
  173. */
  174. describe("formatTwoStageRoutes", () => {
  175. it("should format one-dimensional routes into two-stage routes", () => {
  176. const routes = [
  177. { path: "/", component: "Home" },
  178. { path: "/about", component: "About" }
  179. ];
  180. const result = formatTwoStageRoutes(routes);
  181. expect(result[0].children.length).toBe(1);
  182. expect(result[0].children[0].path).toBe("/about");
  183. });
  184. });
  185. /**
  186. * @description 测试 initRouter 函数
  187. * @function initRouter
  188. * @test 验证初始化路由的逻辑
  189. */
  190. describe("initRouter", () => {
  191. it("should initialize router with cached routes", () => {
  192. const mockHandleAsyncRoutes = vi.fn();
  193. vi.spyOn(storageLocal(), "getItem").mockReturnValue([{ path: "/cached" }]);
  194. vi.spyOn(usePermissionStoreHook(), "handleWholeMenus").mockImplementation(
  195. mockHandleAsyncRoutes
  196. );
  197. initRouter();
  198. expect(mockHandleAsyncRoutes).toHaveBeenCalledWith([{ path: "/cached" }]);
  199. });
  200. });
  201. /**
  202. * @description 测试 getTopMenu 函数
  203. * @function getTopMenu
  204. * @test 验证获取顶级菜单的逻辑
  205. */
  206. describe("getTopMenu", () => {
  207. it("should return the top menu", () => {
  208. const mockWholeMenus = [
  209. {
  210. children: [{ path: "/top" }]
  211. }
  212. ];
  213. vi.spyOn(usePermissionStoreHook(), "wholeMenus", "get").mockReturnValue(
  214. mockWholeMenus
  215. );
  216. const result = getTopMenu();
  217. expect(result.path).toBe("/top");
  218. });
  219. });
  220. /**
  221. * @description 测试 addPathMatch 函数
  222. * @function addPathMatch
  223. * @test 验证添加通配路由的逻辑
  224. */
  225. describe("addPathMatch", () => {
  226. it("should add a wildcard route if not exists", () => {
  227. const mockAddRoute = vi.fn();
  228. vi.spyOn(router, "hasRoute").mockReturnValue(false);
  229. vi.spyOn(router, "addRoute").mockImplementation(mockAddRoute);
  230. addPathMatch();
  231. expect(mockAddRoute).toHaveBeenCalledWith({
  232. path: "/:pathMatch(.*)",
  233. name: "pathMatch",
  234. redirect: "/error/404"
  235. });
  236. });
  237. });
  238. /**
  239. * @description 测试 getHistoryMode 函数
  240. * @function getHistoryMode
  241. * @test 验证获取路由历史模式的逻辑
  242. */
  243. describe("getHistoryMode", () => {
  244. it("should return hash history mode", () => {
  245. const result = getHistoryMode("hash");
  246. expect(result).toBeDefined();
  247. });
  248. });
  249. /**
  250. * @description 测试 addAsyncRoutes 函数
  251. * @function addAsyncRoutes
  252. * @test 验证添加动态路由的逻辑
  253. */
  254. describe("addAsyncRoutes", () => {
  255. it("should add async routes with correct meta and component", () => {
  256. const routes = [{ path: "/async", meta: {} }];
  257. const result = addAsyncRoutes(routes);
  258. expect(result[0].meta.backstage).toBe(true);
  259. });
  260. });
  261. /**
  262. * @description 测试 hasAuth 函数
  263. * @function hasAuth
  264. * @test 验证是否有按钮级别的权限
  265. */
  266. describe("hasAuth", () => {
  267. it("should return true if auth exists", () => {
  268. vi.spyOn(router.currentRoute.value.meta, "auths", "get").mockReturnValue([
  269. "admin"
  270. ]);
  271. expect(hasAuth("admin")).toBe(true);
  272. });
  273. });
  274. /**
  275. * @description 测试 getAuths 函数
  276. * @function getAuths
  277. * @test 验证获取当前页面按钮级别的权限
  278. */
  279. describe("getAuths", () => {
  280. it("should return current route auths", () => {
  281. vi.spyOn(router.currentRoute.value.meta, "auths", "get").mockReturnValue([
  282. "admin"
  283. ]);
  284. expect(getAuths()).toEqual(["admin"]);
  285. });
  286. });
  287. });