问题
Description
I need to write an e2e test that in some point it has to select an image in UIImagePickerController, I tried to use element(by.type('UIImagePickerController')). tapAtPoint()
with no use. I need a way to select an image. I have found a way to do it with native tests.
Also mocking isn't an option for me since I use a higher version that the one react-native-repackeger needs.
Steps to ReproduceUse with any application that uses image picker
Try to use
element(by.type('UIImagePickerController')).tapAtPoint({ x: 50, y: 200 })
- Detox: 6.0.2
- Node: 8.9.0
- Device: iOS Simulator 6s
- Xcode: 9.2
- macOS: 10.13.1
- React-Native: 0.46.4
There's no logs, the device taps on the right location but the tap doesn't make an effect.
回答1:
Not sure if this is related, but for iOS 11 I can't even see those native view types in the Debug View Hierarchy.
For iOS 9 and 10 however, I would solve the problem like this:
it('select first image from camera roll', async () => {
// select a photo
await element(by.id('select_photo')).tap();
// Choose from Library...
await element(by.traits(['button']).and(by.type('_UIAlertControllerActionView'))).atIndex(1).tap();
// select Cemara Roll, use index 0 for Moments
await element(by.type('UITableViewCellContentView')).atIndex(1).tap();
// select first image
await element(by.type('PUPhotoView')).atIndex(0).tap();
});
There are probably many other possibilities to solve this problem with different native view types and accessibility traits.
I just used the example provided from react-native-image-picker to test with above code:
import React from 'react';
import {
AppRegistry,
StyleSheet,
Text,
View,
PixelRatio,
TouchableOpacity,
Image,
} from 'react-native';
import ImagePicker from 'react-native-image-picker';
export default class App extends React.Component {
state = {
avatarSource: null,
videoSource: null
};
selectPhotoTapped() {
const options = {
quality: 1.0,
maxWidth: 500,
maxHeight: 500,
storageOptions: {
skipBackup: true
}
};
ImagePicker.showImagePicker(options, (response) => {
console.log('Response = ', response);
if (response.didCancel) {
console.log('User cancelled photo picker');
}
else if (response.error) {
console.log('ImagePicker Error: ', response.error);
}
else if (response.customButton) {
console.log('User tapped custom button: ', response.customButton);
}
else {
let source = { uri: response.uri };
// You can also display the image using data:
// let source = { uri: 'data:image/jpeg;base64,' + response.data };
this.setState({
avatarSource: source
});
}
});
}
selectVideoTapped() {
const options = {
title: 'Video Picker',
takePhotoButtonTitle: 'Take Video...',
mediaType: 'video',
videoQuality: 'medium'
};
ImagePicker.showImagePicker(options, (response) => {
console.log('Response = ', response);
if (response.didCancel) {
console.log('User cancelled video picker');
}
else if (response.error) {
console.log('ImagePicker Error: ', response.error);
}
else if (response.customButton) {
console.log('User tapped custom button: ', response.customButton);
}
else {
this.setState({
videoSource: response.uri
});
}
});
}
render() {
return (
<View style={styles.container}>
<TouchableOpacity testID="select_photo" onPress={this.selectPhotoTapped.bind(this)}>
<View style={[styles.avatar, styles.avatarContainer, {marginBottom: 20}]}>
{ this.state.avatarSource === null ? <Text>Select a Photo</Text> :
<Image style={styles.avatar} source={this.state.avatarSource} />
}
</View>
</TouchableOpacity>
<TouchableOpacity onPress={this.selectVideoTapped.bind(this)}>
<View style={[styles.avatar, styles.avatarContainer]}>
<Text>Select a Video</Text>
</View>
</TouchableOpacity>
{ this.state.videoSource &&
<Text style={{margin: 8, textAlign: 'center'}}>{this.state.videoSource}</Text>
}
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
backgroundColor: '#F5FCFF'
},
avatarContainer: {
borderColor: '#9B9B9B',
borderWidth: 1 / PixelRatio.get(),
justifyContent: 'center',
alignItems: 'center'
},
avatar: {
borderRadius: 75,
width: 150,
height: 150
}
});
AppRegistry.registerComponent('example', () => App);
回答2:
Noticed the original question stated that mocks were not an option in the case presented, but I came across this Stack Overflow question a few times in my searches for a solution and thought to share what I ultimately came up with for my situation.
I was able to get around the limitations for the e2e test by wrapping react-native-image-picker
in my own export:
ImagePicker.js
import ImagePicker from 'react-native-image-picker';
export default ImagePicker;
And then creating a mock with a custom extension (i.e. e2e.js
):
ImagePicker.e2e.js
const mockImageData = '/9j/4AAQSkZ...MORE BASE64 DATA OF CUTE KITTENS HERE.../9k=';
export default {
showImagePicker: function showImagePicker(options, callback) {
if (typeof options === 'function') {
callback = options;
}
callback({
data: mockImageData,
});
},
};
Finally, configure the metro bundler to prioritize your custom extension:
[project root]/rn-cli.config.js
const defaultSourceExts = require('metro-config/src/defaults/defaults')
.sourceExts;
module.exports = {
resolver: {
sourceExts: process.env.RN_SRC_EXT
? process.env.RN_SRC_EXT.split(',').concat(defaultSourceExts)
: defaultSourceExts,
},
};
Then run with the RN_SRC_EXT
environment variable set to the custom extension:
RN_SRC_EXT=e2e.js react-native start
See the Detox Mocking Guide for more information.
来源:https://stackoverflow.com/questions/48208456/e2e-select-an-image-from-a-uiimagepickercontroller-with-wix-detox