记录移动扫码权限请求失败

移动扫码权限请求异常时记录原始错误

移动扫码 overlay 测试覆盖权限请求失败路径

移动壳配置检查反查扫码权限失败日志边界

共享决策日志补充扫码权限失败边界
This commit is contained in:
2026-06-20 09:56:57 +08:00
parent 328bf920bd
commit 53312d1b49
4 changed files with 37 additions and 1 deletions
@@ -2180,6 +2180,8 @@ for (const snippet of [
'failQrCodeScan',
'cancelQrCodeScan',
'subscribeQrScannerState',
'logQrScannerPermissionFailure(error)',
"console.warn('mobile QR scanner permission request failed', error)",
]) {
if (!qrScannerOverlaySource.includes(snippet)) {
throw new Error(`mobile shell QR scanner overlay missing ${snippet}`);
@@ -2191,7 +2193,10 @@ for (const snippet of [
'scanQrCode()',
'requestCameraPermissionsAsync',
"message: 'camera permission denied'",
"message: 'camera permission unavailable'",
"message: 'qr scan cancelled'",
"test('logs and rejects the active scan when camera permission request fails'",
"'mobile QR scanner permission request failed'",
"barcodeTypes: ['qr']",
"type: 'qr'",
"value: 'https://app.genarrative.world/works/detail?work=PZ-1'",
@@ -118,6 +118,25 @@ describe('QrScannerOverlay', () => {
expect(overlayHarness.cameraViewProps.current).toBeNull();
});
test('logs and rejects the active scan when camera permission request fails', async () => {
const warnSpy = vi.spyOn(console, 'warn').mockImplementation(() => undefined);
const error = new Error('camera unavailable');
cameraPermissionMock.mockRejectedValue(error);
render(<QrScannerOverlay />);
const scanPromise = scanQrCode();
await expect(scanPromise).rejects.toMatchObject({
code: 'host_error',
message: 'camera permission unavailable',
});
expect(warnSpy).toHaveBeenCalledWith(
'mobile QR scanner permission request failed',
error,
);
expect(overlayHarness.cameraViewProps.current).toBeNull();
});
test('close button cancels the pending scan', async () => {
cameraPermissionMock.mockResolvedValue({
granted: true,
@@ -13,6 +13,10 @@ import {
subscribeQrScannerState,
} from '../host-bridge/scanner';
function logQrScannerPermissionFailure(error: unknown) {
console.warn('mobile QR scanner permission request failed', error);
}
export function QrScannerOverlay() {
const [isActive, setIsActive] = useState(false);
const [requestKey, setRequestKey] = useState(0);
@@ -45,8 +49,9 @@ export function QrScannerOverlay() {
}
setHasPermission(true);
})
.catch(() => {
.catch((error: unknown) => {
if (!disposed) {
logQrScannerPermissionFailure(error);
failQrCodeScan('camera permission unavailable');
}
});
@@ -2951,6 +2951,13 @@
- 影响范围:`apps/mobile-shell/src/shell/lifecycle.ts``apps/mobile-shell/src/shell/lifecycle.test.ts``apps/mobile-shell/scripts/check-config.mjs`
- 验证方式:`npm run mobile-shell:test -- src/shell/lifecycle.test.ts``npm run mobile-shell:config``npm run check:native-shells``npm run check:encoding``git diff --check`
## 2026-06-20 移动扫码权限请求失败不可静默
- 背景:Expo 移动壳 `scanner.scanQrCode` 会通过真实 `expo-camera` 权限 API 启动扫码;如果权限请求 API 自身失败后只返回通用 `host_error` 而不记录原始错误,用户会看到扫码不可用但开发侧难以区分系统拒绝、原生模块异常或设备能力问题。
- 决策:`QrScannerOverlay``Camera.requestCameraPermissionsAsync()` reject 路径必须调用 `logQrScannerPermissionFailure(...)` 输出 `mobile QR scanner permission request failed` 日志,再通过 `failQrCodeScan('camera permission unavailable')` 结束当前请求;`QrScannerOverlay.test.tsx` 必须覆盖该 reject 路径,移动壳配置检查反查日志 helper 和测试断言。
- 影响范围:`apps/mobile-shell/src/shell/QrScannerOverlay.tsx``apps/mobile-shell/src/shell/QrScannerOverlay.test.tsx``apps/mobile-shell/scripts/check-config.mjs`
- 验证方式:`npm run mobile-shell:test -- src/shell/QrScannerOverlay.test.tsx``npm run mobile-shell:typecheck``npm run check:native-shells``npm run check:encoding``git diff --check`
## 2026-06-20 移动分享单测边界
- 背景:Expo 移动壳 `share.open` / `share.setTarget` 已经由 `share.ts` 承接共享 HostBridge 分享 URL 归一和缓存目标,但关键边界主要压在巨型 `bridge.test.ts` 中,后续拆分桥接 helper 时容易遗漏非法显式 payload 不回退缓存、空分享拒绝和缓存目标保留语义。