新增 Expo 与 Tauri 原生宿主壳
新增 HostBridge 原生宿主契约和 H5 native_app transport 新增 Expo React Native 移动壳并收紧 WebView 外链边界 新增 Tauri 桌面壳并用 capability 收口受控命令 更新宿主壳方案、文档索引和共享记忆
This commit is contained in:
@@ -5,6 +5,7 @@ import { afterEach, describe, expect, test, vi } from 'vitest';
|
||||
import {
|
||||
canUseHostShareGrid,
|
||||
getHostRuntime,
|
||||
getNativeAppHostRuntime,
|
||||
isWechatMiniProgramWebViewRuntime,
|
||||
navigateHostNativePage,
|
||||
openHostShareGrid,
|
||||
@@ -17,11 +18,26 @@ import {
|
||||
resolveHostRuntime,
|
||||
setHostShareTarget,
|
||||
} from './hostBridge';
|
||||
import { resetNativeAppHostBridgeForTest } from './nativeAppHostBridge';
|
||||
|
||||
function asTauriInvoke(
|
||||
invoke: (command: string, args?: Record<string, unknown>) => Promise<unknown>,
|
||||
) {
|
||||
return async function tauriInvoke<Result = unknown>(
|
||||
command: string,
|
||||
args?: Record<string, unknown>,
|
||||
) {
|
||||
return (await invoke(command, args)) as Result;
|
||||
};
|
||||
}
|
||||
|
||||
afterEach(() => {
|
||||
vi.restoreAllMocks();
|
||||
window.history.replaceState(null, '', '/');
|
||||
window.wx = undefined;
|
||||
delete window.ReactNativeWebView;
|
||||
delete window.__TAURI__;
|
||||
resetNativeAppHostBridgeForTest();
|
||||
});
|
||||
|
||||
describe('hostBridge', () => {
|
||||
@@ -43,7 +59,26 @@ describe('hostBridge', () => {
|
||||
|
||||
expect(
|
||||
resolveHostRuntime({
|
||||
location: { search: '?clientRuntime=native_app' },
|
||||
location: {
|
||||
search:
|
||||
'?clientRuntime=native_app&hostShell=expo_mobile&hostPlatform=ios&hostVersion=0.1.0',
|
||||
},
|
||||
}),
|
||||
).toMatchObject({
|
||||
kind: 'native_app',
|
||||
hostShell: 'expo_mobile',
|
||||
hostPlatform: 'ios',
|
||||
hostVersion: '0.1.0',
|
||||
});
|
||||
|
||||
expect(
|
||||
resolveHostRuntime({
|
||||
tauri: {
|
||||
core: {
|
||||
invoke: asTauriInvoke(vi.fn(async () => null)),
|
||||
},
|
||||
},
|
||||
location: { search: '' },
|
||||
}).kind,
|
||||
).toBe('native_app');
|
||||
|
||||
@@ -227,4 +262,100 @@ describe('hostBridge', () => {
|
||||
expect(canUseHostShareGrid()).toBe(false);
|
||||
expect(setHostShareTarget({ type: 'test' })).toBe(false);
|
||||
});
|
||||
|
||||
test('原生 App 宿主通过 HostBridge 处理导航、登录和支付', async () => {
|
||||
const invoke = vi.fn(async (_command: string, args?: Record<string, unknown>) => {
|
||||
const request = (args as { request: { id: string; method: string } })
|
||||
.request;
|
||||
return {
|
||||
bridge: 'GenarrativeHostBridge',
|
||||
version: 1,
|
||||
id: request.id,
|
||||
ok: true,
|
||||
result: request.method === 'host.getRuntime'
|
||||
? {
|
||||
shell: 'tauri_desktop',
|
||||
platform: 'linux',
|
||||
hostVersion: '0.1.0',
|
||||
bridgeVersion: 1,
|
||||
capabilities: ['host.getRuntime'],
|
||||
}
|
||||
: true,
|
||||
};
|
||||
});
|
||||
window.history.replaceState(
|
||||
null,
|
||||
'',
|
||||
'/?clientRuntime=native_app&hostShell=tauri_desktop',
|
||||
);
|
||||
window.__TAURI__ = {
|
||||
core: {
|
||||
invoke: asTauriInvoke(invoke),
|
||||
},
|
||||
};
|
||||
|
||||
await expect(navigateHostNativePage('/settings')).resolves.toBe(true);
|
||||
await expect(requestHostLogin()).resolves.toBe(true);
|
||||
await expect(
|
||||
requestHostPayment({
|
||||
payload: null,
|
||||
orderId: 'order-1',
|
||||
}),
|
||||
).resolves.toBe(true);
|
||||
await expect(getNativeAppHostRuntime()).resolves.toMatchObject({
|
||||
shell: 'tauri_desktop',
|
||||
platform: 'linux',
|
||||
});
|
||||
|
||||
expect(invoke).toHaveBeenCalledWith('host_bridge_request', {
|
||||
request: expect.objectContaining({
|
||||
method: 'navigation.openNativePage',
|
||||
payload: { url: '/settings' },
|
||||
}),
|
||||
});
|
||||
expect(invoke).toHaveBeenCalledWith('host_bridge_request', {
|
||||
request: expect.objectContaining({
|
||||
method: 'auth.requestLogin',
|
||||
}),
|
||||
});
|
||||
expect(invoke).toHaveBeenCalledWith('host_bridge_request', {
|
||||
request: expect.objectContaining({
|
||||
method: 'payment.request',
|
||||
payload: {
|
||||
payload: null,
|
||||
orderId: 'order-1',
|
||||
},
|
||||
}),
|
||||
});
|
||||
});
|
||||
|
||||
test('原生 App 宿主不支持能力时回退到 H5 路径', async () => {
|
||||
window.history.replaceState(null, '', '/?clientRuntime=native_app');
|
||||
window.__TAURI__ = {
|
||||
core: {
|
||||
invoke: asTauriInvoke(vi.fn(async (_command: string, args?: Record<string, unknown>) => {
|
||||
const request = (args as { request: { id: string } }).request;
|
||||
return {
|
||||
bridge: 'GenarrativeHostBridge',
|
||||
version: 1,
|
||||
id: request.id,
|
||||
ok: false,
|
||||
error: {
|
||||
code: 'unsupported_method',
|
||||
message: 'unsupported_method',
|
||||
},
|
||||
};
|
||||
})),
|
||||
},
|
||||
};
|
||||
|
||||
await expect(navigateHostNativePage('/settings')).resolves.toBe(false);
|
||||
await expect(requestHostLogin()).resolves.toBe(false);
|
||||
await expect(
|
||||
requestHostPayment({
|
||||
payload: null,
|
||||
orderId: 'order-1',
|
||||
}),
|
||||
).resolves.toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user