Merge "Adding capability to lookup many elements."
This commit is contained in:
@@ -16,12 +16,15 @@
|
||||
|
||||
package android.webkit.webdriver;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Mechanism to locate elements within the DOM of the page.
|
||||
* @hide
|
||||
*/
|
||||
public abstract class By {
|
||||
public abstract WebElement findElement(WebElement element);
|
||||
public abstract List<WebElement> findElements(WebElement element);
|
||||
|
||||
/**
|
||||
* Locates an element by its HTML id attribute.
|
||||
@@ -37,6 +40,11 @@ public abstract class By {
|
||||
return element.findElementById(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<WebElement> findElements(WebElement element) {
|
||||
return element.findElementsById(id); // Yes, it happens a lot.
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "By.id: " + id;
|
||||
@@ -59,6 +67,11 @@ public abstract class By {
|
||||
return element.findElementByLinkText(linkText);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<WebElement> findElements(WebElement element) {
|
||||
return element.findElementsByLinkText(linkText);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "By.linkText: " + linkText;
|
||||
@@ -83,6 +96,11 @@ public abstract class By {
|
||||
return element.findElementByPartialLinkText(linkText);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<WebElement> findElements(WebElement element) {
|
||||
return element.findElementsByPartialLinkText(linkText);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "By.partialLinkText: " + linkText;
|
||||
@@ -104,6 +122,11 @@ public abstract class By {
|
||||
return element.findElementByName(name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<WebElement> findElements(WebElement element) {
|
||||
return element.findElementsByName(name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "By.name: " + name;
|
||||
@@ -124,6 +147,11 @@ public abstract class By {
|
||||
return element.findElementByClassName(className);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<WebElement> findElements(WebElement element) {
|
||||
return element.findElementsByClassName(className);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "By.className: " + className;
|
||||
@@ -145,6 +173,11 @@ public abstract class By {
|
||||
return element.findElementByCss(css);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<WebElement> findElements(WebElement element) {
|
||||
return element.findElementsByCss(css);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "By.css: " + css;
|
||||
@@ -167,6 +200,11 @@ public abstract class By {
|
||||
return element.findElementByTagName(tagName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<WebElement> findElements(WebElement element) {
|
||||
return element.findElementsByTagName(tagName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "By.tagName: " + tagName;
|
||||
@@ -193,6 +231,11 @@ public abstract class By {
|
||||
return element.findElementByXPath(xpath);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<WebElement> findElements(WebElement element) {
|
||||
return element.findElementsByXPath(xpath);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "By.xpath: " + xpath;
|
||||
|
||||
@@ -16,19 +16,19 @@
|
||||
|
||||
package android.webkit.webdriver;
|
||||
|
||||
import com.google.android.collect.Lists;
|
||||
import com.google.android.collect.Maps;
|
||||
import android.os.Handler;
|
||||
import android.os.Message;
|
||||
import android.webkit.WebView;
|
||||
|
||||
import com.android.internal.R;
|
||||
|
||||
import com.google.android.collect.Lists;
|
||||
import com.google.android.collect.Maps;
|
||||
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import android.os.Handler;
|
||||
import android.os.Message;
|
||||
import android.webkit.WebView;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
@@ -190,7 +190,6 @@ public class WebDriver {
|
||||
WebchromeClientWrapper chromeWrapper = new WebchromeClientWrapper(
|
||||
webview.getWebChromeClient(), this);
|
||||
mWebView.setWebChromeClient(chromeWrapper);
|
||||
mDocumentElement = new WebElement(this, "");
|
||||
mWebView.addJavascriptInterface(new JavascriptResultReady(),
|
||||
"webdriver");
|
||||
}
|
||||
@@ -203,6 +202,7 @@ public class WebDriver {
|
||||
*/
|
||||
public void get(String url) {
|
||||
executeCommand(CMD_GET_URL, url, LOADING_TIMEOUT);
|
||||
mDocumentElement = (WebElement) executeScript("return document.body;");
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -223,9 +223,25 @@ public class WebDriver {
|
||||
* no matching element was found.
|
||||
*/
|
||||
public WebElement findElement(By by) {
|
||||
checkNotNull(mDocumentElement, "Load a page using WebDriver.get() "
|
||||
+ "before looking for elements.");
|
||||
return by.findElement(mDocumentElement);
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds all {@link android.webkit.webdriver.WebElement} within the page
|
||||
* using the given method.
|
||||
*
|
||||
* @param by The locating mechanism to use.
|
||||
* @return A list of all {@link android.webkit.webdriver.WebElement} found,
|
||||
* or an empty list if nothing matches.
|
||||
*/
|
||||
public List<WebElement> findElements(By by) {
|
||||
checkNotNull(mDocumentElement, "Load a page using WebDriver.get() "
|
||||
+ "before looking for elements.");
|
||||
return by.findElements(mDocumentElement);
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears the WebView.
|
||||
*/
|
||||
@@ -300,8 +316,10 @@ public class WebDriver {
|
||||
toReturn.append(toAdd.substring(0, toAdd.length() -1) + "}");
|
||||
} else if (args[i] instanceof WebElement) {
|
||||
// WebElement are represented in JavaScript by Objects as
|
||||
// follow: {ELEMENT:"id"}
|
||||
toReturn.append("{" + ELEMENT_KEY + ":\""
|
||||
// follow: {ELEMENT:"id"} where "id" refers to the id
|
||||
// of the HTML element in the javascript cache that can
|
||||
// be accessed throught bot.inject.cache.getCache_()
|
||||
toReturn.append("{\"" + ELEMENT_KEY + "\":\""
|
||||
+ ((WebElement) args[i]).getId() + "\"}");
|
||||
} else if (args[i] instanceof Number || args[i] instanceof Boolean) {
|
||||
toReturn.append(String.valueOf(args[i]));
|
||||
@@ -310,9 +328,10 @@ public class WebDriver {
|
||||
} else {
|
||||
throw new IllegalArgumentException(
|
||||
"Javascript arguments can be "
|
||||
+ "a Number, a Boolean, a String, a WebElement, "
|
||||
+ "or a List or a Map of those. Got: "
|
||||
+ ((args[i] == null) ? "null" : args[i].toString()));
|
||||
+ "a Number, a Boolean, a String, a WebElement, "
|
||||
+ "or a List or a Map of those. Got: "
|
||||
+ ((args[i] == null) ? "null" : args[i].getClass()
|
||||
+ ", value: " + args[i].toString()));
|
||||
}
|
||||
}
|
||||
return toReturn.toString();
|
||||
@@ -457,7 +476,7 @@ public class WebDriver {
|
||||
case STALE_ELEMENT_REFERENCE:
|
||||
throw new WebElementStaleException("WebElement is stale.");
|
||||
default:
|
||||
throw new RuntimeException("Error: " + errorMsg);
|
||||
throw new WebDriverException("Error: " + errorMsg);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -523,4 +542,10 @@ public class WebDriver {
|
||||
}
|
||||
return mJsResult;
|
||||
}
|
||||
|
||||
private void checkNotNull(Object obj, String errosMsg) {
|
||||
if (obj == null) {
|
||||
throw new NullPointerException(errosMsg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
38
core/java/android/webkit/webdriver/WebDriverException.java
Normal file
38
core/java/android/webkit/webdriver/WebDriverException.java
Normal file
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
* Copyright (C) 2011 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package android.webkit.webdriver;
|
||||
|
||||
/**
|
||||
* @hide
|
||||
*/
|
||||
public class WebDriverException extends RuntimeException {
|
||||
public WebDriverException() {
|
||||
super();
|
||||
}
|
||||
|
||||
public WebDriverException(String reason) {
|
||||
super(reason);
|
||||
}
|
||||
|
||||
public WebDriverException(String reason, Throwable cause) {
|
||||
super(reason, cause);
|
||||
}
|
||||
|
||||
public WebDriverException(Throwable cause) {
|
||||
super(cause);
|
||||
}
|
||||
}
|
||||
@@ -18,6 +18,8 @@ package android.webkit.webdriver;
|
||||
|
||||
import com.android.internal.R;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Represents an HTML element. Typically most interactions with a web page
|
||||
* will be performed through this class.
|
||||
@@ -28,6 +30,15 @@ public class WebElement {
|
||||
private final String mId;
|
||||
private final WebDriver mDriver;
|
||||
|
||||
private static final String LOCATOR_ID = "id";
|
||||
private static final String LOCATOR_LINK_TEXT = "linkText";
|
||||
private static final String LOCATOR_PARTIAL_LINK_TEXT = "partialLinkText";
|
||||
private static final String LOCATOR_NAME = "name";
|
||||
private static final String LOCATOR_CLASS_NAME = "className";
|
||||
private static final String LOCATOR_CSS = "css";
|
||||
private static final String LOCATOR_TAG_NAME = "tagName";
|
||||
private static final String LOCATOR_XPATH = "xpath";
|
||||
|
||||
/**
|
||||
* Package constructor to prevent clients from creating a new WebElement
|
||||
* instance.
|
||||
@@ -37,11 +48,10 @@ public class WebElement {
|
||||
* that can be accessed through JavaScript using "bot.inject.cache".
|
||||
*
|
||||
* @param driver The WebDriver instance to use.
|
||||
* @param id The index of the HTML element in the JavaSctipt cache. Pass
|
||||
* an empty String to indicate that this is the
|
||||
* @param id The index of the HTML element in the JavaSctipt cache.
|
||||
* document.documentElement object.
|
||||
*/
|
||||
/* Package */ WebElement(final WebDriver driver, final String id) {
|
||||
/* package */ WebElement(final WebDriver driver, final String id) {
|
||||
this.mId = id;
|
||||
this.mDriver = driver;
|
||||
}
|
||||
@@ -57,6 +67,18 @@ public class WebElement {
|
||||
return by.findElement(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds all {@link android.webkit.webdriver.WebElement} within the page
|
||||
* using the given method.
|
||||
*
|
||||
* @param by The locating mechanism to use.
|
||||
* @return A list of all {@link android.webkit.webdriver.WebElement} found,
|
||||
* or an empty list if nothing matches.
|
||||
*/
|
||||
public List<WebElement> findElements(final By by) {
|
||||
return by.findElements(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the visisble (i.e. not hidden by CSS) innerText of this element,
|
||||
* inlcuding sub-elements.
|
||||
@@ -67,47 +89,143 @@ public class WebElement {
|
||||
*/
|
||||
public String getText() {
|
||||
String getText = mDriver.getResourceAsString(R.raw.get_text_android);
|
||||
if (mId.equals("")) {
|
||||
return null;
|
||||
}
|
||||
return (String) executeAtom(getText, this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the value of an HTML attribute for this element or the value of the
|
||||
* property with the same name if the attribute is not present. If neither
|
||||
* is set, null is returned.
|
||||
*
|
||||
* @param attribute the HTML attribute.
|
||||
* @return the value of that attribute or the value of the property with the
|
||||
* same name if the attribute is not set, or null if neither are set. For
|
||||
* boolean attribute values this will return the string "true" or "false".
|
||||
*/
|
||||
public String getAttribute(String attribute) {
|
||||
String getAttribute = mDriver.getResourceAsString(
|
||||
R.raw.get_attribute_value_android);
|
||||
return (String) executeAtom(getAttribute, this, attribute);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the tag name of this element.
|
||||
*/
|
||||
public String getTagName() {
|
||||
return (String) mDriver.executeScript("return arguments[0].tagName;",
|
||||
this);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return true if this element is enabled, false otherwise.
|
||||
*/
|
||||
public boolean isEnabled() {
|
||||
String isEnabled = mDriver.getResourceAsString(
|
||||
R.raw.is_enabled_android);
|
||||
return (Boolean) executeAtom(isEnabled, this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines whether this element is selected or not. This applies to input
|
||||
* elements such as checkboxes, options in a select, and radio buttons.
|
||||
*
|
||||
* @return True if this element is selected, false otherwise.
|
||||
*/
|
||||
public boolean isSelected() {
|
||||
String isSelected = mDriver.getResourceAsString(
|
||||
R.raw.is_selected_android);
|
||||
return (Boolean) executeAtom(isSelected, this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Selects an element on the page. This works for selecting checkboxes,
|
||||
* options in a select, and radio buttons.
|
||||
*/
|
||||
public void setSelected() {
|
||||
String setSelected = mDriver.getResourceAsString(
|
||||
R.raw.set_selected_android);
|
||||
executeAtom(setSelected, this);
|
||||
}
|
||||
|
||||
/**
|
||||
* This toggles the checkboxe state from selected to not selected, or
|
||||
* from not selected to selected.
|
||||
*
|
||||
* @return True if the toggled element is selected, false otherwise.
|
||||
*/
|
||||
public boolean toggle() {
|
||||
String toggle = mDriver.getResourceAsString(R.raw.toggle_android);
|
||||
return (Boolean) executeAtom(toggle, this);
|
||||
}
|
||||
|
||||
/*package*/ String getId() {
|
||||
return mId;
|
||||
}
|
||||
|
||||
/* package */ WebElement findElementById(final String locator) {
|
||||
return findElement("id", locator);
|
||||
return findElement(LOCATOR_ID, locator);
|
||||
}
|
||||
|
||||
/* package */ WebElement findElementByLinkText(final String linkText) {
|
||||
return findElement("linkText", linkText);
|
||||
return findElement(LOCATOR_LINK_TEXT, linkText);
|
||||
}
|
||||
|
||||
/* package */ WebElement findElementByPartialLinkText(
|
||||
final String linkText) {
|
||||
return findElement("partialLinkText", linkText);
|
||||
return findElement(LOCATOR_PARTIAL_LINK_TEXT, linkText);
|
||||
}
|
||||
|
||||
/* package */ WebElement findElementByName(final String name) {
|
||||
return findElement("name", name);
|
||||
return findElement(LOCATOR_NAME, name);
|
||||
}
|
||||
|
||||
/* package */ WebElement findElementByClassName(final String className) {
|
||||
return findElement("className", className);
|
||||
return findElement(LOCATOR_CLASS_NAME, className);
|
||||
}
|
||||
|
||||
/* package */ WebElement findElementByCss(final String css) {
|
||||
return findElement("css", css);
|
||||
return findElement(LOCATOR_CSS, css);
|
||||
}
|
||||
|
||||
/* package */ WebElement findElementByTagName(final String tagName) {
|
||||
return findElement("tagName", tagName);
|
||||
return findElement(LOCATOR_TAG_NAME, tagName);
|
||||
}
|
||||
|
||||
/* package */ WebElement findElementByXPath(final String xpath) {
|
||||
return findElement("xpath", xpath);
|
||||
return findElement(LOCATOR_XPATH, xpath);
|
||||
}
|
||||
|
||||
/* package */ List<WebElement> findElementsById(final String locator) {
|
||||
return findElements(LOCATOR_ID, locator);
|
||||
}
|
||||
|
||||
/* package */ List<WebElement> findElementsByLinkText(final String linkText) {
|
||||
return findElements(LOCATOR_LINK_TEXT, linkText);
|
||||
}
|
||||
|
||||
/* package */ List<WebElement> findElementsByPartialLinkText(
|
||||
final String linkText) {
|
||||
return findElements(LOCATOR_PARTIAL_LINK_TEXT, linkText);
|
||||
}
|
||||
|
||||
/* package */ List<WebElement> findElementsByName(final String name) {
|
||||
return findElements(LOCATOR_NAME, name);
|
||||
}
|
||||
|
||||
/* package */ List<WebElement> findElementsByClassName(final String className) {
|
||||
return findElements(LOCATOR_CLASS_NAME, className);
|
||||
}
|
||||
|
||||
/* package */ List<WebElement> findElementsByCss(final String css) {
|
||||
return findElements(LOCATOR_CSS, css);
|
||||
}
|
||||
|
||||
/* package */ List<WebElement> findElementsByTagName(final String tagName) {
|
||||
return findElements(LOCATOR_TAG_NAME, tagName);
|
||||
}
|
||||
|
||||
/* package */ List<WebElement> findElementsByXPath(final String xpath) {
|
||||
return findElements(LOCATOR_XPATH, xpath);
|
||||
}
|
||||
|
||||
private Object executeAtom(final String atom, final Object... args) {
|
||||
@@ -116,17 +234,18 @@ public class WebElement {
|
||||
atom + ")(" + scriptArgs + ")");
|
||||
}
|
||||
|
||||
private List<WebElement> findElements(String strategy, String locator) {
|
||||
String findElements = mDriver.getResourceAsString(
|
||||
R.raw.find_elements_android);
|
||||
return (List<WebElement>) executeAtom(findElements,
|
||||
strategy, locator, this);
|
||||
}
|
||||
|
||||
private WebElement findElement(String strategy, String locator) {
|
||||
String findElement = mDriver.getResourceAsString(
|
||||
R.raw.find_element_android);
|
||||
WebElement el;
|
||||
if (mId.equals("")) {
|
||||
// Use default as root which is the document object
|
||||
el = (WebElement) executeAtom(findElement, strategy, locator);
|
||||
} else {
|
||||
// Use this as root
|
||||
el = (WebElement) executeAtom(findElement, strategy, locator, this);
|
||||
}
|
||||
WebElement el = (WebElement) executeAtom(findElement,
|
||||
strategy, locator, this);
|
||||
if (el == null) {
|
||||
throw new WebElementNotFoundException("Could not find element "
|
||||
+ "with " + strategy + ": " + locator);
|
||||
|
||||
Reference in New Issue
Block a user