E2E:从具有Wix Detox的UIImagePickerController中选择图像

Tar*_*sri 5 ios e2e-testing react-native react-native-ios detox

描述

我需要编写一个e2e测试,在某些情况下它必须在UIImagePickerController中选择一个图像,我试图毫无用处地使用element(by.type('UIImagePickerController')). tapAtPoint()它。我需要一种选择图像的方法。我已经找到了一种使用本机测试的方法。

而且对我来说,嘲笑也不是一种选择,因为我使用了一个react-native-repackeger所需的更高版本。

重现步骤
  • 与使用图像选择器的任何应用程序一起使用

  • 尝试使用 element(by.type('UIImagePickerController')).tapAtPoint({ x: 50, y: 200 })

排毒,节点,设备,Xcode和macOS版本
  • 排毒:6.0.2
  • 节点:8.9.0
  • 设备:iOS Simulator 6s
  • Xcode:9.2
  • macOS:10.13.1
  • React-Native:0.46.4
设备和详细的排毒日志

没有日志,设备会在正确的位置点击,但点击不会生效。

Kok*_*eam 7

注意到最初的问题指出在所提出的案例中模拟不是一个选项,但我在搜索解决方案时遇到了这个 Stack Overflow 问题几次,并想分享我最终为我的情况提出的建议。

通过包装react-native-image-picker我自己的导出,我能够绕过 e2e 测试的限制:

图像选取器.js

import ImagePicker from 'react-native-image-picker';

export default ImagePicker;
Run Code Online (Sandbox Code Playgroud)

然后创建一个带有自定义扩展的模拟(即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,
    });
  },
};
Run Code Online (Sandbox Code Playgroud)

最后,配置 Metro bundler 以优先考虑您的自定义扩展:

[项目根目录]/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,
  },
};
Run Code Online (Sandbox Code Playgroud)

然后在将RN_SRC_EXT环境变量设置为自定义扩展的情况下运行:

RN_SRC_EXT=e2e.js react-native start
Run Code Online (Sandbox Code Playgroud)

有关更多信息,请参阅排毒模拟指南


Ant*_*ni4 5

不确定这是否相关,但对于iOS 11,我什至无法在调试视图层次结构中看到这些本机视图类型。

相片 相机胶卷

然而,对于iOS 910,我会这样解决问题:

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();
});
Run Code Online (Sandbox Code Playgroud)

可能还有许多其他可能性可以通过不同的本机视图类型和可访问性特征来解决此问题。

我只是使用了react-native-image-picker提供的示例来测试上面的代码:

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);
Run Code Online (Sandbox Code Playgroud)

预览