import { describe, it, expect, beforeEach, vi } from 'vitest' const mockLogin = vi.hoisted(() => vi.fn()) const mockGetAccountInfo = vi.hoisted(() => vi.fn()) vi.mock('@tarojs/taro', () => ({ default: { login: mockLogin, getAccountInfoSync: mockGetAccountInfo } })) const mockWxMiniappLogin = vi.hoisted(() => vi.fn()) vi.mock('@/api/auth', () => ({ wxMiniappLogin: mockWxMiniappLogin })) const mem = 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 { performWxLogin } from '@/pages/login/wxLoginFlow' const result = (over: Record = {}) => ({ userInfo: { userId: 'u-1', userType: 1, avatar: '', nickName: '张三', mobile: '', gender: 1, region: '', loginFrom: 'miniapp' }, code: 'tk-abc', ...over }) beforeEach(() => { mem.clear() mockLogin.mockReset() mockGetAccountInfo.mockReset() mockWxMiniappLogin.mockReset() mockLogin.mockResolvedValue({ code: 'wx-code-1' }) mockGetAccountInfo.mockReturnValue({ miniProgram: { appId: 'wx-appid-live' } }) }) describe('performWxLogin', () => { it('完整流程:Taro.login → appId/openId 组装 → 返回登录结果', async () => { mockWxMiniappLogin.mockResolvedValue(result()) const res = await performWxLogin() expect(res.code).toBe('tk-abc') expect(mockWxMiniappLogin).toHaveBeenCalledWith({ appId: 'wx-appid-live', code: 'wx-code-1', openId: undefined }) }) it('首次登录(本地无 openId)请求不带 openId,结果含 openId 时写入缓存', async () => { mockWxMiniappLogin.mockResolvedValue(result({ openId: 'o-abc' })) await performWxLogin() expect(mockWxMiniappLogin.mock.calls[0][0].openId).toBeUndefined() expect(mem.get('wx_openid')).toBe('o-abc') }) it('二次登录:本地已有 openId 则随请求回传', async () => { mem.set('wx_openid', 'o-saved') mockWxMiniappLogin.mockResolvedValue(result()) await performWxLogin() expect(mockWxMiniappLogin.mock.calls[0][0].openId).toBe('o-saved') }) it('结果不含 openId 时不覆盖本地缓存', async () => { mem.set('wx_openid', 'o-saved') mockWxMiniappLogin.mockResolvedValue(result()) await performWxLogin() expect(mem.get('wx_openid')).toBe('o-saved') }) it('Taro.login 返回空 code 抛错(不发起登录请求)', async () => { mockLogin.mockResolvedValue({ code: '' }) await expect(performWxLogin()).rejects.toThrow('获取微信登录凭证失败') expect(mockWxMiniappLogin).not.toHaveBeenCalled() }) it('Taro.login reject(errMsg 形状)时抛带 message 的 Error', async () => { mockLogin.mockRejectedValue({ errMsg: 'login:fail:cancel' }) await expect(performWxLogin()).rejects.toThrow('微信登录失败') expect(mockWxMiniappLogin).not.toHaveBeenCalled() }) it('getAccountInfoSync 抛错时回退常量 appId', async () => { mockGetAccountInfo.mockImplementation(() => { throw new Error('not supported') }) mockWxMiniappLogin.mockResolvedValue(result()) await performWxLogin() expect(mockWxMiniappLogin.mock.calls[0][0].appId).toBe('wxe9773bb6f025ab00') }) it('接口业务错(BusinessError)原样透传', async () => { const err = new Error('code 已被使用') err.name = 'BusinessError' mockWxMiniappLogin.mockRejectedValue(err) await expect(performWxLogin()).rejects.toThrow('code 已被使用') }) })