Nidhi Midha
Nidhi Midha

Reputation: 33

Explicit wait not working in Selenium Webdriver

In my code, I need "Please wait..." blocked UI in above panel to first disappear, and then move to below panel, and hence I am putting explicit wait for that particular block span. But even though above panel is blocked its save functionality is still going on, below panel's click functionality starts consecutively.

driver.findElement(By.id("saveHouseholdMember")).click();
WebDriverWait uiBlock = new WebDriverWait(driver, 30);
uiBlock.until(ExpectedConditions.invisibilityOfElementWithText(By.xpath("/html/body/div[2]/form[1]/div/table/tbody/tr/td/span"), "Please wait..."));
driver.findElement(By.id("disabilityFlagRadio")).click();

Here, on click of save button, that panel gets blocked with "Please wait..." message over it. Driver should wait until "Please wait..." text from above panel disappears, and then should perform the below written click functionality, but its not happening as expected. The above panel's save takes around 15 seconds normally.

I referred below link as well, but couldn't find useful inputs. Wait is not working in selenium webdriver

Upvotes: 1

Views: 3847

Answers (1)

Aaron Davis
Aaron Davis

Reputation: 1731

So my guess is that your test is finding and moving past the invisibility check of the element before the panel appears. That's because there is a race condition of what occurs first, the Selenium test check or the website making the panel visible. You can (kind of) fix this by first checking for the panel appearing and only then checking that it has disappeared. This limits some of the race condition aspect of this. It does not however, fix the race condition problem. In the following example, if the panel appears and then disappears before the selenium code executes looking for the panel to be visible it will fail. The best approach would be to look for some state that the code causing the panel to disappear and be done sets when it is complete but I don't know the code of or the logic of the page. One example is if the web developers have the save action (upon completing) add a CSS class to some element. Then your test can wait for an element to get that class. Naive example (with race condition):

// Do stuff to get to the page needed for the test
driver.findElement(By.id("saveHouseholdMember"))
      .click();
// Make sure the panel first appears and then disappears
By panelBy = By.xpath("/html/body/div[2]/form[1]/div/table/tbody/tr/td/span");
new WebDriverWait(driver, 30).until(ExpectedConditions.visibilityOfElementLocated(panelBy));
new WebDriverWait(driver, 30).until(ExpectedConditions.invisibilityOfElementLocated(panelBy));
// Panel is gone, click the next button
driver.findElement(By.id("disabilityFlagRadio"))
      .click();

Note that the 30 in the creation of the web driver wait means that it will keep checking for a matching element in the desired visibility state for up to 30 seconds. If no matching element with the correct visibility state is found in that time a TimeoutException will be thrown. Also, please note that the panel is being located by the XPath that you provided. I don't recommend that XPath expression, however, due to the fact that it is absolute. I shy away from XPath when possible as a Selenium locator due to the fact that it can be brittle and annoying to read for test maintenance. Usually you can get the same element (more easily) via other locator strategies (e.g. CSS selectors, name, id, etc) in a way that is much cleaner to read. When you do absolutely need XPath (e.g. perhaps you need to look for some particular text on the page) then use relative XPath. For the (simplistic) example below:

<html>
    <body>
        <div>
            <span>
                <input name="foo"></input>
            </span>
        </div>
    </body>
</html>

If you use absolute XPath to find that input, it would look like this:

By.xpath("/html/body/div/span/input")

But that selector will break if any part of that entire chain changes (e.g. a div is added that doesn't change the behavior at all will break the tests). Instead you can use relative XPath:

By.xpath("//input[@name='foo']")

Note that even this example is flawed and just an example of relative XPath. If you actually saw the above snippet you should probably just do:

By.name("foo")

Upvotes: 2

Related Questions