-
-
Save ess/1a8ac4128f5d2760a5ef to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment