import { act, renderHook } from '@testing-library/react' import { beforeEach, describe, expect, it, vi } from 'vitest' import { useAuthFlow } from '@/features/auth/useAuthFlow' import { texts } from '@/texts' const mocks = vi.hoisted(() => ({ addToast: vi.fn(), checkLoginOtp: vi.fn(), completeProfile: vi.fn(), replace: vi.fn(), sendLoginOtp: vi.fn(), })) vi.mock('@/lib/toast', () => ({ addToast: mocks.addToast })) vi.mock('next/navigation', () => ({ useRouter: () => ({ replace: mocks.replace }), useSearchParams: () => new URLSearchParams(), })) vi.mock('@/hooks/useAuth', () => ({ default: () => ({ checkLoginOtp: mocks.checkLoginOtp, completeProfile: mocks.completeProfile, sendLoginOtp: mocks.sendLoginOtp, user: null, }), })) describe('useAuthFlow registration consent', () => { beforeEach(() => { vi.clearAllMocks() mocks.checkLoginOtp.mockResolvedValue('pending') }) it('completes the OTP login funnel', async () => { mocks.sendLoginOtp.mockResolvedValue({ purpose: 'login', expiresIn: 120, resendIn: 60 }) mocks.checkLoginOtp.mockResolvedValue('active') const { result } = renderHook(() => useAuthFlow({ navigateOnSuccess: false })) await act(() => result.current.sendOtp({ mobile: '09123456789' })) await act(() => result.current.checkOtp({ code: '1234' })) expect(result.current.otpTtl).toBe(120) expect(result.current.resendCodeTime).toBe(120) }) it('requires accepting terms when the OTP purpose is register', async () => { mocks.sendLoginOtp.mockResolvedValue({ purpose: 'register', expiresIn: 120, resendIn: 60 }) const { result } = renderHook(() => useAuthFlow({ navigateOnSuccess: false })) await act(() => result.current.sendOtp({ mobile: '09123456789' })) expect(result.current.isRegistration).toBe(true) expect(result.current.termsAccepted).toBe(false) await act(() => result.current.checkOtp({ code: '1234' })) expect(mocks.checkLoginOtp).not.toHaveBeenCalled() expect(result.current.termsError).toBe(texts.auth.termsRequiredMessage) act(() => { result.current.updateTermsAccepted(true) }) await act(() => result.current.checkOtp({ code: '1234' })) expect(mocks.checkLoginOtp).toHaveBeenCalledWith({ mobile: '09123456789', code: '1234', termsAccepted: true, }) }) it('skips explicit terms acceptance when requireTermsAcceptance is false', async () => { mocks.sendLoginOtp.mockResolvedValue({ purpose: 'register', expiresIn: 120, resendIn: 60 }) const { result } = renderHook(() => useAuthFlow({ navigateOnSuccess: false, requireTermsAcceptance: false })) await act(() => result.current.sendOtp({ mobile: '09123456789' })) await act(() => result.current.checkOtp({ code: '1234' })) expect(mocks.checkLoginOtp).toHaveBeenCalledWith({ mobile: '09123456789', code: '1234', termsAccepted: true, }) }) it('does not require terms consent for an existing user login', async () => { mocks.sendLoginOtp.mockResolvedValue({ purpose: 'login', expiresIn: 120, resendIn: 60, alreadySent: false }) const { result } = renderHook(() => useAuthFlow({ navigateOnSuccess: false })) await act(() => result.current.sendOtp({ mobile: '09123456789' })) await act(() => result.current.checkOtp({ code: '1234' })) expect(result.current.isRegistration).toBe(false) expect(result.current.termsError).toBeNull() expect(mocks.checkLoginOtp).toHaveBeenCalledOnce() }) it('reuses a still-valid OTP window instead of restarting the 120-second countdown', async () => { mocks.sendLoginOtp.mockResolvedValue({ purpose: 'login', expiresIn: 47, resendIn: 60, alreadySent: true }) const { result } = renderHook(() => useAuthFlow({ navigateOnSuccess: false })) await act(() => result.current.sendOtp({ mobile: '09123456789' })) expect(result.current.step).toBe('otp') expect(result.current.otpReused).toBe(true) expect(result.current.otpTtl).toBe(47) expect(result.current.resendCodeTime).toBe(47) }) it('submits the selected city with the completed profile', async () => { const { result } = renderHook(() => useAuthFlow({ navigateOnSuccess: false })) await act(() => result.current.submitProfile({ firstName: ' علی ', lastName: ' رضایی ', gender: 'male', dateOfBirth: '1991-03-21', cityId: '12', }) ) expect(mocks.completeProfile).toHaveBeenCalledWith({ firstName: 'علی', lastName: 'رضایی', gender: 'male', dateOfBirth: '1991-03-21', cityId: 12, }) }) })