锁定移动壳图片选择失败响应
移动壳图片库启动失败返回稳定错误 移动壳相机启动失败返回稳定错误 移动壳配置检查补充图片选择失败边界反查
This commit is contained in:
@@ -2116,12 +2116,16 @@ for (const snippet of [
|
||||
"message: 'file sharing failed'",
|
||||
'rejects image import before picker launch when library permission is denied',
|
||||
'rejects image import before picker launch when library permission request fails',
|
||||
'maps native image library launch failures to stable import errors',
|
||||
'rejects image capture before camera launch when camera permission request fails',
|
||||
'maps native camera launch failures to stable capture errors',
|
||||
"message: 'photo library permission denied'",
|
||||
"message: 'photo library permission unavailable'",
|
||||
"message: 'photo library unavailable'",
|
||||
'expect(imageLibraryMock).not.toHaveBeenCalled()',
|
||||
"message: 'camera permission denied'",
|
||||
"message: 'camera permission unavailable'",
|
||||
"message: 'camera unavailable'",
|
||||
'expect(cameraMock).not.toHaveBeenCalled()',
|
||||
"mediaTypes: ['images']",
|
||||
"options: { encoding: 'base64' }",
|
||||
|
||||
@@ -354,6 +354,21 @@ describe('mobile HostBridge file actions', () => {
|
||||
expect(imageLibraryMock).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
test('maps native image library launch failures to stable import errors', async () => {
|
||||
libraryPermissionMock.mockResolvedValueOnce({
|
||||
status: ImagePicker.PermissionStatus.GRANTED,
|
||||
granted: true,
|
||||
canAskAgain: true,
|
||||
expires: 'never',
|
||||
});
|
||||
imageLibraryMock.mockRejectedValueOnce(new Error('native image picker failed'));
|
||||
|
||||
await expect(importImageFile()).rejects.toMatchObject({
|
||||
code: 'host_error',
|
||||
message: 'photo library unavailable',
|
||||
});
|
||||
});
|
||||
|
||||
test('rejects image capture before camera launch when camera permission request fails', async () => {
|
||||
cameraPermissionMock.mockRejectedValueOnce(new Error('native permission failed'));
|
||||
|
||||
@@ -364,6 +379,21 @@ describe('mobile HostBridge file actions', () => {
|
||||
expect(cameraMock).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
test('maps native camera launch failures to stable capture errors', async () => {
|
||||
cameraPermissionMock.mockResolvedValueOnce({
|
||||
status: ImagePicker.PermissionStatus.GRANTED,
|
||||
granted: true,
|
||||
canAskAgain: true,
|
||||
expires: 'never',
|
||||
});
|
||||
cameraMock.mockRejectedValueOnce(new Error('native camera failed'));
|
||||
|
||||
await expect(captureImageFile()).rejects.toMatchObject({
|
||||
code: 'host_error',
|
||||
message: 'camera unavailable',
|
||||
});
|
||||
});
|
||||
|
||||
test('imports audio and exports binary files through controlled payloads', async () => {
|
||||
fileBase64Data.set('file:///picked/voice.mp3', MP3_BASE64);
|
||||
fileSizes.set('file:///picked/voice.mp3', Buffer.byteLength('ID3\x04\x00\x00\x00\x00\x00\x10'));
|
||||
|
||||
@@ -331,14 +331,22 @@ export async function importImageFile(): Promise<FileImportImageResult> {
|
||||
} satisfies HostBridgeError;
|
||||
}
|
||||
|
||||
const result = await ImagePicker.launchImageLibraryAsync({
|
||||
allowsEditing: false,
|
||||
allowsMultipleSelection: false,
|
||||
base64: true,
|
||||
exif: false,
|
||||
mediaTypes: ['images'],
|
||||
quality: 1,
|
||||
});
|
||||
let result: ImagePicker.ImagePickerResult;
|
||||
try {
|
||||
result = await ImagePicker.launchImageLibraryAsync({
|
||||
allowsEditing: false,
|
||||
allowsMultipleSelection: false,
|
||||
base64: true,
|
||||
exif: false,
|
||||
mediaTypes: ['images'],
|
||||
quality: 1,
|
||||
});
|
||||
} catch {
|
||||
throw {
|
||||
code: 'host_error',
|
||||
message: 'photo library unavailable',
|
||||
} satisfies HostBridgeError;
|
||||
}
|
||||
|
||||
const payload = imagePickerResultToImportPayload(result, 'selected');
|
||||
if (payload.bytes > HOST_BRIDGE_IMPORT_IMAGE_MAX_BYTES) {
|
||||
@@ -370,13 +378,21 @@ export async function captureImageFile(): Promise<FileImportImageResult> {
|
||||
} satisfies HostBridgeError;
|
||||
}
|
||||
|
||||
const result = await ImagePicker.launchCameraAsync({
|
||||
allowsEditing: false,
|
||||
base64: true,
|
||||
exif: false,
|
||||
mediaTypes: ['images'],
|
||||
quality: 1,
|
||||
});
|
||||
let result: ImagePicker.ImagePickerResult;
|
||||
try {
|
||||
result = await ImagePicker.launchCameraAsync({
|
||||
allowsEditing: false,
|
||||
base64: true,
|
||||
exif: false,
|
||||
mediaTypes: ['images'],
|
||||
quality: 1,
|
||||
});
|
||||
} catch {
|
||||
throw {
|
||||
code: 'host_error',
|
||||
message: 'camera unavailable',
|
||||
} satisfies HostBridgeError;
|
||||
}
|
||||
|
||||
const payload = imagePickerResultToImportPayload(result, 'captured');
|
||||
if (payload.bytes > HOST_BRIDGE_IMPORT_IMAGE_MAX_BYTES) {
|
||||
|
||||
Reference in New Issue
Block a user