Mapping dynamic elements with #FindBy annotations and PageFactory - java

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

Related

Unable to click on element passed from factory method

Hi i am trying to click on an element which location is defined using PageFactory.But it is showing NullPointerException.
Locator class:
#FindBy(xpath = "//*[#content-desc = 'Navigate up']")
public By backButton;
PageObject class:
public AskPage()
{
PageFactory.initElements(driver, AskLocator.class);
}
public void backButtonClick()
{
if(backButtondisplayed())
commonactions.clickElement(driver, askLocator.backButton);
}
Am i doing anything wrong?
Note: I have not added classes. I have just added click method and locator for backbutton in page factory class. i have tried changing return type to WebElemment and it works fine. Now i am wondering can i send "By" object from factory class?
In your code:
You are initialising 'this' class which means you are initialisinf AskPage.class.
PageFactory.initElements(driver, this);
But there is no need to initialise 'AskPage', as you are not locating any web elements.
So the solution is:
PageFactory.initElements(driver, askLocator.class:);
It will do the magic for you now.
Make the web element as static in askLocator.class or create object and access the web element.

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 - PageObjectFactory - referencing element on the same page after it reloads

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!

Categories