Regarding the WebDriverWait data type, I am assuming that only one instance is needed of such data type, which means I could potentially create a wrapper and allow only one instance creation by using a singleton pattern approach.
At the present time I used thread.sleep and basically everywhere I need that function to be called I am extending from the class which probably not the best approach. Also of course I should be using WebDriverWait instead of thread. What should the approach be?
So far I created a page object with web elements and a separate service for the logic itself, so now I need also WebDriverWait in every service since it’s a necessary operation.
A cookie window that gets popped up once customer opened the page:
/**
* acceptCookies -> clickable
* cookieBanner -> just to identify that cookie component showed up.
* PageFactory -> will initialize every WebElement variable with a reference to a corresponding element on the actual web page.
*/
public class CookieModal {
WebDriver driver;
#FindBy(css = ".cookie-accept-all")
public WebElement acceptCookies;
public CookieModal(WebDriver driver) {
this.driver = driver;
PageFactory.initElements(driver, this);
}
}
Then I separated the service (actions):
public class CookieService {
private final CookieModal cookieModal;
public CookieService(WebDriver driver) {
this.cookieModal = new CookieModal(driver);
}
public void acceptCookies() {
cookieModal.acceptCookies.click();
}
}
This must be changed to WebDriverWait, but I also think extending from AbstractPage in every page object is not necessary. Is my structure OK and how should I initialise WebDriverWait?
public class AbstractPage {
// This is not good as thread sleep is not dynamic and you
// have to specify time yourself change to webdriver wait
private AbstractPage microsleep(Integer milliseconds) {
try {
Thread.sleep(milliseconds);
} catch (Throwable e) {
String error = String.format("Unable to put thread to sleep (requested %d milliseconds).", milliseconds);
throw new RuntimeException(error, e);
}
return this;
}
public AbstractPage emulateWaitingUser() {
return microsleep(800);
}
public AbstractPage sleep(Integer seconds) {
return microsleep(1000 * seconds);
}
Selenium supports integrating explicit waits into page objects. This is achieved by using a special way of how you initialize your page. In your example you are doing this:
PageFactory.initElements(driver, this);
which involves some basic default way. However, you can add more complexity here, but you get a more effective architecture at the same time.
You can extend the AjaxElementLocator class where you will override isElementUsable method in the way that would involve any sort of condition and waits. Then you will be initializing your page(s) with that locator through a dedicated LocatorFactory. Some example of how to use all that classes you can find here.
WebDriverWait is commonly used with the ExpectedConditions class. In that case, you cannot just wait for 800 ms. You need to wait until a condition is met. For example, wait until page title is displayed, or wait until loader icon is not visible, wait until login button is clickable, etc.
That means if you wish to instantiate a wait in an abstract method, you will need to add an unknown (and non-abstract) wait condition. You could just instantiate a general wait object and then add a condition when it is known, but it seems a bit incomplete.
Another idea that comes to my mind is declaring WebDriverWait as a field in Cookie Service class and pass it to its methods.
Related
I have a registration form that register many users ,the problem in the first loop when I click on create it go too fast and didn't register the first one and resister the second ...,
so I use Thread.sleep(500);
I want to avoid using sleep
is there a way to do it
here is my code
#Given("user on registration page and create users")
public void user_on_registration_page_and_create_users() throws InterruptedException {
System.out.println(userLoginPageDataList);
for(UserLoginPageData userLoginPageData:userLoginPageDataList){
userRegistrationPage.init();
logger.info("*************************************** init the driver && go to registration page http://localhost:4200/register");
logger.info("*************************************** reading line "+userLoginPageData.getRowIndex() +" from Excel file");
userRegistrationPage.enterUserLogin(userLoginPageData.getUsername());
userRegistrationPage.enterUserPassword(userLoginPageData.getPassword());
userRegistrationPage.enterUserRole(userLoginPageData.getUserRole());
userRegistrationPage.clickOnCreate();
// Thread.sleep(500);
logger.info(userLoginPageData.getUsername()+" is registred");
}
}
You can use explicit(smart) wait.
WebDriverWait w = new WebDriverWait(driver, 5); //will wait 5 seconds most , but if element is visuble in the third second it will wait 3 sec.
w.until(ExpectedConditions.visibilityOfElementLocated(By.id("submit_btn")));
read more on When to use explicit wait vs implicit wait in Selenium Webdriver?
One of the possible solutions (when you work with PageFactory) is to implement your own Locator that can be extended from AjaxElementLocator.
Say you have a form and the form has some noticeable property saying that it is ready to accept the input (this might be some button state or displaying some label, etc).
So you can initialize your page object in the way its fields will be "available" if that condition is met.
This can be achieved using your custom Locator/LocatorFactory in your PageFactory.init().
For example here is the form of two fields. The condition saying it is ready for interaction is then create button is enabled:
class MyForm {
#FindBy(id = "user")
WebElement user;
#FindBy(id = "create")
WebElement create;
public MyForm(SearchContext searchContext){
PageFactory.initElements(field -> new AjaxElementLocator(searchContext, field, 10){
#Override
protected boolean isElementUsable(WebElement element) {
return create.isEnabled();
}
}, this);
}
}
Unless create button is enabled any attempt to invoke fields methods would be failing and the script will fail in 10 seconds of retries.
More details about how you use the conditions with page objects you can find in this post.
After reviewing the selenium docs, I am wondering if I am attempting to implement explicit waits incorrectly.
In the docs, it always shows identifying a new element, then assigning the defined wait to said element
WebDriver driver = new ChromeDriver();
driver.get("https://google.com/ncr");
driver.findElement(By.name("q")).sendKeys("cheese" + Keys.ENTER);
// Initialize and wait till element(link) became clickable - timeout in 10 seconds
WebElement firstResult = new WebDriverWait(driver, Duration.ofSeconds(10))
.until(ExpectedConditions.elementToBeClickable(By.xpath("//a/h3")));
// Print the first result
System.out.println(firstResult.getText());
In this example, a new element firstResult is created, then the defined wait assigned to it.
Is this required? Should always be done this way?
This is why I ask.
I am using the PageFactory model and have my elements defined via the FindBy annotation, as shown here.
// Input field for slice ID
#FindBy(how = How.XPATH, using = "//input[#name='id']")
private WebElement inputSliceId;
Then, in that same class, I have defined some convenience methods to use them.
So now, in my convenience methods, should I do things like this?
inputSliceId = new WebDriverWait(driver, Duration.ofSeconds(10))...
inputSliceId.sendKeys(...
What I have been doing, which is what I'm questioning now, is putting wait statements that are not being assigned directly to the element in question.
For example, I've been doing things like this.
buttonSubmit.click();
WebDriverWait wait = new WebDriverWait(driver, 5);
wait.until(ExpectedConditions.visibilityOfElementLocated(By.xpath("//div[#role='alertdialog']")));
wait.until(ExpectedConditions.invisibilityOfElementLocated(By.xpath("//div[#role='alertdialog']")));
Why? (could totally be wrong here)
Upon clicking the button, I need to wait for a pop-up to display
Once it does, I am then waiting for it to disappear, before proceeding
Here's the main question
Are these two wait lines not really doing anything because I am not assigning them to an element? Or are they still causing the web driver to hold until the conditions specified by the wait occur?
No, You can't assigned wait statement as above to your web element. If you want to wait for your element using Page factory model for a below element then you have to create
public void isLoaded() throws Error {
// Initial loading, called when creating the page object to make sure that the page is loaded to a state where it is ready to interact with us, in our case it means that button is present in DOM and visible.
public class BaseClass
{
private void waitForVisibility(WebElement element) throws Error{
new WebDriverWait(driver, 60)
.until(ExpectedConditions.visibilityOf(element));
}
}
And then in your page object model class you can extend this BaseClass.
public class page extends BaseClass
{
#FindBy(how = How.XPATH, using = "//input[#name='id']")
private WebElement inputSliceId;
waitForVisibility(inputSliceId);
}
I have defined wait in the BaseClass to achieve re-usability of waitForVisibility code across all page object classes.
Also after button clicking if you want to wait for a pop up to be appear then you can include code like below:
#FindBy(xpath = "//div[#role='alertdialog']")
private WebElementFacade alertPopup;
buttonSubmit.click();
waitForVisibility(alertPopup);
There are a couple of things:
If your usecase is to invoke getText() on an element, instead of elementToBeClickable(), using visibilityOfElementLocated() would be just perfect.
To extract the text you don't have to create any new element, instead you can directly invoke visibilityOfElementLocated() once the element is returned as follows:
System.out.println(new WebDriverWait(driver, 20).until(ExpectedConditions.visibilityOfElementLocated(By.xpath("//a/h3"))).getText());
If your usecase doesn't include any validation for the pop-up which gets displayed and disappear, you don't need to induce any waiter for it and safely ignore it. So once you invoke click() on the buttonSubmit you can proceed to your next validation point adjusting the timespan for the pop-up to be displayed and disappear.
So, your optimized code block will be:
buttonSubmit.click();
// consider the total timespan for the wait to be cumulative of: pop-up displayed + pop-up disappear + next interactable element to become clickable
new WebDriverWait(driver, Duration.ofSeconds(20)).until(ExpectedConditions.elementToBeClickable(By.xpath("xpath_next_element_to_click")));
You can find a relevant detailed discussion in How to add explicit wait in PageFactory in PageObjectModel?
First, you can declare a method where set up the wait itself, in a separate class used like base or parent, like that:
protected void waitForElementToBeLoaded(WebElement element) {
wait.until(ExpectedConditions.elementToBeClickable(element));
}
Then, you can declare a second method where use the first one, in a separate class used like fictional page, like that:
public void sendMessageForm( ){
waitForElementToBeLoaded(sendBtn);
sendBtn.click();
}
Finally, every time you use the previous method the wait will be triggered, example:
contactUsPage.sendMessageForm();
I have a table with several rows. Some of these rows may have a specific element and others may not. For sure some will and some won't.
I find the row and have it into a WebElement. Now to see whether an element is there I do the following (assume xp = ".//someelement)
List<WebElement> eles = row.findElements(By.xpath(xp));
if (eles.size() == 0) {
// element is not there
} else {
// element is there
}
This is fine when the element is present. When it is not, it takes like 30 seconds or a minute to figure out that it is not there. If called often this can really slow down the test. I can do
try {
WebElement ele = row.findElement(by.xpath(xp));
} catch (Exception ex) {
// element is not there
}
using a more detailed Exception. This works fine too but same problem. It waits a minute or half a minute.
Is there a way to check more quickly whether an element is present or not? If it were relative to driver (driver.findElementBy()) instead of an element (row.findElementBy()) I think I might know how to do it.
This is Java.
In your first example where you have a List of Elements you are not trying to locate one element; but several (let's say a collection of rows instead of one row). The second element ele is finding (or trying to find) a specific item (let's say 1 row). Hence, ideally you should say in your comments that some elementS were not there for eles . Nevertheless, the time issue is probably down to an implicit or explicit wait. Read here for more.
I prefer the first way where you check for a collection of elements (so you can aim it at a xpath and find all the tags included (or none at all). Ideally though you should go for an explicit wait.
Here is the method for waiting, it will return true/or false based on if the element was present during the polling time (10sec for example); worth noting that if the element is found as present earlier than the 10sec limit the loop will break and will return true. Of course you can play with timeOut to achieve the desired result; don't go to fast though (like 2 sec) otherwise you are risking your test occasionally failing because the tables were not loaded yet:
public boolean waitForElement(String elementXpath, int timeOut) {
try{
WebDriverWait wait = new WebDriverWait(driver, timeOut);
boolean elementPresent=wait.until(ExpectedConditions.visibilityOfElementLocated(By.xpath(elementXpath)).isDisplayed());
System.out.printf("%nElement is present [T/F]..? ")+elementPresent;
}
catch(TimeoutException e1){e1.printStackTrace();elementPresent=false;}
return elementPresent;
}
I'm guessing that you are already using an explicit wait of 30sec for all of your findElement attempts hence the discrepancy.
Hope the above helps,
Best of luck!
Another option is to use WebDriverWait (explicit waits) rather than implicit ones.
This basically makes it so your tests will only wait a long time when you tell them too, otherwise they'll instantly fail if they don't find the elements you're looking for.
Adapted from slide52 of tourdedave's slideshare
// Use this class whenever you have to access the driver
// And you should only have to setDriver in a BeforeMethod when setting up.
// This method shows how to do it with a single browser
// This could be converted quite easily to threadlocals for parallel test runs
public class DriverManager {
private final WebDriver driver;
public static WebDriver getDriver() {
return driver;
}
public static setDriver(WebDriver driver) {
DriverManager.driver = driver;
}
public class WaitUntil {
public static Boolean displayed(By locator, Integer... timeout) {
try {
waitFor(ExpectedConditions.visibilityOfElementLocated(locator),
(timeout.length = 0 : null ? timeout[0];
} catch (TimeoutException exception) {
return false;
}
return true;
}
// add additional methods you want to wait for
// Look at the source of the ExpectedConditions class to see how to create
// your own
// e.g. presence of element
// number of results from locator changes
// element done moving
// url changes, etc.
private static void waitFor(ExpectedCondition<WebElement> condition, Integer timeout) {
timeout = timeout != null ? timeout[0] : 5; //default timeout if no value given
WebDriverWait wait = new WebDriverWait(driver, timeout);
wait.until(condition);
}
}
Then in any class you can
By submitButtonBy = By.cssSelector(".submit);
use WaitUntil.displayed(submitButtonBy);
And it will wait for 5 seconds. If you want to wait for 10:
use WaitUntil.displayed(submitButtonBy, 10);
The nice thing about making a class with a bunch of methods like this is it's easy to add additional exceptions so you can choose to return false if there's a stale element or something else, rather than have to deal with a try catch in page classes or test classes.
So, I have a class A and it has a (public static WebElement element1, element2).
public class myClass {
public static WebElement element1, element2;
public myClass(){
WebDriver driver = new FirefoxDriver();
this.element1 = driver.findElement(By.id("button"));
this.element2 = driver.findElement(By.id("text"));
}
}
And then I have a test class where it has a method called #Test public void testClassA.
#Test
public void testClassA(){
myClass m = new myClass();
m.element1.click();
m.element2.sendKeys("input something");
}
Questions is I am getting org.openqa.selenium.NoSuchElementException: Unable to locate element:{} error. I think my error is happening because the element2 is located in the next page, it shows up after clicking the button. What should I do in my code so that when I assign both elements to findBy method the test is going through the first click and then sendKeys to element2?
The way you have written the code will break in scenarios where
elements are dynamic and also on page navigation.
This is not a good practice to find the webelement in different class altogether and use the object of that class in your test class.
As you can see in code: myClass m = new myClass();, when object of myClass is created, the constructor is triggered and driver finds both the element1 and element2 at once. And, since element2 is still not displayed, it throws an exception.
I don't know what prompted you to follow this practice, instead find the webelement only when you actually need it. There seems to be many alternative and it depends on how you want to design your code.
Use same class to find the element and performing action on that.
Use different methods to find the webelements, instead of using constructors to find them.
Use keywords for webdriver actions if you want to make things generic.
Use properties file to store the locators and dat if you want.
More Standard practice(I guess so):
Use Page Objects to find the webelements.
Use PageFactory in addition to Page Objects.
Good Reference: http://www.guru99.com/page-object-model-pom-page-factory-in-selenium-ultimate-guide.html
As you mentioned that the element2 is present in the next page, you have to wait till the new page loads. Without this wait, if you try finding the element2, it will throw an exception as the element is not found on the current page before the page change.
Solutions:
1) Add a Explicit wait after the element1 click() method. You can wait till the element2 is present after the click().
m.element1.click();
WebElement myDynamicElement = (new WebDriverWait(driver, 10)).until(ExpectedConditions.presenceOfElementLocated(By.id("text")));
m.element2.sendKeys("input something");
2) Simple but I would not recommend this. Use Thread.sleep() to wait for the new page to load.
3) Use Page Object Design Pattern.
You can use webdriver implicitwait to wait for the elements on the page to load for a certain period of time.
driver.manage().timeouts().implicitlyWait(8, TimeUnit.SECONDS);
As you can see in the above code i used 8 seconds for the elemnets on the page to load. Read more about wait in Webdriver
Use a try catch block to handle the exception.
#Test
public void testClassA(){
driver.manage().timeouts().implicitlyWait(8, TimeUnit.SECONDS);
try{
myClass m = new myClass();
m.element1.click();
m.element2.sendKeys("input something");
}catch(NoSuchElementException e){
e.printStackTrace();
}
}
How to make the WebDriver wait until the page loading stops completely.
Means, it waits and checks whether entire page has been loaded or not, then only it proceeds with the next line execution.
The biggest problem is that there is no generic, one-size-fits-all solution that will work for even a majority of users. The concept of "when is my page finished loading" is rendered nearly meaningless in today's dynamic, AJAX-heavy, JavaScript-dependent web. One can wait for the browser to determine network traffic is complete, but that doesn't take JavaScript execution into account. One could define "complete" as the page's onload event having fired, but that overlooks the possibility of the page using setTimeout(). Furthermore, none of these definitions take frames or iframes into account.
When it comes to Selenium, there are a couple of factors to consider. Remember that the Selenium RC API is 10 years old. When it was designed and developed, the architecture of typical web pages made a method like waitForPageToLoad practical. The WebDriver API, on the other hand, recognizes the current reality. Individual driver implementations usually will try to wait for a page load during an explicit page navigation (e.g., driver.get()), but this wait will be a "best effort", and is not a guarantee. Please note that navigation caused by user interaction (e.g., element.click()) will be less likely to fully wait, because such interactions are asynchronous, and thus inherently have race conditions.
The correct approach for WebDriver is to wait for the element you want to interact with to appear on the subsequent page. This is best accomplished with a WebDriverWait or a similar construct. You might find some of these other constructs in the support library, mainly in those dealing with the Page Object pattern. You could also try setting the implicit wait timeout in your driver instance, but I believe using it obscures intent.
That's actually the default behavior of Selenium - it waits until all requests are complete before going on to the next line of code.
There is a design pattern provided through the Selenium support library SlowLoadableComponent that would do what you want: https://selenium.googlecode.com/svn/trunk/docs/api/java/org/openqa/selenium/support/ui/SlowLoadableComponent.html. The gist is that you write your page object to extend SlowLoadableComponent. You will have to provide implementations for two abstract methods in SlowLoadableComponent: load() and isLoaded()
TheisLoaded() method should check everything you need to consider your page 'loaded'. The load() method performs the actions necessary to load your page object. You specify a load timeout for your page object (I do this through the page object's constructor). When you invoke the get() method on your page object, which is inherited from SlowLoadableComponent, it will call isLoaded(). If your page object is not loaded, it will then call load() to load your page object. It will continue to do this until your page object is loaded or until your timeout expires.
You will have to define yourself what it means for your page object to be loaded, however. There is no out of the box way for Selenium to determine if your particular page object is loaded or not because these determinations are so context-sensitive. For example, consider a page object representing the login page for a web app. It is 'loaded' if the username and password entry text boxes and the submit login button are visible. This does not apply to a page object representing some other page in a web app. You have to custom tailor the 'is loaded' criteria for any given page object.
Here is a simple example. Basic abstract loadable object:
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.support.PageFactory;
import org.openqa.selenium.support.ui.SlowLoadableComponent;
import org.openqa.selenium.support.ui.SystemClock;
public abstract class AbstractLoadableComponent<T extends AbstractLoadableComponent<T>> extends SlowLoadableComponent<T> {
public static final int DEFAULT_TIMEOUT_IN_SECONDS = 30;
private final WebDriver driver;
private final int timeoutInSeconds;
public AbstractLoadableComponent(final WebDriver driver, final int timeoutInSeconds) {
super(new SystemClock(), timeoutInSeconds);
this.driver = driver;
this.timeoutInSeconds = timeoutInSeconds;
this.load();
}
public final WebDriver getDriver() {
return driver;
}
public final int getTimeoutInSeconds() {
return timeoutInSeconds;
}
#Override
protected void load() {
PageFactory.initElements(getDriver(), this);
}
}
Basic abstract page object:
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.support.ui.SlowLoadableComponent;
public abstract class AbstractPage<T extends AbstractPage<T>> extends AbstractLoadableComponent<T> {
private final String url;
public AbstractPage(final WebDriver driver) {
this(driver, driver.getCurrentUrl(), DEFAULT_TIMEOUT_IN_SECONDS);
}
public AbstractPage(final WebDriver driver, final String url) {
this(driver, url, DEFAULT_TIMEOUT_IN_SECONDS);
}
public AbstractPage(final WebDriver driver, final String url, final int timeoutInSeconds) {
super(driver, timeoutInSeconds);
this.url = url;
}
public final String getUrl() {
return url;
}
#Override
protected void load() {
super.load();
if(url != null) {
getDriver().get(url);
}
}
}
Basic concrete page object class for a login page:
import org.openqa.selenium.NoSuchElementException;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.support.FindBy;
import org.openqa.selenium.support.How;
import static org.testng.Assert.assertTrue;
public final class LoginPage extends AbstractPage<LoginPage> {
#FindBy(how = How.ID, using = "username")
private WebElement usernameBox;
#FindBy(how = How.ID, using = "password")
private WebElement passwordBox;
#FindBy(how = How.NAME, using = "login")
private WebElement loginButton;
public LoginPage(final WebDriver driver) {
this(driver, driver.getCurrentUrl(), DEFAULT_TIMEOUT_IN_SECONDS);
}
public LoginPage(final WebDriver driver, final String url) {
this(driver, url, DEFAULT_TIMEOUT_IN_SECONDS);
}
public LoginPage(final WebDriver driver, final String url, final int timeoutInSeconds) {
super(driver, url, timeoutInSeconds);
}
#Override
protected final void isLoaded() throws Error {
try {
assertTrue(usernameBox.isDisplayed(), "Username text box is not displayed");
assertTrue(passwordBox.isDisplayed(), "Password text box is not displayed");
assertTrue(loginButton.isDisplayed(), "Login button is not displayed");
} catch(NoSuchElementException nsee) {
throw new Error(nsee);
}
}
}
driver.manage.implicitlywait(3, TimeUnit.Seconds) will hep.