Skip to content

Instantly share code, notes, and snippets.

@sj26
Last active January 30, 2019 14:45
Show Gist options
  • Star 4 You must be signed in to star a gist
  • Fork 2 You must be signed in to fork a gist
  • Save sj26/5675951 to your computer and use it in GitHub Desktop.
Save sj26/5675951 to your computer and use it in GitHub Desktop.
Some helpers for interactive with jQuery UI from Capybara, but only when using Selenium WebDriver.
require "capybara"
# Encapsulate some jQuery UI interactions so we can call them cleanly using Capybara.
module Capybara::JQueryUI
# Find the jQuery UI Autocomplete widget corresponding to this element
def autocompleter
jquery_result(jquery_this(".autocomplete('widget')").first)
end
# Fill in an autocompletable field and accept a completion.
#
# If you've pre-filled, select the first autocompletion result with:
#
# autocomplete("field name")
#
# but you can also do the whole lot in a single call:
#
# autocomplete("Author", with: "jac", text: "Jackie", exact: false)
#
def autocomplete field, options={}
options, field = field, nil if field.is_a? Hash
if field
find_field(field).autocomplete(options)
else
set(options.delete(:with)) if options[:with]
jquery_this(%{.autocomplete("search")})
if options.empty?
autocompleter.find("li:first-of-type a").click
else
autocompleter.find("li a", options).click
end
end
end
# Find the jQuery UI Datepicker widget corresponding to this element
def datepicker
jquery_result(jquery_this(".datepicker('widget')").first)
end
# Pick a date from a jQuery UI Datepicker
#
# datepick 2.days.from_now, from: "Deadline"
#
# Note that per http://api.jqueryui.com/datepicker/#method-setDate you
# can use a relative date string, like "+1d"
def datepick date, options={}
if options[:from]
find_field(options.delete(:from)).datepick(date, options)
else
date_and_or_time_pick date, "datepicker", options
end
end
# Find the jQuery UI Datetimepicker widget corresponding to this element
def datetimepicker
jquery_result(jquery_this(".datetimepicker('widget')").first)
end
# Pick a date and time from a jQuery UI Datetimepicker
#
# datetimepick 2.days.from_now, from: "Deadline"
#
def datetimepick datetime, options={}
if options[:from]
find_field(options.delete(:from)).datetimepick(datetime, options)
else
date_and_or_time_pick datetime, "datetimepicker", options
end
end
# Find the jQuery UI Timepicker widget corresponding to this element
def timepicker
jquery_result(jquery_this(".timepicker('widget')").first)
end
# Pick a time from a jQuery UI Timepicker
#
# timepick "12:00pm", from: "Deadline"
#
def timepick time, options={}
if options[:from]
find_field(options.delete(:from)).timepick(time, options)
else
date_and_or_time_pick time, "timepicker", options
end
end
private
def date_and_or_time_pick date_and_or_time, widget, options={}
synchronize do
# Javascript dates are awful, use the millisecond representation for fidelity.
js_value = if date_and_or_time.is_a? Date or date_and_or_time.is_a? Time
"new Date(#{date_and_or_time.to_i * 1000})"
else
date_and_or_time.to_json
end
click
jquery_this(%{.#{widget}("setDate", #{js_value})})
end
end
# Execute some javascript essentially starting with "$(this)" ...
#
# You can also pass arguments which will be JSON round-tripped and
# accessable in your javascript starting with `arguments[1]`.
#
# Please only use this to construct strong ruby encapsulations of jquery
# actions within this module.
def jquery_this dot_something, *args
# From https://code.google.com/p/selenium/wiki/JsonWireProtocol#POST_/session/:sessionId/execute
# Arguments may be any JSON-primitive, array, or JSON object. JSON objects
# that define a WebElement reference will be converted to the corresponding
# DOM element. Likewise, any WebElements in the script result will be
# returned to the client as WebElement JSON objects.
session.driver.browser.execute_script %{return $(arguments[0])#{dot_something}}, self.native, *args
end
# Script evaluations can yield selenium nodes. We need to wrap them in a
# capybara node so we can call capybara actions on them.
def wrap_selenium_node element
Capybara::Node::Base.new(session, Capybara::Selenium::Node.new(session.driver, element))
end
end
# The actions are included into Capybara's Node class so nodes traversed
# through Capybara's API can utilise the jQuery UI actions just like they
# use `click`, `set`, etc.
class Capybara::Node::Base
include Capybara::JQueryUI
end
# The Capybara DSL is included into rspec's ExampleGroups so these methods
# can be used in spec bodies. They just delegate to the page, which is the
# default Capybara::Session.
module Capybara::DSL
delegate :autocomplete, :datepick, :datetimepick, :timepick, to: :page
end
# ... and the session delegates the actions to the current_scope, which is
# always a Node, starting as the whole HTML document and being modified by
# `within` calls.
class Capybara::Session
delegate :autocomplete, :datepick, :datetimepick, :timepick, to: :current_scope
end
@pupeno
Copy link

pupeno commented Nov 6, 2014

I think this should be released as a gem. What do you think?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment