We are a software consultancy based in Berlin, Germany. We deliver
high quality web apps in short timespans.

Upstream Agile GmbH

Testing the AnyTime date picker with Cucumber/Selenium

March 20, 2011 by Alex

On cobot we are making extensive use of the AnyTime date picker. When it came to integration testing with Cucumber/Capybara, until recently I got away with using the Rack::Test driver and doing

And I fill in "2010-01-01 12:45:00" for "Date"

Yesterday I finally needed to enter a date in a Scenarion that was using Javascript (with the Selenium driver. With JavaScript enabled the above step doesn’t work anymore because when Selenium tries to type into the text field Anytime pops up and resets the text field’s contents. After some fiddling around I decided that I would make selenium click the actual buttons on the date picker. Not only did this seem to be the least hackish way, it would also mean I would be testing the real thing, i.e. clicking on buttons like a user would. This becomes especially important once you start with internationalization and different time formats (e.g. 24h vs. 12h system), where you want to make sure AnyTime generates the proper date string.

Long story short, it took me almost a day to figure out how to do this. I played around with a lot of variations, but in the end this is what you have to do:

When /I select the time "([^"]+)" from "([^"]+)"/ do |time_string, label|
  time = Time.parse(time_string)
  And %Q{I fill in "" for "#{label}"}
  find_field(label).click

  click_on_selectors ".AnyTime-btn:visible:contains(#{time.year})",
    ".AnyTime-mon#{time.month}-btn:visible",
    ".AnyTime-dom-btn:contains(#{time.day}):visible:first",
    ".AnyTime-hr#{time.hour}-btn:visible",
    ".AnyTime-x-btn:visible"
end

def click_on_selectors(*selectors)

  def recurse(*selectors)
    if selectors.any?
      wait_for_css_selector_fn(selectors.first,
        "$('#{escape_javascript selectors.first}').click(); #{recurse(*selectors[1..-1])}")
    else
      'window.__capybara_wait = false;'
    end
  end

  page.evaluate_script "window.__capybara_wait = true"
  page.evaluate_script recurse(*selectors)
  wait_until 10 do
    page.evaluate_script "!window.__capybara_wait"
  end
end


include ActionView::Helpers::JavaScriptHelper
def wait_for_css_selector_fn(selector, after)
  <<-JS
    (function() {
      var time = new Date().getTime();
      var runDelayed = function() {
        if(!$('#{escape_javascript selector}').length) {
          if(time < new Date().getTime() - 5000) {
            throw('waited too long for #{escape_javascript selector}')
          } else {
            window.setTimeout(runDelayed, 100);
          }
        } else {
          #{after};
        };
      }
      window.setTimeout(runDelayed, 100);
    })();
  JS
end

In you Cucumber feature you can then call:

When I fill in the time "2010-01-01 12:00" for "Date"

What the above code essentially does is open the date picker and click on all the required buttons. Just as important (and that was the tricky part) it asynchronously waits for the necessary events (open/close date picker, date shows up in text field). Enjoy.