capybara-ui 0.10.0 → 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/capybara/ui.rb +33 -0
- data/lib/capybara/ui/assertions.rb +21 -0
- data/lib/{capybara-ui → capybara/ui}/capybara.rb +0 -0
- data/lib/capybara/ui/checkpoint.rb +113 -0
- data/lib/capybara/ui/conversions.rb +33 -0
- data/lib/capybara/ui/cucumber.rb +5 -0
- data/lib/capybara/ui/dsl.rb +109 -0
- data/lib/capybara/ui/instance_conversions.rb +21 -0
- data/lib/{capybara-ui → capybara/ui}/matchers.rb +4 -4
- data/lib/capybara/ui/optional_dependencies.rb +7 -0
- data/lib/capybara/ui/rails.rb +5 -0
- data/lib/capybara/ui/rails/role.rb +11 -0
- data/lib/capybara/ui/role.rb +21 -0
- data/lib/capybara/ui/text_table.rb +109 -0
- data/lib/capybara/ui/text_table/cell_text.rb +9 -0
- data/lib/capybara/ui/text_table/mapping.rb +42 -0
- data/lib/capybara/ui/text_table/transformations.rb +15 -0
- data/lib/capybara/ui/text_table/void_mapping.rb +10 -0
- data/lib/capybara/ui/version.rb +5 -0
- data/lib/capybara/ui/widgets.rb +63 -0
- data/lib/capybara/ui/widgets/check_box.rb +28 -0
- data/lib/capybara/ui/widgets/cucumber_methods.rb +75 -0
- data/lib/capybara/ui/widgets/document.rb +21 -0
- data/lib/capybara/ui/widgets/dsl.rb +49 -0
- data/lib/capybara/ui/widgets/field.rb +24 -0
- data/lib/capybara/ui/widgets/field_group.rb +331 -0
- data/lib/capybara/ui/widgets/form.rb +28 -0
- data/lib/capybara/ui/widgets/list.rb +202 -0
- data/lib/capybara/ui/widgets/list_item.rb +24 -0
- data/lib/capybara/ui/widgets/parts/container.rb +48 -0
- data/lib/capybara/ui/widgets/parts/struct.rb +119 -0
- data/lib/capybara/ui/widgets/radio_button.rb +64 -0
- data/lib/capybara/ui/widgets/select.rb +59 -0
- data/lib/capybara/ui/widgets/string_value.rb +45 -0
- data/lib/capybara/ui/widgets/table.rb +78 -0
- data/lib/capybara/ui/widgets/text_field.rb +29 -0
- data/lib/capybara/ui/widgets/widget.rb +394 -0
- data/lib/capybara/ui/widgets/widget/node_filter.rb +50 -0
- data/lib/capybara/ui/widgets/widget_class.rb +13 -0
- data/lib/capybara/ui/widgets/widget_name.rb +58 -0
- metadata +47 -43
- data/lib/capybara-ui.rb +0 -31
- data/lib/capybara-ui/assertions.rb +0 -19
- data/lib/capybara-ui/checkpoint.rb +0 -111
- data/lib/capybara-ui/conversions.rb +0 -31
- data/lib/capybara-ui/cucumber.rb +0 -5
- data/lib/capybara-ui/dsl.rb +0 -107
- data/lib/capybara-ui/instance_conversions.rb +0 -19
- data/lib/capybara-ui/optional_dependencies.rb +0 -5
- data/lib/capybara-ui/rails.rb +0 -5
- data/lib/capybara-ui/rails/role.rb +0 -9
- data/lib/capybara-ui/role.rb +0 -19
- data/lib/capybara-ui/text_table.rb +0 -107
- data/lib/capybara-ui/text_table/cell_text.rb +0 -7
- data/lib/capybara-ui/text_table/mapping.rb +0 -40
- data/lib/capybara-ui/text_table/transformations.rb +0 -13
- data/lib/capybara-ui/text_table/void_mapping.rb +0 -8
- data/lib/capybara-ui/version.rb +0 -3
- data/lib/capybara-ui/widgets.rb +0 -61
- data/lib/capybara-ui/widgets/check_box.rb +0 -26
- data/lib/capybara-ui/widgets/cucumber_methods.rb +0 -73
- data/lib/capybara-ui/widgets/document.rb +0 -19
- data/lib/capybara-ui/widgets/dsl.rb +0 -47
- data/lib/capybara-ui/widgets/field.rb +0 -22
- data/lib/capybara-ui/widgets/field_group.rb +0 -329
- data/lib/capybara-ui/widgets/form.rb +0 -26
- data/lib/capybara-ui/widgets/list.rb +0 -200
- data/lib/capybara-ui/widgets/list_item.rb +0 -22
- data/lib/capybara-ui/widgets/parts/container.rb +0 -46
- data/lib/capybara-ui/widgets/parts/struct.rb +0 -117
- data/lib/capybara-ui/widgets/radio_button.rb +0 -62
- data/lib/capybara-ui/widgets/select.rb +0 -57
- data/lib/capybara-ui/widgets/string_value.rb +0 -43
- data/lib/capybara-ui/widgets/table.rb +0 -76
- data/lib/capybara-ui/widgets/text_field.rb +0 -27
- data/lib/capybara-ui/widgets/widget.rb +0 -392
- data/lib/capybara-ui/widgets/widget/node_filter.rb +0 -48
- data/lib/capybara-ui/widgets/widget_class.rb +0 -11
- data/lib/capybara-ui/widgets/widget_name.rb +0 -56
@@ -0,0 +1,59 @@
|
|
1
|
+
module Capybara
|
2
|
+
module UI
|
3
|
+
# A select.
|
4
|
+
class Select < Field
|
5
|
+
def selected
|
6
|
+
root.all(:xpath, ".//option", visible: true).select(&:selected?).first
|
7
|
+
end
|
8
|
+
|
9
|
+
module Selectable
|
10
|
+
def select
|
11
|
+
root.select_option
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
widget :option, -> (opt) {
|
16
|
+
opt.is_a?(Regexp) ? ["option", text: opt] : [:option, opt]
|
17
|
+
} do
|
18
|
+
include Selectable
|
19
|
+
end
|
20
|
+
|
21
|
+
widget :option_by_value, -> (opt) { "option[value = #{opt.inspect}]" } do
|
22
|
+
include Selectable
|
23
|
+
end
|
24
|
+
|
25
|
+
# @return [String] The text of the selected option.
|
26
|
+
def get
|
27
|
+
selected.text unless selected.nil?
|
28
|
+
end
|
29
|
+
|
30
|
+
# @return [String] The value of the selected option.
|
31
|
+
def value
|
32
|
+
selected.value unless selected.nil?
|
33
|
+
end
|
34
|
+
|
35
|
+
# Selects the given +option+.
|
36
|
+
#
|
37
|
+
# You may pass in the option text or value.
|
38
|
+
def set(option)
|
39
|
+
widget(:option, option).select
|
40
|
+
rescue
|
41
|
+
begin
|
42
|
+
widget(:option_by_value, option).select
|
43
|
+
rescue Capybara::UI::MissingWidget => e
|
44
|
+
raise InvalidOption.new(e.message).
|
45
|
+
tap { |x| x.set_backtrace e.backtrace }
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
# @!method to_s
|
50
|
+
# @return the text of the selected option, or the empty string if
|
51
|
+
# no option is selected.
|
52
|
+
def_delegator :get, :to_s
|
53
|
+
|
54
|
+
def to_cell
|
55
|
+
get
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
module Capybara
|
2
|
+
module UI
|
3
|
+
class StringValue < String
|
4
|
+
def to_date(format = nil)
|
5
|
+
format ? Date.strptime(self, format) : super()
|
6
|
+
end
|
7
|
+
|
8
|
+
def to_key
|
9
|
+
fst, rest = first, self[1..-1]
|
10
|
+
decamelized = fst + rest.gsub(/([A-Z])/, '_\1')
|
11
|
+
underscored = decamelized.gsub(/[\W_]+/, '_')
|
12
|
+
stripped = underscored.gsub(/^_|_$/, '')
|
13
|
+
downcased = stripped.downcase
|
14
|
+
key = downcased.to_sym
|
15
|
+
|
16
|
+
key
|
17
|
+
end
|
18
|
+
|
19
|
+
class Money
|
20
|
+
extend Forwardable
|
21
|
+
|
22
|
+
delegate %w(to_i to_f) => :str
|
23
|
+
|
24
|
+
def initialize(str)
|
25
|
+
fail ArgumentError, "can't convert `#{str}` to money" \
|
26
|
+
unless str =~ /^-?\$\d+(?:,\d{3})*(?:\.\d+)?/
|
27
|
+
|
28
|
+
@str = (str =~ /^-/ ? '-' : '') + str.gsub(/^-?\$|,/, '')
|
29
|
+
end
|
30
|
+
|
31
|
+
private
|
32
|
+
|
33
|
+
attr_reader :str
|
34
|
+
end
|
35
|
+
|
36
|
+
def to_usd
|
37
|
+
Money.new(self)
|
38
|
+
end
|
39
|
+
|
40
|
+
def to_split
|
41
|
+
split(',').map(&:strip).map { |e| self.class.new(e) }
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,78 @@
|
|
1
|
+
module Capybara
|
2
|
+
module UI
|
3
|
+
class Table < Capybara::UI::Widget
|
4
|
+
root 'table'
|
5
|
+
|
6
|
+
class Row < Capybara::UI::List
|
7
|
+
def self.column(*args, &block)
|
8
|
+
item(*args, &block)
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
def self.header_row(selector, &block)
|
13
|
+
widget :header_row, selector, Row, &block
|
14
|
+
end
|
15
|
+
|
16
|
+
header_row 'thead tr' do
|
17
|
+
column 'th'
|
18
|
+
end
|
19
|
+
|
20
|
+
def self.data_row(selector, &block)
|
21
|
+
widget :data_row, selector, Row, &block
|
22
|
+
end
|
23
|
+
|
24
|
+
data_row 'tbody tr' do
|
25
|
+
column 'td'
|
26
|
+
end
|
27
|
+
|
28
|
+
class Columns
|
29
|
+
include Enumerable
|
30
|
+
|
31
|
+
def initialize(parent)
|
32
|
+
@parent = parent
|
33
|
+
end
|
34
|
+
|
35
|
+
def [](header_or_index)
|
36
|
+
case header_or_index
|
37
|
+
when Integer
|
38
|
+
values_by_index(header_or_index)
|
39
|
+
when String
|
40
|
+
values_by_header(header_or_index)
|
41
|
+
else
|
42
|
+
raise TypeError,
|
43
|
+
"can't convert #{header_or_index.inspect} to Integer or String"
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def each(&block)
|
48
|
+
parent.each(&block)
|
49
|
+
end
|
50
|
+
|
51
|
+
private
|
52
|
+
|
53
|
+
attr_reader :parent
|
54
|
+
|
55
|
+
def values_by_index(index)
|
56
|
+
parent.rows.transpose[index]
|
57
|
+
end
|
58
|
+
|
59
|
+
def values_by_header(header)
|
60
|
+
values_by_index(find_header_index(header))
|
61
|
+
end
|
62
|
+
|
63
|
+
def find_header_index(header)
|
64
|
+
parent.widget(:header_row).value.find_index(header) or
|
65
|
+
raise ArgumentError, "header not found: #{header.inspect}"
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
def columns
|
70
|
+
Columns.new(self)
|
71
|
+
end
|
72
|
+
|
73
|
+
def rows
|
74
|
+
widgets(:data_row).map(&:value)
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
module Capybara
|
2
|
+
module UI
|
3
|
+
# A text field.
|
4
|
+
class TextField < Field
|
5
|
+
# @!method get
|
6
|
+
# @return The text field value.
|
7
|
+
def_delegator :root, :value, :get
|
8
|
+
|
9
|
+
# @!method set(value)
|
10
|
+
# Sets the text field value.
|
11
|
+
#
|
12
|
+
# @param value [String] the value to set.
|
13
|
+
def_delegator :root, :set
|
14
|
+
|
15
|
+
# @!method to_s
|
16
|
+
# @return the text field value, or the empty string if the field is
|
17
|
+
# empty.
|
18
|
+
def_delegator :get, :to_s
|
19
|
+
|
20
|
+
def to_cell
|
21
|
+
get
|
22
|
+
end
|
23
|
+
|
24
|
+
def content?
|
25
|
+
get.respond_to?(:empty?) ? ! get.empty? : !! get
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,394 @@
|
|
1
|
+
module Capybara
|
2
|
+
module UI
|
3
|
+
class Widget
|
4
|
+
extend Forwardable
|
5
|
+
extend Widgets::DSL
|
6
|
+
|
7
|
+
include WidgetParts::Struct
|
8
|
+
include WidgetParts::Container
|
9
|
+
include CucumberMethods
|
10
|
+
|
11
|
+
class Removed < StandardError; end
|
12
|
+
|
13
|
+
attr_reader :root
|
14
|
+
|
15
|
+
# @!group Widget macros
|
16
|
+
|
17
|
+
# Defines a new action.
|
18
|
+
#
|
19
|
+
# This is a shortcut to help defining a widget and a method that clicks
|
20
|
+
# on that widget. You can then send a widget instance the message given
|
21
|
+
# by +name+.
|
22
|
+
#
|
23
|
+
# You can access the underlying widget by appending "_widget" to the
|
24
|
+
# action name.
|
25
|
+
#
|
26
|
+
# @example
|
27
|
+
# # Consider the widget will encapsulate the following HTML
|
28
|
+
# #
|
29
|
+
# # <div id="profile">
|
30
|
+
# # <a href="/profiles/1/edit" rel="edit">Edit</a>
|
31
|
+
# # </div>
|
32
|
+
# class PirateProfile < Capybara::UI::Widget
|
33
|
+
# root "#profile"
|
34
|
+
#
|
35
|
+
# # Declare the action
|
36
|
+
# action :edit, '[rel = edit]'
|
37
|
+
# end
|
38
|
+
#
|
39
|
+
# pirate_profile = widget(:pirate_profile)
|
40
|
+
#
|
41
|
+
# # Access the action widget
|
42
|
+
# action_widget = pirate_profile.widget(:edit_widget)
|
43
|
+
# action_widget = pirate_profile.edit_widget
|
44
|
+
#
|
45
|
+
# # Click the link
|
46
|
+
# pirate_profile.edit
|
47
|
+
#
|
48
|
+
# @param name the name of the action
|
49
|
+
# @param selector the selector for the widget that will be clicked
|
50
|
+
def self.action(name, selector = nil)
|
51
|
+
block = if selector
|
52
|
+
wname = :"#{name}_widget"
|
53
|
+
|
54
|
+
widget wname, selector
|
55
|
+
|
56
|
+
-> { widget(wname).click; self }
|
57
|
+
else
|
58
|
+
-> { click; self }
|
59
|
+
end
|
60
|
+
|
61
|
+
define_method name, &block
|
62
|
+
end
|
63
|
+
|
64
|
+
# Creates a delegator for one child widget message.
|
65
|
+
#
|
66
|
+
# Since widgets are accessed through {WidgetParts::Container#widget}, we
|
67
|
+
# can't use {Forwardable} to delegate messages to widgets.
|
68
|
+
#
|
69
|
+
# @param name the name of the receiver child widget
|
70
|
+
# @param widget_message the name of the message to be sent to the child widget
|
71
|
+
# @param method_name the name of the delegator. If +nil+ the method will
|
72
|
+
# have the same name as the message it will send.
|
73
|
+
def self.widget_delegator(name, widget_message, method_name = nil)
|
74
|
+
method_name = method_name || widget_message
|
75
|
+
|
76
|
+
class_eval <<-RUBY
|
77
|
+
def #{method_name}(*args)
|
78
|
+
if args.size == 1
|
79
|
+
widget(:#{name}).#{widget_message} args.first
|
80
|
+
else
|
81
|
+
widget(:#{name}).#{widget_message} *args
|
82
|
+
end
|
83
|
+
end
|
84
|
+
RUBY
|
85
|
+
end
|
86
|
+
|
87
|
+
# @!endgroup
|
88
|
+
|
89
|
+
# Finds a single instance of the current widget in +node+.
|
90
|
+
#
|
91
|
+
# @param node the node we want to search in
|
92
|
+
#
|
93
|
+
# @return a new instance of the current widget class.
|
94
|
+
#
|
95
|
+
# @raise [Capybara::ElementNotFoundError] if the widget can't be found
|
96
|
+
def self.find_in(parent, *args)
|
97
|
+
new(filter.node(parent, *args))
|
98
|
+
rescue Capybara::Ambiguous => e
|
99
|
+
raise AmbiguousWidget.new(e.message).
|
100
|
+
tap { |x| x.set_backtrace e.backtrace }
|
101
|
+
rescue Capybara::ElementNotFound => e
|
102
|
+
raise MissingWidget.new(e.message).
|
103
|
+
tap { |x| x.set_backtrace e.backtrace }
|
104
|
+
end
|
105
|
+
|
106
|
+
def self.find_all_in(parent, *args)
|
107
|
+
filter.nodes(parent, *args).map { |e| new(e) }
|
108
|
+
end
|
109
|
+
|
110
|
+
# Determines if an instance of this widget class exists in
|
111
|
+
# +parent_node+.
|
112
|
+
#
|
113
|
+
# @param parent_node [Capybara::Node] the node we want to search in
|
114
|
+
#
|
115
|
+
# @return +true+ if a widget instance is found, +false+ otherwise.
|
116
|
+
def self.present_in?(parent, *args)
|
117
|
+
filter.node?(parent, *args)
|
118
|
+
end
|
119
|
+
|
120
|
+
def self.not_present_in?(parent, *args)
|
121
|
+
filter.nodeless?(parent, *args)
|
122
|
+
end
|
123
|
+
|
124
|
+
# Sets this widget's default selector.
|
125
|
+
#
|
126
|
+
# You can pass more than one argument to it, or a single Array. Any valid
|
127
|
+
# Capybara selector accepted by Capybara::Node::Finders#find will work.
|
128
|
+
#
|
129
|
+
# === Examples
|
130
|
+
#
|
131
|
+
# Most of the time, your selectors will be Strings:
|
132
|
+
#
|
133
|
+
# class MyWidget < Capybara::UI::Widget
|
134
|
+
# root '.selector'
|
135
|
+
# end
|
136
|
+
#
|
137
|
+
# This will match any element with a class of "selector". For example:
|
138
|
+
#
|
139
|
+
# <span class="selector">Pick me!</span>
|
140
|
+
#
|
141
|
+
# ==== Composite selectors
|
142
|
+
#
|
143
|
+
# If you're using CSS as the query language, it's useful to be able to use
|
144
|
+
# +text: 'Some text'+ to zero in on a specific node:
|
145
|
+
#
|
146
|
+
# class MySpecificWidget < Capybara::UI::Widget
|
147
|
+
# root '.selector', text: 'Pick me!'
|
148
|
+
# end
|
149
|
+
#
|
150
|
+
# This is especially useful, e.g., when you want to create a widget
|
151
|
+
# to match a specific error or notification:
|
152
|
+
#
|
153
|
+
# class NoFreeSpace < Capybara::UI::Widget
|
154
|
+
# root '.error', text: 'No free space left!'
|
155
|
+
# end
|
156
|
+
#
|
157
|
+
# So, given the following HTML:
|
158
|
+
#
|
159
|
+
# <body>
|
160
|
+
# <div class="error">No free space left!</div>
|
161
|
+
#
|
162
|
+
# <!-- ... -->
|
163
|
+
# </body>
|
164
|
+
#
|
165
|
+
# You can test for the error's present using the following code:
|
166
|
+
#
|
167
|
+
# document.visible?(:no_free_space) #=> true
|
168
|
+
#
|
169
|
+
# Note: When you want to match text, consider using +I18n.t+ instead of
|
170
|
+
# hard-coding the text, so that your tests don't break when the text changes.
|
171
|
+
#
|
172
|
+
# Finally, you may want to override the query language:
|
173
|
+
#
|
174
|
+
# class MyWidgetUsesXPath < Capybara::UI::Widget
|
175
|
+
# root :xpath, '//some/node'
|
176
|
+
# end
|
177
|
+
def self.root(*selector, &block)
|
178
|
+
@filter = NodeFilter.new(block || selector)
|
179
|
+
end
|
180
|
+
|
181
|
+
class MissingSelector < StandardError
|
182
|
+
end
|
183
|
+
|
184
|
+
def self.filter
|
185
|
+
@filter || superclass.filter
|
186
|
+
rescue NoMethodError
|
187
|
+
raise MissingSelector, 'no selector defined'
|
188
|
+
end
|
189
|
+
|
190
|
+
def self.filter?
|
191
|
+
filter rescue false
|
192
|
+
end
|
193
|
+
|
194
|
+
def self.selector
|
195
|
+
filter.selector
|
196
|
+
end
|
197
|
+
|
198
|
+
def initialize(root)
|
199
|
+
@root = root
|
200
|
+
end
|
201
|
+
|
202
|
+
# Clicks the current widget, or the child widget given by +name+.
|
203
|
+
#
|
204
|
+
# === Usage
|
205
|
+
#
|
206
|
+
# Given the following widget definition:
|
207
|
+
#
|
208
|
+
# class Container < Capybara::UI::Widget
|
209
|
+
# root '#container'
|
210
|
+
#
|
211
|
+
# widget :link, 'a'
|
212
|
+
# end
|
213
|
+
#
|
214
|
+
# Send +click+ with no arguments to trigger a +click+ event on +#container+.
|
215
|
+
#
|
216
|
+
# widget(:container).click
|
217
|
+
#
|
218
|
+
# This is the equivalent of doing the following using Capybara:
|
219
|
+
#
|
220
|
+
# find('#container').click
|
221
|
+
#
|
222
|
+
# Send +click :link+ to trigger a +click+ event on +a+:
|
223
|
+
#
|
224
|
+
# widget(:container).click :link
|
225
|
+
#
|
226
|
+
# This is the equivalent of doing the following using Capybara:
|
227
|
+
#
|
228
|
+
# find('#container a').click
|
229
|
+
def click(*args)
|
230
|
+
if args.empty?
|
231
|
+
root.click
|
232
|
+
else
|
233
|
+
widget(*args).click
|
234
|
+
end
|
235
|
+
end
|
236
|
+
|
237
|
+
# Hovers over the current widget, or the child widget given by +name+.
|
238
|
+
#
|
239
|
+
# === Usage
|
240
|
+
#
|
241
|
+
# Given the following widget definition:
|
242
|
+
#
|
243
|
+
# class Container < Capybara::UI::Widget
|
244
|
+
# root '#container'
|
245
|
+
#
|
246
|
+
# widget :link, 'a'
|
247
|
+
# end
|
248
|
+
#
|
249
|
+
# Send +hover+ with no arguments to trigger a +hover+ event on +#container+.
|
250
|
+
#
|
251
|
+
# widget(:container).hover
|
252
|
+
#
|
253
|
+
# This is the equivalent of doing the following using Capybara:
|
254
|
+
#
|
255
|
+
# find('#container').hover
|
256
|
+
#
|
257
|
+
# Send +hover :link+ to trigger a +hover+ event on +a+:
|
258
|
+
#
|
259
|
+
# widget(:container).hover :link
|
260
|
+
#
|
261
|
+
# This is the equivalent of doing the following using Capybara:
|
262
|
+
#
|
263
|
+
# find('#container a').hover
|
264
|
+
def hover(*args)
|
265
|
+
if args.empty?
|
266
|
+
root.hover
|
267
|
+
else
|
268
|
+
widget(*args).hover
|
269
|
+
end
|
270
|
+
end
|
271
|
+
|
272
|
+
# Double clicks the current widget, or the child widget given by +name+.
|
273
|
+
#
|
274
|
+
# === Usage
|
275
|
+
#
|
276
|
+
# Given the following widget definition:
|
277
|
+
#
|
278
|
+
# class Container < Capybara::UI::Widget
|
279
|
+
# root '#container'
|
280
|
+
#
|
281
|
+
# widget :link, 'a'
|
282
|
+
# end
|
283
|
+
#
|
284
|
+
# Send +double_click+ with no arguments to trigger an +ondblclick+ event on +#container+.
|
285
|
+
#
|
286
|
+
# widget(:container).double_click
|
287
|
+
#
|
288
|
+
# This is the equivalent of doing the following using Capybara:
|
289
|
+
#
|
290
|
+
# find('#container').double_click
|
291
|
+
def double_click(*args)
|
292
|
+
if args.empty?
|
293
|
+
root.double_click
|
294
|
+
else
|
295
|
+
widget(*args).double_click
|
296
|
+
end
|
297
|
+
end
|
298
|
+
|
299
|
+
# Right clicks the current widget, or the child widget given by +name+.
|
300
|
+
#
|
301
|
+
# === Usage
|
302
|
+
#
|
303
|
+
# Given the following widget definition:
|
304
|
+
#
|
305
|
+
# class Container < Capybara::UI::Widget
|
306
|
+
# root '#container'
|
307
|
+
#
|
308
|
+
# widget :link, 'a'
|
309
|
+
# end
|
310
|
+
#
|
311
|
+
# Send +right_click+ with no arguments to trigger an +oncontextmenu+ event on +#container+.
|
312
|
+
#
|
313
|
+
# widget(:container).right_click
|
314
|
+
#
|
315
|
+
# This is the equivalent of doing the following using Capybara:
|
316
|
+
#
|
317
|
+
# find('#container').right_click
|
318
|
+
def right_click(*args)
|
319
|
+
if args.empty?
|
320
|
+
root.right_click
|
321
|
+
else
|
322
|
+
widget(*args).right_click
|
323
|
+
end
|
324
|
+
end
|
325
|
+
|
326
|
+
# Determines if the widget underlying an action exists.
|
327
|
+
#
|
328
|
+
# @param name the name of the action
|
329
|
+
#
|
330
|
+
# @raise Missing if an action with +name+ can't be found.
|
331
|
+
#
|
332
|
+
# @return [Boolean] +true+ if the action widget is found, +false+
|
333
|
+
# otherwise.
|
334
|
+
def has_action?(name)
|
335
|
+
raise Missing, "couldn't find `#{name}' action" unless respond_to?(name)
|
336
|
+
|
337
|
+
visible?(:"#{name}_widget")
|
338
|
+
end
|
339
|
+
|
340
|
+
def id
|
341
|
+
root['id']
|
342
|
+
end
|
343
|
+
|
344
|
+
def classes
|
345
|
+
root['class'].split
|
346
|
+
end
|
347
|
+
|
348
|
+
# Determines if the widget has a specific class
|
349
|
+
#
|
350
|
+
# @param name the name of the class
|
351
|
+
#
|
352
|
+
# @return [Boolean] +true+ if the class is found, +false+ otherwise
|
353
|
+
def class?(name)
|
354
|
+
classes.include?(name)
|
355
|
+
end
|
356
|
+
|
357
|
+
def html
|
358
|
+
xml = Nokogiri::HTML(page.body).at(root.path).to_xml
|
359
|
+
|
360
|
+
Nokogiri::XML(xml, &:noblanks).to_xhtml.gsub("\n", "")
|
361
|
+
end
|
362
|
+
|
363
|
+
def text
|
364
|
+
StringValue.new(root.text.strip)
|
365
|
+
end
|
366
|
+
|
367
|
+
# Converts this widget into a string representation suitable to be displayed
|
368
|
+
# in a Cucumber table cell. By default calls #text.
|
369
|
+
#
|
370
|
+
# This method will be called by methods that build tables or rows (usually
|
371
|
+
# #to_table or #to_row) so, in general, you won't call it directly, but feel
|
372
|
+
# free to override it when needed.
|
373
|
+
#
|
374
|
+
# Returns a String.
|
375
|
+
def to_cell
|
376
|
+
text
|
377
|
+
end
|
378
|
+
|
379
|
+
def to_s
|
380
|
+
text
|
381
|
+
end
|
382
|
+
|
383
|
+
def value
|
384
|
+
text
|
385
|
+
end
|
386
|
+
|
387
|
+
private
|
388
|
+
|
389
|
+
def page
|
390
|
+
Capybara.current_session
|
391
|
+
end
|
392
|
+
end
|
393
|
+
end
|
394
|
+
end
|