UiAutomator uiautomatorviewer 自动生成 xpath (兼容 android 和 iOS)

bauul · February 12, 2017 · Last by hello replied at May 13, 2019 · 4169 hits

事由

最近在做appium工具升级的事儿,但是新升级的appium未提供打包好的工具包,测试同学不能直接获取控件树信息,需要使用命令行起webdriveragent或app-inspector(macaca)工具来获取。
然后正好最近在论坛上看到别人做了使用android端获取控件树的idea。心想这确实是个好主意,测试同学使用一个工具就好了,不必在两个工具之间来回切换了。

原理

  1. 获取控件树信息

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>
  1. 展示

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;
}

控件坐标与截图对应(宽和高的比例:screenshot.width / driver.manage().window().getSize().width):

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;
}
}

结果展示:

欢迎指教,感谢testerhome

附言 1  ·  December 13, 2017
如果觉得我的文章对您有用,请随意打赏。您的支持将鼓励我继续创作!
共收到 22 条回复 时间 点赞

更新一下,兼容ios9.3以下的版本

@xdf
达峰哥,可以指点一下,这边坐标值和图片像素不匹配的问题吗?感谢

更新一下,坐标值和图片的对应方法,兼容部分APP的坐标值存在13位小数的问题

直接windows电脑连接ios设备就可以了吗?

bauul #6 · March 08, 2017 作者

这个当然是不行的了,是在mac电脑上跑uiautomatorviewer来显示ios设备的控件信息啊

不好意思,想问的是,最后是要修改uiautomatorviewer的源码还是怎样,没太懂😅

bauul #8 · March 08, 2017 作者
志阳、 回复

嗯嗯,是的,需要修改uiautomatorviewer的源码,源码在google上可以搜到的。

没有可执行文件或者工程么?

bauul #10 · March 27, 2017 作者
蓝翔 回复

公司产品,主要这和另一个工具结合使用的,方法是拿到appium driver调用getPageSource方法

bauul #11 · December 14, 2017 作者

@Lihuazhang
同一个人点了两个赞?

跟着大神的脚步,开始研究抓取 ios的xpath~

bauul #15 · June 19, 2018 作者
hello 回复

我是弱鸡,😜

bauul 回复

别谦虚了,请收下我的双膝,刚开始玩这个, 有很多不懂的地方。不知是否方便加个Q

bauul · #17 · June 19, 2018 作者
Author only

仅楼主可见,看不到,512433465 方便的话,加一下我Q

simple 专栏文章:[精华帖] 社区历年精华帖分类归总 中提及了此贴 13 Dec 14:44

IOS端查看元素怎么用,楼主能说下嘛

bauul #23 · February 19, 2019 作者

上面讲了啊,需要修改uiautomatorviewer的源码,把控件信息塞到树上,就可以了。至于获取控件信息的方法现在主要是调用Facebook的WDA的API,你可以看一下appium的源码,里面有

bauul 回复

需要通启动wda服务是吗

bauul #25 · February 19, 2019 作者

是的

你好,能否将uautomatorviewer有关界面操作手机获取控件属性等信息集成在自己的SSM框架中的网页里?不用他自带的页面

bauul #27 · May 13, 2019 作者
Altndong 回复

可以啊

Altndong 回复

ATX 就是这么干的吧

需要 Sign In 后方可回复, 如果你还没有账号请点击这里 Sign Up