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

    版权声明:本文为博主原创文章,未经博主同意不得转载。

    https://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()为例,

    	public ArrayList<WebElement> getWebElements(boolean onlySufficientlyVisible){
    		boolean javaScriptWasExecuted = executeJavaScriptFunction("allWebElements();");
    		
    		return getWebElements(javaScriptWasExecuted, onlySufficientlyVisible);
    	}
    	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:
    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)
    	@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

    	public void enableJavascriptAndSetRobotiumWebClient(List<WebView> 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等字段。


    	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<String, String> attributes = new Hashtable<String, String>();
    		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;
    	}
    	/**
    	 * Sets the location of a {@code WebElement} 
    	 * 
    	 * @param webElement the {@code TextView} object to set location 
    	 * @param webView the {@code WebView} the text is shown in
    	 * @param x the x location to set
    	 * @param y the y location to set
    	 * @param width the width to set
    	 * @param height the height to set
    	 */
    
    	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一样依据坐标发送点击事件。



查看全文
  • 相关阅读:
    js 检测浏览器
    js获取url参数
    js 使用Math函数取得数组最大最少值
    js 取一定范围内的整数
    遍历文件夹内所有文件
    'weinre' 不是内部或外部命令,也不是可运行的程序 或批处理文件。 解决方案
    解决图片缓存导致页面刷新无效果问题
    JAVA中的几种基本数据类型是什么,各自占用多少字节
    Mac终端git,svn提交代码步骤
    小程序分享链接功能
  • 原文地址:https://www.cnblogs.com/ldxsuanfa/p/10705119.html
  • Copyright © 2011-2022 走看看