0

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?

undetected Selenium
  • 183,867
  • 41
  • 278
  • 352
bboursaw73
  • 1,128
  • 2
  • 13
  • 26
  • it's not really that you are "assigning them to an element"... but rather that the webdriverwait will return something or throw. You don't need to store that if you don't need it. Either way it will wait until the expected condition is met. Different expected conditions have different types and that is what is returned. (The most common are bool or webelement): https://www.selenium.dev/selenium/docs/api/java/org/openqa/selenium/support/ui/ExpectedConditions.html – pcalkins Apr 01 '20 at 20:54

3 Answers3

1

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);
SeleniumUser
  • 4,065
  • 2
  • 7
  • 30
1

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?

undetected Selenium
  • 183,867
  • 41
  • 278
  • 352
0

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();
Vict01
  • 300
  • 1
  • 10