package net.thucydides.core.annotations.locators;
import net.thucydides.core.annotations.findby.By;
import net.thucydides.core.annotations.findby.FindBy;
import net.thucydides.core.annotations.findby.How;
import org.openqa.selenium.support.ByIdOrName;
import org.openqa.selenium.support.FindBys;
import org.openqa.selenium.support.pagefactory.Annotations;
import java.lang.reflect.Field;
import java.util.HashSet;
import java.util.Set;
import static org.apache.commons.lang3.StringUtils.isNotEmpty;
public class SmartAnnotations extends Annotations {
private Field field;
public SmartAnnotations(Field field) {
super(field);
this.field = field;
}
private void assertValidAnnotations() {
FindBys findBys = field.getAnnotation(FindBys.class);
FindBy myFindBy = field.getAnnotation(FindBy.class);
if (findBys != null && myFindBy != null) {
throw new IllegalArgumentException("If you use a '@FindBys' annotation, "
+ "you must not also use a '@FindBy' annotation");
}
}
// @Override
public org.openqa.selenium.By buildBy() {
assertValidAnnotations();
org.openqa.selenium.By ans = null;
//default implementation in case if org.openqa.selenium.support.FindBy was used
org.openqa.selenium.support.FindBy findBy = field.getAnnotation(org.openqa.selenium.support.FindBy.class);
if (ans == null && findBy != null) {
ans = super.buildByFromFindBy(findBy);
}
//my additions to FindBy
FindBy myFindBy = field.getAnnotation(FindBy.class);
if (ans == null && myFindBy != null) {
ans = buildByFromFindBy(myFindBy);
}
FindBys findBys = field.getAnnotation(FindBys.class);
if (ans == null && findBys != null) {
ans = buildByFromFindBys(findBys);
}
if (ans == null) {
ans = buildByFromDefault();
}
if (ans == null) {
throw new IllegalArgumentException("Cannot determine how to locate element " + field);
}
return ans;
}
protected org.openqa.selenium.By buildByFromFindBy(FindBy myFindBy) {
assertValidFindBy(myFindBy);
org.openqa.selenium.By ans = buildByFromShortFindBy(myFindBy);
if (ans == null) {
ans = buildByFromLongFindBy(myFindBy);
}
return ans;
}
protected org.openqa.selenium.By buildByFromLongFindBy(FindBy myFindBy) {
How how = myFindBy.how();
String using = myFindBy.using();
switch (how) {
case CLASS_NAME:
return org.openqa.selenium.By.className(using);
case CSS:
return org.openqa.selenium.By.cssSelector(using);
case ID:
return org.openqa.selenium.By.id(using);
case ID_OR_NAME:
return new ByIdOrName(using);
case LINK_TEXT:
return org.openqa.selenium.By.linkText(using);
case NAME:
return org.openqa.selenium.By.name(using);
case PARTIAL_LINK_TEXT:
return org.openqa.selenium.By.partialLinkText(using);
case TAG_NAME:
return org.openqa.selenium.By.tagName(using);
case XPATH:
return org.openqa.selenium.By.xpath(using);
case JQUERY:
return By.jquery(using);
case SCLOCATOR:
return By.sclocator(using);
default:
// Note that this shouldn't happen (eg, the above matches all
// possible values for the How enum)
throw new IllegalArgumentException("Cannot determine how to locate element " + field);
}
}
protected org.openqa.selenium.By buildByFromShortFindBy(FindBy myFindBy) {
if (isNotEmpty(myFindBy.className())) {
return org.openqa.selenium.By.className(myFindBy.className());
}
if (isNotEmpty(myFindBy.css())) {
return org.openqa.selenium.By.cssSelector(myFindBy.css());
}
if (isNotEmpty(myFindBy.id())) {
return org.openqa.selenium.By.id(myFindBy.id());
}
if (isNotEmpty(myFindBy.linkText())) {
return org.openqa.selenium.By.linkText(myFindBy.linkText());
}
if (isNotEmpty(myFindBy.name())) {
return org.openqa.selenium.By.name(myFindBy.name());
}
if (isNotEmpty(myFindBy.ngModel())) {
return org.openqa.selenium.By.cssSelector("*[ng-model='" + myFindBy.ngModel() + "']");
}
if (isNotEmpty(myFindBy.partialLinkText())) {
return org.openqa.selenium.By.partialLinkText(myFindBy.partialLinkText());
}
if (isNotEmpty(myFindBy.tagName())) {
return org.openqa.selenium.By.tagName(myFindBy.tagName());
}
if (isNotEmpty(myFindBy.xpath())) {
return org.openqa.selenium.By.xpath(myFindBy.xpath());
}
if (isNotEmpty(myFindBy.sclocator())) {
return By.sclocator(myFindBy.sclocator());
}
if (isNotEmpty(myFindBy.jquery())) {
return By.jquery(myFindBy.jquery());
}
// Fall through
return null;
}
private void assertValidFindBy(FindBy findBy) {
if (findBy.how() != null) {
if (findBy.using() == null) {
throw new IllegalArgumentException(
"If you set the 'how' property, you must also set 'using'");
}
}
Set<String> finders = new HashSet<String>();
if (!"".equals(findBy.using())) finders.add("how: " + findBy.using());
if (!"".equals(findBy.className())) finders.add("class name:" + findBy.className());
if (!"".equals(findBy.css())) finders.add("css:" + findBy.css());
if (!"".equals(findBy.id())) finders.add("id: " + findBy.id());
if (!"".equals(findBy.linkText())) finders.add("link text: " + findBy.linkText());
if (!"".equals(findBy.name())) finders.add("name: " + findBy.name());
if (!"".equals(findBy.ngModel())) finders.add("ngModel: " + findBy.ngModel());
if (!"".equals(findBy.partialLinkText())) finders.add("partial link text: " + findBy.partialLinkText());
if (!"".equals(findBy.tagName())) finders.add("tag name: " + findBy.tagName());
if (!"".equals(findBy.xpath())) finders.add("xpath: " + findBy.xpath());
if (!"".equals(findBy.sclocator())) finders.add("scLocator: " + findBy.sclocator());
if (!"".equals(findBy.jquery())) finders.add("jquery: " + findBy.jquery());
// A zero count is okay: it means to look by name or id.
if (finders.size() > 1) {
throw new IllegalArgumentException(
String.format("You must specify at most one location strategy. Number found: %d (%s)",
finders.size(), finders.toString()));
}
}
}