/* @vitest-environment jsdom */ import { act, renderHook } from '@testing-library/react'; import { afterEach, expect, test, vi } from 'vitest'; import { canUseNativeHostCapability, subscribeHostNavigationCanGoBack, subscribeHostRuntimeChange, } from '../services/host-bridge/hostBridge'; import { useHostNavigationCanGoBack } from './useHostNavigationCanGoBack'; vi.mock('../services/host-bridge/hostBridge', () => ({ canUseNativeHostCapability: vi.fn(() => false), subscribeHostNavigationCanGoBack: vi.fn(() => () => undefined), subscribeHostRuntimeChange: vi.fn(() => () => undefined), })); const canUseNativeHostCapabilityMock = vi.mocked(canUseNativeHostCapability); const subscribeHostNavigationCanGoBackMock = vi.mocked( subscribeHostNavigationCanGoBack, ); const subscribeHostRuntimeChangeMock = vi.mocked(subscribeHostRuntimeChange); function setNavigationCapability(enabled: boolean) { canUseNativeHostCapabilityMock.mockImplementation((capability) => enabled ? capability === 'host.events' || capability === 'navigation.canGoBack' : false, ); } afterEach(() => { vi.clearAllMocks(); }); test('host navigation can-go-back stays unsupported without both event capabilities', () => { setNavigationCapability(false); const { result } = renderHook(() => useHostNavigationCanGoBack()); expect(result.current).toEqual({ canGoBack: false, isSupported: false, }); expect(subscribeHostNavigationCanGoBackMock).not.toHaveBeenCalled(); }); test('host navigation can-go-back follows native app events', () => { setNavigationCapability(true); const unsubscribe = vi.fn(); let navigationListener: | Parameters[0] | null = null; subscribeHostNavigationCanGoBackMock.mockImplementation((listener) => { navigationListener = listener; return unsubscribe; }); const { result, unmount } = renderHook(() => useHostNavigationCanGoBack()); expect(result.current).toEqual({ canGoBack: false, isSupported: true, }); act(() => { navigationListener?.({ canGoBack: true }); }); expect(result.current).toEqual({ canGoBack: true, isSupported: true, }); act(() => { navigationListener?.({ canGoBack: false }); }); expect(result.current).toEqual({ canGoBack: false, isSupported: true, }); unmount(); expect(unsubscribe).toHaveBeenCalledTimes(1); }); test('host navigation can-go-back subscribes after runtime refresh exposes capabilities', () => { setNavigationCapability(false); let runtimeListener: Parameters[0] | null = null; let navigationListener: | Parameters[0] | null = null; subscribeHostRuntimeChangeMock.mockImplementation((listener) => { runtimeListener = listener; return () => undefined; }); subscribeHostNavigationCanGoBackMock.mockImplementation((listener) => { navigationListener = listener; return () => undefined; }); const { result } = renderHook(() => useHostNavigationCanGoBack()); expect(result.current.isSupported).toBe(false); setNavigationCapability(true); act(() => { runtimeListener?.(); }); expect(result.current.isSupported).toBe(true); expect(subscribeHostNavigationCanGoBackMock).toHaveBeenCalledTimes(1); act(() => { navigationListener?.({ canGoBack: true }); }); expect(result.current.canGoBack).toBe(true); });