• KTAllTestSuite的 import 展开看看。
    最好还是直接贴代码吧。截图看得很累……

  • github 上已经有人报过类似的 bug 了:https://github.com/appium/appium/issues/4611
    官方团队目前还没有把修复这个 bug 提上 milestone。

  • 找到 windows 下可以使用的可能原因了。我分析的源码是master上的 (1.3.5解析 xpath 的部分和我分析的源码一样)...切换到1.3.4的 tag 后看到 dump file 用的是 UiDevice 的 dump 方法。
    相关源码:
    appium-1.3.4:
    https://github.com/appium/appium/blob/v1.3.4/lib/devices/android/bootstrap/src/io/appium/android/bootstrap/utils/XMLHierarchy.java
    appium-1.3.5:
    https://github.com/appium/appium/blob/v1.3.5/lib/devices/android/bootstrap/src/io/appium/android/bootstrap/utils/XMLHierarchy.java

    @jinjun0620 你在 windows 下用的是打包好的 exe 程序吗?windows 的 exe 最新版本的是 1.3.4.1,mac 的最新版本是 1.3.5。所以用 exe 不会出现这个问题。

  • @zuoan 我没在 x86 模拟器下装过……像手机那样下载安装 apk 不行吗?

  • @doctorq 也有可能。现在我的电脑都开着 shadowsocks 了。

  • 只要 android 的版本是 4.4 或以上,webview 就是 chrome 内核的。
    如果 android 版本是 4.4 以下,appium 会使用 selendroid 控制 webview。此时需要在初始化 driver 时指定使用 seledroid:"automationName": "selendroid"

    另外就算你装了 chrome.apk,webview 也不会自动变成 chrome 内核。如果你想在 4.4 以下获得 chrome 内核的 webview,只能在应用里加上crosswalk。不过是这会让应用变大(crosswalk是在应用里加入了完整的 chrome 内核,所以会大不少)。

  • @doctorq 或者你查一下 grunt 里面的源码吧,看看那个 task 具体做了什么。
    reset.sh 的 log 在用了--verbose后还是挺清晰的。
    解决以后分享一下哈。我这个只记录了 mac 的,没有试过在 windows 下配置。

  • @jinjun0620 这么神奇?今晚回去研究研究。这是一个很有趣的问题。因为根据上面的分析应该无论哪个平台都找不到的(bootstrap 是在 android 上运行的)。

  • @doctorq 执行成功的标志是最后一行是

    ---- reset.sh completed successfully ----
    
  • 用 appium 的 bootstrap debug 过了, 确实 appium 1.3.5 在 android 平台下的 findElementByXPath 不支持中文

    appium 用 xpath 定位的原理是先用 dump 把当前界面所有元素保存到一个 xml 文件里面,然后再读取 dump 出来的 xml 文件,最后用 xpath 去查那个 xml 文件。(xpath 本来设计就是用来做 xml 的元素定位的)
    不能支持中文(严格地说是不支持所有非 ascii 字符)的原因是这个 dump 用的是 bootstrap 里面自己的 dump,而不是 uiautomator 的,这个 dump 会把所有不支持的字符替换成?,所以你用包含中文的 xpath 去找会找不到。

    借个地方贴一下相关源码方便后面研究:
    通过 xpath 查找元素的函数:
    io.appium.android.bootstrap.utils.XMLHierarchy.java

    public static ArrayList<ClassInstancePair> getClassInstancePairs(String xpathExpression)
              throws ElementNotFoundException, InvalidSelectorException, ParserConfigurationException {
        XPath xpath = XPathFactory.newInstance().newXPath();
        XPathExpression exp = null;
        try {
          exp = xpath.compile(xpathExpression);
        } catch (XPathExpressionException e) {
          throw new InvalidSelectorException(e.getMessage());
        }
    
        Node formattedXmlRoot;
    
        formattedXmlRoot = getFormattedXMLDoc();
    
        return getClassInstancePairs(exp, formattedXmlRoot);
      }
    
      public static ArrayList<ClassInstancePair> getClassInstancePairs(XPathExpression xpathExpression, Node root) throws ElementNotFoundException {
    
        NodeList nodes;
        try {
          nodes = (NodeList) xpathExpression.evaluate(root, XPathConstants.NODESET);
        } catch (XPathExpressionException e) {
          e.printStackTrace();
          throw new ElementNotFoundException("XMLWindowHierarchy could not be parsed: " + e.getMessage());
        }
    
        ArrayList<ClassInstancePair> pairs = new ArrayList<ClassInstancePair>();
        for (int i = 0; i < nodes.getLength(); i++) {
          if (nodes.item(i).getNodeType() == Node.ELEMENT_NODE) {
            try {
              pairs.add(getPairFromNode(nodes.item(i)));
            } catch (PairCreationException e) { }
          }
        }
    
        return pairs;
      }
    

    替换字符的函数:
    io.appium.uiautomator.core.AccessibilityNodeInfoDumper.java

    private static String stripInvalidXMLChars(CharSequence cs) {
            StringBuilder ret = new StringBuilder();
            char ch;
            for (int i = 0; i < cs.length(); i++) {
                ch = cs.charAt(i);
                // code below from Html#withinStyle, this is a temporary workaround because XML
                // serializer does not support surrogates
                if (ch >= 0xD800 && ch <= 0xDFFF) {
                    if (ch < 0xDC00 && i + 1 < cs.length()) {
                        char d = cs.charAt(i + 1);
                        if (d >= 0xDC00 && d <= 0xDFFF) {
                            i++;
                            ret.append("?");
                        }
                    }
                } else if (ch > 0x7E || ch < ' ') {
                    ret.append("?");
                } else {
                    ret.append(ch);
                }
            }
            return ret.toString();
        }
    

    其他方法(如 findElementByAccessibilityId)能找到包含中文的元素的原因是它直接使用 uiautomator 的对应方法。而 uiautomator api 并没有根据 xpath 查找元素的方法,所以 appium 是自己另外实现的。

    这里替换掉字符的原因应该是基于安全性考虑( evaluate 是一个权限很高的函数,不对参数进行过滤的话很危险)。

    @doctorq 我们那个项目第一个任务可以考虑修复这个问题,让 appium 支持通过含有非 ascii 字符的 xpath 来查找元素。

  • 我的意思是你的 Java 文件应该使用 utf-8 编码。这样中文的编码才和 app 对应元素一致(android testview 内容默认使用 utf-8 编码)。
    不过我也没使用 Java 的 client 试验过。明天试一下。

  • 你用的什么编码?是 utf-8 吗?
    要支持中文应该要 utf-8 的吧。
    可以肯定 appium 的 xpath 定位应该是支持中文的。

  • @doctorq 对,其实就是一种流行的 http api 编写风格。因为没有严格的规定,所以连规范都说不上。
    曾经尝试写一个系统的 REST api,查了不少资料。当时天真地以为 REST api+html5 可以一套代码通杀 Web+Mobile,结果被 Android 的 html5 性能打败了……

  • @doctorq ok,我更新一下文章

  • REST 是指 RESTful 的 api 吗。个人理解就是 http 的 GET、POST、PUT 和 DELETE 对应资源的 CRUD(增删改查),把所有的对象(例如论坛里的用户、用户组、帖子、专区)都设计成资源。然后就能像写 CRUD 那样使用 api 了。
    话说 appium 使用 Http REST 方式是因为遵循了webDriver 的规范吧?

  • 你的代码是直接复制粘贴上来的吗?第一个代码的 xpath 有错,@text=‘中文'第一个引号是中文引号。

  • Appium 中 iOS 下的 Hybrid at 2015年02月23日

    学习了!这样的原理剖析不仅学会了原理,还知道了应该如何正确使用这个功能。

  • 能附上 Appium 端的错误信息吗?

  • @cosyman 所以我注明了 需要能连接国外网络。确实 chromedriver 没代理下不下来。你有调整过 reset.sh 来让它下载 chromedriver 的节点改为国内镜像吗?有的话麻烦在这里说一下,我更新到帖子内容里。谢谢。

  • @lihuazhang 现在还没做到这一步。我后面跑一下测试后再加上。看官方文档的话 unittest 只需要一个命令就可以跑了,不过里面有没有坑现在还不清楚……

  • @lihuazhang 好的,明白了。第一组双星号前面必须有一个空格。谢谢指导。