The SQA2 Blog: Automation
You may have experienced it at one point or another. You’re building out this great automation script when you come across an element you need to click. You create the element locator, plug it into your script, and then run your test. Everything works great until you get to the click for that element. And then… nothing. It just sits there like you didn’t tell your automation to click it. Sound familiar? Well you’re not alone. You’ve encountered some WebDriver click issues. I was feeling this misery a few weeks ago while automating a client’s HTML5 AngularJS app.
How to Resolve WebDriver Click Issues
How do you solve it? Well, there are a lot of strategies to resolving this issue. We’ll explore some options on what you can do to move forward to get that automation to click! In each section, we’ll explore the various scenarios you may be encountering, then demonstrate what I’ve done to resolve each one. All solutions are written in Python.
There are times when you are running through your automation and elements don’t get clicked. Other times, they click just fine or maybe it just doesn’t click at all. One of the most common WebDriver click issues is getting the timing right. Your automation code is capable of out-pacing the element’s being rendered to the screen. If the timing is right (or wrong, in this case), the WebDriver element click may fire before the element is ready for it.
Solution: Wait For Element Present
The best approach to resolve this issue, and where you should first start debugging when you encounter a WebDriver click issue, is to introduce waits. It should be best practice that throughout your automation, you first explicitly wait for elements you are about to interact with. You accomplish this with a combination of WebDriverWait and Expected Conditions in WebDriver.
from selenium.webdriver.support.ui import WebDriverWait from selenium.webdriver.support import expected_conditions as EC #Set timeout for 10 seconds timeout = 10 expected_element = (By.CSS_SELECTOR, elementIdentifier) WebDriverWait(driver, timeout).until(EC.presence_of_element_located(expected_element)) expected_element.click()
In the above example, we import WebDriverWait and Expected Conditions as EC. We then set a timeout for WebDriver to wait for the element to be located. Once it’s located, we click the element now that we have ensured it’s present.
One of the other common WebDriver click issues is the visibility of web elements when trying to click them. They may be found in the browser DOM but their visibility is another issue. This is a common issue encountered these days with websites becoming more and more dynamic every day. With responsive UI frameworks like Angular, Meteor, ReactJS, and many others gaining popularity, automation has to account for dynamic rendering. You can no longer expect that when the page load is done, the page is ready for interaction. Elements you want to interact with may not be visible yet until timing or an event triggers the element’s visibility.
Solution: Wait For Element Visibility
There is a solution to solve this issue similar to the Timing issue we mentioned above. However, first you must understand the application you are automating. Now since you are all automating different applications, I can’t provide any help in manipulation of your specific applications. It is up to you to trigger what events need to occur to reliably make the expected element appear. Once you do that, we can use an approach that combines WebDriverWait and another Expected Condition. This time, we will use the visibility condition for the expected element.
from selenium.webdriver.support.ui import WebDriverWait from selenium.webdriver.support import expected_conditions as EC #Set timeout for 10 seconds timeout = 10 expected_element = (By.CSS_SELECTOR, elementIdentifier) WebDriverWait(driver, timeout).until(EC.visibility_of(expected_element)) expected_element.click()
This time, we perform almost the same steps as the Timing solution, but we wait for the visibility of the element.
Problem: It’s Still Not Clicking!
If you’ve tried the above two solutions and it’s still not clicking the element, then your WebDriver click issues might be related to the element selector. There used to be an option to highlight elements. This option is not built into WebDriver. To solve this, I created a highlight method in a custom WebDriver wrapper that does the highlighting for me.
Solution: Bust Out the Highlighter
My solution below is a revised version of dariodiaz’s solution hosted on Github.
def highlight(self, element): """Highlights a Selenium WebDriver element to indicate successful selector targeting.""" driver = element._parent def apply_style(s): driver.execute_script("arguments.setAttribute('style', arguments);", element, s) original_style = element.get_attribute('style') count = 0 colors = ['yellow', 'red'] while count < 10: if ((count % 2) == 0): background = colors border = colors else: background = colors border = colors apply_style("background: %s; border: 2px solid %s;" % (background, border)) time.sleep(.2) count += 1 apply_style(original_style)
This solution takes a WebElement as input. It will then toggle the border and background colors and red and yellow for 2 seconds. Running this will allow you to see very clearly what element, if any, your automation is targeting. If no element flashes on the screen, either its not selecting anything, or there is a visibility issue. You may have overlapping elements on the screen that mask the element you are targeting.
Problem: I Can See Clearly Now, It’s Still Not Working
It’s hard to believe that even if you try the top three solutions for resolving WebDriver Click Issues, it’s still possible to have a problem. This is the boat I was in while trying to click on some elements for a business partner’s AngularJS HTML5 application. Unfortunately, I was not seeing the symptoms explained above. In my scenario, and possibly yours if you have read this far, is that it just isn’t clicking.
My first instinct was to blame AngularJS. Surely, that framework is doing something to interfere with my click. However, after much research and playing with the browser DOM, I concluded that AngularJS wasn’t to blame. The culprit was surprising and was a huge “Gotcha!” moment for me when I figured it out.
The solution came when I installed a Chrome Plugin called coordinates that allowed me to see the coordinates that my mouse was at on the application. While I hovered over the element I wanted to click, I noticed that despite being rendered on a massive Mac monitor some 2560 pixels across, the coordinates showed around 800 pixels. How was that possible? I found that the application, in an attempt to be responsive, implemented a CSS property “transform” with the value “scale(n)” on the body tag. This changed the size of the content without adjusting it’s pixel density. The higher n got (around 1.5 in my case), the farther from the expected location the element appeared.
Solution: Nullify “transform: scale(n)”
The solution was quite clear now that I had discovered the problem. The “transform” property on the body tag was placing the elements at a different location than what WebDriver expected. Nullifying, or setting scale to 1, put all elements back into their expected locations.
element = driver.find_element_by_tag_name("body") driver.execute_script("arguments.style.transform='scale(1)';", element)
We understand the frustration when you’re trying to get something like this to work. You try tons of approaches but it just doesn’t seem to work. We’re sharing this knowledge so that fewer people get bogged down by these strange WebDriver click issues. We hope that this gets you moving forward in your automation. If you’ve experienced similar issues and have other suggestions, please let us know so we can add more helpful solutions to the list.
Reach Out for Automation Help!
Still having trouble with your automation? We can help! Reach out to us so an experienced QA professional will help you determine the right solution for your organization.