Files
iNaturalistReactNative/tests/vision-camera/vision-camera.js
Johannes Klein f8c2e55007 Add fetchPlaceName mock (#3640)
* WIP: fetchPlaceName mock to workaround issue on Android phones without Play Services

* Update vision-camera.js

* Fixes an error while rendering this mock
2026-05-20 15:18:46 +02:00

177 lines
5.2 KiB
JavaScript

import {
copyAssetsFileIOS,
copyFile,
ExternalDirectoryPath,
TemporaryDirectoryPath,
} from "@dr.pogodin/react-native-fs";
import { CameraRoll } from "@react-native-camera-roll/camera-roll";
import React from "react";
import { Platform, View } from "react-native";
const mockFrame = {
isValid: true,
width: 1920,
height: 1080,
bytesPerRow: 1920,
planesCount: 1,
isMirrored: false,
timestamp: 0,
orientation: "portrait",
pixelFormat: "yuv",
// Returns a fake ArrayBuffer
toArrayBuffer: () => new ArrayBuffer( 1920 * 1080 ),
toString: () => "Frame",
getNativeBuffer: () => ( {
// Returns a fake pointer
pointer: 0,
delete: () => null,
} ),
incrementRefCount: () => null,
decrementRefCount: () => null,
};
const style = { flex: 1, backgroundColor: "red" };
export class mockCamera extends React.PureComponent {
static async getAvailableCameraDevices() {
return [
{
position: "back",
},
];
}
/*
Every time the component updates we are running the frame processor that is a prop
to the camera component. We are running the frame processor with a mocked frame that
does not include any kind of image data at all.
Running it only on component update means it only is called a few times and not
every second (or so - depending on fps). This is enough to satisfy the e2e test
though because the mocked prediction needs to appear only once to be found by the
test matcher. I tried running it with a timer every second but since it never idles
the test never finishes.
*/
componentDidUpdate() {
const { frameProcessor, isActive } = this.props;
if ( isActive === false || !frameProcessor ) {
return;
}
frameProcessor.frameProcessor( mockFrame );
}
// eslint-disable-next-line class-methods-use-this, react/no-unused-class-component-methods
async takePhoto() {
if ( Platform.OS === "android" ) {
/*
On Android, copyAssetsFileIOS is not available. Instead, copy the test
image that was pushed to the app's external files directory by
iNatE2eBeforeAll (via `adb push e2e/animal.jpg`).
ExternalDirectoryPath = /sdcard/Android/data/<package>/files/
which is accessible by the app without extra permissions.
*/
const sourcePath = `${ExternalDirectoryPath}/e2e_test.jpg`;
const destPath = `${TemporaryDirectoryPath}/temp.jpg`;
try {
await copyFile( sourcePath, destPath );
return {
path: destPath,
metadata: { Orientation: 1 },
};
} catch ( err ) {
console.log( "Error copying test photo on Android:", err );
return null;
}
}
return CameraRoll.getPhotos( {
first: 20,
assetType: "Photos",
} )
.then( async r => {
/*
Basically, here, we are reading the newest twenty photos from the simulators gallery
and return the oldest one of those. Copy it to a new path and treat it as a new photo.
*/
const testPhoto = r.edges[r.edges.length - 1].node.image;
let oldUri = testPhoto.uri;
if ( testPhoto.uri.includes( "ph://" ) ) {
let id = testPhoto.uri.replace( "ph://", "" );
id = id.substring( 0, id.indexOf( "/" ) );
oldUri = `assets-library://asset/asset.jpg?id=${id}&ext=jpg`;
console.log( `Converted file uri to ${oldUri}` );
}
const encodedUri = encodeURI( oldUri );
const destPath = `${TemporaryDirectoryPath}/temp.jpg`;
const newPath = await copyAssetsFileIOS(
encodedUri,
destPath,
0,
0,
);
const photo = { uri: newPath, predictions: [] };
if ( typeof photo !== "object" ) {
console.log( "photo is not an object", typeof photo );
return null;
}
return {
...testPhoto,
path: newPath,
metadata: {
Orientation: testPhoto.orientation,
},
};
} )
.catch( err => {
console.log( "Error getting photos", err );
return null;
} );
}
render() {
return <View style={style} collapsable={false} />;
}
}
export const mockSortDevices = ( _left, _right ) => 1;
const device = {
physicalDevices: ["wide-angle-camera"],
hasFlash: true,
hasTorch: true,
id: "1",
isMultiCam: true,
maxZoom: 12.931958198547363,
minZoom: 1,
name: "front (1)",
neutralZoom: 1,
position: "front",
supportsDepthCapture: false,
supportsFocus: true,
supportsLowLightBoost: false,
supportsParallelVideoProcessing: true,
supportsRawCapture: true,
};
export const mockUseCameraDevice = _deviceType => device;
export const mockUseCameraDevices = () => [device];
export const mockUseCameraFormat = _device => {
const format = {
autoFocusSystem: "contrast-detection",
fieldOfView: 83.97117848314457,
maxFps: 60,
maxISO: 11377,
minFps: 15,
minISO: 44,
photoHeight: 3024,
photoWidth: 4032,
pixelFormats: ["yuv", "native"],
supportsDepthCapture: false,
supportsPhotoHdr: false,
supportsVideoHdr: false,
videoHeight: 2160,
videoStabilizationModes: ["off", "cinematic", "cinematic-extended"],
videoWidth: 3840,
};
return format;
};