补齐移动壳深链失败观测

移动壳 Deep Link 解析返回状态并记录拒绝路径

ShellApp 记录初始链接读取失败且保留安全入口

移动壳配置检查和文档同步深链失败边界
This commit is contained in:
2026-06-20 18:57:13 +08:00
parent 26f95cf398
commit fc9a557978
7 changed files with 166 additions and 17 deletions
+61 -1
View File
@@ -17,6 +17,7 @@ const shellHarness = vi.hoisted(() => {
const cameraViewProps = { current: null as Record<string, unknown> | null };
const injectJavaScriptError = { current: null as Error | null };
const injectedScripts = [] as string[];
const linkingUrlListeners = [] as Array<(event: { url: string }) => void>;
const networkListeners = [] as Array<(state: Record<string, unknown>) => void>;
return {
@@ -31,8 +32,10 @@ const shellHarness = vi.hoisted(() => {
cameraViewProps.current = null;
injectJavaScriptError.current = null;
injectedScripts.length = 0;
linkingUrlListeners.length = 0;
networkListeners.length = 0;
},
linkingUrlListeners,
webViewProps,
};
});
@@ -184,7 +187,10 @@ vi.mock('react-native', () => ({
addEventListener: vi.fn(() => ({ remove: vi.fn() })),
},
Linking: {
addEventListener: vi.fn(() => ({ remove: vi.fn() })),
addEventListener: vi.fn((_event, listener) => {
shellHarness.linkingUrlListeners.push(listener);
return { remove: vi.fn() };
}),
canOpenURL: vi.fn(async () => true),
getInitialURL: vi.fn(async () => null),
openURL: vi.fn(async () => undefined),
@@ -613,4 +619,58 @@ describe('ShellApp HostBridge event injection', () => {
warnSpy.mockRestore();
});
test('initial deep link read failures are logged without replacing the current WebView URL', async () => {
const { Linking } = await import('react-native');
const initialUrlError = new Error('initial URL unavailable');
vi.mocked(Linking.getInitialURL).mockRejectedValueOnce(initialUrlError);
const warnSpy = vi.spyOn(console, 'warn').mockImplementation(() => undefined);
const ShellApp = await importShellApp();
render(<ShellApp />);
const webViewProps = shellHarness.webViewProps.current as {
source?: { uri?: string };
};
const initialWebUrl = webViewProps.source?.uri;
await waitFor(() => {
expect(warnSpy).toHaveBeenCalledWith(
'mobile shell deep link failed for initial_url.read',
initialUrlError,
);
});
expect(shellHarness.webViewProps.current?.source).toEqual({
uri: initialWebUrl,
});
warnSpy.mockRestore();
});
test('runtime deep link rejections are logged and fall back to a safe WebView URL', async () => {
const warnSpy = vi.spyOn(console, 'warn').mockImplementation(() => undefined);
const ShellApp = await importShellApp();
render(<ShellApp />);
const webViewProps = shellHarness.webViewProps.current as {
source?: { uri?: string };
};
const initialWebUrl = webViewProps.source?.uri;
shellHarness.linkingUrlListeners[0]?.({
url: 'https://outside.example/works/detail?work=PZ-1',
});
await waitFor(() => {
expect(warnSpy).toHaveBeenCalledWith(
'mobile shell deep link failed for runtime_url.rejected',
'https://outside.example/works/detail?work=PZ-1',
);
});
expect(shellHarness.webViewProps.current?.source).toEqual({
uri: initialWebUrl,
});
warnSpy.mockRestore();
});
});