分享

robotium原理之获取WebElement元素

 liuchangxin81 2014-09-11
  robotium框架支持WebView,在robotium中有getWebElements()、getWebElements(By by)等方法来获取android中的WebView的元素,并提供了 clickOnWebElement方法来完成点击事件.android中的原生控件是比较好攻取的,那么对于WebView这个框架是怎么获取的呢。
第一步:利用JS获取页面中的所有元素 
         在PC上,获取网页的元素可以通过注入javascript元素来完成,以Chrome浏览器为例,打开工具——JavaScript控制台(快捷方 式:Ctrl+Shift+J),输入 javascript:prompt(document.URL)即会弹出含当前页面的URL的提示框,因此通过编写适当的JS脚本是可以在这个弹出框中 显示所有页面元素的。RobotiumWeb.js就是此功能实现用的JS脚本。以solo中getWebElements()为例,
  1. public ArrayList<WebElement> getWebElements(boolean onlySufficientlyVisible){  
  2.     boolean javaScriptWasExecuted = executeJavaScriptFunction("allWebElements();");  
  3.       
  4.     return getWebElements(javaScriptWasExecuted, onlySufficientlyVisible);  
  5. }  
  1. private boolean executeJavaScriptFunction(final String function){  
  2.     final WebView webView = viewFetcher.getFreshestView(viewFetcher.getCurrentViews(WebView.classtrue));  
  3.   
  4.     if(webView == null){  
  5.         return false;  
  6.     }  
  7.                //做一些JS注入执行前的准备工作,例如将WebView设为可允许执行JS等,并将RobotiumWeb.js中的脚本以String形式返回  
  8.     final String javaScript = prepareForStartOfJavascriptExecution();  
  9.   
  10.     activityUtils.getCurrentActivity(false).runOnUiThread(new Runnable() {  
  11.         public void run() {  
  12.             if(webView != null){  
  13.                 webView.loadUrl("javascript:" + javaScript + function);  
  14.             }  
  15.         }  
  16.     });  
  17.     return true;  
  18. }  
        可以看出这个方法执行的是allWebElements();函数,即类似执行RobotiumWeb.js文件中如下JS代码片段:
可以把如下片段放到JavaScript控制台中看效果
[javascript] view plaincopy在CODE上查看代码片派生到我的代码片
  1. javascript:  
  2. function allWebElements() {  
  3.     for (var key in document.all){  
  4.         try{  
  5.             promptElement(document.all[key]);   //调用promptElement(element)函数          
  6.         }catch(ignored){}  
  7.     }  
  8.     finished();    //执行完后,调用finished()函数  
  9. }  
  10.   
  11. function promptElement(element) {  
  12.     var id = element.id;  
  13.     var text = element.innerText;  
  14.     if(text.trim().length == 0){  
  15.         text = element.value;  
  16.     }  
  17.     var name = element.getAttribute('name');  
  18.     var className = element.className;  
  19.     var tagName = element.tagName;  
  20.     var attributes = "";  
  21.     var htmlAttributes = element.attributes;  
  22.     for (var i = 0, htmlAttribute; htmlAttribute = htmlAttributes[i]; i++){  
  23.         attributes += htmlAttribute.name + "::" + htmlAttribute.value;  
  24.         if (i + 1 < htmlAttributes.length) {  
  25.             attributes += "#$";  
  26.         }  
  27.     }  
  28.   
  29.     var rect = element.getBoundingClientRect();  
  30.     if(rect.width > 0 && rect.height > 0 && rect.left >= 0 && rect.top >= 0){  
  31.         prompt(id + ';,' + text + ';,' + name + ";," + className + ";," + tagName + ";," + rect.left + ';,' + rect.top + ';,' + rect.width + ';,' + rect.height + ';,' + attributes);   //弹出包含id、text、name等字段的提示框  
  32.     }  
  33. }  
  34. function finished(){  
  35.     prompt('robotium-finished');    //弹出包含robotium-finished字符串的提示框,用于标识脚本注入执行结束  
  36. }  

        从脚本中可以看出JS获得页面元素后还进行了一定的格式化处理,在每个元素之间加了;,符号,这也是为了在后面代码中更加方便地解析。脚本的最后调用了 finished()函数,即弹出包含robotium-finished的提示框。这一步完成了页面元素的获取,那么提示框中包含的内容在 Android中怎么获取呢?

第二步:在Android中获取WebView中prompt提示框中的信息
        在Android的Webkit包中有个WebChromeClient类,这个类中的onJsPrompt方法就是用于处理WebView中的提示框 的,当WebView中有JS提示框时,会回调该方法,String message参数将包含提示框中的信息,因此robotium写了个继承自WebChromeClient类的RobotiumWebClient类。 覆写了onJsPrompt
