Selenium - PageObjectFactory - referencing element on the same page after it reloads - java

I am testing a web page using Webdriver and Java.
The test page contains List of Records and i click on the "Employee ID" title hyperlink and the records should be sorted in the ascending order of the employee IDs and there should be a small icon beside the "Employee ID" column indicating that the results are now sorted.
Here is the code i have:
public Class ResultsPage extends SlowLoadableComponent<ResultsPage> {
#FindAll({ #FindBy(how = How.XPATH, using = "some xpath"), #FindBy(how = How.XPATH, using = "another xpath") })
public List<WebElement> resultsTableElement;
#FindBy(how = How.XPATH, using = "//a[#title='A system assigned identifier for the Employee record.']")
public WebElement employeeIDColumnTitle;
#FindBy(how = How.XPATH, using = "//a[#title='A system assigned identifier for the Employee record.']/following-sibling::img")
public WebElement ascOrDescIcon;
public ResultsPage(WebDriver driver) {
super(new SystemClock(),20);
this.driver = driver;
wait = new WebDriverWait(driver, Start.TIME_OUT);
PageFactory.initElements(driver,this);
}
#Override
protected void load() {
PageFactory.initElements(driver, this);
LOGGER.info("From the load method");
}
#Override
protected void isLoaded() throws Error {
boolean loaded = false;
if (resultsTableElement.size() > 0) {
loaded = true;
}
LOGGER.warn("isloaded method failed ");
Assert.assertTrue(loaded, "Looks like the Claim Results Search Frame is not loaded yet");
}
public void testThis() {
//some code here
systemIDColumnTitle.click();
PageFactory.initElements(driver, this); //Calling the initElements of the same page again to see that the element
LOGGER.info(ascOrDescIcon.getAttribute("src")); //This line always fails saying that the element is not found.
}
}

From the comment you have specified, why can't you have locator defined in the same page object like:
#FindBy(id="newElement")
private WebElement newElement
This newElement points to the new element that got created after certain operation.
Edit:
As per your comment, you can directly call the get methods rather than instantiating another time. Kindly understand, that all #FindBy WebElements are proxys; Only when you call methods on it, they will be fetched (using the locator you have given) and does the specific operation.
So thats the reason, you can have the elements on your pageobject and calling initElements wont throw error, even the #FindBy aren't found!

Related

Mapping dynamic elements with #FindBy annotations and PageFactory

For the Page Object example class below, I have an accountsLink private member which maps to a non-dynamic element on the Login page when it loads. It is initialized using the FindBy annotation when the initElements method is called from the constructor.
public class Login {
private WebDriver driver;
#FindBy(id = "account")
private WebElement accountsLink;
//constructor, elements are initialized by the PageFactory
public MainPage(WebDriver driver) {
this.driver = driver;
PageFactory.initElements(driver, this);
}
//clicking accounts opens a dynamic ajax menu which has a Sign In Button
public SignInPage clickAccountsLink() {
accountsLink.click();
WebElement signInButton = driver.findElement(By.id("signin"));
signInButton.click();
return new SignInPage(driver);
}
}
Now for the problem. I have another element (signInButton) which is dynamically loaded only when you click the accountsLink element. This action doesn't take you to another page but only brings up an ajax menu where the sign in button will appear.
My question is, since the signInButton element only appears when the accountsLink element is clicked, can it be declared as a member of the Login class with a FindBy annotation or do I have to stick with my current solution of using a driver.findElement(By.id("signin")) inside the clickAccountsLink method?
I hope my question makes sense.
When PageFactory.initElements is called it parses the current DOM. If the WebElement doesn't exist in that time it can't be given as a value to a variable, exactly as you can't locate non-existing WebElement using driver.findElement.
Your solution is the way to go, although I would use explicit wait and Expected Conditions when loading the signInButton.
You can declare, I don't think it's gonna give you any error. Page Factory creates a dummy element when it initialized the class. It creates the actual element only when you intersect with the element for the first time.
For example in following class NoExistingElement element doesn't exist and it won't give me any error, If I will call the enterText method of class. The test case will pass without any error.
However, If i will try to call any function on NoExistingElement element then only it will fail with Webdriver exception, ElementNotFoundException
public class GoogleSearch {
#FindBy(name="q")
static WebElement searchBox;
#FindBy(name = "qqqqq")
WebElement NoExistingElement;
public GoogleSearch(WebDriver driver){
PageFactory.initElements(driver, this);
driver.get("https://www.google.com");
}
public void searchOnGoogle(String text){
searchBox.sendKeys(text);
}
}

