class Capybara::UI::Widget

Attributes

root[R]

Public Class Methods

action(name, selector = nil) click to toggle source

Defines a new action.

This is a shortcut to help defining a widget and a method that clicks on that widget. You can then send a widget instance the message given by name.

You can access the underlying widget by appending “_widget” to the action name.

@example

# Consider the widget will encapsulate the following HTML
#
# <div id="profile">
#  <a href="/profiles/1/edit" rel="edit">Edit</a>
# </div>
class PirateProfile < Capybara::UI::Widget
  root "#profile"

  # Declare the action
  action :edit, '[rel = edit]'
end

pirate_profile = widget(:pirate_profile)

# Access the action widget
action_widget = pirate_profile.widget(:edit_widget)
action_widget = pirate_profile.edit_widget

# Click the link
pirate_profile.edit

@param name the name of the action @param selector the selector for the widget that will be clicked

# File lib/capybara/ui/widgets/widget.rb, line 50
def self.action(name, selector = nil)
  block = if selector
            wname = :"#{name}_widget"

            widget wname, selector

            -> { widget(wname).click; self }
          else
            -> { click; self }
          end

  define_method name, &block
end
filter() click to toggle source
# File lib/capybara/ui/widgets/widget.rb, line 184
def self.filter
  @filter || superclass.filter
rescue NoMethodError
  raise MissingSelector, 'no selector defined'
end
filter?() click to toggle source
# File lib/capybara/ui/widgets/widget.rb, line 190
def self.filter?
  filter rescue false
end
find_all_in(parent, *args) click to toggle source
# File lib/capybara/ui/widgets/widget.rb, line 106
def self.find_all_in(parent, *args)
  filter.nodes(parent, *args).map { |e| new(e) }
end
find_in(parent, *args) click to toggle source

Finds a single instance of the current widget in node.

@param node the node we want to search in

@return a new instance of the current widget class.

@raise [Capybara::ElementNotFoundError] if the widget can't be found

# File lib/capybara/ui/widgets/widget.rb, line 96
def self.find_in(parent, *args)
  new(filter.node(parent, *args))
rescue Capybara::Ambiguous => e
  raise AmbiguousWidget.new(e.message).
    tap { |x| x.set_backtrace e.backtrace }
rescue Capybara::ElementNotFound => e
  raise MissingWidget.new(e.message).
    tap { |x| x.set_backtrace e.backtrace }
end
new(root) click to toggle source
# File lib/capybara/ui/widgets/widget.rb, line 198
def initialize(root)
  @root = root
end
not_present_in?(parent, *args) click to toggle source
# File lib/capybara/ui/widgets/widget.rb, line 120
def self.not_present_in?(parent, *args)
  filter.nodeless?(parent, *args)
end
present_in?(parent, *args) click to toggle source

Determines if an instance of this widget class exists in parent_node.

@param parent_node [Capybara::Node] the node we want to search in

@return true if a widget instance is found, false otherwise.

# File lib/capybara/ui/widgets/widget.rb, line 116
def self.present_in?(parent, *args)
  filter.node?(parent, *args)
end
root(*selector, &block) click to toggle source

Sets this widget's default selector.

You can pass more than one argument to it, or a single Array. Any valid Capybara selector accepted by Capybara::Node::Finders#find will work.

Examples

Most of the time, your selectors will be Strings:

class MyWidget < Capybara::UI::Widget
  root '.selector'
end

This will match any element with a class of “selector”. For example:

<span class="selector">Pick me!</span>

Composite selectors

If you're using CSS as the query language, it's useful to be able to use +text: 'Some text'+ to zero in on a specific node:

class MySpecificWidget < Capybara::UI::Widget
  root '.selector', text: 'Pick me!'
end

This is especially useful, e.g., when you want to create a widget to match a specific error or notification:

class NoFreeSpace < Capybara::UI::Widget
  root '.error', text: 'No free space left!'
end

So, given the following HTML:

<body>
  <div class="error">No free space left!</div>

  <!-- ... -->
</body>

You can test for the error's present using the following code:

document.visible?(:no_free_space) #=> true

Note: When you want to match text, consider using I18n.t instead of hard-coding the text, so that your tests don't break when the text changes.

Finally, you may want to override the query language:

class MyWidgetUsesXPath < Capybara::UI::Widget
  root :xpath, '//some/node'
end
# File lib/capybara/ui/widgets/widget.rb, line 177
def self.root(*selector, &block)
  @filter = NodeFilter.new(block || selector)
end
selector() click to toggle source
# File lib/capybara/ui/widgets/widget.rb, line 194
def self.selector
  filter.selector
end
widget_delegator(name, widget_message, method_name = nil) click to toggle source

Creates a delegator for one child widget message.

