6

我想使用屏幕截图测试我的 React Native 应用程序。UIAutomation javascript 文件将由fastlane执行,并应向我提供我需要的所有子视图。这部分工作正常。

我的主要问题是我不明白我是如何点击一个元素的。我发现的每个示例都是简单的objective-c,并使用标准元素进行导航,例如标签栏。TouchableHighlight我的应用程序有一个汉堡图标,它有一个打开菜单的点击事件。我正在寻找一种可能性来引用单个TouchableHighlight元素以便与之交互。

这样的答案加分,我不需要编写Objective-C。

4

4 回答 4

4

Fastlane(更具体的快照)已弃用 UI 测试的 UI 自动化。如果您需要更新 gem,您的 UIA javascript 将不适用于 UI 测试(用 Obj C 或 Swift 编写)

为什么更改为 UI 测试?

UI 自动化已弃用 UI 测试将在未来发展并支持更多功能 UI 测试更容易调试 UI 测试是用 Swift 或 Objective C 编写的 UI 测试可以以更清洁和更好的方式执行

https://github.com/fastlane/snapshot

看起来使用 React Native 的其他人在 UI 测试和快照方面取得了一些进展:https ://github.com/fastlane/snapshot/issues/267

于 2015-10-29T14:32:25.080 回答
3

我不熟悉 fastlane,但您可能想尝试一下 Jest,因为它已得到官方支持。诚然,它们并没有完全覆盖,而且在某些情况下,您很可能不得不推出自己的解决方案,因为本机反应还很年轻,但这应该让您开始正确的快照测试(仅限 iOS)

于 2015-10-29T21:49:49.393 回答
2

注意:我们使用 detox 进行测试,所以我device.getPlatform()用于测试 iOS 或 Android。

我最终做的是混合使用 JavaScript 库(pixelmatchpngjs),使用fs和使用命令行命令(xcrun simctladb)。

const {device} = require('detox');
const {execSync} = require('child_process');
const fs = require('fs');
const {existsSync, mkdirSync, readFileSync, writeFileSync, unlinkSync} = fs;
const PNG = require('pngjs').PNG;
const pixelmatch = require('pixelmatch');

const IOS_SCREENSHOT_OPTIONS = {
  timeout: 1000,
  killSignal: 'SIGKILL'
};

function getScreenShotDirectory() { ... }
function getActualFileName(testName) { ... }
function getExpectedFileName(testName) { ... }
function getDiffFileName(testName) { ... }

async function takeScreenshot(testName) {
  const actualFileName = getActualFileName(testName);
  const directoryName = getScreenShotDirectory();
  if (!existsSync(directoryName)) {
    mkdirSync(directoryName, {recursive: true});
  }

  if (device.getPlatform() === 'ios') {
    execSync(`xcrun simctl io booted screenshot "${actualFileName}"`, IOS_SCREENSHOT_OPTIONS);
    await removeIosStatusBar(actualFileName);
  } else {
    execSync(`adb exec-out screencap -p > "${actualFileName}"`);
  }
}

const compareScreenshot = async testName => {
  const actualFileName = getActualFileName(testName);
  await takeScreenshot(testName);
  const expectedFileName = getExpectedFileName(testName);
  const actualImage = PNG.sync.read(readFileSync(actualFileName));
  if (!existsSync(expectedFileName)) {
    console.warn(`No expected image for ${testName} @ ${expectedFileName}`);
    return false;
  }

  const expectedImage = PNG.sync.read(readFileSync(getExpectedFileName(testName)));
  const {width, height} = actualImage;
  const diffImage = new PNG({width, height});
  const numDiffPixels = pixelmatch(actualImage.data, expectedImage.data, diffImage.data, width, height);

  if (numDiffPixels === 0) {
    unlinkSync(actualFileName);
    return true;
  } else {
    const percentDiffPixels = numDiffPixels / (width * height);
    console.warn(
      `Images are different ${testName} numDiffPixels=${numDiffPixels} percentDiffPixels=${percentDiffPixels}`
    );
    writeFileSync(getDiffFileName(testName), PNG.sync.write(diffImage));
    return false;
  }
};