How to Write a Function for WebElement Click in Selenium Webdriver

I am Having a Lot of WebElements
For Example I Declared a WebElement a
#FindBy(id="BtnLogin")
private WebElement btnLogin;
In the Same Manner I created "N" number of WebElements
Every time I Cant use "driver.findElement()" function So I wrote a function
public static void WebElementClick(WebElement we)
{
we.click();
}
When Ever the Control is Going to The Line we.click() in the WebElementclick Function it is Showing NullPointerException as a Result My Purpose is Failing
I am Not Getting What to Do,Some One Please Help Me on this :)
Your WebElementClick should receive the selector and it should: find element -> click, you can get an example from the above link.
In your case you it seems that you are not using wait and the WebElementClick it tries to click on the string.
Using find will return an object that will make click available.
The method should contain something like: driver.findElement(By.xpath("your_selector"));
Ant then use click on what this method returns.You can use also css if you want to.
public class testJava{
#Test
public void testMethod() throws InterruptedException {
WebDriver driver = new FirefoxDriver();
pageClass pageClass = PageFactory.initElements(driver, pageClass.class);
driver.get("http://www.facebook.com");
Thread.sleep(5000);
pageClass.clickLoginBtn();
}}
public class pageClass {
#FindBy(id = "loginbutton")
private WebElement loginBtn;
WebDriver driver;
public pageClass(WebDriver driver) {
this.driver = driver;
}
public void clickLoginBtn()
{
click(loginBtn);
}
public void click(WebElement we)
{
we.click();
}}
Its best practice to use the page class & test class..Try this it will help you i guess.
You are suppose to use driver to find & click the element.
I think that driver may try to click element before it's presented. Good practice before clicking WebElement is to wait for WebElement being clickable. I would try:
public static void WebElementClick(WebElement we)
{
wait.forElementClickable(we);
we.click();
}

Advanced Stale elements

I have been reading about stale elements and am still a bit confused. For instance, the following won't work, correct?
public void clickFoo(WebElement ele) {
try {
ele.click();
} catch (StaleElementReferenceException ex) {
ele.click();
}
}
because if ele is stale, it will remain stale. The best thing is to redo the driver.findElement(By), but as you can see in this example, there is no xpath. You can attempt to ele.getAttribute("id") and use that, but if the element has no id, this also will not work. All methods calling this would have to put the try/catch around it, which may not be feasible.
Is there some other way the element could be refound? Also, assuming there is an id, would the id remain the same after the element goes stale? What in the WebElement object ele is different once it goes stale?
(Java Eclipse)
I would recommend you NOT create a method like the above. There's no need to add another function layer on top of .click(). Just call .click() on the element itself.
driver.findElement(By.id("test-id")).click();
or
WebElement e = driver.findElement(By.id("test-id"));
e.click();
One way that I use regularly to avoid stale elements is find the element only when you need it and generally I do this by in a page object method. Here's a quick example.
The page object for a home page.
public class HomePage
{
private WebDriver driver;
public WebElement staleElement;
private By waitForLocator = By.id("sampleId");
// please put the variable declarations in alphabetical order
private By sampleElementLocator = By.id("sampleId");
public HomePage(WebDriver driver)
{
this.driver = driver;
// wait for page to finish loading
new WebDriverWait(driver, 10).until(ExpectedConditions.presenceOfElementLocated(waitForLocator));
// see if we're on the right page
if (!driver.getCurrentUrl().contains("samplePage.jsp"))
{
throw new IllegalStateException("This is not the XXXX Sample page. Current URL: " + driver.getCurrentUrl());
}
}
public void clickSampleElement()
{
// sample method code goes here
driver.findElement(sampleElementLocator).click();
}
}
To use it
WebDriver driver = new FirefoxDriver();
driver.manage().window().maximize();
driver.get("http://www.example.com");
HomePage homePage = new HomePage(driver);
homePage.clickSampleElement();
// do stuff that changes the page and makes the element stale
homePage.clickSampleElement();
Now I no longer have to rely on an old reference. I just call the method again and it does all the work for me.
There are many references on the page object model. Here's one from the Selenium wiki. http://www.seleniumhq.org/docs/06_test_design_considerations.jsp#page-object-design-pattern
If you want to read more info on what a stale element is, the docs have a good explanation. http://docs.seleniumhq.org/exceptions/stale_element_reference.jsp

