/* MonkeyTalk - a cross-platform functional testing tool
Copyright (C) 2012 Gorilla Logic, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>. */
package com.gorillalogic.agents.html.automators;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.openqa.selenium.By;
import org.openqa.selenium.Dimension;
import org.openqa.selenium.NoSuchElementException;
import org.openqa.selenium.Point;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import com.gorillalogic.monkeytalk.Command;
import com.gorillalogic.monkeytalk.automators.AutomatorConstants;
public class WebElementAutomator extends AutomatorBase {
public static String componentType = "HtmlTag";
public static String[] aliases = { "View" };
private WebElement element = null;
@Override
public String getComponentType() {
return componentType;
}
@Override
public void init(WebDriver driver, String monkeyId) {
super.init(driver, monkeyId);
}
public WebElement getElement() {
if (element != null) {
return element;
}
String xpath = getLocatorExpr();
try {
List<WebElement> elements = driver.findElements(By.xpath(xpath));
if (elements.size() == 0) {
throw new IllegalArgumentException("Unable to find html element with monkeyID '" + this.monkeyId + "'");
}
int monkeyIndex = 0;
if (getMonkeyOrdinal() != null) {
String monkeyOrdinal = getMonkeyOrdinal().get(1);
monkeyIndex = Integer.parseInt(monkeyOrdinal) - 1;
if (Integer.parseInt(monkeyOrdinal) > elements.size())
throw new IllegalArgumentException("Unable to find html element with monkeyID '" + this.monkeyId + "'");
}
driver.switchTo().defaultContent();
element = elements.get(monkeyIndex);
} catch (NoSuchElementException e1) {
// OK
}
if (element != null) {
return element;
}
List<WebElement> frameset = driver.findElements(By.tagName("iframe"));
for (WebElement framename : frameset) {
try {
List<WebElement> elements = driver.findElements(By.xpath(xpath));
int monkeyIndex = 0;
if (getMonkeyOrdinal() != null) {
String monkeyOrdinal = getMonkeyOrdinal().get(1);
monkeyIndex = Integer.parseInt(monkeyOrdinal) - 1;
if (Integer.parseInt(monkeyOrdinal) > elements.size())
throw new IllegalArgumentException("Unable to find button with monkeyID '" + this.monkeyId + "'");
}
driver.switchTo().defaultContent();
driver.switchTo().frame(framename);
element = elements.get(monkeyIndex);
break;
} catch (NoSuchElementException e) {
}
}
if (element == null) {
throw new IllegalArgumentException("Unable to find " + getComponentType()
+ " with monkeyId '" + monkeyId + "'");
}
return element;
}
public String getElementExpr() {
return "//*";
}
public String getOrdinal() {
String firstChar = this.monkeyId.substring(0, 1);
if (firstChar.equals("#"))
return this.monkeyId.substring(1);
else if (this.monkeyId.equals("*"))
return "1";
return null;
}
public ArrayList<String> getMonkeyOrdinal() {
Pattern pattern = Pattern.compile("\\(\\d+\\)$");
// String firstChar = this.monkeyId.substring(0, 1);
Matcher matcher = pattern.matcher(this.monkeyId);
if (matcher.find()) {
String ordinal = matcher.group().replace("(", "");
ordinal = ordinal.replace(")", "");
// this.monkeyId = this.monkeyId.substring(0, matcher.start());
ArrayList<String> ordinalMID = new ArrayList<String>();
ordinalMID.add(this.monkeyId.substring(0, matcher.start()));
ordinalMID.add(ordinal);
return ordinalMID;
}
return null;
}
/**
* "where" part of xpath expr (without []'s)
*
* @param monkeyId
* @return
*/
public String getWhereExpr(String monkeyId) {
String eqId = " = '" + monkeyId + "'";
// The "." in the expession below matches the textContent of the node.
// For some (perhaps never TBD) reason, @textContent doesn't match
// when nodes contain tags, for example <a>this is <b>it</b></a>
// . == 'this is it' matches but @textContent='this is it' doesn't
// even though element.textContent will return 'this is it'.
return "@id" + eqId + " or @name" + eqId + " or @value" + eqId + " or . " + eqId
+ " or @title" + eqId + " or @styleClass" + eqId;
}
@Override
public String play(Command command) {
String action = command.getAction();
if (isTapAction(action)) {
tap();
return null;
}
if (isEnterTextAction(action)) {
enterText(command);
return null;
}
return super.play(command);
}
protected void clear(Command command) {
getElement().clear();
}
protected void enterText(Command command) {
getElement().sendKeys(getArg(command, 0));
if ("enter".equals(getOptArg(command, 1))) {
getElement().sendKeys("\n");
}
}
private boolean isEnterTextAction(String action) {
return action.equalsIgnoreCase(AutomatorConstants.ACTION_ENTER_TEXT);
}
/**
* Override to customize tap
*/
protected void tap() {
getElement().click();
//new WebDriverBackedSelenium(driver, driver.getCurrentUrl()).clickAt(getLocatorExpr(),"0,0");
}
/**
* Override to customize what actions are taps
*/
private final static String[] taps = { AutomatorConstants.ACTION_TAP,
AutomatorConstants.ACTION_CLICK };
protected boolean isTapAction(String action) {
for (String s : taps) {
if (action.equalsIgnoreCase(s)) {
return true;
}
}
return false;
}
protected String getLocatorExpr() {
String where = getWhereExpr(this.monkeyId);
String base = getElementExpr();
if (getMonkeyOrdinal() != null) {
String strippedId = getMonkeyOrdinal().get(0);
where = getWhereExpr(strippedId);
} else if (getOrdinal() != null) {
where = getOrdinal();
base = "(" + base + ")";
}
return base + "[" + where + "]";
}
@Override
protected void assertExists() {
getElement();
}
@Override
protected String getProperty(String prop) {
String value = getElement().getAttribute(prop);
return value == null ? super.getProperty(prop) : value;
}
@Override
protected Rect getBoundingRectangle() {
Dimension dim=getElement().getSize();
Point p=getElement().getLocation();
return new Rect(p.x, p.y, dim.height, dim.width);
}
}