Debugging your scripts with Pry

One cool thing that came out of SelConf 2013 (I wasn’t able to attend … just experienced it online) was Dave Haeffner‘s new Selenium tutorial Elemental Selenium. Its an email newsletter published once a week with a Selenium tip (in Ruby!) focusing on the basic foundations of how to solve certain problems. This is mainly aimed at beginning Selenium users, but anyone using Selenium and Ruby would find the tips useful. I have learned some new ideas and ways to address certain problems that I hadn’t thought of before from some of his tips. You can also browse the repository of archived tips on the website as well.

After Dave posted tip 11 about building a simple REPL to debug your tests, I started up a conversation with him about the benefits of using Pry over writing a simple debugger. The sum total of the conversation resulted in Dave asking me to write a guest tip on the subject.

LOL, I think it turned out pretty well. Go check it out and let me know your thoughts.

Advertisements

Did I select the right element?

Time and time again, I will find unexpected behavior in my tests (either from an error, or the wrong text being displayed, etc), and I need to fallback to determining if I’m even using the correct element.

Two very handy methods that I created allow me to debug whether I have selected the right element. They are highlight and html:

highlight:

This method allows me put a temporary bright yellow border around the element in question. This gives me a visual clue of which element is selected when I look at the browser.

  def highlight element, time = 3
    orig_style = element.attribute("style")
    @driver.execute_script("arguments[0].setAttribute(arguments[1], arguments[2])", element, "style", "border: 2px solid yellow; color: yellow; font-weight: bold;")
    if time > 0
      sleep time
      @driver.execute_script("arguments[0].setAttribute(arguments[1], arguments[2])", element, "style", orig_style)
    end
  end
  some_field = @driver.find_element(:css => "input.text") #=> <Selenium::WebDriver::Element ... >
  highlight some_field #=> nil (But look at the browser, and you should see the element get highlighted for three seconds)
  highlight some_field, 10 #=> nil (But look at the browser, and you should see the element get highlighted for ten seconds)
  highlight some_field, 0 #=> nil (But look at the browser, and you should see the element get highlighted, and stay highlighted)

html:

This method allows me to see the actual HTML code of the element in question. I can use it to compare to the DOM of the actual page if I still don’t “see” the element on the page.

  def html element
    @driver.execute_script("var f = document.createElement('div').appendChild(arguments[0].cloneNode(true)); return f.parentNode.innerHTML", element)
  end
  some_div = @driver.find_element(:css => "div.something") #=> <Selenium::WebDriver::Element ... >
  puts html(some_div) #=> "<div class=\"something\" ... </div>"

———————–

If you wanted to get really clever, you could open up the Selenium::WebDriver::Element module directly and add those methods in directly:

module Selenium::WebDriver
  Element.module_eval do
    def highlight time = 3
      orig_style = self.attribute("style")
      @driver.execute_script("arguments[0].setAttribute(arguments[1], arguments[2])", self, "style", "border: 2px solid yellow; color: yellow; font-weight: bold;")
      if time > 0
        sleep time
        @driver.execute_script("arguments[0].setAttribute(arguments[1], arguments[2])", self, "style", orig_style)
      end
    end
    def html element
      @driver.execute_script("var f = document.createElement('div').appendChild(arguments[0].cloneNode(true)); return f.parentNode.innerHTML", self)
    end
  end
end
some_field.highlight #=> nil (But look at the browser, and you should see the element get highlighted for three seconds)
some_div.html #=> "<div class=\"something\" ... </div>"

Using both of these methods in conjunction, I can always figure out which element I have selected during my interactive coding process.

How did I miss this? Ruby bindings support CSS compound selectors!

I just came across this … but it has saved me from begging a developer to make a simple DOM tweak to a part of the site that no one really cares too much about.

CSS selectors in general web design allow you to specify multiple selectors, called compound selectors, to apply styling to using a comma between each selector. Why shouldn’t SWD?

Well, today I learned that indeed it does (since the 2.24.0 version from 06-19-2012)!

How do you use this? Well, let’s say you have a portion of the DOM that could have either a “p” or an “a” tag, you could write something like this to select one OR the either :

@driver.find_element(:css => "a, p")

This will select the first element that SWD comes across, regardless of which order you write the selectors. Thus, given this HTML

<div id="outer">
  <span id="inner">Foo</span>
</div>
<span id="last">Bar</div>

You could write a selector

@driver.find_element(:css => "#last, #outer")

And it would select the div with the id of “outer” because it is the first selector that is found in the DOM. Same goes for child descendents. Writing a selector like this

@driver.find_element(:css => "span#inner, #outer")

will select the “outer” div again.