Files
Genarrative/src/hooks/useHostNavigationCanGoBack.test.tsx
T
kdletters 071faa482c 统一 Rust 与 TypeScript 格式化门禁
纳入 AGC Cargo workspace 的统一 rustfmt 检查与格式化入口

完成项目 TypeScript/Prettier 与 Rust 全量格式化

修复 Pingora expected executable 门禁的空白敏感误报

同步开发运维文档与 AGC skill pack 格式化忽略规则
2026-09-01 16:28:34 +08:00

120 lines
3.4 KiB
TypeScript

/* @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<typeof subscribeHostNavigationCanGoBack>[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<typeof subscribeHostRuntimeChange>[0] | null =
null;
let navigationListener:
| Parameters<typeof subscribeHostNavigationCanGoBack>[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);
});