zoukankan      html  css  js  c++  java
  • robotium原理之获取WebElement元素

    转自:http://blog.csdn.net/hunterno4/article/details/35569665

          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()为例,
     
    [java]
    public ArrayList getWebElements(boolean onlySufficientlyVisible){
    1. boolean javaScriptWasExecuted = executeJavaScriptFunction("allWebElements();");
    2. return getWebElements(javaScriptWasExecuted, onlySufficientlyVisible);
    3. }
        public ArrayList getWebElements(boolean onlySufficientlyVisible){
                    boolean javaScriptWasExecuted = executeJavaScriptFunction("allWebElements();");
                    
                    return getWebElements(javaScriptWasExecuted, onlySufficientlyVisible);
            }
    
    [java]
     
    1. private boolean executeJavaScriptFunction(final String function){
    2. final WebView webView = viewFetcher.getFreshestView(viewFetcher.getCurrentViews(WebView.class, true));
    3. if(webView == null){
    4. return false;
    5. }
    6. //做一些JS注入执行前的准备工作,例如将WebView设为可允许执行JS等,并将RobotiumWeb.js中的脚本以String形式返回
    7. final String javaScript = prepareForStartOfJavascriptExecution();
    8. activityUtils.getCurrentActivity(false).runOnUiThread(new Runnable() {
    9. public void run() {
    10. if(webView != null){
    11. webView.loadUrl("javascript:" + javaScript + function);
    12. }
    13. }
    14. });
    15. return true;
    16. }
          private boolean executeJavaScriptFunction(final String function){
                    final WebView webView = viewFetcher.getFreshestView(viewFetcher.getCurrentViews(WebView.class, true));
    
                    if(webView == null){
                            return false;
                    }
                    //做一些JS注入执行前的准备工作,例如将WebView设为可允许执行JS等,并将RobotiumWeb.js中的脚本以String形式返回
                    final String javaScript = prepareForStartOfJavascriptExecution();
    
                    activityUtils.getCurrentActivity(false).runOnUiThread(new Runnable() {
                            public void run() {
                                    if(webView != null){
                                            webView.loadUrl("javascript:" + javaScript + function);
                                    }
                            }
                    });
                    return true;
            }
    
    可以看出这个方法执行的是allWebElements();函数,即类似执行RobotiumWeb.js文件中如下JS代码片段:
    可以把如下片段放到JavaScript控制台中看效果
     
    javascript:
    1. function allWebElements() {
    2. for (var key in document.all){
    3. try{
    4. promptElement(document.all[key]); //调用promptElement(element)函数
    5. }catch(ignored){}
    6. }
    7. finished(); //执行完后,调用finished()函数
    8. }
    9. function promptElement(element) {
    10. var id = element.id;
    11. var text = element.innerText;
    12. if(text.trim().length == 0){
    13. text = element.value;
    14. }
    15. var name = element.getAttribute('name');
    16. var className = element.className;
    17. var tagName = element.tagName;
    18. var attributes = "";
    19. var htmlAttributes = element.attributes;
    20. for (var i = 0, htmlAttribute; htmlAttribute = htmlAttributes[i]; i++){
    21. attributes += htmlAttribute.name + "::" + htmlAttribute.value;
    22. if (i + 1 < htmlAttributes.length) {
    23. attributes += "#$";
    24. }
    25. }
    26. var rect = element.getBoundingClientRect();
    27. if(rect.width > 0 && rect.height > 0 && rect.left >= 0 && rect.top >= 0){
    28. prompt(id + ';,' + text + ';,' + name + ";," + className + ";," + tagName + ";," + rect.left + ';,' + rect.top + ';,' + rect.width + ';,' + rect.height + ';,' + attributes); //弹出包含id、text、name等字段的提示框
    29. }
    30. }
    31. function finished(){
    32. prompt('robotium-finished'); //弹出包含robotium-finished字符串的提示框,用于标识脚本注入执行结束
    33. }
    javascript:
    function allWebElements() {
            for (var key in document.all){
                    try{
                            promptElement(document.all[key]);       //调用promptElement(element)函数            
                    }catch(ignored){}
            }
            finished();    //执行完后,调用finished()函数
    }
    
    function promptElement(element) {
            var id = element.id;
            var text = element.innerText;
            if(text.trim().length == 0){
                    text = element.value;
            }
            var name = element.getAttribute('name');
            var className = element.className;
            var tagName = element.tagName;
            var attributes = "";
            var htmlAttributes = element.attributes;
            for (var i = 0, htmlAttribute; htmlAttribute = htmlAttributes[i]; i++){
                    attributes += htmlAttribute.name + "::" + htmlAttribute.value;
                    if (i + 1 < htmlAttributes.length) {
                            attributes += "#$";
                    }
            }
    
            var rect = element.getBoundingClientRect();
            if(rect.width > 0 && rect.height > 0 && rect.left >= 0 && rect.top >= 0){
                    prompt(id + ';,' + text + ';,' + name + ";," + className + ";," + tagName + ";," + rect.left + ';,' + rect.top + ';,' + rect.width + ';,' + rect.height + ';,' + attributes);   //弹出包含id、text、name等字段的提示框
            }
    }
    function finished(){
            prompt('robotium-finished');    //弹出包含robotium-finished字符串的提示框,用于标识脚本注入执行结束
    }
    

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

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

    网页:http://www.robotium.cn/archives/239,也提到了一种方法:
        Robotium不支持操作WebView的时候为了解决自动化测试过程中处理内嵌WebView应用的网页元素时通常采用向WebView中注入JavaScript来操作页面元素并可进行回调。原理很简单,首先得到程序内嵌WebView控件然后设置其js可用并注入js脚本,交给脚本处理。
    public void testAddJsToWebView(){
    class ExecObj{
    public void showMessage(){
    Toast.makeText(solo.getCurrentActivity(), "注入js,由js处理并调用ExecObj对象的方法!", Toast.LENGTH_LONG).show();
    }
    }
    solo.sleep(1000);
    //获得webview控件
    webView = (WebView) solo.getView(R.id.webView);
    webView.loadUrl("http://www.robotium.cn");
    solo.sleep(4000);
    webView.getSettings().setJavaScriptEnabled(true);
    webView.addJavascriptInterface(new ExecObj(), "injectedObject");
    webView.loadUrl("javascript:injectedObject.showMessage()");
    solo.sleep(10000);
    }
    
    ----夫英雄者,胸怀大志,腹有良谋,有包藏宇宙之机,吞吐天地之志者也。
  • 相关阅读:
    Hash 函数资源链接汇总
    Zookeeper 初体验之——伪分布式安装
    Zookeeper 初体验之——JAVA API 初探
    布隆过滤器(Bloom Filter)详解
    7天学会Maven(第一天——了解 Maven)
    仿中关村在线首页弹出式广告插件(jQuery版)
    介绍几款在线代码编辑器
    兼容浏览 firefox、chrome、ie 的flash(swf) 的代码!
    我的Discuz!X2 、Ucenter 1.6、ASP.NET 应用程序整合经历
    2011年最后一博:仿HAO123的邮箱登录
  • 原文地址:https://www.cnblogs.com/eagleking0318/p/6520953.html
Copyright © 2011-2022 走看看