136 lines
3.7 KiB
JavaScript
136 lines
3.7 KiB
JavaScript
import { describe, it, expect, beforeEach, vi } from 'vitest'
|
||
import { setActivePinia, createPinia } from 'pinia'
|
||
import { useAuthStore } from '@/stores/auth'
|
||
import { useMenuStore } from '@/stores/menus'
|
||
|
||
// mock 后端数据接口,避免真实网络请求
|
||
vi.mock('@/services/api', () => ({
|
||
loadDataApi: vi.fn(),
|
||
}))
|
||
|
||
import { loadDataApi } from '@/services/api'
|
||
|
||
// 模块中心等系统管理员专用页面不进 s_menu(入口固定在 AppSidebar、路由层 adminOnly 拦截),
|
||
// 因此这里用业务菜单构造数据,验证菜单 store 本身无任何编码/账号特判。
|
||
const mockMenus = [
|
||
{
|
||
b_id: '1',
|
||
b_code: 'base',
|
||
b_name: '基础数据',
|
||
b_menu_type: 'directory',
|
||
b_parent_id: null,
|
||
b_canuse: 1,
|
||
},
|
||
{
|
||
b_id: '2',
|
||
b_code: 'contact',
|
||
b_name: '往来单位',
|
||
b_menu_type: 'page',
|
||
b_parent_id: '1',
|
||
b_route: '/base/contact',
|
||
b_canuse: 1,
|
||
},
|
||
{
|
||
// 没有任何子项的目录:可见性裁剪后不应渲染
|
||
b_id: '3',
|
||
b_code: 'empty-dir',
|
||
b_name: '空目录',
|
||
b_menu_type: 'directory',
|
||
b_parent_id: null,
|
||
b_canuse: 1,
|
||
},
|
||
]
|
||
|
||
function mockSModuleData(menus) {
|
||
loadDataApi.mockImplementation((view) =>
|
||
Promise.resolve({
|
||
data: view === 's_menu' ? menus : [],
|
||
code: 0,
|
||
success: true,
|
||
}),
|
||
)
|
||
}
|
||
|
||
function setUser(account, id) {
|
||
const auth = useAuthStore()
|
||
auth.setSession({ token: 't', user: { id, account } }, 'ORG')
|
||
}
|
||
|
||
/** 冲刷微任务:让 setSession 触发的 watch(clear) 先执行 */
|
||
const flush = () => Promise.resolve()
|
||
|
||
function collectCodes(nodes) {
|
||
const codes = []
|
||
for (const node of nodes) {
|
||
codes.push(String(node.b_code))
|
||
if (node.children) codes.push(...collectCodes(node.children))
|
||
}
|
||
return codes
|
||
}
|
||
|
||
beforeEach(() => {
|
||
setActivePinia(createPinia())
|
||
loadDataApi.mockReset()
|
||
})
|
||
|
||
describe('menus store(s_menu 动态菜单)', () => {
|
||
it('普通用户:全部启用菜单按树渲染,空目录剪枝(无编码特判)', async () => {
|
||
mockSModuleData(mockMenus)
|
||
setUser('u1', 123)
|
||
await flush()
|
||
const menus = useMenuStore()
|
||
await menus.load()
|
||
|
||
const codes = collectCodes(menus.visibleMenus)
|
||
expect(codes).toContain('base')
|
||
expect(codes).toContain('contact')
|
||
// 目录没有可见子菜单时不渲染
|
||
expect(codes).not.toContain('empty-dir')
|
||
})
|
||
|
||
it('管理员特判不在菜单 store:g3soft 与普通用户渲染一致', async () => {
|
||
// 模块中心的管控在路由守卫(meta.adminOnly)与侧栏渲染(仅 g3soft 追加固定入口),
|
||
// 菜单 store 对所有登录主体一视同仁
|
||
mockSModuleData(mockMenus)
|
||
setUser('g3soft', 1)
|
||
await flush()
|
||
const menus = useMenuStore()
|
||
await menus.load()
|
||
|
||
const codes = collectCodes(menus.visibleMenus)
|
||
expect(codes).toHaveLength(2)
|
||
expect(codes).toEqual(expect.arrayContaining(['base', 'contact']))
|
||
})
|
||
|
||
it('同一主体重复 load 不重复发请求', async () => {
|
||
mockSModuleData(mockMenus)
|
||
setUser('u1', 123)
|
||
await flush()
|
||
const menus = useMenuStore()
|
||
await menus.load()
|
||
await menus.load()
|
||
expect(loadDataApi).toHaveBeenCalledTimes(1)
|
||
})
|
||
|
||
it('切换账号:缓存清空并重新加载', async () => {
|
||
mockSModuleData(mockMenus)
|
||
setUser('u1', 123)
|
||
await flush()
|
||
const menus = useMenuStore()
|
||
await menus.load()
|
||
expect(menus.rawMenus.length).toBe(mockMenus.length)
|
||
|
||
// 切换到另一个主体:watch(clear) 清空已加载数据
|
||
setUser('u2', 999)
|
||
await flush()
|
||
await flush()
|
||
expect(menus.rawMenus.length).toBe(0)
|
||
expect(menus.visibleMenus).toEqual([])
|
||
|
||
// 新主体 load 重新拉取
|
||
loadDataApi.mockClear()
|
||
await menus.load()
|
||
expect(menus.rawMenus.length).toBe(mockMenus.length)
|
||
})
|
||
})
|