Class: Dill::Widget
- Inherits:
-
Object
- Object
- Dill::Widget
- Extended by:
- Forwardable
- Includes:
- WidgetContainer
- Defined in:
- lib/dill/widget.rb
Direct Known Subclasses
AutoTable::Row, BaseTable, FieldGroup, FieldGroup::Field, List, ListItem
Defined Under Namespace
Classes: Removed
Instance Attribute Summary collapse
-
#root ⇒ Object
readonly
Returns the root node (a Capybara::Node::Element) of the current widget.
Widget macros collapse
-
.action(name, selector) ⇒ Object
Defines a new action.
-
.widget(name, *rest) { ... } ⇒ Object
Declares a new child widget.
-
.widget_delegator(name, widget_message, method_name = nil) ⇒ Object
Creates a delegator for one child widget message.
Class Method Summary collapse
-
.find_in(node) ⇒ Object
Finds a single instance of the current widget in
node. -
.present_in?(parent_node) ⇒ Boolean
Determines if an instance of this widget class exists in
parent_node. -
.root(*selector) ⇒ Object
Sets this widget’s default selector.
-
.selector ⇒ Object
Returns the selector specified with
root.
Instance Method Summary collapse
-
#!=(value) ⇒ Object
Compares the current widget with
value, waiting for the comparison to returnfalse. -
#!~(regexp) ⇒ Object
Calls !~ on this widget’s text content.
-
#<(value) ⇒ Object
Returns
trueif this widget’s representation is less thanvalue. -
#<=(value) ⇒ Object
Returns
trueif this widget’s representation is less than or equal tovalue. -
#==(value) ⇒ Object
Compares the current widget with
value, waiting for the comparison to returntrue. -
#=~(regexp) ⇒ Object
Calls =~ on this widget’s text content.
-
#>(value) ⇒ Object
Returns
trueif this widget’s representation is greater thanvalue. -
#>=(value) ⇒ Object
Returns
trueif this widget’s representation is greater than or equal tovalue. -
#click(name = nil) ⇒ Object
Clicks the current widget, or the child widget given by
name. -
#diff(diffable, wait_time = Capybara.default_wait_time) ⇒ Object
Compares this widget with the given
diffable, as long as it responds to the same protocol as Cucumber::Ast::Table#diff!. -
#has_action?(name) ⇒ Boolean
Determines if the widget underlying an action exists.
-
#initialize(root) ⇒ Widget
constructor
A new instance of Widget.
- #inspect ⇒ Object
-
#match(pattern, position = 0) {|the| ... } ⇒ MatchData
Calls
matchon this widget’s text content. -
#reload(wait_time = Capybara.default_wait_time) { ... } ⇒ Object
Reloads the widget, waiting for its contents to change (by default), or until
wait_timeexpires. - #text ⇒ Object (also: #to_s)
-
#to_cell ⇒ Object
Converts this widget into a string representation suitable to be displayed in a Cucumber table cell.
- #to_f ⇒ Object
- #to_i ⇒ Object
Methods included from WidgetContainer
Constructor Details
Instance Attribute Details
#root ⇒ Object
Returns the root node (a Capybara::Node::Element) of the current widget.
261 262 263 |
# File 'lib/dill/widget.rb', line 261 def root @root end |
Class Method Details
.action(name, selector) ⇒ 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.
44 45 46 47 48 49 50 51 52 53 54 |
# File 'lib/dill/widget.rb', line 44 def self.action(name, selector) wname = :"#{name}_widget" wname, selector define_method name do (wname).click self end end |
.find_in(node) ⇒ Object
Finds a single instance of the current widget in node.
184 185 186 |
# File 'lib/dill/widget.rb', line 184 def self.find_in(node) new(node.find(*selector)) end |
.present_in?(parent_node) ⇒ Boolean
Determines if an instance of this widget class exists in parent_node.
194 195 196 |
# File 'lib/dill/widget.rb', line 194 def self.present_in?(parent_node) parent_node.has_selector?(*selector) end |
.root(*selector) ⇒ 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 < Dill::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 < Dill::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 < Dill::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.(: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 < Dill::Widget
root :xpath, '//some/node'
end
251 252 253 |
# File 'lib/dill/widget.rb', line 251 def self.root(*selector) @selector = selector.flatten end |
.selector ⇒ Object
Returns the selector specified with root.
256 257 258 |
# File 'lib/dill/widget.rb', line 256 def self.selector @selector end |
.widget(name, selector, type = Widget) ⇒ Object .widget(name, type) ⇒ Object
Declares a new child widget.
Child widgets are accessible inside the container widget using the Dill::WidgetContainer#widget message, or by sending a message name. They are automatically scoped to the parent widget’s root node.
106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 |
# File 'lib/dill/widget.rb', line 106 def self.(name, *rest, &block) raise ArgumentError, "`#{name}' is a reserved name" \ if WidgetContainer.instance_methods.include?(name.to_sym) case rest.first when Class arg_count = rest.size + 1 raise ArgumentError, "wrong number of arguments (#{arg_count} for 2)" \ unless arg_count == 2 type = rest.first raise TypeError, "can't convert `#{type}' to Widget" \ unless type.methods.include?(:selector) raise ArgumentError, "missing root selector for `#{type}'" \ unless type.selector selector = type.selector when String, Array arg_count = rest.size + 1 case arg_count when 0, 1 raise ArgumentError, "wrong number of arguments (#{arg_count} for 2)" when 2 selector, type = [*rest, Widget] when 3 selector, type = rest raise TypeError, "can't convert `#{type}' to Widget" \ unless Class === type else raise ArgumentError, "wrong number of arguments (#{arg_count} for 3)" end else raise ArgumentError, "unknown method signature: #{rest.inspect}" end child = WidgetClass.new(selector, type, &block) const_set(Dill::WidgetName.new(name).to_sym, child) define_method name do (name) end end |
.widget_delegator(name, widget_message, method_name = nil) ⇒ Object
Creates a delegator for one child widget message.
Since widgets are accessed through Dill::WidgetContainer#widget, we can’t use Forwardable to delegate messages to widgets.
161 162 163 164 165 166 167 168 169 170 171 172 173 |
# File 'lib/dill/widget.rb', line 161 def self.(name, , method_name = nil) method_name = method_name || class_eval <<-RUBY def #{method_name}(*args) if args.size == 1 widget(:#{name}).#{} args.first else widget(:#{name}).#{} *args end end RUBY end |
Instance Method Details
#!=(value) ⇒ Object
Compares the current widget with value, waiting for the comparison to return false.
319 320 321 |
# File 'lib/dill/widget.rb', line 319 def !=(value) test { cast_to_type_of(value) != value } end |
#!~(regexp) ⇒ Object
Calls !~ on this widget’s text content.
313 314 315 |
# File 'lib/dill/widget.rb', line 313 def !~(regexp) test { to_s !~ regexp } end |
#<(value) ⇒ Object
Returns true if this widget’s representation is less than value.
Waits for the result to be true for the time defined in ‘Capybara.default_wait_time`.
271 272 273 |
# File 'lib/dill/widget.rb', line 271 def <(value) test { cast_to_type_of(value) < value } end |
#<=(value) ⇒ Object
Returns true if this widget’s representation is less than or equal to value.
Waits for the result to be true for the time defined in ‘Capybara.default_wait_time`.
280 281 282 |
# File 'lib/dill/widget.rb', line 280 def <=(value) test { cast_to_type_of(value) <= value } end |
#==(value) ⇒ Object
Compares the current widget with value, waiting for the comparison to return true.
303 304 305 |
# File 'lib/dill/widget.rb', line 303 def ==(value) test { cast_to_type_of(value) == value } end |
#=~(regexp) ⇒ Object
Calls =~ on this widget’s text content.
308 309 310 |
# File 'lib/dill/widget.rb', line 308 def =~(regexp) test { to_s =~ regexp } end |
#>(value) ⇒ Object
Returns true if this widget’s representation is greater than value.
Waits for the result to be true for the time defined in ‘Capybara.default_wait_time`.
288 289 290 |
# File 'lib/dill/widget.rb', line 288 def >(value) test { cast_to_type_of(value) > value } end |
#>=(value) ⇒ Object
Returns true if this widget’s representation is greater than or equal to value.
Waits for the result to be true for the time defined in ‘Capybara.default_wait_time`.
297 298 299 |
# File 'lib/dill/widget.rb', line 297 def >=(value) test { cast_to_type_of(value) >= value } end |
#click(name = nil) ⇒ Object
Clicks the current widget, or the child widget given by name.
Usage
Given the following widget definition:
class Container < Dill::Widget
root '#container'
:link, 'a'
end
Send click with no arguments to trigger a click event on #container.
(: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:
(:container).click :link
This is the equivalent of doing the following using Capybara:
find('#container a').click
350 351 352 353 354 355 356 |
# File 'lib/dill/widget.rb', line 350 def click(name = nil) if name (name).click else root.click end end |
#diff(diffable, wait_time = Capybara.default_wait_time) ⇒ Object
Compares this widget with the given diffable, as long as it responds to the same protocol as Cucumber::Ast::Table#diff!.
Waits wait_time seconds for the comparison to be successful, otherwise raises Cucumber::Ast::Table::Different on failure.
This is especially useful when you’re not sure if the widget is in the proper state to be compared with the table.
Example
Then(/^some step that takes in a cucumber table$/) do |table|
(:my_widget).diff table
end
372 373 374 375 376 377 378 379 380 381 |
# File 'lib/dill/widget.rb', line 372 def diff(diffable, wait_time = Capybara.default_wait_time) # #diff! raises an exception if the comparison fails, or returns nil if it # doesn't. We don't need to worry about failure, because that will be # propagated, but we need to return +true+ when it succeeds, to end the # comparison. # # We use WidgetCheckpoint instead of #test because we want the # succeed-or-raise behavior. WidgetCheckpoint.wait_for(wait_time) { diffable.diff!(to_table) || true } end |
#has_action?(name) ⇒ Boolean
Determines if the widget underlying an action exists.
391 392 393 394 395 |
# File 'lib/dill/widget.rb', line 391 def has_action?(name) raise Missing, "couldn't find `#{name}' action" unless respond_to?(name) (:"#{name}_widget") end |
#inspect ⇒ Object
397 398 399 400 401 402 403 404 405 406 407 408 |
# File 'lib/dill/widget.rb', line 397 def inspect inspection = "<!-- #{self.class.name}: -->\n" root = self.root begin xml = Nokogiri::HTML(page.body).at(root.path).to_xml inspection << Nokogiri::XML(xml, &:noblanks).to_xhtml rescue Capybara::NotSupportedByDriverError inspection << "<#{root.tag_name}>\n#{to_s}" end end |
#match(pattern, position = 0) {|the| ... } ⇒ MatchData
Calls match on this widget’s text content.
If a block is given, passes the resulting match data to the block.
459 460 461 |
# File 'lib/dill/widget.rb', line 459 def match(pattern, position = 0, &block) test { to_s.match(pattern, position, &block) } end |
#reload(wait_time = Capybara.default_wait_time) { ... } ⇒ Object
Reloads the widget, waiting for its contents to change (by default), or until wait_time expires.
Call this method to make sure a widget has enough time to update itself.
You can pass a block to this method to control what it means for the widget to be reloaded.
*Note: does not account for multiple changes to the widget yet.*
432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 |
# File 'lib/dill/widget.rb', line 432 def reload(wait_time = Capybara.default_wait_time, &condition) unless test old_root = root test = ->{ old_root != root } end test wait_time, &condition begin root.inspect rescue raise Removed, "widget was removed" end self end |
#text ⇒ Object Also known as: to_s
463 464 465 |
# File 'lib/dill/widget.rb', line 463 def text NodeText.new(root) end |
#to_cell ⇒ Object
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.
475 476 477 |
# File 'lib/dill/widget.rb', line 475 def to_cell to_s end |
#to_f ⇒ Object
483 484 485 |
# File 'lib/dill/widget.rb', line 483 def to_f to_s.to_f end |
#to_i ⇒ Object
479 480 481 |
# File 'lib/dill/widget.rb', line 479 def to_i to_s.to_i end |