Merge "Adding capability to lookup many elements."

This commit is contained in:
John Reck
2011-04-26 09:31:48 -07:00
committed by Android (Google) Code Review
4 changed files with 260 additions and 35 deletions

View File

@@ -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;

View File

@@ -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);
}
}
}

View 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);
}
}

View File

@@ -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);