Locating Troublesome Page Elements That Don’t Want To Be Found
Sometimes there’s an item on a page you want to click on or detect. You can see it in the browser’s DOM, but still Watir / WebDriver can’t locate it when you try different ways of finding it.
The mystery lies in the DOM and how it can trick you by not showing you what’s actually there.
Take the example above. Here’s a green alert message that’s briefly displayed at the top of my page. I want to detect the alert when it appears.
The alert contains the text “Your payment option has been successfully update” which sits inside a div tag with a unique list of classes we can use to locate it.
Many UI frameworks use generic components for alerts and user messaging. This alert modal could be used on the same page to display different messages. I want to identify when this particular alert appears so I need to include the text to find it.
# The green fade-in modal that appears at the top of the page during a 'success' operation. def self.top_success_alert_modal Driver.instance.div(class: ['alert-success'], text: 'Your payment option has been successfully updated') end
Working The Problem
I set a breakpoint in my code where I was trying to locate my mystery element. Then I start my test code in debug mode, and I let it run until it pauses.
Now we’re going to use a particularly powerful feature of Visual Studio Code; the “Debug Console”.
When your code is paused, you can use the Debug Console to enter commands, read and set variables, and even call methods.
First enter the Ruby code that should find our alert modal:
Driver.instance.div(class: [‘alert-success’], text: ‘Your payment option has been successfully updated’).present? # false
Watir can’t find the element. Huh? I can clearly see that its in the DOM.
Start By Dumbing It Down
When you’re having trouble finding something in the DOM, the first thing you should do is simplify your locators. Let’s reduce the complexity of the search and only look for a div tag with the class=”alert-success” attribute.
Driver.instance.div(class: [‘alert-success’]).present? # true
This returns true. Ok great.
Sometimes there are components defined in the DOM of a webpage that aren’t actually displayed at the particular moment.
Alerts and other elements may be hidden from view but ready to be triggered when needed. This makes our job harder because we need to make especially sure we’re addressing the element we think we are.
To make sure Watir isn’t spotting another div tag with class=”alert-success” somewhere else on the page, we can try returning the number of divs that match our locators. Notice how I’m using “divs” instead of “div” – this makes Watir return a collection of all tags that match, not just the first one that matches.
Driver.instance.divs(class: ['alert-success']).count # 1
Ok so there’s 1 div of class=”alert-success” on this page, but when I specify the text that’s clearly in the element, Watir comes up empty? Weird.
To make it a bit cleaner, let’s grab this element and assign it to a variable.
my_element = Driver.instance.div(class: ['alert-success'])
Now let’s get ‘my_element’ to display any text it may contain.
element.text # “\u00D7\nYour payment option has been successfully updated”
BINGO! Check out those extra characters at the start of the string.
Turns out that’s a multiplication sign (\u00D7), followed by a new-line character (\n).
Pro Tip: Add this website to your arsenal of useful tools. http://www.fileformat.info/info/charset/UTF-8/list.htm
Looking more closely at the DOM we can see the “x” inside the button tag just above the string.
Most people, including me, wouldn’t expect the “x” and even the completely non-obvious new-line character to interfere with locating the div tag. But they did.
Scenarios like this aren’t always obvious, and can be frustrating. By interrogating the element we did find, it can help us better visualize what’s actually going on.
Oh yeah, so how can we detect this particular alert? We can do a substring search using a regex:
Driver.instance.div(class: ['alert-success'], text: /Your payment option has been successfully updated/).present? # true