补强移动壳能力流程门禁
根级原生壳检查反查 Expo 关键能力真实流程 宿主壳方案文档记录移动能力到实现与测试合同 项目记忆同步原生壳能力真实链路约束
This commit is contained in:
@@ -239,6 +239,170 @@ const wechatCapabilityFlowContracts = [
|
||||
],
|
||||
},
|
||||
];
|
||||
const mobileCapabilityFlowContracts = [
|
||||
{
|
||||
capability: 'notification.showLocal',
|
||||
files: [
|
||||
'apps/mobile-shell/app.json',
|
||||
'apps/mobile-shell/src/host-bridge/dispatch.ts',
|
||||
'apps/mobile-shell/src/host-bridge/notifications.ts',
|
||||
'apps/mobile-shell/scripts/check-config.mjs',
|
||||
'apps/mobile-shell/scripts/check-expo-config.mjs',
|
||||
nativeShellPlanPath,
|
||||
],
|
||||
snippets: [
|
||||
['apps/mobile-shell/app.json', 'android.permission.POST_NOTIFICATIONS'],
|
||||
[
|
||||
'apps/mobile-shell/src/host-bridge/dispatch.ts',
|
||||
'showMobileHostBridgeLocalNotification(request)',
|
||||
],
|
||||
[
|
||||
'apps/mobile-shell/src/host-bridge/notifications.ts',
|
||||
'Notifications.scheduleNotificationAsync',
|
||||
],
|
||||
[
|
||||
'apps/mobile-shell/src/host-bridge/notifications.ts',
|
||||
'Notifications.setNotificationChannelAsync',
|
||||
],
|
||||
[
|
||||
'apps/mobile-shell/src/host-bridge/notifications.ts',
|
||||
'HOST_BRIDGE_MOBILE_LOCAL_NOTIFICATION_CHANNEL_ID',
|
||||
],
|
||||
[
|
||||
'apps/mobile-shell/scripts/check-config.mjs',
|
||||
'HOST_BRIDGE_MOBILE_LOCAL_NOTIFICATION_CHANNEL_ID',
|
||||
],
|
||||
[
|
||||
'apps/mobile-shell/scripts/check-expo-config.mjs',
|
||||
'android.permission.POST_NOTIFICATIONS',
|
||||
],
|
||||
[nativeShellPlanPath, 'Expo 移动壳的关键能力'],
|
||||
],
|
||||
tests: [
|
||||
'apps/mobile-shell/src/host-bridge/notifications.test.ts',
|
||||
'apps/mobile-shell/src/host-bridge/bridge.test.ts',
|
||||
],
|
||||
},
|
||||
{
|
||||
capability: 'file.captureImage',
|
||||
files: [
|
||||
'apps/mobile-shell/app.json',
|
||||
'apps/mobile-shell/src/host-bridge/dispatch.ts',
|
||||
'apps/mobile-shell/src/host-bridge/files.ts',
|
||||
'apps/mobile-shell/scripts/check-config.mjs',
|
||||
nativeShellPlanPath,
|
||||
],
|
||||
snippets: [
|
||||
['apps/mobile-shell/app.json', 'expo-image-picker'],
|
||||
[
|
||||
'apps/mobile-shell/src/host-bridge/dispatch.ts',
|
||||
'captureMobileHostBridgeImageFile(request)',
|
||||
],
|
||||
[
|
||||
'apps/mobile-shell/src/host-bridge/files.ts',
|
||||
'ImagePicker.requestCameraPermissionsAsync',
|
||||
],
|
||||
[
|
||||
'apps/mobile-shell/src/host-bridge/files.ts',
|
||||
'ImagePicker.launchCameraAsync',
|
||||
],
|
||||
['apps/mobile-shell/scripts/check-config.mjs', 'file.captureImage'],
|
||||
[nativeShellPlanPath, 'Expo 移动壳的关键能力'],
|
||||
],
|
||||
tests: [
|
||||
'apps/mobile-shell/src/host-bridge/files.test.ts',
|
||||
'apps/mobile-shell/src/host-bridge/bridge.test.ts',
|
||||
],
|
||||
},
|
||||
{
|
||||
capability: 'scanner.scanQrCode',
|
||||
files: [
|
||||
'apps/mobile-shell/app.json',
|
||||
'apps/mobile-shell/src/host-bridge/dispatch.ts',
|
||||
'apps/mobile-shell/src/host-bridge/scanner.ts',
|
||||
'apps/mobile-shell/src/shell/QrScannerOverlay.tsx',
|
||||
'apps/mobile-shell/scripts/check-config.mjs',
|
||||
nativeShellPlanPath,
|
||||
],
|
||||
snippets: [
|
||||
['apps/mobile-shell/app.json', 'expo-camera'],
|
||||
[
|
||||
'apps/mobile-shell/src/host-bridge/dispatch.ts',
|
||||
'scanMobileHostBridgeQrCode(request)',
|
||||
],
|
||||
[
|
||||
'apps/mobile-shell/src/host-bridge/scanner.ts',
|
||||
'HOST_BRIDGE_SCANNER_TIMEOUT_MS',
|
||||
],
|
||||
[
|
||||
'apps/mobile-shell/src/shell/QrScannerOverlay.tsx',
|
||||
'Camera.requestCameraPermissionsAsync',
|
||||
],
|
||||
['apps/mobile-shell/src/shell/QrScannerOverlay.tsx', 'CameraView'],
|
||||
['apps/mobile-shell/scripts/check-config.mjs', 'scanner.scanQrCode'],
|
||||
[nativeShellPlanPath, 'Expo 移动壳的关键能力'],
|
||||
],
|
||||
tests: [
|
||||
'apps/mobile-shell/src/host-bridge/scanner.test.ts',
|
||||
'apps/mobile-shell/src/shell/QrScannerOverlay.test.tsx',
|
||||
'apps/mobile-shell/src/shell/ShellApp.test.tsx',
|
||||
'apps/mobile-shell/src/host-bridge/bridge.test.ts',
|
||||
],
|
||||
},
|
||||
{
|
||||
capability: 'network.statusChanged',
|
||||
files: [
|
||||
'apps/mobile-shell/src/shell/network.ts',
|
||||
'apps/mobile-shell/src/shell/ShellApp.tsx',
|
||||
'apps/mobile-shell/scripts/check-config.mjs',
|
||||
nativeShellPlanPath,
|
||||
],
|
||||
snippets: [
|
||||
[
|
||||
'apps/mobile-shell/src/shell/network.ts',
|
||||
'Network.addNetworkStateListener',
|
||||
],
|
||||
[
|
||||
'apps/mobile-shell/src/shell/ShellApp.tsx',
|
||||
"injectHostBridgeEvent('network.statusChanged', payload)",
|
||||
],
|
||||
[
|
||||
'apps/mobile-shell/src/shell/ShellApp.tsx',
|
||||
"logMobileHostEventFailure('network.statusChanged', error)",
|
||||
],
|
||||
['apps/mobile-shell/scripts/check-config.mjs', 'network.statusChanged'],
|
||||
[nativeShellPlanPath, 'Expo 移动壳的关键能力'],
|
||||
],
|
||||
tests: [
|
||||
'apps/mobile-shell/src/shell/network.test.ts',
|
||||
'apps/mobile-shell/src/shell/ShellApp.test.tsx',
|
||||
'apps/mobile-shell/src/host-bridge/bridge.test.ts',
|
||||
],
|
||||
},
|
||||
{
|
||||
capability: 'share.open',
|
||||
files: [
|
||||
'apps/mobile-shell/src/host-bridge/dispatch.ts',
|
||||
'apps/mobile-shell/src/host-bridge/share.ts',
|
||||
'apps/mobile-shell/scripts/check-config.mjs',
|
||||
nativeShellPlanPath,
|
||||
],
|
||||
snippets: [
|
||||
['apps/mobile-shell/src/host-bridge/dispatch.ts', 'openShare(request)'],
|
||||
['apps/mobile-shell/src/host-bridge/share.ts', 'Share.share'],
|
||||
[
|
||||
'apps/mobile-shell/src/host-bridge/share.ts',
|
||||
'normalizeHostBridgeShareOpenPayload',
|
||||
],
|
||||
['apps/mobile-shell/scripts/check-config.mjs', 'share.open'],
|
||||
[nativeShellPlanPath, 'Expo 移动壳的关键能力'],
|
||||
],
|
||||
tests: [
|
||||
'apps/mobile-shell/src/host-bridge/share.test.ts',
|
||||
'apps/mobile-shell/src/host-bridge/bridge.test.ts',
|
||||
],
|
||||
},
|
||||
];
|
||||
const h5NativeAppRouteFlowContracts = [
|
||||
{
|
||||
route: '/child-motion-demo',
|
||||
@@ -2043,6 +2207,66 @@ function assertWechatMiniProgramCapabilityFlows() {
|
||||
}
|
||||
}
|
||||
|
||||
function assertExpoMobileCapabilityFlows() {
|
||||
const sharedContractSource = fs.readFileSync(sharedHostBridgeContractPath, 'utf8');
|
||||
const declaredCapabilities = extractTsStringArray(
|
||||
sharedContractSource,
|
||||
'HOST_BRIDGE_EXPO_MOBILE_BASE_CAPABILITIES',
|
||||
);
|
||||
const declaredCapabilitySet = new Set(declaredCapabilities);
|
||||
const contractedCapabilities = mobileCapabilityFlowContracts.map(
|
||||
({ capability }) => capability,
|
||||
);
|
||||
|
||||
assertUniqueList(
|
||||
contractedCapabilities,
|
||||
'Expo mobile capability flow contracts',
|
||||
);
|
||||
|
||||
const mobileShellTestSet = new Set(
|
||||
expectedMobileHostBridgeFiles
|
||||
.filter((fileName) => fileName.includes('.test.'))
|
||||
.map((fileName) => `apps/mobile-shell/src/host-bridge/${fileName}`)
|
||||
.concat(
|
||||
expectedMobileShellFiles
|
||||
.filter((fileName) => fileName.includes('.test.'))
|
||||
.map((fileName) => `apps/mobile-shell/src/shell/${fileName}`),
|
||||
),
|
||||
);
|
||||
|
||||
for (const contract of mobileCapabilityFlowContracts) {
|
||||
if (!declaredCapabilitySet.has(contract.capability)) {
|
||||
throw new Error(
|
||||
`Expo mobile capability flow contract declared for missing capability: ${contract.capability}`,
|
||||
);
|
||||
}
|
||||
|
||||
for (const filePath of contract.files) {
|
||||
if (!fs.existsSync(filePath)) {
|
||||
throw new Error(
|
||||
`Expo mobile ${contract.capability} flow file is missing: ${filePath}`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
for (const [filePath, snippet] of contract.snippets) {
|
||||
assertFileIncludesSnippet(
|
||||
filePath,
|
||||
snippet,
|
||||
`Expo mobile ${contract.capability} flow`,
|
||||
);
|
||||
}
|
||||
|
||||
for (const testPath of contract.tests) {
|
||||
if (!mobileShellTestSet.has(testPath)) {
|
||||
throw new Error(
|
||||
`Expo mobile ${contract.capability} flow test is not part of mobile-shell:test: ${testPath}`,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function assertH5NativeAppRouteFlows() {
|
||||
const h5HostBridgeTestSet = new Set(h5HostBridgeTests);
|
||||
for (const contract of h5NativeAppRouteFlowContracts) {
|
||||
@@ -2537,6 +2761,9 @@ assertWechatMiniProgramRouteParity();
|
||||
console.log('[check:native-shells] wechat-mini-program-capability-flows');
|
||||
assertWechatMiniProgramCapabilityFlows();
|
||||
|
||||
console.log('[check:native-shells] expo-mobile-capability-flows');
|
||||
assertExpoMobileCapabilityFlows();
|
||||
|
||||
console.log('[check:native-shells] h5-native-app-route-flows');
|
||||
assertH5NativeAppRouteFlows();
|
||||
|
||||
|
||||
Reference in New Issue
Block a user