/** * useNavigationFlow Hook - Null Safety Tests * * Tests for null/undefined handling in navigation hook */ import { renderHook, act } from '@testing-library/react-native'; import { useNavigationFlow } from '../useNavigationFlow'; import { ROUTES } from '@/services/NavigationController'; import type { Beneficiary } from '@/types'; // Mock expo-router const mockPush = jest.fn(); const mockReplace = jest.fn(); jest.mock('expo-router', () => ({ useRouter: () => ({ push: mockPush, replace: mockReplace, }), })); describe('useNavigationFlow - Null Safety', () => { beforeEach(() => { jest.clearAllMocks(); // Suppress console warnings during tests jest.spyOn(console, 'warn').mockImplementation(() => {}); jest.spyOn(console, 'error').mockImplementation(() => {}); }); afterEach(() => { jest.restoreAllMocks(); }); describe('navigate', () => { it('should handle null navigation result', () => { const { result } = renderHook(() => useNavigationFlow()); act(() => { result.current.navigate(null as any); }); expect(mockPush).not.toHaveBeenCalled(); expect(mockReplace).not.toHaveBeenCalled(); expect(console.warn).toHaveBeenCalledWith( '[useNavigationFlow] Invalid navigation result:', null ); }); it('should handle undefined navigation result', () => { const { result } = renderHook(() => useNavigationFlow()); act(() => { result.current.navigate(undefined as any); }); expect(mockPush).not.toHaveBeenCalled(); expect(mockReplace).not.toHaveBeenCalled(); expect(console.warn).toHaveBeenCalledWith( '[useNavigationFlow] Invalid navigation result:', undefined ); }); it('should handle navigation result without path', () => { const { result } = renderHook(() => useNavigationFlow()); act(() => { result.current.navigate({ path: '' } as any); }); expect(mockPush).not.toHaveBeenCalled(); expect(mockReplace).not.toHaveBeenCalled(); }); it('should navigate successfully with valid result', () => { const { result } = renderHook(() => useNavigationFlow()); act(() => { result.current.navigate({ path: ROUTES.TABS.DASHBOARD }); }); expect(mockPush).toHaveBeenCalledWith(ROUTES.TABS.DASHBOARD); }); it('should use replace when specified', () => { const { result } = renderHook(() => useNavigationFlow()); act(() => { result.current.navigate({ path: ROUTES.TABS.DASHBOARD }, true); }); expect(mockReplace).toHaveBeenCalledWith(ROUTES.TABS.DASHBOARD); expect(mockPush).not.toHaveBeenCalled(); }); }); describe('navigateAfterLogin', () => { it('should handle null profile', () => { const { result } = renderHook(() => useNavigationFlow()); act(() => { result.current.navigateAfterLogin(null, []); }); expect(mockReplace).toHaveBeenCalledWith(ROUTES.AUTH.ENTER_NAME); }); it('should handle undefined profile', () => { const { result } = renderHook(() => useNavigationFlow()); act(() => { result.current.navigateAfterLogin(undefined, []); }); expect(mockReplace).toHaveBeenCalledWith(ROUTES.AUTH.ENTER_NAME); }); it('should handle null beneficiaries', () => { const { result } = renderHook(() => useNavigationFlow()); const profile = { id: 1, email: 'test@example.com', firstName: 'John', lastName: 'Doe', phone: null, }; act(() => { result.current.navigateAfterLogin(profile, null); }); expect(mockReplace).toHaveBeenCalledWith(ROUTES.AUTH.ADD_LOVED_ONE); }); it('should handle undefined beneficiaries', () => { const { result } = renderHook(() => useNavigationFlow()); const profile = { id: 1, email: 'test@example.com', firstName: 'John', lastName: 'Doe', phone: null, }; act(() => { result.current.navigateAfterLogin(profile, undefined); }); expect(mockReplace).toHaveBeenCalledWith(ROUTES.AUTH.ADD_LOVED_ONE); }); }); describe('navigateAfterAddBeneficiary', () => { it('should handle null beneficiaryId', () => { const { result } = renderHook(() => useNavigationFlow()); act(() => { result.current.navigateAfterAddBeneficiary(null, false); }); expect(mockPush).toHaveBeenCalledWith(ROUTES.AUTH.ADD_LOVED_ONE); }); it('should handle undefined beneficiaryId', () => { const { result } = renderHook(() => useNavigationFlow()); act(() => { result.current.navigateAfterAddBeneficiary(undefined, false); }); expect(mockPush).toHaveBeenCalledWith(ROUTES.AUTH.ADD_LOVED_ONE); }); it('should navigate successfully with valid beneficiaryId', () => { const { result } = renderHook(() => useNavigationFlow()); act(() => { result.current.navigateAfterAddBeneficiary(1, false); }); expect(mockPush).toHaveBeenCalledWith({ pathname: ROUTES.AUTH.PURCHASE, params: { beneficiaryId: 1 }, }); }); }); describe('navigateAfterPurchase', () => { it('should handle null beneficiaryId', () => { const { result } = renderHook(() => useNavigationFlow()); act(() => { result.current.navigateAfterPurchase(null, {}); }); expect(mockPush).toHaveBeenCalledWith(ROUTES.AUTH.ADD_LOVED_ONE); }); it('should handle undefined beneficiaryId', () => { const { result } = renderHook(() => useNavigationFlow()); act(() => { result.current.navigateAfterPurchase(undefined, {}); }); expect(mockPush).toHaveBeenCalledWith(ROUTES.AUTH.ADD_LOVED_ONE); }); it('should handle null options', () => { const { result } = renderHook(() => useNavigationFlow()); act(() => { result.current.navigateAfterPurchase(1, null); }); expect(mockPush).toHaveBeenCalled(); }); it('should handle undefined options', () => { const { result } = renderHook(() => useNavigationFlow()); act(() => { result.current.navigateAfterPurchase(1, undefined); }); expect(mockPush).toHaveBeenCalled(); }); }); describe('navigateToBeneficiary', () => { it('should handle null beneficiary', () => { const { result } = renderHook(() => useNavigationFlow()); act(() => { result.current.navigateToBeneficiary(null, 'view'); }); expect(mockPush).toHaveBeenCalledWith(ROUTES.TABS.BENEFICIARIES); }); it('should handle undefined beneficiary', () => { const { result } = renderHook(() => useNavigationFlow()); act(() => { result.current.navigateToBeneficiary(undefined, 'view'); }); expect(mockPush).toHaveBeenCalledWith(ROUTES.TABS.BENEFICIARIES); }); it('should navigate successfully with valid beneficiary', () => { const { result } = renderHook(() => useNavigationFlow()); const beneficiary: Beneficiary = { id: 1, name: 'Mom', displayName: 'Mom', status: 'online', }; act(() => { result.current.navigateToBeneficiary(beneficiary, 'view'); }); expect(mockPush).toHaveBeenCalledWith(ROUTES.BENEFICIARY.DETAIL(1)); }); }); describe('goToPurchase', () => { it('should handle null beneficiaryId', () => { const { result } = renderHook(() => useNavigationFlow()); act(() => { result.current.goToPurchase(null); }); expect(mockPush).not.toHaveBeenCalled(); expect(console.warn).toHaveBeenCalledWith( '[useNavigationFlow] Invalid beneficiaryId for purchase:', null ); }); it('should handle undefined beneficiaryId', () => { const { result } = renderHook(() => useNavigationFlow()); act(() => { result.current.goToPurchase(undefined); }); expect(mockPush).not.toHaveBeenCalled(); expect(console.warn).toHaveBeenCalledWith( '[useNavigationFlow] Invalid beneficiaryId for purchase:', undefined ); }); it('should handle invalid beneficiaryId type', () => { const { result } = renderHook(() => useNavigationFlow()); act(() => { result.current.goToPurchase('invalid' as any); }); expect(mockPush).not.toHaveBeenCalled(); expect(console.warn).toHaveBeenCalledWith( '[useNavigationFlow] Invalid beneficiaryId for purchase:', 'invalid' ); }); it('should navigate successfully with valid beneficiaryId', () => { const { result } = renderHook(() => useNavigationFlow()); act(() => { result.current.goToPurchase(1); }); expect(mockPush).toHaveBeenCalledWith({ pathname: ROUTES.AUTH.PURCHASE, params: { beneficiaryId: 1 }, }); }); }); describe('goToActivate', () => { it('should handle null beneficiaryId', () => { const { result } = renderHook(() => useNavigationFlow()); act(() => { result.current.goToActivate(null); }); expect(mockPush).not.toHaveBeenCalled(); expect(console.warn).toHaveBeenCalledWith( '[useNavigationFlow] Invalid beneficiaryId for activation:', null ); }); it('should handle undefined beneficiaryId', () => { const { result } = renderHook(() => useNavigationFlow()); act(() => { result.current.goToActivate(undefined); }); expect(mockPush).not.toHaveBeenCalled(); expect(console.warn).toHaveBeenCalledWith( '[useNavigationFlow] Invalid beneficiaryId for activation:', undefined ); }); it('should navigate successfully with valid beneficiaryId', () => { const { result } = renderHook(() => useNavigationFlow()); act(() => { result.current.goToActivate(1); }); expect(mockPush).toHaveBeenCalledWith({ pathname: ROUTES.AUTH.ACTIVATE, params: { beneficiaryId: 1 }, }); }); it('should include demo param when provided', () => { const { result } = renderHook(() => useNavigationFlow()); act(() => { result.current.goToActivate(1, true); }); expect(mockPush).toHaveBeenCalledWith({ pathname: ROUTES.AUTH.ACTIVATE, params: { beneficiaryId: 1, demo: true }, }); }); }); describe('navigation shortcuts', () => { it('should navigate to dashboard', () => { const { result } = renderHook(() => useNavigationFlow()); act(() => { result.current.goToDashboard(); }); expect(mockReplace).toHaveBeenCalledWith(ROUTES.TABS.DASHBOARD); }); it('should navigate to login', () => { const { result } = renderHook(() => useNavigationFlow()); act(() => { result.current.goToLogin(); }); expect(mockReplace).toHaveBeenCalledWith(ROUTES.AUTH.LOGIN); }); it('should navigate to add beneficiary', () => { const { result } = renderHook(() => useNavigationFlow()); act(() => { result.current.goToAddBeneficiary(); }); expect(mockPush).toHaveBeenCalledWith(ROUTES.AUTH.ADD_LOVED_ONE); }); it('should navigate to profile', () => { const { result } = renderHook(() => useNavigationFlow()); act(() => { result.current.goToProfile(); }); expect(mockPush).toHaveBeenCalledWith(ROUTES.TABS.PROFILE); }); }); });