Page Class using PageFactory

I am new to PageFactory and referring this tutorial https://www.toptal.com/selenium/test-automation-in-selenium-using-page-object-model-and-page-factory
An example from this page:
public class HomePage {
private WebDriver driver;
//Page URL
private static String PAGE_URL="https://www.toptal.com";
//Locators
//Apply as Developer Button
#FindBy(how = How.LINK_TEXT, using = "APPLY AS A DEVELOPER")
private WebElement developerApplyButton;
//Constructor
public HomePage(WebDriver driver){
this.driver=driver;
driver.get(PAGE_URL);
//Initialise Elements
PageFactory.initElements(driver, this);
}
public void clickOnDeveloperApplyButton(){
developerApplyButton.click();
}
}
Why create a private instance of WebDriver? It is appearing with yellow line for me.
When to use how = HOW and when we can straight away use xpath= //id..?
In constructor we are again passing WebDriver parameters?
The yellow line is because you are not using the driver variable declared with the class but the one you are passing as the variable to the constructor. Try using :
this.driver.get(PAGE_URL);
//Initialise Elements
PageFactory.initElements(this.driver, this);
You can use xpath = "//div" straight away when you are not using how.LINK_TEXT as you also have to provide the text for searching the link if you want to locate an element via LINK_TEXT.
In the constructor, you are passing the driver instance so when the page object is created, it has a driver instance as it is not the page instantiating the driver.

Selenium WebDriver: wait for element to be present when locating with WebDriver.findElement is impossible

