iQuery的一个主要目标是提供一个跨平台的控件查询机制,那需要考虑如下几个平台差异性:

  编程语言的差异,例如iOS可以使用Object-C、JavaScript等语言编程,Android平台使用Java,而Windows 8平台使用C#、C++,网页自动化程序例如Selenium又支持很多编程语言。

  iQuery在设计时考虑到这些差异性,我们复用antlr这个工具,它已经提供了生成多种编程语言代码的功能,可以很快生成C#、C++、JavaScript、Java、Object-C、Python、Ruby等代码,这样只要维护一套语法可以了。

  虽然当前只实现了Java和JavaScript的版本,但对其他编程语言的支持也很容易实现,这是iQuery的第一个扩展点。

  控件的差异性,不仅各平台有一些不同的控件,例如Android上的ExpandableListView在 iOS上找不到对应的控件,而且同一个控件在不同平台的名字也不一样,例如iOS上的UIASwitch基本上可以等价于Android和 Windows 8上的Radio,这样都是在设计iQuery都需要去考虑的。

  针对各平台控件的差异性,iQuery的做法是提供按类型名查询控件的语法,例如在Andorid上可以直接用

  “>> ExpandableListView”

  这样的查询语句找到界面上所有的ExpandableListView,而在iOS上可以使用

  “>> UIAScrollView”

  而针对同一控件在各平台名字不同的情况,iQuery的做法是提供一个伪类的概念,这个概念也是借自jQuery,例如下面的伪类统一表示了各平台下可以当作单选框按钮的控件:

  “:radio”

  然而,在实现iQuery的时候,我们并不能假设:radio在Android上是RadioButton控件,在iOS上应该是 UIASwitch,在Windows Phone上是RadioBox控件,因此我们决定将定义伪类的控制权交给开发者,这是iQuery的第二个扩展点。

  控件属性的差异性,例如同是按钮控件,在iOS上是UIAButton.name(),而在Android上却又是mText属性,甚至有些属性在不同的平台上,有的存在,有的不存在,比如说Android上有mBottom属性,在iOS上不存在。

  跟解决控件的差异性方法类似,iQuery除了提供按属性名和方法名读取属性值以外,例如

  iOS上的

  “:button [name = ‘确定’]”

  Android上的

  “:button [mText = ‘确定’]”

  为了统一表达控件在各平台通用的属性,iQuery还提供了伪属性的概念,比如上面的例子可以使用下面的查询完成:

  “:button [:text = ‘确定’]”

  跟伪类一样,伪属性也是可以由开发者自定义,这是iQuery的第三个扩展点。

  UI自动化框架的差异,例如 iOS UI自动化测试的框架必然和Andorid UI自动化测试框架不同,但无论如何不同,当今大部分操作系统的图形界面都有下面的性质:

  界面由控件树组成。

  各控件有属性的概念。

  为了尽可能的支持更多的框架,iQuery将上面的性质封装成两个接口,因此对新平台的支持,只要实现这两个接口可以了

  本文介绍扩展伪类、伪属性和添加对新平台支持的方法,后续文章会解释支持其他编程语言的做法。

  扩展伪类

  在Java版本中,在iQA.Runtime.jar包里,可以通过iQueryParser. registerPseudoClass这个函数注册一个新的伪类,步骤如下:

  使用

  iQueryParser. createParser(String iquery, boolean registerPseudo)

  创建一个iQueryParser实例。

  再使用iQueryParser. registerPseudoClass(String name, IPseudoClass func)注册一个新的伪类,例如下面的代码,注册一个名为text的伪类,过滤方式为所有类型名以EditText结尾的控件:

  parser.registerPseudoClass("text", new IPseudoClass() {

  public boolean resolve(ITreeNode node) {

  return filterByNameEndsWith(node, "EditText");

  }

  });

  在JavaScript版本中,暂时不支持扩展伪类的做法,后续版本会添加这个功能。

  扩展伪属性

  在Java版中,通过iQueryParser.registerPseudoAttribute函数注册一个新的伪属性,步骤如下:

  使用iQueryParser. createParser(String iquery, boolean registerPseudo)创建一个iQueryParser实例。

  再使用iQueryParser. registerPseudoAttribute (String name, IPseudoAttribute func)注册一个新的伪属性,例如下面的代码,注册一个名为bottom的伪类:

  parser.registerPseudoAttribute("bottom", new IPseudoAttribute() {

  public String resolve(ITreeNode node) {

  return node.getProperty("mBottom").getValue();

  }

  });

  在iOS的JavaScript版本中的做法是:

  引入以下几个JavaScript文件:

  #import "common.js";

  #import "antlr3-all-min.js";

  #import "iQueryLexer.js";

  #import "iQueryParser.js";

  #import "error.js";

  创建一个iQuery实例:

  var iq = new iQuery(selector);

  注册伪属性:

  iq.parser.registerPseudoAttrs("bottom", function(uiaobj) {

  if ( uiaobj != undefined && uiaobj.rect != undefined ) {

  var rect = uiaobj.rect();

  return rect.origin.y + rect.size.height;

  }

  });

  添加对新平台的支持

  当前支持Java版本的扩展,扩展方式是:

  在工程里添加iQA.Runtime.jar包依赖。