Unable to click on element passed from factory method - java

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.

Related

Selenium Webdriver - Relative xpath

I am a beginner in Selenium. I do not have any hands on experience in it. Last month I had enrolled for a Selenium beginner to advanced course where I have few activities where I can do hands on.
I am stuck at a certain place. Let me explain my issue.
This is the activity description:
RelativeXpathLocator
URL: http://webapps.tekstac.com/Shopify/
Test Procedure:
Use the template code.
Don't make any changes in DriverSetup file.
Only in the suggested section add the code to,
Invoke the driver using getWebDriver() method defined in DriverSetup()
Identify the web element of the value 'SivaKumar' using xpath locator and return it.
Using the same web element, get the text and return it.
The code that I wrote for this:
//Add required imports
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.By;
import org.openqa.selenium.WebElement;
public class RelativeXpathLocator //DO NOT Change the class Name
{
static String baseUrl = "http://webapps.tekstac.com/Shopify/";
public WebDriver createDriver() //DO NOT change the method signature
{
DriverSetup ds = new DriverSetup();
return ds.getWebDriver();
//Implement code to create Driver from DriverSetup and return it
}
public WebElement getRelativeXpathLocator(WebDriver driver)//DO NOT change the method signature
{
WebElement l = driver.findElement(By.xpath("//*[#id="tbrow"]/td[3]"));
return (l);
/*Replace this comment by the code statement to get the Web element */
/*Find and return the element */
}
public String getName(WebElement element)//DO NOT change the method signature
{
//Get the attribute value from the element and return it
}
public static void main(String[] args){
RelativeXpathLocator pl=new RelativeXpathLocator();
//Add required code
}
}
Note: Stuck at Public Webelement getRelativeXpathLocator
Now after I typed the code and complied to check if it threw any error, I was able to see expect ")" ";" errors in the xpath line.
I've been struggling for hours to get this to work but in vain.
Please advice.
Thanks.
The error is double quotation " in xpath , so change the xpath
From
driver.findElement(By.xpath("//*[#id="tbrow"]/td[3]"));
To
driver.findElement(By.xpath("//*[#id='tbrow']/td[3]"));

Passing WebElement as parameter to implicit wait method

I wrote the below method in my Page.class for reusing implicit wait.
public WebDriver waitForElementToLoad(WebElement element)
{
WebDriverWait wait = new WebDriverWait(driver, 60);
wait.until(ExpectedConditions.presenceOfElementLocated((By) element));
return (driver);
}
In my test.class I am using page factory elements, for example:
//Save button
#FindBy(xpath = "//*[#*='Save']")
private WebElement saveButton;
Now I am trying to call:
waitForElementToLoad(saveButton);
from test.Class but I am getting below error.
"java.lang.ClassCastException: class com.sun.proxy.$Proxy12 cannot be
cast to class org.openqa.selenium.By (com.sun.proxy.$Proxy12 and
org.openqa.selenium.By are in unnamed module of loader 'app')"
I also tried
WebElement saveButton = driver.findElement(By.xpath("//*[#*='Save']"));
waitForElementToLoad(saveButton);
but no luck.
How can I make this work?
WebDriverWait is explicit wait, not implicit. And you can't cast WebElement to By.
If saveButton is not null than it was already found by the page factory, waiting for it presence is meaningless, that's why you don't have an overload with WebElement. Wait for visibility instead
wait.until(ExpectedConditions.visibilityOf(element));
If you need to use ExpectedConditions with some your "Page" class that contains asynchronously loaded elements I would recommend to use the following approach:
Extend AjaxElementLocator where override protected boolean isElementUsable(WebElement element) so that it uses ExpectedConditions to decide if the element is ready for use
Extend AjaxElementLocatorFactory where override public ElementLocator createLocator(Field field) method so that it now returns the instance of your extended class
Where you initialize elements use PageFactory.initElements(new MyAjaxElementLocatorFactory(driver, 10), this); where MyAjaxElementLocatorFactory is the class that you created on step 2
This error message...
java.lang.ClassCastException: class com.sun.proxy.$Proxy12 cannot be cast to class org.openqa.selenium.By (com.sun.proxy.$Proxy12 and org.openqa.selenium.By are in unnamed module of loader 'app')
...implies that the ClassCastException occured while trying to cast a proxy object.
You need to consider a few things as follows:
The wait you introduced within waitForElementToLoad() isn't implicit wait as such but an ExplicitWait. See: What is the internal working difference between Implicit Wait and Explicit Wait
presenceOfElementLocated() doesn't garuntees the visibility or interactablity of any element. So for visibility or interactablity you need to use the matching ExpectedConditions either visibilityOf(WebElement element) or elementToBeClickable(By locator). For details, see: Selenium: How selenium identifies elements visible or not? Is is possible that it is loaded in DOM but not rendered on UI?
If your usecase involves inducing WebDriverWait you pass the WebElement saveButton in the form of a proxy object within waitForElementToLoad() as follows:
package com.pol.zoho.PageObjects;
import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.support.FindBy;
import org.openqa.selenium.support.PageFactory;
import org.openqa.selenium.support.ui.ExpectedConditions;
import org.openqa.selenium.support.ui.WebDriverWait;
public class ZohoLoginPage {
WebDriver driver;
public ZohoLoginPage(WebDriver driver)
{
PageFactory.initElements(driver, this);
}
//Save button
#FindBy(xpath = "//*[#*='Save']")
private WebElement saveButton;
public void waitForElementToLoad()
{
WebElement element = new WebDriverWait(driver, 20).until(ExpectedConditions.elementToBeClickable(ZohoLoginPage.getWebElement()));
saveButton.click();
}
public WebElement getWebElement()
{
return saveButton;
}
}
Reference
You can find a couple of relevant discussions in:
How to add explicit wait in PageFactory in PageObjectModel?
How to wait for invisibility of an element through PageFactory using Selenium and Java

Pagefactory initialization

I implemented a framework with POM and Page Factory approach. I have a baseTest class with lots of init e.g: (every other test class extends it)
Registration regPage = PageFactory.initelements(driver,Registration.class);
Login loginPage = PageFactory.initelements(driver,login.class);
Details detailsPage = PageFactory.initelements(driver,details.class);
.. (more than 10)
It is working fine but I would like to find more elegant/structured way to handle it.
I tried the build it into the constructor:
public Registeration(WebDriver driver) {
super(driver);
PageFactory.initelements(driver,Registration.class(or can be this));
}
It this case, I got a huge heapmemory error however it would be very nice because I could use an assert to validate every pageObject in the constructor using title or whatever element on the page, right?
How can I structure my inits and how can I handle it with PageFactory using constructor?
Thanks!
Its because initElements stuck in infinite loop. PageFactory's InitElement function looks first for page's constructor with webdriver argument. Its like Page Creation call by your runner --> initElements (2nd line)--> Page Constructor called by initElements and this keeps circling around. So, you cannot initiate the page class within constructor of page using pagefactory. You may structure something like this
public class MyTest{
#Test
public void test(){
TestFactory.invokeBrowser();
MyPage page = TestFactory.getPage(MyPage.class);
}
}
//TestFactory class
public class TestFactory{
private static WebDriver driver;
public static void invokeBrowser(){
WebDriver driver = new ChromeDriver();
//invokes the browser
this.driver = driver;
}
public static <T> T getPage(Class<T> page){
return PageFactory.initElements(driver,page);
}
}

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

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.

Categories