Class: Dill::Widget
- Inherits:
-
Object
- Object
- Dill::Widget
- Extended by:
- Forwardable
- Defined in:
- lib/dill/widgets/widget.rb
Direct Known Subclasses
AutoTable::Row, BaseTable, Field, FieldGroup, List, ListItem
Defined Under Namespace
Classes: MissingSelector, Removed
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(parent, *args) ⇒ Object
Finds a single instance of the current widget in
node. -
.present_in?(parent) ⇒ Boolean
Determines if an instance of this widget class exists in
parent_node. -
.root(*selector, &block) ⇒ Object
Sets this widget’s default selector.
-
.selector(*args) ⇒ Object
Returns the selector specified with
root.
Instance Method Summary collapse
-
#absent? ⇒ Boolean
Alias for #gone?.
-
#click(*args) ⇒ Object
Clicks the current widget, or the child widget given by
name. -
#diff(table, wait_time = Capybara.default_wait_time) ⇒ Object
Compares this widget with the given Cucumber
table. -
#gone? ⇒ Boolean
Returns
trueif the widget is not visible, or has been removed from the DOM. -
#has_action?(name) ⇒ Boolean
Determines if the widget underlying an action exists.
-
#initialize(node = nil, &query) ⇒ Widget
constructor
A new instance of Widget.
- #inspect ⇒ Object
-
#present? ⇒ Boolean
Returns
trueif widget is visible. - #root ⇒ Object
- #text ⇒ Object
-
#to_cell ⇒ Object
Converts this widget into a string representation suitable to be displayed in a Cucumber table cell.
- #to_s ⇒ Object
- #value ⇒ Object
Methods included from Dill::WidgetParts::Container
#has_no_widget?, #has_widget?, #widget
Methods included from Dill::WidgetParts::Struct
Constructor Details
#initialize(node = nil, &query) ⇒ Widget
Returns a new instance of Widget.
272 273 274 275 |
# File 'lib/dill/widgets/widget.rb', line 272 def initialize(node = nil, &query) self.node = node self.query = query 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.
45 46 47 48 49 50 51 52 53 54 55 |
# File 'lib/dill/widgets/widget.rb', line 45 def self.action(name, selector) wname = :"#{name}_widget" wname, selector define_method name do (wname).click self end end |
.find_in(parent, *args) ⇒ Object
Finds a single instance of the current widget in node.
183 184 185 |
# File 'lib/dill/widgets/widget.rb', line 183 def self.find_in(parent, *args) new { parent.root.find(*selector(*args)) } end |
.present_in?(parent) ⇒ Boolean
Determines if an instance of this widget class exists in parent_node.
193 194 195 |
# File 'lib/dill/widgets/widget.rb', line 193 def self.present_in?(parent) find_in(parent).present? 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 < Dill::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 < 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
250 251 252 |
# File 'lib/dill/widgets/widget.rb', line 250 def self.root(*selector, &block) @selector = block ? [block] : selector.flatten end |
.selector(*args) ⇒ Object
Returns the selector specified with root.
258 259 260 261 262 263 264 265 266 267 268 269 270 |
# File 'lib/dill/widgets/widget.rb', line 258 def self.selector(*args) if @selector fst = @selector.first fst.respond_to?(:call) ? fst.call(*args) : @selector else if superclass.respond_to?(:selector) superclass.selector else raise MissingSelector, 'no selector defined' end end 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::WidgetParts::Container#widget message, or by sending a message name. They are automatically scoped to the parent widget’s root node.
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 |
# File 'lib/dill/widgets/widget.rb', line 107 def self.(name, *rest, &block) raise ArgumentError, "`#{name}' is a reserved name" \ if WidgetParts::Container.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, Proc 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) child end |
.widget_delegator(name, widget_message, method_name = nil) ⇒ Object
Creates a delegator for one child widget message.
Since widgets are accessed through Dill::WidgetParts::Container#widget, we can’t use Forwardable to delegate messages to widgets.
160 161 162 163 164 165 166 167 168 169 170 171 172 |
# File 'lib/dill/widgets/widget.rb', line 160 def self.(name, , method_name = nil) method_name = method_name || class_eval " def \#{method_name}(*args)\n if args.size == 1\n widget(:\#{name}).\#{widget_message} args.first\n else\n widget(:\#{name}).\#{widget_message} *args\n end\n end\n RUBY\nend\n" |
Instance Method Details
#absent? ⇒ Boolean
Alias for #gone?
278 279 280 |
# File 'lib/dill/widgets/widget.rb', line 278 def absent? gone? end |
#click(*args) ⇒ 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
309 310 311 312 313 314 315 |
# File 'lib/dill/widgets/widget.rb', line 309 def click(*args) if args.empty? root.click else (*args).click end end |
#diff(table, wait_time = Capybara.default_wait_time) ⇒ Object
Compares this widget with the given Cucumber table.
Example
Then(/^some step that takes in a cucumber table$/) do |table|
(:my_widget).diff table
end
324 325 326 |
# File 'lib/dill/widgets/widget.rb', line 324 def diff(table, wait_time = .default_wait_time) table.diff!(to_table) || true end |
#gone? ⇒ Boolean
Returns true if the widget is not visible, or has been removed from the DOM.
330 331 332 |
# File 'lib/dill/widgets/widget.rb', line 330 def gone? ! root rescue true end |
#has_action?(name) ⇒ Boolean
Determines if the widget underlying an action exists.
342 343 344 345 346 |
# File 'lib/dill/widgets/widget.rb', line 342 def has_action?(name) raise Missing, "couldn't find `#{name}' action" unless respond_to?(name) (:"#{name}_widget") end |
#inspect ⇒ Object
348 349 350 351 352 353 354 355 356 357 358 359 360 361 |
# File 'lib/dill/widgets/widget.rb', line 348 def inspect inspection = "<!-- #{self.class.name}: -->\n" begin root = self.root xml = Nokogiri::HTML(page.body).at(root.path).to_xml inspection << Nokogiri::XML(xml, &:noblanks).to_xhtml rescue ::NotSupportedByDriverError inspection << "<#{root.tag_name}>\n#{to_s}" rescue ::ElementNotFound, *page.driver.invalid_element_errors "#<DETACHED>" end end |
#present? ⇒ Boolean
Returns true if widget is visible.
364 365 366 |
# File 'lib/dill/widgets/widget.rb', line 364 def present? !! root rescue false end |
#root ⇒ Object
368 369 370 |
# File 'lib/dill/widgets/widget.rb', line 368 def root node || query.() end |
#text ⇒ Object
372 373 374 |
# File 'lib/dill/widgets/widget.rb', line 372 def text root.text.strip 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.
384 385 386 |
# File 'lib/dill/widgets/widget.rb', line 384 def to_cell text end |
#to_s ⇒ Object
388 389 390 |
# File 'lib/dill/widgets/widget.rb', line 388 def to_s text end |
#value ⇒ Object
392 393 394 |
# File 'lib/dill/widgets/widget.rb', line 392 def value text end |