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


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

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


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.

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


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.


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.


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


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.

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