为了改善您的测试结果,您应该使用 Android 的演示模式,例如:

execSync('adb shell settings put global sysui_demo_allowed 1');
execSync('adb shell am broadcast -a com.android.systemui.demo -e command ...');
execSync('adb shell am broadcast -a com.android.systemui.demo -e command exit');

从 xcode 11 你有:

execSync('xcrun simctl status_bar <device> override ...')

我使用以下代码从 iOS 中删除了状态栏(但它会降低性能):

const IOS_STATUS_BAR_HEIGHT = 40;
async function removeIosStatusBar(imageFileName) {
  return new Promise((resolve, reject) => {
    const image = PNG.sync.read(readFileSync(imageFileName));
    let {width, height} = image;
    height -= IOS_STATUS_BAR_HEIGHT;
    const dst = new PNG({width, height});
    fs.createReadStream(imageFileName)
      .pipe(new PNG())
      .on('error', error => reject(error))
      .on('parsed', function () {
        this.bitblt(dst, 0, IOS_STATUS_BAR_HEIGHT, width, height, 0, 0);
        dst
          .pack()
          .pipe(fs.createWriteStream(imageFileName))
          .on('error', error => reject(error))
          .on('finish', () => resolve(imageFileName));
      });
  });
}
于 2019-10-23T08:20:20.860 回答
-1

创建一个新项目。

$ react-native -v
react-native-cli: 2.0.1

$ react-native init NativeSnapshots

$ cd NativeSnapshots

$ react-native run-ios

测试它的工作原理,启动欢迎屏幕。

$ cd ios

$ fastlane snapshot init

快车道输出:

[14:37:56]: For more information, check out https://docs.fastlane.tools/getting-started/ios/setup/#use-a-gemfile
✅  Successfully created SnapshotHelper.swift './SnapshotHelper.swift'
✅  Successfully created new Snapfile at './Snapfile'
-------------------------------------------------------
Open your Xcode project and make sure to do the following:
1) Add a new UI Test target to your project
2) Add the ./fastlane/SnapshotHelper.swift to your UI Test target
   You can move the file anywhere you want
3) Call `setupSnapshot(app)` when launching your app

  let app = XCUIApplication()
  setupSnapshot(app)
  app.launch()

4) Add `snapshot("0Launch")` to wherever you want to create the screenshots

More information on GitHub: https://github.com/fastlane/fastlane/tree/master/snapshot

第 1 步:将新的 UI 测试目标添加到您的项目中

Xcode 版本 8.3.3 > 打开 NativeSnapshots.xcodeproj

File > New > Target > iOS UI Testing Bundle

用户界面目标

第 2 步:将 ./fastlane/SnapshotHelper.swift 添加到您的 UI 测试目标

Highlight NativeSnapshotsUITests
File > Add Files to NativeSnapshots
Select ./fastlane/SnapshotHelper.swift, Enter

第 3 步:启动应用程序时调用 setupSnapshot(app)

NativeSnapshotsUITests/NativeSnapshotsUITests.swift在 Xcode 中打开。

代替:

    XCUIApplication().launch()

和:

    let app = XCUIApplication()
    setupSnapshot(app)
    app.launch()

第 4 步:添加snapshot("0Launch")到您要创建屏幕截图的任何位置

在 UI 测试的 testExample() 中添加快照调用。

func testExample() {
    snapshot("0Launch")
}

编辑 Snapfile 以避免出现巨大的矩阵。

devices([
  "iPhone 6"
])

languages([
  "en-US"
])

scheme "NativeSnapshots"

它应该准备好了。

$ cd ios && fastlane snapshot

复制自aj0strow

于 2017-09-15T16:19:00.243 回答