补齐移动壳能力清单单测

移动壳 capabilities helper 增加独立单测覆盖

原生壳结构门禁登记移动能力清单测试

宿主壳文档和共享决策同步能力清单测试边界
This commit is contained in:
2026-06-20 08:38:40 +08:00
parent cb926835b3
commit e5970a4452
6 changed files with 80 additions and 2 deletions
@@ -27,6 +27,11 @@ const badgePath = new URL('../src/host-bridge/badge.ts', import.meta.url);
const badgeSource = fs.readFileSync(badgePath, 'utf8');
const badgeTestPath = new URL('../src/host-bridge/badge.test.ts', import.meta.url);
const badgeTestSource = fs.readFileSync(badgeTestPath, 'utf8');
const capabilitiesTestPath = new URL(
'../src/host-bridge/capabilities.test.ts',
import.meta.url,
);
const capabilitiesTestSource = fs.readFileSync(capabilitiesTestPath, 'utf8');
const clipboardPath = new URL('../src/host-bridge/clipboard.ts', import.meta.url);
const clipboardSource = fs.readFileSync(clipboardPath, 'utf8');
const dispatchPath = new URL('../src/host-bridge/dispatch.ts', import.meta.url);
@@ -2262,6 +2267,22 @@ if (!iosMobileCapabilitySet.has('app.setBadgeCount')) {
if (mobileCapabilitySet.has('app.setBadgeCount')) {
throw new Error('Android mobile shell base capabilities must not include app.setBadgeCount');
}
for (const snippet of [
'MOBILE_HOST_CAPABILITIES',
'IOS_MOBILE_HOST_CAPABILITIES',
'resolveMobileHostCapabilities',
'HOST_BRIDGE_EXPO_MOBILE_BASE_CAPABILITIES',
'HOST_BRIDGE_EXPO_MOBILE_IOS_CAPABILITIES',
"resolveMobileHostCapabilities('android')",
"resolveMobileHostCapabilities('ios')",
"not.toContain('app.setBadgeCount')",
"not.toContain('auth.requestLogin')",
"not.toContain('payment.request')",
]) {
if (!capabilitiesTestSource.includes(snippet)) {
throw new Error(`mobile shell capability profile test missing ${snippet}`);
}
}
if (
!dispatchSource.includes('setMobileAppBadgeCount(request)') ||
dispatchSource.includes('ok(request, setMobileAppBadgeCount') ||
@@ -0,0 +1,55 @@
import { describe, expect, test, vi } from 'vitest';
import {
HOST_BRIDGE_EXPO_MOBILE_BASE_CAPABILITIES,
HOST_BRIDGE_EXPO_MOBILE_IOS_CAPABILITIES,
} from '../../../../packages/shared/src/contracts/hostBridge';
import {
IOS_MOBILE_HOST_CAPABILITIES,
MOBILE_HOST_CAPABILITIES,
resolveMobileHostCapabilities,
} from './capabilities';
vi.mock('react-native', () => ({
Platform: {
OS: 'android',
},
}));
describe('mobile HostBridge capability profile', () => {
test('uses the shared Expo mobile capability profiles directly', () => {
expect(MOBILE_HOST_CAPABILITIES).toBe(
HOST_BRIDGE_EXPO_MOBILE_BASE_CAPABILITIES,
);
expect(IOS_MOBILE_HOST_CAPABILITIES).toBe(
HOST_BRIDGE_EXPO_MOBILE_IOS_CAPABILITIES,
);
});
test('keeps Android on the base profile without iOS-only badge support', () => {
expect(resolveMobileHostCapabilities('android')).toBe(
MOBILE_HOST_CAPABILITIES,
);
expect(resolveMobileHostCapabilities('android')).not.toContain('app.setBadgeCount');
});
test('adds only real iOS extra capabilities on iOS', () => {
expect(resolveMobileHostCapabilities('ios')).toBe(
IOS_MOBILE_HOST_CAPABILITIES,
);
expect(resolveMobileHostCapabilities('ios')).toEqual([
...MOBILE_HOST_CAPABILITIES,
'app.setBadgeCount',
]);
});
test('does not advertise SDK-backed capabilities before real native flows exist', () => {
for (const profile of [
MOBILE_HOST_CAPABILITIES,
IOS_MOBILE_HOST_CAPABILITIES,
]) {
expect(profile).not.toContain('auth.requestLogin');
expect(profile).not.toContain('payment.request');
}
});
});
@@ -48,6 +48,7 @@
- 2026-06-18 能力声明收紧:`packages/shared/src/contracts/hostBridge.ts` 提供 HostBridge method / capability 白名单,H5 的 `getHostRuntime()` 会解析并过滤 `hostCapabilities``openHostShare``writeHostClipboardText``requestHostHapticsImpact``setHostAppTitle``exportHostTextFile` 等 native 能力只在宿主声明对应 capability 后调用。发布分享弹窗只有声明 `share.open` 时才显示“系统分享”,避免旧壳或裁剪壳露出不可用入口。
- 2026-06-20 H5 原生能力门控收口:除 `host.getRuntime` 为了支持旧入口 URL 缺少 capability 时回读真实 runtime 可保留特殊判断外,H5 facade 中所有 native_app request 能力都必须通过 `canUseNativeHostCapability(...)` 统一门控,不得在业务能力函数内直接读取 `runtime.hostCapabilities.includes(...)`,避免各能力复制门控规则;根级 `npm run check:native-shells` 会从共享 `HOST_BRIDGE_METHODS` 自动派生需门控的 request capability 清单,新增 method 时必须同步补齐 H5 facade 门控。
- 2026-06-20 移动壳未声明 method 覆盖:Expo 移动壳对未进入 `HOST_BRIDGE_EXPO_MOBILE_IOS_CAPABILITIES` 的共享 request method 必须由测试从 `HOST_BRIDGE_METHODS` 自动派生覆盖,并在请求到达时返回明确 `unsupported_method`;平台差异能力如 Android 不声明的 `app.setBadgeCount` 保持独立 `unsupported_capability` 语义,不混入未声明 method 清单,移动壳配置检查必须反查 Android 角标请求失败测试仍存在。
- 2026-06-20 移动壳能力清单单测边界:`apps/mobile-shell/src/host-bridge/capabilities.test.ts` 直接覆盖 Expo 移动壳 `MOBILE_HOST_CAPABILITIES` / `IOS_MOBILE_HOST_CAPABILITIES` 必须引用共享 `HOST_BRIDGE_EXPO_MOBILE_*` profileAndroid 只使用 base profile 且不声明 `app.setBadgeCount`,iOS 只额外声明真实角标能力,并且两端在真实 SDK / 渠道流程落地前不得声明 `auth.requestLogin``payment.request`。根级 `npm run check:native-shells` 会把该测试文件列入移动桥接层结构清单,避免移动壳 capability profile 本地复制或伪声明。
- 2026-06-20 壳文档能力清单反查:`npm run check:native-shells` 会同时反查移动壳 / 桌面壳主状态段落能力清单和后续完整能力清单;主状态段落按集合检查,允许按叙述需要调整顺序,但不得漏写或多写 capability,完整能力清单继续按共享 profile 顺序检查。
- 2026-06-18 宿主 runtime 回读:主 App 启动时会通过真实 `host.getRuntime` 回读 Expo / Tauri runtime 并缓存过滤后的能力清单,能力来源为 URL `hostCapabilities` 与宿主真实回包的并集;裁剪壳或旧入口 URL 缺少 `hostCapabilities` 时也能启用真实声明能力,但仍不会仅凭 `native_app` 或 transport 存在推断能力可用。该回读请求的短超时由共享契约 `HOST_BRIDGE_RUNTIME_REFRESH_TIMEOUT_MS` 声明,H5 facade 不得本地重声明。
- 2026-06-18 壳能力防漂移:`npm run mobile-shell:typecheck``npm run desktop-shell:typecheck` 会校验 Expo / Tauri 壳声明的 capability 均来自共享 HostBridge 白名单,并校验壳 runtime 回包、H5 URL `hostCapabilities` 和实现分支保持一致;微信小程序 `WECHAT_HOST_CAPABILITIES``miniprogram/host-bridge/protocol.test.js` 和根级 `npm run check:native-shells` 反查共享 `HOST_BRIDGE_WECHAT_MINI_PROGRAM_CAPABILITIES`。新增能力必须先更新契约和真实壳实现,再通过这些检查。
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+1
View File
@@ -255,6 +255,7 @@ const expectedMobileHostBridgeFiles = [
'badge.ts',
'bridge.test.ts',
'bridge.ts',
'capabilities.test.ts',
'capabilities.ts',
'clipboard.test.ts',
'clipboard.ts',