I really like how selenium 2 by convention pushes you towards using PageObjects as POJOs, and then simply using the PageFactory to instantiate the fields in this class.
What I am finding limiting is that we reuse a lot of elements on many different pages. The big problem is that these reused components do not have the same id / name when they appear on different pages; however the tests we would run for each of them is the same.
As an example we collect dates in many places. So an example page object for this could be (month, day fields removed):
public class DatePageObject {
private WebDriver driver;
DatePageObject(WebDriver driver) {
this.driver = driver;
}
@FindBy( id = "someIdForThisInstance")
private WebElement year;
public void testYearNumeric() {
this.year.sendKeys('aa');
this.year.submit();
//Logic to determine Error message shows up
}
}
Then I could simply test this with the code below:
public class Test {
public static void main(String[] args) {
WebDriver driver = new FirefoxDriver();
DatePageObject dpo = PageFactory.initElements(driver, DriverPageObject.class);
driver.get("Some URL");
dpo.testYearNumeric();
}
}
What I'd really like to do is have a setup whereby with Spring I can inject that id/name/xpath, etc... into the application.
Is there a way I can do this, without losing the ability to utilize the PageFactory?
Edit 1 -- Adding ideal base level classes, working on Custom Locators and Factories.
public class PageElement {
private WebElement element;
private How how;
private String using;
PageElement(How how, String using) {
this.how = how;
this.using = using;
}
//Getters and Setters
}
public class PageWidget {
private List<PageElement> widgetElements;
}
public class Screen {
private List<PageWidget> fullPage;
private WebDriver driver;
public Screen(WebDriver driver) {
this.driver = driver;
for (PageWidget pw : fullPage) {
CustomPageFactory.initElements(driver, pw.class);
}
}
Edit 2 -- Just as a note, as long as you are running Selenium 2.0.a5 or greater, you can now give the driver an implicit timeout value.
So you can replace your code with:
private class CustomElementLocator implements ElementLocator {
private WebDriver driver;
private int timeOutInSeconds;
private final By by;
public CustomElementLocator(WebDriver driver, Field field,
int timeOutInSeconds) {
this.driver = driver;
this.timeOutInSeconds = timeOutInSeconds;
CustomAnnotations annotations = new CustomAnnotations(field);
this.by = annotations.buildBy();
driver.manage().timeouts().implicitlyWait(15, TimeUnit.SECONDS); //Set this value in a more realistic place
}
public WebElement findElement() {
return driver.findElement(by);
}
}
See Question&Answers more detail:
os