/** * OmniCard AI (CRMmp) - mpuniapp 统一登录骨架测试套件 (TDD Red -> Green) * * 严格按照 AGENTS.md 3.3、4.3、5.1 规范与 PRD pages/common/login 设计编写 */ type UniToastOption = { title: string; icon?: string; }; type UniRequestSuccessRes = { statusCode: number; data: unknown; header: Record; }; type UniRequestOption = { url: string; method?: string; data?: Record; success?: (res: UniRequestSuccessRes) => void; fail?: (err: { errMsg: string }) => void; complete?: () => void; }; type LoginState = { tab: 'phone' | 'email'; countryCode: string; phone: string; smsCode: string; email: string; submitting: boolean; errorMessage: string; }; type UseLoginModule = { createLoginState: () => LoginState; completeLogin: (token: string, tenantId?: string) => boolean; submitPhoneLogin: (state: LoginState) => boolean; sendMagicLink: (state: LoginState) => boolean; sendSmsCode: (state: LoginState) => boolean; tapSocialLogin: (provider: 'google' | 'apple') => void; }; describe('OmniCard AI 用户端统一登录业务逻辑测试 (useLogin.uts)', () => { let storageMap: Map; let toastCalls: UniToastOption[]; let lastRequestOptions: UniRequestOption | null; let requestCallCount: number; let navigateBackCount: number; beforeEach(() => { storageMap = new Map(); toastCalls = []; lastRequestOptions = null; requestCallCount = 0; navigateBackCount = 0; jest.resetModules(); (global as { uni?: Record }).uni = { getStorageSync: (key: string) => storageMap.get(key) || '', setStorageSync: (key: string, val: string) => storageMap.set(key, val), removeStorageSync: (key: string) => storageMap.delete(key), showToast: (options: UniToastOption) => { toastCalls.push(options); }, reLaunch: jest.fn(), navigateBack: () => { navigateBackCount += 1; }, getLocale: () => 'zh-CN', request: (options: UniRequestOption) => { requestCallCount += 1; lastRequestOptions = options; setTimeout(() => { options.success?.({ statusCode: 200, data: { code: 1000, message: 'success', data: {} }, header: {}, }); options.complete?.(); }, 0); return { abort: jest.fn(), }; }, }; }); afterEach(() => { delete (global as { uni?: Record }).uni; }); function loadUseLogin(): UseLoginModule { // eslint-disable-next-line @typescript-eslint/no-require-imports return require('../../../mpuniapp/pages/common/useLogin.uts') as UseLoginModule; } describe('1. completeLogin 写入本地凭证', () => { it('1.1 completeLogin 会 setStorage token', () => { const { completeLogin } = loadUseLogin(); const ok = completeLogin('mock_jwt_login_t09'); expect(ok).toBe(true); expect(storageMap.get('token')).toBe('mock_jwt_login_t09'); }); it('1.2 传入 tenantId 时同步写入 tenant_id', () => { const { completeLogin } = loadUseLogin(); completeLogin('mock_jwt_login_t09', 'tenant_omni_88'); expect(storageMap.get('token')).toBe('mock_jwt_login_t09'); expect(storageMap.get('tenant_id')).toBe('tenant_omni_88'); }); it('1.3 未传 tenantId 时不写入 tenant_id', () => { const { completeLogin } = loadUseLogin(); completeLogin('mock_jwt_only_token'); expect(storageMap.get('token')).toBe('mock_jwt_only_token'); expect(storageMap.has('tenant_id')).toBe(false); }); }); describe('2. 空手机号 / 空邮箱不提交', () => { it('2.1 空手机号不提交验证码登录', () => { const { createLoginState, submitPhoneLogin } = loadUseLogin(); const state = createLoginState(); state.smsCode = '123456'; const ok = submitPhoneLogin(state); expect(ok).toBe(false); expect(storageMap.has('token')).toBe(false); expect(requestCallCount).toBe(0); expect(lastRequestOptions).toBeNull(); expect(toastCalls.length).toBeGreaterThan(0); expect(toastCalls[0].title).toContain('手机号'); }); it('2.2 空邮箱不提交 Magic Link', () => { const { createLoginState, sendMagicLink } = loadUseLogin(); const state = createLoginState(); state.tab = 'email'; const ok = sendMagicLink(state); expect(ok).toBe(false); expect(storageMap.has('token')).toBe(false); expect(requestCallCount).toBe(0); expect(lastRequestOptions).toBeNull(); expect(toastCalls.length).toBeGreaterThan(0); expect(toastCalls[0].title).toContain('邮箱'); }); }); describe('3. 社交登录不伪造成功', () => { it('3.1 Google / Apple 仅 toast 即将开放且不写 token', () => { const { tapSocialLogin } = loadUseLogin(); tapSocialLogin('google'); tapSocialLogin('apple'); expect(storageMap.has('token')).toBe(false); expect(navigateBackCount).toBe(0); expect(requestCallCount).toBe(0); expect(toastCalls.length).toBe(2); expect(toastCalls[0].title).toContain('即将开放'); expect(toastCalls[1].title).toContain('即将开放'); }); }); });