3
  1. I'm trying to click on a button which is visible on a page by using webdriver wait but webdriver is only able to click on the button once I add a Thread.sleep to the code.

  2. I have also checked the button to see whether its visible (True) before I execute my code and it in turn returns = true.

//Button Visiblity check:

List<WebElement> signOutList = driver.findElements(.xpath(".//*[starts-with(@id,'fulfillment-time-type-section-')]/div[2]//button"));
Assert.assertTrue(signOutList.size() > 0);

//The code below Doesn't click on the button

By timeDropdownButton = By.xpath(".//*[starts-with(@id,'fulfillment-time-type-section-')]/div[2]//button");
WebElement myDynamicElement = (new WebDriverWait(driver, 10))
              .until(ExpectedConditions.elementToBeClickable(timeDropdownButton));
myDynamicElement.click();

//The code below Does click on the button:

Thread.sleep(500);
By timeDropdownButton = By.xpath(".//*[starts-with(@id,'fulfillment-time-type-section-')]/div[2]//button");
WebElement myDynamicElement = (new WebDriverWait(driver, 10))
              .until(ExpectedConditions.elementToBeClickable(timeDropdownButton));
myDynamicElement.click();


Please note I have also tried to click the button using JS code and WebDriver actions etc

I don't know why 'Wait Clickable' only works when I combine with 'Thread.sleep'?

The button I'm trying to click

Gbru
  • 1,065
  • 3
  • 24
  • 56
  • did you tried with increasing the time from 10 sec to more..??? – Saurabh Gaur Jul 22 '16 at 14:32
  • Hi Saurabh, I tried the following code but it still didnt work (Changed from 10 to 1000): WebElement myDynamicElement = (new WebDriverWait(driver, 1000)) – Gbru Jul 22 '16 at 14:40
  • Try once using `ExpectedConditions.visibilityOfElementLocated`..and let me know.. – Saurabh Gaur Jul 22 '16 at 14:47
  • thanks for you help Saurabh, Im afraid even trying ***ExpectedConditions.visibilityOfElementLocated*** it still dont work – Gbru Jul 22 '16 at 14:48
  • Could you share what is the exception occurred?? – Saurabh Gaur Jul 22 '16 at 14:58
  • thats the problem I dont even seem to be getting an exception, the button is visible becuase i have tested it using the listed method, it seems the button is only clickable when i add Thread.sleep to the code :/ – Gbru Jul 22 '16 at 15:00
  • Means when you are going to click without `Thread.sleep`...no exception occurred and nothing heppen with click. Right?? – Saurabh Gaur Jul 22 '16 at 15:04
  • yes that it correct – Gbru Jul 22 '16 at 15:06
  • The Steps which Im taking are: Access: www.pizzahut.co.uk > Click on 'Pizza' [Button] > Click on any 'Start You Order' [Button] > Enter postcode 'TS1 4AG' > click 'Find a Hut' [button] > click 'Start Your Order' [button] The driver will only click on the last button 'Start Your Order' if i use Thread.sleep – Gbru Jul 22 '16 at 15:10

5 Answers5

2

You want to avoid Thread.sleep in tests as much as possible generally. It might not seem so important with just a few tests but there are a lot of problems inherent in their use. First off, if you have a bunch of tests the run time of the test suite can become unmanageable. Second, they are sometimes not enough. For instance, waiting 500 milliseconds might be enough with production machines in general, but if the web server is under heavy load or in a test environment it might take 750 milliseconds to be ready. Then you are dealing with non-deterministic failures. It is best to use constructs like WebDriverWait and give them a sane (but overly-generous) maximum value so that you don't have to wait longer than necessary but if it fails it means there was something seriously wrong with the environment under test.

Also the Pizza Hut site here is making heavy use of asynchronous java script and floating panels, etc. All of that requires a bit more care when using Selenium since elements need to be ready to go before interacting with them but they are often already in the DOM. This means that Selenium by default will probably find them quickly before the JavaScript has completed and they are not actually in a ready to use state. You will want to make use of ExpectedConditions as you were already trying to do. The button you have been having troubles with needs the wait for JavaScript to finish that was already suggested but it needs to not be on page load but rather right before clicking the button. Example that worked on my machine:

@Test
public void foo() {
    WebDriver driver = new FirefoxDriver();
    driver.manage()
          .window()
          .maximize();
    // Move through the page to the point you are interested in
    driver.get("https://www.pizzahut.co.uk/");
    waitForElement(driver, By.cssSelector(".hidden-xs [title='Pizza']")).click();
    waitForElement(driver, By.cssSelector("form[action='/menu/startyourorder']")).submit();
    WebElement postElement = waitForElement(driver, By.id("ajax-postcode-txt"));
    postElement.sendKeys("TS1 4AG" + Keys.ENTER);
    // Wait for the java script to finish executing
    waitForJavaScript(driver);
    // Finally you should be able to click on the link
    waitForElement(driver, By.partialLinkText("Start Your Order")).click();
    // continue on ... then quit the driver
    driver.quit();
}