Since widgets are accessed through {WidgetParts::Container#widget}, we can't use {Forwardable} to delegate messages to widgets.

@param name the name of the receiver child widget @param widget_message the name of the message to be sent to the child widget @param method_name the name of the delegator. If nil the method will

have the same name as the message it will send.
# File lib/capybara/ui/widgets/widget.rb, line 73
      def self.widget_delegator(name, widget_message, method_name = nil)
        method_name = method_name || widget_message

        class_eval <<-RUBY
          def #{method_name}(*args)
            if args.size == 1
              widget(:#{name}).#{widget_message} args.first
            else
              widget(:#{name}).#{widget_message} *args
            end
          end
        RUBY
      end

Public Instance Methods

class?(name) click to toggle source

Determines if the widget has a specific class

@param name the name of the class

@return [Boolean] true if the class is found, false otherwise

# File lib/capybara/ui/widgets/widget.rb, line 353
def class?(name)
  classes.include?(name)
end
classes() click to toggle source
# File lib/capybara/ui/widgets/widget.rb, line 344
def classes
  root['class'].split
end
click(*args) click to toggle source

Clicks the current widget, or the child widget given by name.

Usage

Given the following widget definition:

class Container < Capybara::UI::Widget
  root '#container'

  widget :link, 'a'
end

Send click with no arguments to trigger a click event on #container.

widget(:container).click

This is the equivalent of doing the following using Capybara:

find('#container').click

Send +click :link+ to trigger a click event on a:

widget(:container).click :link

This is the equivalent of doing the following using Capybara:

find('#container a').click
# File lib/capybara/ui/widgets/widget.rb, line 229
def click(*args)
  if args.empty?
    root.click
  else
    widget(*args).click
  end
end
double_click(*args) click to toggle source

Double clicks the current widget, or the child widget given by name.

Usage

Given the following widget definition:

class Container < Capybara::UI::Widget
  root '#container'

  widget :link, 'a'
end

Send double_click with no arguments to trigger an ondblclick event on #container.

widget(:container).double_click

This is the equivalent of doing the following using Capybara:

find('#container').double_click
# File lib/capybara/ui/widgets/widget.rb, line 291
def double_click(*args)
  if args.empty?
    root.double_click
  else
    widget(*args).double_click
  end
end
has_action?(name) click to toggle source

Determines if the widget underlying an action exists.

@param name the name of the action

@raise Missing if an action with name can't be found.

@return [Boolean] true if the action widget is found, false

otherwise.
# File lib/capybara/ui/widgets/widget.rb, line 334
def has_action?(name)
  raise Missing, "couldn't find `#{name}' action" unless respond_to?(name)

  visible?(:"#{name}_widget")
end
hover(*args) click to toggle source

Hovers over the current widget, or the child widget given by name.

Usage

Given the following widget definition:

class Container < Capybara::UI::Widget
  root '#container'

  widget :link, 'a'
end

Send hover with no arguments to trigger a hover event on #container.

widget(:container).hover

This is the equivalent of doing the following using Capybara:

find('#container').hover

Send +hover :link+ to trigger a hover event on a:

widget(:container).hover :link

This is the equivalent of doing the following using Capybara:

find('#container a').hover
# File lib/capybara/ui/widgets/widget.rb, line 264
def hover(*args)
  if args.empty?
    root.hover
  else
    widget(*args).hover
  end
end
html() click to toggle source
# File lib/capybara/ui/widgets/widget.rb, line 357
def html
  xml = Nokogiri::HTML(page.body).at(root.path).to_xml

  Nokogiri::XML(xml, &:noblanks).to_xhtml.gsub("\n", "")
end
id() click to toggle source
# File lib/capybara/ui/widgets/widget.rb, line 340
def id
  root['id']
end
right_click(*args) click to toggle source

Right clicks the current widget, or the child widget given by name.

Usage

Given the following widget definition:

class Container < Capybara::UI::Widget
  root '#container'

  widget :link, 'a'
end

Send right_click with no arguments to trigger an oncontextmenu event on #container.

widget(:container).right_click

This is the equivalent of doing the following using Capybara:

find('#container').right_click
# File lib/capybara/ui/widgets/widget.rb, line 318
def right_click(*args)
  if args.empty?
    root.right_click
  else
    widget(*args).right_click
  end
end
text() click to toggle source
# File lib/capybara/ui/widgets/widget.rb, line 363
def text
  StringValue.new(root.text.strip)
end
to_cell() click to toggle source

Converts this widget into a string representation suitable to be displayed in a Cucumber table cell. By default calls text.

This method will be called by methods that build tables or rows (usually to_table or to_row) so, in general, you won't call it directly, but feel free to override it when needed.

Returns a String.

# File lib/capybara/ui/widgets/widget.rb, line 375
def to_cell
  text
end
to_s() click to toggle source
# File lib/capybara/ui/widgets/widget.rb, line 379
def to_s
  text
end
value() click to toggle source
# File lib/capybara/ui/widgets/widget.rb, line 383
def value
  text
end

Private Instance Methods

page() click to toggle source
# File lib/capybara/ui/widgets/widget.rb, line 389
def page
  Capybara.current_session
end