It's convenient to wait for an WebElement to be present with WebDriverWait and ExpectedConditions.
The problem is, what if WebElement.findElment was the only possible way to locate the element , 'cause it has no id, no name, no unique class?
WebDriverWait's constructor accepts only WebDriver as arguments, not WebElement.
I've set the implicitlyWait time, so it seems not a good idea to use try{} catch(NoSuchElementException e){}, 'cause I don't want to wait that long time for this element.
Here's the scenario:
There's one web page with a form containing many input tags. Each input tag has a format requirement.
A dynamic div tag would be present after this input tag when the format requirement is not satisfied.
As there're so many input tags, I create a general method like:
public WebElement txtBox(String name) {
return driver.findElement(By.name(name));
}
instead of creating a data member for each input tag.
Then I create a method isValid to check whether user inputs in some input are valid. All I should do in isValid is to check whether a div tag is present after inputboxToCheck, with code like this:
public boolean isValid(WebElement inputboxToCheck) {
WebElementWait wait = new WebElementWait(inputboxToCheck, 1);
try {
wait.until(ExpectedConditions.presenceOfElementLocated(By.xpath("./following-sibling::div")));
return false;
} catch (TimeOutException e) {
return true;
}
}
WebElementWait is an imaginary (not exist) class which works the same way as WebDriverWait.
The WebElementWait class as metioned above:
package org.openqa.selenium.support.ui;
import java.util.concurrent.TimeUnit;
import org.openqa.selenium.NotFoundException;
import org.openqa.selenium.WebElement;
public class WebElementWait extends FluentWait<WebElement> {
public final static long DEFAULT_SLEEP_TIMEOUT = 500;
public WebElementWait(WebElement element, long timeOutInSeconds) {
this(element, new SystemClock(), Sleeper.SYSTEM_SLEEPER, timeOutInSeconds, DEFAULT_SLEEP_TIMEOUT);
}
public WebElementWait(WebElement element, long timeOutInSeconds, long sleepInMillis) {
this(element, new SystemClock(), Sleeper.SYSTEM_SLEEPER, timeOutInSeconds, sleepInMillis);
}
protected WebElementWait(WebElement element, Clock clock, Sleeper sleeper, long timeOutInSeconds,
long sleepTimeOut) {
super(element, clock, sleeper);
withTimeout(timeOutInSeconds, TimeUnit.SECONDS);
pollingEvery(sleepTimeOut, TimeUnit.MILLISECONDS);
ignoring(NotFoundException.class);
}
}
It's the same as WebDriverWait, except that the WebDriver argument is replaced with WebElement.
Then, the isValid method:
//import com.google.common.base.Function;
//import org.openqa.selenium.TimeoutException;
public boolean isValid(WebElement e) {
try {
WebElementWait wait = new WebElementWait(e, 1);
//#SuppressWarnings("unused")
//WebElement icon =
wait.until(new Function<WebElement, WebElement>() {
public WebElement apply(WebElement d) {
return d.findElement(By
.xpath("./following-sibling::div[class='invalid-icon']"));
}
});
return false;
} catch (TimeoutException exception) {
return true;
}
}
I don't know if this help you, but it permits to wait element how much time do you want.
public WebElement findDynamicElement(By by, int timeOut) {
WebDriverWait wait = new WebDriverWait(driver, timeOut);
WebElement element = wait.until(ExpectedConditions.visibilityOfElementLocated(by));
return element;
}
findDynamicElement(By.xpath("//body") , 30);
A more universal variant of user2432405's solution would be using SearchContext type rather then WebElement:
public class SearchContextWait extends FluentWait<SearchContext> {
...
This allows to do waits on both WebDriver and WebElement similarly as the SearchContext interface is the ancestor of both WebDriver and WebElement. The isValid method needs adjustment too:
...
WebElement icon = wait
.until(new Function<SearchContext, WebElement>() {
public WebElement apply(SearchContext d) {
...
Unfortunately, you lose all conveniences of ExpectedConditions.xxxx() methods as they use the WebDriver interface internally.
I found this blog: Checking for an element – exists?, visible?, present? - https://jkotests.wordpress.com/2012/11/02/checking-for-an-element-exists-visible-present/
And it brought up the differences between exists, visible, and present.
exists? – Returns whether this element actually exists.
present? – Returns true if the element exists and is visible on the page
visible? – If any parent element isn’t visible then we cannot write to the element. The only reliable way to determine this is to iterate
up the DOM element tree checking every element to make sure it’s
visible.
Exists will tell you if what you are searching for is anywhere in the DOM; however, WebDriver does not seem to have a built in way to check if an element exists similar to plain driver.findElement(By.name(name)).
And, as explained in the blog, Exists is not the same as Present. So I can't use ExpectedConditions.presenceOfAllElementLocatedBy(By.cssSelector(cssSelector)
My solution: (looking for feedback here :)
public WebElement waitForElementExists(String selector, String timeout) {
Wait<WebDriver> wait = new WebDriverWait(driver, timeout);
WebElement element = wait.until(new Function<WebDriver, WebElement>() {
public WebElement apply(WebDriver driver) {
return driver.findElement(By.cssSelector(selector));
}
});
return element;
}

Categories