最近在做 appium 工具升级的事儿,但是新升级的 appium 未提供打包好的工具包,测试同学不能直接获取控件树信息,需要使用命令行起 webdriveragent 或 app-inspector(macaca) 工具来获取。
然后正好最近在论坛上看到别人做了使用 android 端获取控件树的 idea。心想这确实是个好主意,测试同学使用一个工具就好了,不必在两个工具之间来回切换了。
a. 使用 appium 原生方法 getPageSource,结果格式如下:
<?xml version="1.0" encoding="UTF-8"?><AppiumAUT><XCUIElementTypeApplication type="XCUIElementTypeApplication" name="苏宁金融" label="苏宁金融" visible="true" enabled="true" x="0" y="0" width="375" height="667">
<XCUIElementTypeWindow type="XCUIElementTypeWindow" visible="true" enabled="true" x="0" y="0" width="375" height="667">
<XCUIElementTypeOther type="XCUIElementTypeOther" visible="true" enabled="true" x="0" y="0" width="375" height="667">
</XCUIElementTypeWindow>
<XCUIElementTypeWindow type="XCUIElementTypeWindow" visible="false" enabled="true" x="0" y="0" width="375" height="667">
<XCUIElementTypeWindow type="XCUIElementTypeWindow" visible="true" enabled="true" x="0" y="0" width="375" height="667">
</XCUIElementTypeApplication></AppiumAUT>
a. 上面的结果需要替换一下才能正常的显示到 uautomatorviewer 中去,如果不替换,则需要修改原码的解析规则(本人选择的替换,这个容易些吧)
替换规则:
if (UiAutomatorViewer.testPlatform.equals(UiAutomatorViewer.AOS)) {
xmlStr = xmlStr.replaceAll("<\\/android\\.[a-z]+\\.[A-za-z]+", "<\\/node");
xmlStr = xmlStr.replaceAll("<android\\.[a-z]+\\.[A-za-z]+", "<node");
} else {
xmlStr = xmlStr.replaceAll("AppiumAUT", "hierarchy");
if (xmlStr.contains("/UIA")) {
xmlStr = xmlStr.replaceAll("/UIA.[A-za-z]+", "/node");
xmlStr = xmlStr.replaceAll("UIA", "node type=\"UIA");
xmlStr = xmlStr.replaceAll(" name=", "\" name=");
} else {
xmlStr = xmlStr.replaceAll("XCUIElement.* type", "node type");
xmlStr = xmlStr.replaceAll("/XCUIElementType.[A-za-z]+", "/node");
}
}
b. 生成 xpath
public String getXpathWithRootNode(BasicTreeNode btn) {
String xpath = "";
while (btn != null && btn.getAttributesArray() != null) {
String elementTyle = ((AttributePair)btn.getAttributesArray()[0]).value;
xpath = elementTyle + "[" + getIndex(btn, btn.getParent()) + "]/" + xpath;
btn = btn.getParent();
}
//System.out.println(xpath);
return xpath.substring(0, xpath.length()-1);
}
public int getIndex(BasicTreeNode child, BasicTreeNode parent) {
int index = 1;
String dType = ((AttributePair)child.getAttributesArray()[0]).value;
for (BasicTreeNode n : parent.getChildren()) {
if (n.equals(child)) {
break;
}
if (dType.equals(((AttributePair)n.getAttributesArray()[0]).value)) {
index++;
}
}
return index;
}
public void setSelectedNode(BasicTreeNode node) {
this.mSelectedNode = node;
if ((this.mSelectedNode instanceof UiNode)) {
UiNode uiNode = (UiNode) this.mSelectedNode;
if (UiAutomatorViewer.testPlatform.equals(UiAutomatorViewer.AOS)) {
this.mCurrentDrawingRect = new Rectangle(uiNode.x, uiNode.y,
uiNode.width, uiNode.height);
} else {
/** 如果是iphone设备,需要根据图片与实际window的比例,计算出控件在截图中的位置*/
String width = uiNode.getAttribute("width");
String height = uiNode.getAttribute("height");
if (uiNode.getAttribute("width").contains(".")) {
int indexW = width.indexOf(".");
width = width.substring(0, indexW+2);
}
if (uiNode.getAttribute("height").contains(".")) {
int indexH = height.indexOf(".");
height = height.substring(0, indexH+2);
}
String x = uiNode.getAttribute("x");
String y = uiNode.getAttribute("y");
if (x.contains(".")) {
int indexX = x.indexOf(".");
x = x.substring(0, indexX+2);
}
if (y.contains(".")) {
int indexY = y.indexOf(".");
y = y.substring(0, indexY+2);
}
this.mCurrentDrawingRect = new Rectangle((int)(Float.parseFloat(x) * UiAutomatorViewer.iosScreenZoomInPercentWidth), (int)(Float.parseFloat(y) * UiAutomatorViewer.iosScreenZoomInPercentHeight),
(int)(Float.parseFloat(width) * UiAutomatorViewer.iosScreenZoomInPercentWidth), (int)(Float.parseFloat(height) * UiAutomatorViewer.iosScreenZoomInPercentHeight));
}
} else {
this.mCurrentDrawingRect = null;
}
}
附上 uiautomatorviewer 源码位置:https://android.googlesource.com/platform/tools/swt/+/master/uiautomatorviewer