0

So I am very new and trying to figure out something for use in python.

The element:

<div id="scroll2" class="fm2 p8 cur m_bt2" onclick="javascript:displayResultsLogin('scroll2')"> Show More Results </div>

Xpath: //*[@id="scroll2"]

I execute, using shell:

wait = WebDriverWait(driver, 10)
element = wait.until(EC.element_to_be_clickable((By.XPATH, "//*[contains(text(), ' Show More Results')]")))
element.click()

Once the above has executed, the element:

<div id="scroll3" class="fm2 p8 cur m_bt2" onclick="javascript:displayResultsLogin('scroll3')"> Show More Results </div>

XPath: //*[@id="scroll3"]

When I execute the same command again, it throws an exception:

wait = WebDriverWait(driver, 10)
element = wait.until(EC.element_to_be_clickable((By.XPATH, "//*[contains(text(), ' Show More Results')]")))
element.click()

Exception

Traceback (most recent call last): File "", line 1, in element = wait.until(EC.element_to_be_clickable((By.XPATH, "//*[contains(text(), ' Show More Results')]"))) File "C:\Python34\lib\site-packages\selenium\webdriver\support\wait.py", line 80, in until raise TimeoutException(message, screen, stacktrace) selenium.common.exceptions.TimeoutException: Message:

Unable to understand why. Or a way around it.

Sid
  • 3,749
  • 7
  • 29
  • 62
  • Show complete exception log – Andersson Jan 19 '17 at 09:09
  • @Andersson updated with compete exception log. The thing is I can see the element displayed and ready for click. – Sid Jan 19 '17 at 09:34
  • Does it actually visible on page or you should scroll down first to be able to see this button? – Andersson Jan 19 '17 at 09:37
  • Scroll down a bit, but the same was true for the first time this button was clicked. I am goign to try a scroll down command before this, but still very curious why it worked first time(button was in similar position, out of view) and didn't tghe next – Sid Jan 19 '17 at 09:47

1 Answers1

3

I strongly believe this has to do with the way that the application you’re testing is built: It seems like clicking scroll2 button would make that one invisible and add the scroll3 button to the page.

After that, when you call wait.until(...) what happens is that WebDriver will find the scroll2 button (because the XPath expression that only checks for the inner text will still match it), and then will wait for it to get clickable, which it never will because it's no longer visible.

You can recreate that with a simple web page like this:

<html>
    <head>    
        <script>
            document.onreadystatechange = function() {
                var element = document.getElementById("1");

                element.addEventListener("click", function() {
                    this.style.display = "none";

                    document.getElementById("2").style = "";
                });
            }
        </script>
    </head>
    <body>
        <div id="1">Show More Results</div>
        <div id="2" style="display:none">Show More Results</div>
    </body>
</html>

And this python snippet:

wait = WebDriverWait(driver, 10)
element = wait.until(EC.element_to_be_clickable((By.XPATH, "//*[contains(text(), 'Show More Results')]")))
element.click()

wait = WebDriverWait(driver, 10)
element = wait.until(EC.element_to_be_clickable((By.XPATH, "//*[contains(text(), 'Show More Results')]")))
element.click()

Here also the second wait.until(...) will time out even though the page visually looks correct.

Essentially there are two solutions to this:

  • Change the HTML, so that once the scroll3 button gets added, the scroll2 button actually gets removed from the DOM, or
  • change the test so that it'll locate the button it wants to click on in a more stable way. For instance, for the example mentioned above you could add the display style to the XPath locator to make sure to only find displayed elements.

Example:

wait = WebDriverWait(driver, 10)
element = wait.until(EC.element_to_be_clickable((By.XPATH, "//*[contains(text(), 'Show More Results') and not(contains(@style, 'display: none'))]")))
element.click()

wait = WebDriverWait(driver, 10)
element = wait.until(EC.element_to_be_clickable((By.XPATH, "//*[contains(text(), 'Show More Results') and not(contains(@style, 'display: none'))]")))
element.click()

Note that you might have to include ancestor visibility as well in your locator, for details see this question: How do I select only visible elements using XPath?

Community
  • 1
  • 1
ralph.mayr
  • 1,320
  • 8
  • 12
  • Thanks @ralph.mayr , it went a little over my head for now. Just started learning. I will definitely try to understand it more. In the meantime, I removed the `wait.until` and used an if statement, it seems to work a bit better. Got into a new bit of trouble related to a form showing up every 2-3 clicks. Trying to post about it but can't post for 90 minutes. – Sid Jan 19 '17 at 18:51