import { describe, it, expect, beforeEach, vi } from 'vitest' import { nextTick } from 'vue' import { createTestPinia } from '../helpers/app.js' import { useAppStore } from '@/stores/app' import { useAuthStore } from '@/stores/auth' // app store 的 initThemeSync 依赖 theme 模块操作 documentElement,jest 环境可安全执行 vi.mock('@/theme/index', () => ({ applyPrimaryColor: vi.fn(), applyDarkMode: vi.fn(), })) beforeEach(() => { localStorage.clear() sessionStorage.clear() vi.clearAllMocks() }) describe('app store · 主题与布局', () => { it('初始状态:暗色关、默认主色、侧栏展开、工作台页签固定', () => { createTestPinia() const app = useAppStore() expect(app.darkMode).toBe(false) expect(app.sidebarCollapsed).toBe(false) expect(app.locale).toBe('zh-CN') expect(app.visitedTabs).toHaveLength(1) expect(app.visitedTabs[0]).toMatchObject({ key: '/dashboard', closable: false }) }) it('setDark / toggleDark 切换暗色', () => { createTestPinia() const app = useAppStore() app.setDark(true) expect(app.darkMode).toBe(true) app.toggleDark() expect(app.darkMode).toBe(false) }) it('setPrimary / toggleSidebar / setSidebar / toggleLocale', () => { createTestPinia() const app = useAppStore() app.setPrimary('#123456') expect(app.primaryColor).toBe('#123456') app.toggleSidebar() expect(app.sidebarCollapsed).toBe(true) app.setSidebar(false) expect(app.sidebarCollapsed).toBe(false) app.toggleLocale() expect(app.locale).toBe('en-US') }) }) describe('app store · 页签管理', () => { function seedTabs() { const app = useAppStore() app.addVisitedTab({ key: '/a', label: 'A', closable: true, name: 'A' }) app.addVisitedTab({ key: '/b', label: 'B', closable: true, name: 'B' }) app.addVisitedTab({ key: '/c', label: 'C', closable: true, name: 'C' }) app.refreshRoute('/a') app.refreshRoute('/a') app.refreshRoute('/b') return app } it('addVisitedTab 去重:重复 key 不重复添加', () => { createTestPinia() const app = seedTabs() app.addVisitedTab({ key: '/a', label: 'A2', closable: true, name: 'A' }) expect(app.visitedTabs.filter((t) => t.key === '/a')).toHaveLength(1) }) it('cachedViews:按 visitedTabs 去重取组件名', () => { createTestPinia() const app = seedTabs() app.addVisitedTab({ key: '/a', label: 'A', closable: true, name: 'A' }) expect(app.cachedViews).toEqual(['Dashboard', 'A', 'B', 'C']) }) it('removeVisitedTab:移除页签并清理该页刷新计数', () => { createTestPinia() const app = seedTabs() app.removeVisitedTab('/a') expect(app.visitedTabs.some((t) => t.key === '/a')).toBe(false) expect(app.routeRefreshKeys['/a']).toBeUndefined() }) it('closeOthers:保留工作台与指定页签', () => { createTestPinia() const app = seedTabs() app.closeOthers('/b') expect(app.visitedTabs.map((t) => t.key)).toEqual(['/dashboard', '/b']) expect(app.routeRefreshKeys['/a']).toBeUndefined() }) it('closeLeft:保留工作台、指定页签及其右侧', () => { createTestPinia() const app = seedTabs() app.closeLeft('/b') expect(app.visitedTabs.map((t) => t.key)).toEqual(['/dashboard', '/b', '/c']) }) it('closeLeft:指定工作台时不裁剪', () => { createTestPinia() const app = seedTabs() app.closeLeft('/dashboard') expect(app.visitedTabs).toHaveLength(4) }) it('closeRight:保留指定页签及其左侧', () => { createTestPinia() const app = seedTabs() app.closeRight('/b') expect(app.visitedTabs.map((t) => t.key)).toEqual(['/dashboard', '/a', '/b']) }) it('closeAll:仅保留工作台并清理全部刷新计数', () => { createTestPinia() const app = seedTabs() app.closeAll() expect(app.visitedTabs.map((t) => t.key)).toEqual(['/dashboard']) expect(Object.keys(app.routeRefreshKeys)).toHaveLength(0) }) it('refreshRoute / getRouteRefreshKey:计数递增与读取', () => { createTestPinia() const app = useAppStore() expect(app.getRouteRefreshKey('/x')).toBe(0) app.refreshRoute('/x') app.refreshRoute('/x') expect(app.getRouteRefreshKey('/x')).toBe(2) }) }) describe('auth store · 用户信息回显(userInfo)', () => { it('saveLastLogin 写入机构与账号', () => { createTestPinia() const auth = useAuthStore() auth.saveLastLogin('ORG1', 'admin') expect(auth.userInfo).toEqual({ orgId: 'ORG1', account: 'admin' }) }) it('持久化 key 为 fms-user(localStorage)', async () => { createTestPinia() const auth = useAuthStore() auth.saveLastLogin('ORG9', 'u9') // persist 插件通过 $subscribe 同步写 storage,冲刷微任务兜底 await nextTick() expect(localStorage.getItem('fms-user')).toContain('ORG9') expect(localStorage.getItem('fms-user')).toContain('"account":"u9"') }) }) describe('auth store', () => { it('setSession:token/orgid/user 三种字段形态兼容', () => { createTestPinia() const auth = useAuthStore() auth.setSession( { access_token: 'at', orgid: 'O1', user_info: { id: 7 } }, 'O_FALLBACK', ) expect(auth.loginInfo.token).toBe('at') expect(auth.userInfo.orgId).toBe('O1') expect(auth.loginInfo.user).toEqual({ id: 7 }) expect(auth.isAuthenticated).toBe(true) }) it('setSession:缺失字段时回退到 organizationId 参数', () => { createTestPinia() const auth = useAuthStore() auth.setSession({ token: 't' }, 'ORG-FB') expect(auth.userInfo.orgId).toBe('ORG-FB') }) it('clearSession:清会话凭证,保留 userInfo 回显数据', () => { createTestPinia() const auth = useAuthStore() auth.setSession({ token: 't', user: { id: 1 } }, 'ORG') auth.saveLastLogin('ORG', 'admin') auth.clearSession() expect(auth.loginInfo.token).toBe('') expect(auth.loginInfo.user).toBe(null) expect(auth.isAuthenticated).toBe(false) // userInfo 是登录页回显数据,不属于会话,退出登录后保留 expect(auth.userInfo).toEqual({ orgId: 'ORG', account: 'admin' }) }) it('持久化:会话写 sessionStorage(fms-login)、回显写 localStorage(fms-user)', async () => { createTestPinia() const auth = useAuthStore() auth.setSession({ token: 't', user: { id: 1 } }, 'ORG') auth.saveLastLogin('ORG', 'admin') await nextTick() expect(sessionStorage.getItem('fms-login')).toContain('"token":"t"') expect(localStorage.getItem('fms-user')).toContain('"orgId":"ORG"') // 会话与回显物理隔离:清会话不影响回显 key auth.clearSession() await nextTick() expect(localStorage.getItem('fms-user')).toContain('"orgId":"ORG"') }) })