8
描述

我需要编写一个 e2e 测试,在某些时候它必须在 UIImagePickerController 中选择一个图像,我尝试使用element(by.type('UIImagePickerController')). tapAtPoint()但没有用。我需要一种选择图像的方法。我找到了一种使用本机测试的方法。

对我来说,模拟也不是一种选择,因为我使用的是 react-native-repackeger 需要的更高版本。

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

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

Detox、Node、Device、Xcode 和 macOS 版本
  • 排毒:6.0.2
  • 节点:8.9.0
  • 设备:iOS模拟器6s
  • Xcode:9.2
  • macOS:10.13.1
  • 反应原生:0.46.4
设备和详细排毒日志

没有日志,设备点击了正确的位置,但点击没有效果。

4

2 回答 2

9

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

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

ImagePicker.js

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

export default ImagePicker;

然后使用自定义扩展(即)创建一个模拟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,
    });
  },
};

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

[项目根目录]/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,
  },
};

然后使用RN_SRC_EXT设置为自定义扩展的环境变量运行:

RN_SRC_EXT=e2e.js react-native start

有关详细信息,请参阅排毒模拟指南

于 2019-03-13T21:01:09.677 回答
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();
});

使用不同的原生视图类型和可访问性特征可能还有许多其他可能性来解决这个问题。

我只是使用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);

预习

于 2018-01-12T00:33:26.317 回答