onJsPrompt(WebView view, String url, String message, String defaultValue, JsPromptResult result)
  1. @Override  
  2. public boolean onJsPrompt(WebView view, String url, String message, String defaultValue, JsPromptResult r) {  
  3.   
  4.     if(message != null && (message.contains(";,") || message.contains("robotium-finished"))){  
  5.                        //如果提示框中包含robotium-finished字符串,即表示那段JS注入脚本执行完毕了  
  6.         if(message.equals("robotium-finished")){  
  7.             webElementCreator.setFinished(true);  
  8.         }  
  9.         else{  
  10.             webElementCreator.createWebElementAndAddInList(message, view);//有人提示框中的内容,那么就可以对提示框中的内容进行处理了  
  11.         }  
  12.         r.confirm();  
  13.         return true;  
  14.     }  
  15.     else {  
  16.         if(originalWebChromeClient != null) {  
  17.             return originalWebChromeClient.onJsPrompt(view, url, message, defaultValue, r);   
  18.         }  
  19.         return true;  
  20.     }  
  21.   
  22. }  
      
     另外,原本的WebView默认是不允许执行JS的,因此需要先执行enableJavascriptAndSetRobotiumWebClient方法。将JavaScriptEnabled设置为true,将将WebChromeClient设置为robotiumWebClient
  1. public void enableJavascriptAndSetRobotiumWebClient(List<WebView> webViews, WebChromeClient originalWebChromeClient){  
  2.     this.originalWebChromeClient = originalWebChromeClient;  
  3.   
  4.     for(final WebView webView : webViews){  
  5.   
  6.         if(webView != null){   
  7.             inst.runOnMainSync(new Runnable() {  
  8.                 public void run() {  
  9.                     webView.getSettings().setJavaScriptEnabled(true);  
  10.                     webView.setWebChromeClient(robotiumWebClient);  
  11.   
  12.                 }  
  13.             });  
  14.         }  
  15.     }  
  16. }  
第三步:将提示框中的消息存入WebElement Java bean中
        获取到了prompt提示框中的消息后,接下来就是对这些已经过处理含特殊格式的消息进行解析处理了,依次得到WebElement的id、text、name等字段。

  1. private WebElement createWebElementAndSetLocation(String information, WebView webView){  
  2.     String[] data = information.split(";,");            //将消息按;,符号分割,其中;,符号是在前面执行JS时加入的  
  3.     String[] elements = null;  
  4.     int x = 0;  
  5.     int y = 0;  
  6.     int width = 0;  
  7.     int height = 0;  
  8.     Hashtable<String, String> attributes = new Hashtable<String, String>();  
  9.     try{  
  10.         x = Math.round(Float.valueOf(data[5]));  
  11.         y = Math.round(Float.valueOf(data[6]));  
  12.         width = Math.round(Float.valueOf(data[7]));  
  13.         height = Math.round(Float.valueOf(data[8]));      
  14.         elements = data[9].split("\\#\\$");  
  15.     }catch(Exception ignored){}  
  16.   
  17.     if(elements != null) {  
  18.         for (int index = 0; index < elements.length; index++){  
  19.             String[] element = elements[index].split("::");  
  20.             if (element.length > 1) {  
  21.                 attributes.put(element[0], element[1]);  
  22.             } else {  
  23.                 attributes.put(element[0], element[0]);  
  24.             }  
  25.         }  
  26.     }  
  27.   
  28.     WebElement webElement = null;  
  29.   
  30.     try{  
  31.         webElement = new WebElement(data[0], data[1], data[2], data[3], data[4], attributes);//将id、text、name等字段存入  
  32.         setLocation(webElement, webView, x, y, width, height);  
  33.     }catch(Exception ignored) {}  
  34.   
  35.     return webElement;  
  36. }  
  1. /** 
  2.  * Sets the location of a {@code WebElement}  
  3.  *  
  4.  * @param webElement the {@code TextView} object to set location  
  5.  * @param webView the {@code WebView} the text is shown in 
  6.  * @param x the x location to set 
  7.  * @param y the y location to set 
  8.  * @param width the width to set 
  9.  * @param height the height to set 
  10.  */  
  11.   
  12. private void setLocation(WebElement webElement, WebView webView, int x, int y, int width, int height ){  
  13.     float scale = webView.getScale();  
  14.     int[] locationOfWebViewXY = new int[2];  
  15.     webView.getLocationOnScreen(locationOfWebViewXY);  
  16.   
  17.     int locationX = (int) (locationOfWebViewXY[0] + (x + (Math.floor(width / 2))) * scale);  
  18.     int locationY = (int) (locationOfWebViewXY[1] + (y + (Math.floor(height / 2))) * scale);  
  19.   
  20.     webElement.setLocationX(locationX);  
  21.     webElement.setLocationY(locationY);  
  22. }  
至此,WebElement对象中包含了id、text、name等字段,还包含了x、y坐标,知道了坐标后就可以像其它Android中的原生View一样根据坐标发送点击事件。

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多