按宿主能力声明启用原生能力

HostBridge 契约沉淀 method 与 capability 白名单

H5 解析 hostCapabilities 并按能力调用原生桥

发布分享弹窗仅在声明 share.open 时显示系统分享

补充能力声明测试和宿主壳文档
This commit is contained in:
2026-06-18 00:48:13 +08:00
parent ee49c26868
commit 38ed2227d3
13 changed files with 194 additions and 35 deletions

View File

@@ -2,8 +2,10 @@
import { afterEach, describe, expect, test, vi } from 'vitest';
import type { HostBridgeCapability } from '../../../packages/shared/src/contracts/hostBridge';
import {
canUseHostShareGrid,
canUseNativeHostCapability,
exportHostTextFile,
getHostRuntime,
getNativeAppHostRuntime,
@@ -37,6 +39,17 @@ function asTauriInvoke(
};
}
function nativeAppPath(capabilities: HostBridgeCapability[] = []) {
const params = new URLSearchParams({
clientRuntime: 'native_app',
hostShell: 'tauri_desktop',
});
if (capabilities.length > 0) {
params.set('hostCapabilities', capabilities.join(','));
}
return `/?${params.toString()}`;
}
afterEach(() => {
vi.restoreAllMocks();
window.history.replaceState(null, '', '/');
@@ -67,7 +80,7 @@ describe('hostBridge', () => {
resolveHostRuntime({
location: {
search:
'?clientRuntime=native_app&hostShell=expo_mobile&hostPlatform=ios&hostVersion=0.1.0',
'?clientRuntime=native_app&hostShell=expo_mobile&hostPlatform=ios&hostVersion=0.1.0&hostCapabilities=share.open,unknown,clipboard.writeText',
},
}),
).toMatchObject({
@@ -75,6 +88,7 @@ describe('hostBridge', () => {
hostShell: 'expo_mobile',
hostPlatform: 'ios',
hostVersion: '0.1.0',
hostCapabilities: ['share.open', 'clipboard.writeText'],
});
expect(
@@ -93,6 +107,32 @@ describe('hostBridge', () => {
);
});
test('按宿主能力声明判断原生 App 能力是否可用', () => {
expect(
canUseNativeHostCapability('share.open', {
location: {
search:
'?clientRuntime=native_app&hostCapabilities=share.open,app.openExternalUrl',
},
}),
).toBe(true);
expect(
canUseNativeHostCapability('clipboard.writeText', {
location: {
search:
'?clientRuntime=native_app&hostCapabilities=share.open,app.openExternalUrl',
},
}),
).toBe(false);
expect(
canUseNativeHostCapability('share.open', {
location: {
search: '?clientRuntime=browser&hostCapabilities=share.open',
},
}),
).toBe(false);
});
test('通过微信小程序原生页请求登录', async () => {
const navigateTo = vi.fn((options) => {
options.success?.();
@@ -292,7 +332,17 @@ describe('hostBridge', () => {
window.history.replaceState(
null,
'',
'/?clientRuntime=native_app&hostShell=tauri_desktop',
nativeAppPath([
'host.getRuntime',
'navigation.openNativePage',
'auth.requestLogin',
'payment.request',
'clipboard.writeText',
'haptics.impact',
'app.openExternalUrl',
'app.setTitle',
'share.open',
]),
);
window.__TAURI__ = {
core: {
@@ -397,7 +447,7 @@ describe('hostBridge', () => {
});
test('原生 App 宿主不支持能力时回退到 H5 路径', async () => {
window.history.replaceState(null, '', '/?clientRuntime=native_app');
window.history.replaceState(null, '', nativeAppPath());
window.__TAURI__ = {
core: {
invoke: asTauriInvoke(vi.fn(async (_command: string, args?: Record<string, unknown>) => {
@@ -476,7 +526,11 @@ describe('hostBridge', () => {
};
},
);
window.history.replaceState(null, '', '/?clientRuntime=native_app');
window.history.replaceState(
null,
'',
nativeAppPath(['file.exportText']),
);
window.__TAURI__ = {
core: {
invoke: asTauriInvoke(invoke),
@@ -507,7 +561,11 @@ describe('hostBridge', () => {
});
test('原生 App 宿主不支持文本导出时回退 H5', async () => {
window.history.replaceState(null, '', '/?clientRuntime=native_app');
window.history.replaceState(
null,
'',
nativeAppPath(['file.exportText']),
);
window.__TAURI__ = {
core: {
invoke: asTauriInvoke(