收紧移动壳能力声明与迟到响应边界
移动壳卸载后丢弃迟到 HostBridge 响应 移动壳 ShellApp 测试覆盖卸载后异步响应不注入 移动壳配置检查拒绝只返回 unsupported 的声明能力 共享决策记录补充移动壳消息注入和能力声明约定
This commit is contained in:
@@ -368,6 +368,60 @@ describe('ShellApp QR scanner HostBridge flow', () => {
|
||||
|
||||
warnSpy.mockRestore();
|
||||
});
|
||||
|
||||
test('drops delayed HostBridge responses after shell unmount', async () => {
|
||||
const ShellApp = await importShellApp();
|
||||
let resolveNetworkStatus:
|
||||
(state: Awaited<ReturnType<typeof Network.getNetworkStateAsync>>) => void =
|
||||
() => {
|
||||
throw new Error('network status resolver missing');
|
||||
};
|
||||
vi.mocked(Network.getNetworkStateAsync).mockReturnValueOnce(
|
||||
new Promise((resolve) => {
|
||||
resolveNetworkStatus = resolve;
|
||||
}),
|
||||
);
|
||||
const { unmount } = render(<ShellApp />);
|
||||
|
||||
const webViewProps = shellHarness.webViewProps.current as {
|
||||
onMessage?: (event: {
|
||||
nativeEvent: {
|
||||
data: string;
|
||||
url: string;
|
||||
};
|
||||
}) => void;
|
||||
source?: { uri?: string };
|
||||
};
|
||||
const webViewUrl = webViewProps.source?.uri ?? 'https://app.genarrative.world/';
|
||||
|
||||
webViewProps.onMessage?.({
|
||||
nativeEvent: {
|
||||
data: JSON.stringify({
|
||||
bridge: HOST_BRIDGE_PROTOCOL,
|
||||
version: HOST_BRIDGE_VERSION,
|
||||
id: 'network-request-1',
|
||||
method: 'network.status',
|
||||
}),
|
||||
url: webViewUrl,
|
||||
},
|
||||
});
|
||||
unmount();
|
||||
|
||||
resolveNetworkStatus({
|
||||
isConnected: true,
|
||||
isInternetReachable: true,
|
||||
type: Network.NetworkStateType.WIFI,
|
||||
});
|
||||
|
||||
await waitFor(() => {
|
||||
expect(Network.getNetworkStateAsync).toHaveBeenCalled();
|
||||
});
|
||||
expect(
|
||||
shellHarness.injectedScripts.some((script) =>
|
||||
script.includes('network-request-1'),
|
||||
),
|
||||
).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('ShellApp HostBridge event injection', () => {
|
||||
|
||||
@@ -89,6 +89,7 @@ type MobileWebViewHttpErrorEvent = {
|
||||
|
||||
export default function ShellApp() {
|
||||
const webViewRef = useRef<WebView>(null);
|
||||
const isShellMountedRef = useRef(true);
|
||||
const nativeCanGoBackRef = useRef(false);
|
||||
const h5CanGoBackRef = useRef(false);
|
||||
const [canGoBack, setCanGoBack] = useState(false);
|
||||
@@ -112,11 +113,22 @@ export default function ShellApp() {
|
||||
const reloadCurrentWebView = useCallback(() => {
|
||||
webViewRef.current?.reload();
|
||||
}, []);
|
||||
useEffect(() => {
|
||||
isShellMountedRef.current = true;
|
||||
|
||||
return () => {
|
||||
isShellMountedRef.current = false;
|
||||
};
|
||||
}, []);
|
||||
const injectHostBridgeMessage = useCallback(
|
||||
(
|
||||
message: unknown,
|
||||
onError: (error: unknown) => void,
|
||||
) => {
|
||||
if (!isShellMountedRef.current) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
webViewRef.current?.injectJavaScript(
|
||||
buildHostBridgeMessageScript(message),
|
||||
|
||||
Reference in New Issue
Block a user