I have 2 pages (landing page and registration page). In landing page, I am storing webelements using #FindBy annotations. I need to use the Webelements oflanding page in the registration page. How can I proceed?
You just need to create an object of the class and access it like variable
Suppose class A having #FindBy function and variable is suppose myelement
Then use (it is Java, try similar in whatever lang you are using):
A aobject= new A();
A.myelement;
Related
I am trying to learn the PageFactory model. I understood the fact that when we do a initElements, the WebElements are located. Say for example, I click on a webelement and because of which there is a change in one of the other webelements in DOM. Now, obviously I would get a StaleElementReferenceException here. How would I resolve this issue?
Should I find that specific WebElement again knowing the fact that there can be a change in the WebElement's properties in the DOM? or is there an another way to handle this?
StaleElementReferenceException
StaleElementReferenceException extends WebDriverException and indicates that the previous reference of the element is now stale and the element reference is no longer present on the DOM of the page.
Common Reasons
The common reasons behind facing StaleElementReferenceException are as follows:
The element has been deleted entirely.
The element is no longer attached to the DOM.
The webpage on which the element was part of has been refreshed.
The (previous) element has been deleted by a JavaScript or AjaxCall and is replaced by a (new) element with the same ID or other attributes.
Solution : If an (old) element has been replaced with new identical one, the simple strategy would be to use findElement() or findElements to look out for the element again.
Answering your queries
When we do a initElements, the WebElements are located : When you call initElements() method, all the WebElements of that page will get initialized. For example,
LoginPageNew login_page = PageFactory.initElements(driver, LoginPageNew.class);
This line of code will initialize all the static WebElements defined within the scope of the LoginPageNew.class whenever and wherever it is invoked from your Automation Script.
I click on a webelement and because of which there is a change in one of the other webelements in DOM : This is pretty much possible.
As an example, in general invoking click() on a <input> tag wouldn't trigger any change of any of the WebElements on the HTML DOM.
Where as invoking click() on a <button> tag or <a> tag may call a JavaScript or a Ajax which inturn may delete an element or can replace the (previous) element by a (new) element with the same ID or other attributes.
Conclusion
So, if WebDriver throws a StaleElementReferenceException, that implies even though the element still exists, the reference is lost. We should discard the current reference we have and replace it by locating the WebElement once again when it gets attached to the DOM. That means you have to again reinitialize the class through initElements() method which inturn reinitializes all the WebElements defined in that page.
Solution
If a old element has been replaced with new identical one, the simple strategy would be to invoke WebDriverWait inconjunction with ExpectedConditions to look out for the element.
You can find relevant detailed discussions in:
How to add explicit wait in PageFactory in PageObjectModel?
References
Here are the references of this discussion:
Stale Element Reference Exception
Class StaleElementReferenceException
Selenium: How to tell if RemoteWebDriver.findElements(By) can throw StaleElementReferenceException at all?
This is a known problem with the PageFactory implementation.
If you are unlucky enough for the element to become stale in the instant between the element being found, and then the element being clicked upon, you will get this error. Unfortunately the PageFactory code does not try to find the element again if it has become stale and it throws an Exception.
I would classify this as a bug with PageFactory, it should auto re-find the element if it ever becomes stale (unless the #CacheLookup annotation is used).
The suggestion to recall initElements isn't going to fix anything, you only need to init the elements once because that binds a Java proxy class to the element in question. The page factory implementation is supposed to remove the possibility of StaleElementReferenceExceptions (hence why this is a bug)
Stale element exception is thrown in two cases
The element is no longer attached to the DOM.
The element has been deleted entirely.
When this happen you wrap your code in try catch block then you can loop and retry as many times as you need until it succeeds.
public void waitForElementPresent(final By by, int timeout){
WebDriverWait wait = (WebDriverWait)new WebDriverWait(driver,timeout)
.ignoring(StaleElementReferenceException.class);
wait.until(new ExpectedCondition<Boolean>(){
#Override
public Boolean apply(WebDriver webDriver) {
WebElement element = webDriver.findElement(by);
return element != null && element.isDisplayed();
}
});
}
Simply put: when implementing the POM framework we have Pagefactory initialize all the elements in a pageobject. Why do we do this instead of storing the xpath/css selectors as strings and calling those as needed instead?
ex:
#FindBy(xpath = "//Button[text()='Add and Edit']")
#CacheLookup
private WebElement addAndEdit;
vs
private String addAndEdit;
This is not defined as part of the page object model, this is defined by PageFactory which is a helper class in initializing elements in a page object. The intent there is to set up a proxy so that the element's location strategy is defined by the #FindBy and you can reference the WebElement and it will go look it up for you when you use the reference.
If you are going to store locators, don't store a string... store instead a By locator. It has the extra information of the TYPE of locator, e.g. By.id, By.cssSelector, etc. I think this is much cleaner approach and will prevent you from having to somehow determine what type of locator that string variable is. This is the approach I use in all the Selenium projects that I have written and/or maintain.
According to the Selenium contributors, PageFactory should not be used. See this yt video of Simon Stewart, Lead Committer, stating don't use it (27:26) and so on and why. The link starts the section that leads into his comments on PageFactory.
Please read the whole Q before disliking or commenting something. I have searched on internet before posting it here. I'm having the below project structure.
pages(package)
> Homepage.java
test(package)
> Flipkart.java
Inside Homepage.java i have declared all the WebElements using POM Page Factory methods and created respective method to click on Electronics link.
#FindBy(xpath = '//input[#title='Electronics']')
private WebElement lnkElectronics;
Inside Fipkart.java I'm calling the Electronics click method.
My doubt over here is the declared WebElement is specifically for Electronics.
Is there a way i can create a WebElement with type like mentioned below and pass value for %s dynamically from main method?
#FindBy(xpath = '//input[#title='%s']')
private WebElement lnkElectronics;
Answer referenced from Page Object Model in Selenium
You cannot create a FindBy with Variable, FindBy accepts only constants.
In case if you want to achieve that variability then you should write or find the element using normal findElement method
As per the Test Design Consideration following Page Object Design Pattern :
A Page Object is an object-oriented class that serves as an interface to a page of the Application Under Test. Your #Tests uses the methods of this Page Object class whenever they need to interact with the User Interface of that page. The benefit is that if the UI changes for the page your #Tests themselves don’t needs to be changed. Only the code within the Page Object needs to be changed.
Advantages :
Clean separation between test code and page specific code such as locators, methods and layout.
A single repository for the operations offered by the page rather than having these services scattered throughout the tests.
Based on these Page Factory features you won't be able to create any generic WebElement for which you can pass value dynamically from main() or #Test annotated method e.g.
#FindBy(xpath = '//input[#title='%s']')
private WebElement lnkElectronics;
You can find almost a similar discussion in Where should I define modal specific code in Selenium Page Object Model pattern
WorkAround : on page class you can define a method, and can pass the text on the fly from the calling class to click on specific tab
if you want to click any common text on the page. You can create a method as given below and can pass the text on the fly to click on that specific tab on that page
public void clickTab(String tabText){
String tabxpath = "//div[contains(text(), '" + tabText + "')]";
driver.findElement(By.xpath(tabxpath)).click();
}
How can I get the geb webdriver to move to an element on the page that may not be in the view of the browser. The element exists on the page and is displayed but there may be a possibility that the element will appear on the bottom of the page and the browser would need to scroll down in order to bring the element into view.
import geb.Page
class myPage extends Page {
static url = "mypage.xhtml"
static at = {title == "myPage"}
static content = {
someElement = {$("#bottomOfPage")}
anotherElement = {$(".someClass",5)}
}
void clickElement(){
//possibility to fail because element may not be displayed in browsers view
//if not in view, scroll to element, then click
someElement.click()
}
}
Using this page as an example, If an element is not in view on the page and possibly at the very bottom, how can I tell the webdriver to bring the element into view? I have some complicated tests that are failing when the page contents are not in view. In some cases, the driver will move to the element even if it's not in view. But I would like to explicitly tell the driver to move to an element if its not in view for the cases that it does not move on its own.
The selenium library does have a moveToElement() function but it only accepts objects of the WebElement class. Using geb, when creating page classes, all elements that are used in my tests are declared in the content section and thus are of the SimplePageContent class. SimplePageContent cannot be casted to a WebElement either. moveToElement(By.id("bottomOfPage")) does work but is not ideal because I do not want hard coded values. Id much rather pass the page content object itself if possible.
From my experience I can say that if you ask WebDriver to click on an element that is not currently in view it will first scroll to that element and then click on it. So you're either hitting some kind of bug if it does not happen or the fact that the element is not in view is not what's causing your element not to be clicked.
If you still want to move to the element explicitly using Actions.moveToElement() then you can easily turn objects returned from your content definition using the fact that they implement Navigator which comes with a firstElement() which you should use if you want to get the first WebElement of a multi element Navigator or singleElement() when your Navigator has exactly one WebElement.
There is also a simpler way, which won't require extracting WebElements from your Navigators - use an interact {} block:
interact {
moveToElement(someElement)
}
For instance, let's say I have class FanPage, with this annotation
#FindBy(how = How.ID, using = "ctl00__lvph_Add")
private WebElement _AddFanButton;
and then in my test code I say
fanPage = homePage.GoToFanPage()
which does
return PageFactory.initElements(driver, CC_VendorStatisticsMetadata.class);
Now if my annotation is incorrect (let's say it should be ctl00_lvph_AddFan), I would expect my call to initElements to fail. However, it doesn't and it simply returns a FanPage object to me. It only fails if I try to use _AddFanButton.
How do I get PageFactory to look for my annotations from the start?
You don't. The PageFactory does lazy initialization, and that's how it's designed.
Consider a Page Object where certain of your elements don't exist on the page until some action is taken. Since Page Objects are intended to encapsulate business logic and not just the elements on the page, this is a perfectly logical scenario. In that case, your initElements() call would fail on page object initialization every single time, and not give you the chance to call the business logic method that would cause the element to appear.
It's possible that the PageFactory will not work for you if this is a requirement for your test framework. In that case, you'd do well to construct your own implementation.