Class: Capybara::UI::Widget

Inherits:
Object
  • Object
show all
Extended by:
Capybara::UI::Widgets::DSL, Forwardable
Includes:
CucumberMethods, Capybara::UI::WidgetParts::Container, Capybara::UI::WidgetParts::Struct
Defined in:
lib/capybara/ui/widgets/widget.rb,
lib/capybara/ui/widgets/widget/node_filter.rb

Direct Known Subclasses

Field, FieldGroup, List, ListItem, Table

Defined Under Namespace

Classes: MissingSelector, NodeFilter, Removed

Constant Summary

Constants included from Capybara::UI

VERSION

Instance Attribute Summary collapse

Widget macros collapse

Class Method Summary collapse

Instance Method Summary collapse

Methods included from Capybara::UI::Widgets::DSL

form, list, widget

Methods included from Capybara::UI::WidgetParts::Container

#has_widget?, #not_visible?, #visible?, #widget, #widgets

Methods included from Capybara::UI

#deprecate

Methods included from Constructors

#Decimal, #Integer, #Widget

Methods included from Capybara::UI::WidgetParts::Struct

included

Constructor Details

#initialize(root) ⇒ Widget

Returns a new instance of Widget.


198
199
200
# File 'lib/capybara/ui/widgets/widget.rb', line 198

def initialize(root)
  @root = root
end

Instance Attribute Details

#rootObject (readonly)

Returns the value of attribute root


13
14
15
# File 'lib/capybara/ui/widgets/widget.rb', line 13

def root
  @root
end

Class Method Details

.action(name, selector = nil) ⇒ Object

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.

Examples:

# 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

Parameters:

  • name

    the name of the action

  • selector (defaults to: nil)

    the selector for the widget that will be clicked


50
51
52
53
54
55
56
57
58
59
60
61
62
# 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

.filterObject


184
185
186
187
188
# File 'lib/capybara/ui/widgets/widget.rb', line 184

def self.filter
  @filter || superclass.filter
rescue NoMethodError
  raise MissingSelector, 'no selector defined'
end

.filter?Boolean

Returns:

  • (Boolean)

190
191
192
# File 'lib/capybara/ui/widgets/widget.rb', line 190

def self.filter?
  filter rescue false
end

.find_all_in(parent, *args) ⇒ Object


106
107
108
# 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) ⇒ Object

Finds a single instance of the current widget in node.

Parameters:

  • node

    the node we want to search in

Returns:

  • a new instance of the current widget class.

Raises:

  • (Capybara::ElementNotFoundError)

    if the widget can't be found


96
97
98
99
100
101
102
103
104
# 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

.not_present_in?(parent, *args) ⇒ Boolean

Returns:

  • (Boolean)

120
121
122
# 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) ⇒ Boolean

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

Parameters:

  • parent_node (Capybara::Node)

    the node we want to search in

Returns:

  • (Boolean)

    true if a widget instance is found, false otherwise.


116
117
118
# File 'lib/capybara/ui/widgets/widget.rb', line 116

def self.present_in?(parent, *args)
  filter.node?(parent, *args)
end

.root(*selector, &block) ⇒ Object

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:

Pick me!

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

177
178
179
# File 'lib/capybara/ui/widgets/widget.rb', line 177

def self.root(*selector, &block)
  @filter = NodeFilter.new(block || selector)
end

.selectorObject


194
195
196
# File 'lib/capybara/ui/widgets/widget.rb', line 194

def self.selector
  filter.selector
end

.widget_delegator(name, widget_message, method_name = nil) ⇒ Object

Creates a delegator for one child widget message.

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

Parameters:

  • name

    the name of the receiver child widget

  • widget_message

    the name of the message to be sent to the child widget

  • method_name (defaults to: nil)

    the name of the delegator. If nil the method will have the same name as the message it will send.


73
74
75
76
77
78
79
80
81
82
83
84
85
# 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

Instance Method Details

#class?(name) ⇒ Boolean

Determines if the widget has a specific class

Parameters:

  • name

    the name of the class

Returns:

  • (Boolean)

    true if the class is found, false otherwise


353
354
355
# File 'lib/capybara/ui/widgets/widget.rb', line 353

def class?(name)
  classes.include?(name)
end

#classesObject


344
345
346
# File 'lib/capybara/ui/widgets/widget.rb', line 344

def classes
  root['class'].split
end

#click(*args) ⇒ Object

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

229
230
231
232
233
234
235
# 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) ⇒ Object

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

291
292
293
294
295
296
297
# 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) ⇒ Boolean

Determines if the widget underlying an action exists.

Parameters:

  • name

    the name of the action

Returns:

  • (Boolean)

    true if the action widget is found, false otherwise.

Raises:

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


334
335
336
337
338
# 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) ⇒ Object

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

264
265
266
267
268
269
270
# File 'lib/capybara/ui/widgets/widget.rb', line 264

def hover(*args)
  if args.empty?
    root.hover
  else
    widget(*args).hover
  end
end

#htmlObject


357
358
359
360
361
# 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

#idObject


340
341
342
# File 'lib/capybara/ui/widgets/widget.rb', line 340

def id
  root['id']
end

#right_click(*args) ⇒ Object

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

318
319
320
321
322
323
324
# 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

#textObject


363
364
365
# File 'lib/capybara/ui/widgets/widget.rb', line 363

def text
  StringValue.new(root.text.strip)
end

#to_cellObject

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.


375
376
377
# File 'lib/capybara/ui/widgets/widget.rb', line 375

def to_cell
  text
end

#to_sObject


379
380
381
# File 'lib/capybara/ui/widgets/widget.rb', line 379

def to_s
  text
end

#valueObject


383
384
385
# File 'lib/capybara/ui/widgets/widget.rb', line 383

def value
  text
end