private WebElement waitForElement(WebDriver driver, By locator) {
    return new WebDriverWait(driver, 10).until(ExpectedConditions.elementToBeClickable(locator));
}

private void waitForJavaScript(WebDriver driver) {
    new WebDriverWait(driver, 10).until(new Predicate<WebDriver>() {
                                            public boolean apply(WebDriver driver) {
                                                return ((JavascriptExecutor) driver).executeScript("return document.readyState")
                                                                                    .equals("complete");
                                            }
                                        }
    );
}

In a WebDriverWait the given int argument is the number of seconds it will keep trying the given check. If it gets to the set amount (10 seconds in the above example) it will throw a TimeoutException.

Aaron Davis
  • 1,731
  • 10
  • 13
0

There is still probably active Javascript being executed on the page. In the first split second it might be modifying it in a way that the ExpectedCondition can't tell. When I have problems with unexpected behavior, I find waiting for the page to load (javascript scripts included) usually solves most of the problems.

To wait for the page to load, you can call some Javascript of your one to check the document's readyState. Try waiting for the page to load and THEN waiting for the element to be clickable.

A quick search returns this (code taken from here):

wait.until( new Predicate<WebDriver>() {
            public boolean apply(WebDriver driver) {
                return ((JavascriptExecutor)driver).executeScript("return document.readyState").equals("complete");
            }
        }
    );
Community
  • 1
  • 1
Parker Beck
  • 195
  • 3
  • 10
  • thanks for your help @Parker , I tried he above code but it still didnt work :( I have combined thread sleep with the above and it seems to be doing the required task – Gbru Jul 25 '16 at 08:37
0

Following method seems to work correctly:

public void waitForPageLoad() {
    ExpectedCondition<Boolean> expectation = new
            ExpectedCondition<Boolean>() {
                public Boolean apply(WebDriver driver) {
                    return ((JavascriptExecutor) driver).executeScript("return document.readyState").toString().equals("complete");
                }
            };
    try {
        Thread.sleep(1000);
        WebDriverWait wait = new WebDriverWait(driver, 30);
        wait.until(expectation);
    } catch (Throwable error) {
        Assert.fail("Timeout waiting for Page Load Request to complete.");
    }
}
Gbru
  • 1,065
  • 3
  • 24
  • 56
0

Try a PageFactory implementation to see if the selenium API solves the problem for you.

public class OrderMyPizzaPage {
        @FindBy(css="form[action='/menu/startyourorder']")
        WebElement startMyOrder;

        public void startOrder() {
            startMyOrder.click();
        }
    }

Great, and now if we assume that everything else has happened successfully and we're to the point where we can click on 'start your order'

 public void myExecutingMethod(WebDriver driver) {
        //previous steps occur and we've just performed interactions which should cause the 'start your order' button to be on the dom

        OrderMyPizzaPage orderPage = PageFactory.initElements(driver, OrderMyPizzaPage.class);
        orderPage.startOrder();

        //And now maybe it worked?
    }

If that doesn't work, try something a little more brute-force. Create a custom predicate for your WebDriverWait that checks for a post-click condition to be true before releasing. If it's not true, then have the predicate re-execute the click!

Because of time-zone issues I wasn't able to get the lookup for a successful post click content, so there's a missing reference in the snippet. It's flagged as FIXME

public void startOrder(WebDriver driver) {
    WebDriverWait wait = new WebDriverWait(driver, 30);
    //Throttle the polling!
    wait.pollingEvery(1500, TimeUnit.MILLISECONDS);
    wait.until(new Predicate<WebDriver>() {
        public boolean apply(WebDriver input) {
            boolean successfulClick = false;
            //FIXME:  Need to check for something that should occur after the click happens successfully
            By lookupAfterClick = By.cssSelector("");
            WebElement nextElement = ExpectedConditions.visibilityOfElementLocated(lookupAfterClick).apply(input);
            if (nextElement == null) {
                System.out.println("Not there yet, Keep trying!");
                By startOrderLookup = By.cssSelector("form[action='/menu/startyourorder']");
                WebElement startOrder =ExpectedConditions.visibilityOfElementLocated(startOrderLookup).apply(input);
                if (startOrder != null) {
                    System.out.println("Clicking Start Order!");
                    startOrder.click();                        
                } else {
                    System.out.println("Start Order isn't visible!");
                }
            } else {
                System.out.println("Hey, That worked!");
                successfulClick = true;
            }
            return successfulClick;
        }
    });        
}
Jeremiah
  • 1,145
  • 6
  • 8
0

have you tried using urlContains ? i facing same problem that you asked, somehow thread sleep proccesed normal

detail like below

(new WebDriverWait(driver, 50)).until(ExpectedConditions.urlContains("/completing-profile?step=skin_type"));
Prasetyo Budi
  • 96
  • 1
  • 8