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.
Related
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);
}
}
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.
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);
}
}
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();
}
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!