锁定移动扫码取消边界
移动扫码覆盖权限请求迟到时不重新激活相机视图 移动壳配置检查反查扫码取消异步边界测试 共享决策记录补充扫码权限异步取消约定
This commit is contained in:
@@ -2402,6 +2402,7 @@ for (const snippet of [
|
||||
"message: 'qr scanner unavailable'",
|
||||
"message: 'qr scan cancelled'",
|
||||
"test('logs and rejects the active scan when camera permission request fails'",
|
||||
"test('ignores late camera permission after the scan is cancelled'",
|
||||
"'mobile QR scanner permission request failed'",
|
||||
"barcodeTypes: ['qr']",
|
||||
"type: 'qr'",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/* @vitest-environment jsdom */
|
||||
|
||||
import { render, waitFor } from '@testing-library/react';
|
||||
import { act, render, waitFor } from '@testing-library/react';
|
||||
import * as CameraModule from 'expo-camera';
|
||||
import React from 'react';
|
||||
import { afterEach, describe, expect, test, vi } from 'vitest';
|
||||
@@ -62,6 +62,23 @@ const cameraPermissionMock = vi.mocked(
|
||||
CameraModule.Camera.requestCameraPermissionsAsync,
|
||||
);
|
||||
|
||||
function createDeferredPermission() {
|
||||
let resolvePermission!: (
|
||||
permission: Awaited<
|
||||
ReturnType<typeof CameraModule.Camera.requestCameraPermissionsAsync>
|
||||
>,
|
||||
) => void;
|
||||
const promise = new Promise<
|
||||
Awaited<ReturnType<typeof CameraModule.Camera.requestCameraPermissionsAsync>>
|
||||
>((resolve) => {
|
||||
resolvePermission = resolve;
|
||||
});
|
||||
return {
|
||||
promise,
|
||||
resolvePermission,
|
||||
};
|
||||
}
|
||||
|
||||
describe('QrScannerOverlay', () => {
|
||||
afterEach(() => {
|
||||
resetQrScannerForTest();
|
||||
@@ -156,4 +173,32 @@ describe('QrScannerOverlay', () => {
|
||||
message: 'qr scan cancelled',
|
||||
});
|
||||
});
|
||||
|
||||
test('ignores late camera permission after the scan is cancelled', async () => {
|
||||
const permission = createDeferredPermission();
|
||||
cameraPermissionMock.mockReturnValue(permission.promise);
|
||||
const screen = render(<QrScannerOverlay />);
|
||||
|
||||
const scanPromise = scanQrCode();
|
||||
|
||||
await waitFor(() => {
|
||||
expect(screen.getByText('关闭')).toBeTruthy();
|
||||
});
|
||||
|
||||
screen.getByText('关闭').click();
|
||||
|
||||
await expect(scanPromise).rejects.toMatchObject({
|
||||
code: 'cancelled',
|
||||
message: 'qr scan cancelled',
|
||||
});
|
||||
|
||||
await act(async () => {
|
||||
permission.resolvePermission({
|
||||
granted: true,
|
||||
} as Awaited<ReturnType<typeof CameraModule.Camera.requestCameraPermissionsAsync>>);
|
||||
await permission.promise;
|
||||
});
|
||||
|
||||
expect(overlayHarness.cameraViewProps.current).toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -16,6 +16,14 @@
|
||||
|
||||
---
|
||||
|
||||
## 2026-06-20 移动扫码权限异步取消边界
|
||||
|
||||
- 背景:Expo 移动壳 `scanner.scanQrCode` 会打开真实相机权限请求和扫码 overlay;如果用户在系统权限 Promise 返回前关闭扫码,旧权限结果不能重新激活 CameraView,也不能完成已经取消的 HostBridge 请求。
|
||||
- 决策:`QrScannerOverlay` 必须在 active/requestKey 变化和组件清理时忽略迟到的权限结果;移动壳配置检查必须反查“取消后迟到权限不重新打开 CameraView”的测试用例。
|
||||
- 影响范围:`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`。
|
||||
- 关联文档:`docs/【前端架构】ExpoReactNative与Tauri宿主壳方案-2026-06-17.md`、`docs/【前端架构】宿主壳能力统一协议-2026-06-17.md`。
|
||||
|
||||
## 2026-06-20 桌面壳系统能力调用顺序门禁
|
||||
|
||||
- 背景:Tauri 桌面壳文件导出和本地通知已经在运行时代码中先校验 HostBridge payload,再打开系统保存对话框、读取通知权限或请求通知权限;如果后续重构把系统能力调用提前,非法请求会触达原生系统边界。
|
||||
|
||||
Reference in New Issue
Block a user