记录移动壳外链打开失败
移动 WebView 外链交给系统打开失败时记录错误 移动壳测试和配置检查拒绝静默吞掉外链失败 共享决策日志补充外链打开失败边界
This commit is contained in:
@@ -1380,12 +1380,19 @@ for (const snippet of [
|
||||
'handleBlockedFileDownload',
|
||||
'onFileDownload={handleBlockedFileDownload}',
|
||||
'setSupportMultipleWindows={false}',
|
||||
'logMobileShellNavigationFailure',
|
||||
'mobile shell navigation failed for',
|
||||
'external_navigation.open',
|
||||
]) {
|
||||
if (!shellAppSource.includes(snippet)) {
|
||||
throw new Error(`mobile shell ShellApp missing ${snippet}`);
|
||||
}
|
||||
}
|
||||
|
||||
if (shellAppSource.includes('catch(() => undefined)')) {
|
||||
throw new Error('mobile shell ShellApp must not hide async navigation failures');
|
||||
}
|
||||
|
||||
for (const snippet of [
|
||||
"describe('mobile shell safe area'",
|
||||
"test('protects the WebView from every device edge'",
|
||||
@@ -2230,6 +2237,8 @@ for (const snippet of [
|
||||
'shellHarness.networkListeners[0]?.({',
|
||||
"type: 'genarrative.mobile.historyState'",
|
||||
'mobile host event failed for network.statusChanged',
|
||||
'external WebView navigation open failure is logged instead of hidden',
|
||||
'mobile shell navigation failed for external_navigation.open',
|
||||
]) {
|
||||
if (!shellAppTestSource.includes(snippet)) {
|
||||
throw new Error(`mobile shell ShellApp HostBridge event test missing ${snippet}`);
|
||||
|
||||
@@ -449,4 +449,32 @@ describe('ShellApp HostBridge event injection', () => {
|
||||
|
||||
warnSpy.mockRestore();
|
||||
});
|
||||
|
||||
test('external WebView navigation open failure is logged instead of hidden', async () => {
|
||||
const ShellApp = await importShellApp();
|
||||
render(<ShellApp />);
|
||||
|
||||
const webViewProps = shellHarness.webViewProps.current as {
|
||||
onShouldStartLoadWithRequest?: (request: { url: string }) => boolean;
|
||||
};
|
||||
const navigationError = new Error('system browser unavailable');
|
||||
const warnSpy = vi.spyOn(console, 'warn').mockImplementation(() => undefined);
|
||||
const { Linking } = await import('react-native');
|
||||
vi.mocked(Linking.openURL).mockRejectedValueOnce(navigationError);
|
||||
|
||||
expect(
|
||||
webViewProps.onShouldStartLoadWithRequest?.({
|
||||
url: 'https://outside.example/work/1',
|
||||
}),
|
||||
).toBe(false);
|
||||
|
||||
await waitFor(() => {
|
||||
expect(warnSpy).toHaveBeenCalledWith(
|
||||
'mobile shell navigation failed for external_navigation.open',
|
||||
navigationError,
|
||||
);
|
||||
});
|
||||
|
||||
warnSpy.mockRestore();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -77,6 +77,10 @@ function logMobileHostEventFailure(label: HostBridgeEventName, error: unknown) {
|
||||
console.warn(`mobile host event failed for ${label}`, error);
|
||||
}
|
||||
|
||||
function logMobileShellNavigationFailure(label: string, error: unknown) {
|
||||
console.warn(`mobile shell navigation failed for ${label}`, error);
|
||||
}
|
||||
|
||||
type MobileWebViewLoadErrorEvent = {
|
||||
nativeEvent: {
|
||||
url: string;
|
||||
@@ -275,7 +279,9 @@ export default function ShellApp() {
|
||||
}
|
||||
|
||||
void openMobileShellExternalNavigation(Linking, request.url).catch(
|
||||
() => undefined,
|
||||
(error: unknown) => {
|
||||
logMobileShellNavigationFailure('external_navigation.open', error);
|
||||
},
|
||||
);
|
||||
return false;
|
||||
};
|
||||
|
||||
@@ -2944,6 +2944,13 @@
|
||||
- 影响范围:`apps/mobile-shell/src/shell/ShellApp.tsx`、`apps/mobile-shell/src/shell/ShellApp.test.tsx`、`apps/mobile-shell/scripts/check-config.mjs`、宿主壳能力统一协议文档。
|
||||
- 验证方式:`npm run mobile-shell:test -- src/shell/ShellApp.test.tsx`、`npm run mobile-shell:typecheck`、`npm run check:native-shells`、`npm run check:encoding`、`git diff --check`。
|
||||
|
||||
## 2026-06-20 移动壳 WebView 外链打开失败不可静默
|
||||
|
||||
- 背景:Expo WebView 外域导航会离开带 HostBridge 的主站容器并交给系统浏览器或系统应用;如果 `Linking.openURL(...)` reject 后静默吞掉,用户会看到点击外链无反应且开发侧无法区分协议、系统能力或原生模块失败。
|
||||
- 决策:`ShellApp` 的 WebView 外链分流必须继续复用 `openMobileShellExternalNavigation(Linking, request.url)`,但 Promise reject 路径必须调用 `logMobileShellNavigationFailure('external_navigation.open', error)` 记录错误;配置检查拒绝 `ShellApp` 重新出现 `catch(() => undefined)`,并反查外链失败日志测试。
|
||||
- 影响范围:`apps/mobile-shell/src/shell/ShellApp.tsx`、`apps/mobile-shell/src/shell/ShellApp.test.tsx`、`apps/mobile-shell/scripts/check-config.mjs`。
|
||||
- 验证方式:`npm run mobile-shell:test -- src/shell/ShellApp.test.tsx`、`npm run mobile-shell:typecheck`、`npm run check:native-shells`、`npm run check:encoding`、`git diff --check`。
|
||||
|
||||
## 2026-06-20 移动壳生命周期映射门禁
|
||||
|
||||
- 背景:Expo `AppState` 的 `active`、`background`、`inactive` 以及未知状态都会进入 `app.lifecycle` 事件;如果归一化映射漂移,H5 游戏循环、背景音乐和固定玩法音频会错误恢复或暂停。
|
||||
|
||||
Reference in New Issue
Block a user