注意:我们使用 detox 进行测试,所以我device.getPlatform()
用于测试 iOS 或 Android。
我最终做的是混合使用 JavaScript 库(pixelmatch和pngjs),使用fs和使用命令行命令(xcrun simctl
和adb
)。
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));
});
});
}