import { describe, it, expect, beforeEach, vi } from 'vitest' // navigation → Taro 只透传最终参数,mock 记录调用 const mockNavigateTo = vi.hoisted(() => vi.fn()) const mockReLaunch = vi.hoisted(() => vi.fn()) const mockRedirectTo = vi.hoisted(() => vi.fn()) const mockSwitchTab = vi.hoisted(() => vi.fn()) vi.mock('@tarojs/taro', () => ({ default: { navigateTo: mockNavigateTo, reLaunch: mockReLaunch, redirectTo: mockRedirectTo, switchTab: mockSwitchTab, navigateBack: vi.fn() } })) // store mock:authState 可变,支持模拟已登录/未登录两种状态 const authState = vi.hoisted(() => ({ token: null as string | null })) vi.mock('@/store', () => ({ store: { getState: () => ({ auth: { token: authState.token } }) } })) // storage mock:内存 Map,用于验证 store 未恢复时的兜底读取 const mem = vi.hoisted(() => new Map()) vi.mock('@/utils/storage', () => ({ storage: { get: (key: string): T | null => (mem.has(key) ? (mem.get(key) as T) : null), set: (key: string, value: T): void => { mem.set(key, value) }, remove: (key: string): void => { mem.delete(key) } } })) import { navigateTo, reLaunch, redirectTo, switchTab } from '@/utils/navigation' import { isProtectedPage } from '@/utils/auth' import { ROUTES } from '@/constants/routes' beforeEach(() => { authState.token = null mem.clear() mockNavigateTo.mockClear() mockReLaunch.mockClear() mockRedirectTo.mockClear() mockSwitchTab.mockClear() }) describe('navigation 登录守卫', () => { it('未登录 navigateTo 受保护页:url 被改写为登录页', () => { navigateTo(ROUTES.FAMILY) expect(mockNavigateTo).toHaveBeenCalledWith({ url: ROUTES.LOGIN }) }) it('未登录 reLaunch/redirectTo 到受保护页被守卫(tab 页在游客白名单放行)', () => { reLaunch(ROUTES.DEVICE_ADD) redirectTo(ROUTES.FAMILY) expect(mockReLaunch).toHaveBeenCalledWith({ url: ROUTES.LOGIN }) expect(mockRedirectTo).toHaveBeenCalledWith({ url: ROUTES.LOGIN }) }) it('未登录 switchTab 到 tab 页:放行(游客模式白名单,微信审核要求)', () => { switchTab(ROUTES.EVENT) expect(mockSwitchTab).toHaveBeenCalledWith({ url: ROUTES.EVENT }) }) it('已登录 navigateTo:原样放行', () => { authState.token = 'tk-1' navigateTo(ROUTES.FAMILY) expect(mockNavigateTo).toHaveBeenCalledWith({ url: ROUTES.FAMILY }) }) it('未登录访问登录页:放行(登录页在公开名单)', () => { navigateTo(ROUTES.LOGIN) expect(mockNavigateTo).toHaveBeenCalledWith({ url: ROUTES.LOGIN }) }) it('带 query 的受保护页:跳登录页不带残留参数', () => { navigateTo(`${ROUTES.FAMILY_DETAIL}?id=3`) expect(mockNavigateTo).toHaveBeenCalledWith({ url: ROUTES.LOGIN }) }) it('store 无 token 但 storage 有(冷启动兜底):放行', () => { mem.set('auth_token', 'tk-saved') navigateTo(ROUTES.HOME) expect(mockNavigateTo).toHaveBeenCalledWith({ url: ROUTES.HOME }) }) }) describe('isProtectedPage', () => { it('登录页与四个 tab 页返回 false(游客可浏览),其余页面返回 true', () => { expect(isProtectedPage(ROUTES.LOGIN)).toBe(false) expect(isProtectedPage(ROUTES.HOME)).toBe(false) expect(isProtectedPage(ROUTES.DEVICE_LIST)).toBe(false) expect(isProtectedPage(ROUTES.EVENT)).toBe(false) expect(isProtectedPage(ROUTES.MINE)).toBe(false) expect(isProtectedPage(ROUTES.FAMILY)).toBe(true) expect(isProtectedPage(`${ROUTES.LOGIN}?from=xxx`)).toBe(false) }) })