import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest' import { setActivePinia, createPinia } from 'pinia' import { useAuthStore } from '@/stores/auth' import { usePermissionStore } from '@/stores/permissions' import { createTestPinia } from './helpers/app.js' import router from '@/router' // mock 后端数据接口 vi.mock('@/services/api', () => ({ loadDataApi: vi.fn(), })) import { loadDataApi } from '@/services/api' const mockModules = [ { b_id: '1', b_code: 'm1', b_name: 'M1', b_canuse: 1, b_canmenu: 1, b_module_type: 'directory', b_parent_id: null, b_icon: '', b_route: '', }, ] beforeEach(() => { localStorage.clear() sessionStorage.clear() setActivePinia(createPinia()) loadDataApi.mockReset() loadDataApi.mockImplementation((view) => Promise.resolve({ data: view === 's_module' ? mockModules : [], code: 0, success: true }), ) // 回到登录页,断开受保护路由状态 return router.push('/login').catch(() => {}) }) afterEach(() => { if (router.hasRoute('biz')) router.removeRoute('biz') }) describe('路由守卫 权限加载(三场景)', () => { it('场景·未登录:访问受保护页 → 重定向 /login?redirect', async () => { await router.push('/dashboard') expect(router.currentRoute.value.path).toBe('/login') expect(router.currentRoute.value.query.redirect).toBe('/dashboard') }) it('场景·登录:访问受保护页 → 触发 load,菜单数据就绪(同主体不重复请求)', async () => { const auth = useAuthStore() auth.setSession({ token: 't', user: { id: 123, account: 'u1', name: 'U' } }, 'ORG') await router.push('/dashboard') expect(loadDataApi).toHaveBeenCalled() expect(usePermissionStore().modules.length).toBe(1) // 再次进入另一受保护页(同主体)不应重复发请求 loadDataApi.mockClear() await router.push('/demo') expect(loadDataApi).not.toHaveBeenCalled() }) it('场景·登录:访问无权限 moduleId 路由 → 重定向 /dashboard', async () => { const auth = useAuthStore() auth.setSession({ token: 't', user: { id: 123, account: 'u1', name: 'U' } }, 'ORG') router.addRoute({ path: '/biz', name: 'biz', component: { render: () => null }, meta: { requiresAuth: true, moduleId: 'm-no' }, }) await router.push('/biz') expect(router.currentRoute.value.path).toBe('/dashboard') }) it('场景·刷新已登录态:persist 恢复后守卫 load 成功,无 clear 竞态', async () => { // 按 pinia-plugin-persistedstate v4 的实际存储格式写入(纯 state JSON,无 {state, version} 包装) sessionStorage.setItem( 'fms-login', JSON.stringify({ loginInfo: { token: 't', user: { id: 123, account: 'u1', name: 'U' } } }), ) localStorage.setItem( 'fms-user', JSON.stringify({ userInfo: { orgId: 'ORG', account: 'u1' } }), ) // 用 createTestPinia(内部已 install,persist 插件真实注册),等价于刷新页面 createTestPinia() const a = useAuthStore() // 首次创建 await a.$hydrate() // 显式从 localStorage 恢复(等价于刷新页面) expect(useAuthStore().isAuthenticated).toBe(true) await router.push('/dashboard') expect(usePermissionStore().modules.length).toBe(1) }) it('场景·切换账号:旧主体请求作废,新主体重新加载', async () => { const auth = useAuthStore() auth.setSession({ token: 't', user: { id: 123, account: 'u1', name: 'U' } }, 'ORG') await router.push('/dashboard') expect(usePermissionStore().modules.length).toBe(1) // 切换到另一账号 auth.setSession({ token: 't2', user: { id: 999, account: 'u2', name: 'U2' } }, 'ORG2') loadDataApi.mockClear() await router.push('/demo') expect(loadDataApi).toHaveBeenCalled() // 新主体重新加载 expect(usePermissionStore().modules.length).toBe(1) }) })