capybara 2.14.0 → 2.14.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/History.md +12 -0
- data/README.md +1 -1
- data/lib/capybara.rb +16 -15
- data/lib/capybara/minitest.rb +92 -113
- data/lib/capybara/minitest/spec.rb +12 -37
- data/lib/capybara/node/matchers.rb +6 -5
- data/lib/capybara/queries/base_query.rb +4 -0
- data/lib/capybara/queries/current_path_query.rb +1 -0
- data/lib/capybara/queries/selector_query.rb +39 -12
- data/lib/capybara/queries/text_query.rb +1 -1
- data/lib/capybara/queries/title_query.rb +1 -0
- data/lib/capybara/rack_test/driver.rb +1 -0
- data/lib/capybara/rspec/matcher_proxies.rb +10 -6
- data/lib/capybara/rspec/matchers.rb +29 -0
- data/lib/capybara/selector.rb +61 -50
- data/lib/capybara/selector/expression_filter.rb +40 -0
- data/lib/capybara/selector/filter_set.rb +22 -3
- data/lib/capybara/selector/selector.rb +33 -12
- data/lib/capybara/selenium/driver.rb +130 -25
- data/lib/capybara/selenium/node.rb +3 -3
- data/lib/capybara/session/config.rb +29 -23
- data/lib/capybara/session/matchers.rb +3 -0
- data/lib/capybara/spec/session/accept_alert_spec.rb +1 -1
- data/lib/capybara/spec/session/all_spec.rb +2 -1
- data/lib/capybara/spec/session/assert_selector.rb +1 -1
- data/lib/capybara/spec/session/assert_title.rb +22 -9
- data/lib/capybara/spec/session/dismiss_confirm_spec.rb +3 -3
- data/lib/capybara/spec/session/dismiss_prompt_spec.rb +1 -1
- data/lib/capybara/spec/session/find_spec.rb +3 -2
- data/lib/capybara/spec/session/first_spec.rb +10 -5
- data/lib/capybara/spec/session/has_css_spec.rb +11 -0
- data/lib/capybara/spec/session/has_current_path_spec.rb +5 -3
- data/lib/capybara/spec/session/has_select_spec.rb +62 -4
- data/lib/capybara/spec/session/has_text_spec.rb +5 -3
- data/lib/capybara/spec/session/has_title_spec.rb +4 -2
- data/lib/capybara/spec/session/has_xpath_spec.rb +5 -3
- data/lib/capybara/spec/session/node_spec.rb +8 -4
- data/lib/capybara/spec/session/window/become_closed_spec.rb +4 -4
- data/lib/capybara/spec/session/window/window_opened_by_spec.rb +4 -4
- data/lib/capybara/spec/spec_helper.rb +1 -1
- data/lib/capybara/spec/views/form.erb +22 -1
- data/lib/capybara/spec/views/with_html.erb +1 -1
- data/lib/capybara/version.rb +1 -1
- data/spec/capybara_spec.rb +16 -0
- data/spec/filter_set_spec.rb +28 -0
- data/spec/minitest_spec_spec.rb +4 -4
- data/spec/per_session_config_spec.rb +4 -4
- data/spec/result_spec.rb +20 -0
- data/spec/selector_spec.rb +2 -1
- data/spec/selenium_spec_chrome.rb +12 -1
- data/spec/selenium_spec_firefox.rb +2 -1
- data/spec/selenium_spec_marionette.rb +4 -3
- data/spec/shared_selenium_session.rb +14 -7
- metadata +18 -2
@@ -9,7 +9,7 @@ module Capybara
|
|
9
9
|
|
10
10
|
def initialize(*args, &filter_block)
|
11
11
|
@options = if args.last.is_a?(Hash) then args.pop.dup else {} end
|
12
|
-
|
12
|
+
super(@options)
|
13
13
|
|
14
14
|
@filter_block = filter_block
|
15
15
|
|
@@ -47,7 +47,7 @@ module Capybara
|
|
47
47
|
@description << " with#{" exact" if exact_text == true} text #{options[:text].inspect}" if options[:text]
|
48
48
|
@description << " with exact text #{options[:exact_text]}" if options[:exact_text].is_a?(String)
|
49
49
|
@description << " with id #{options[:id]}" if options[:id]
|
50
|
-
@description << " with classes #{Array(options[:class]).join(',')}]" if options[:class]
|
50
|
+
@description << " with classes [#{Array(options[:class]).join(',')}]" if options[:class]
|
51
51
|
@description << selector.description(options)
|
52
52
|
@description << " that also matches the custom filter block" if @filter_block
|
53
53
|
@description
|
@@ -81,7 +81,7 @@ module Capybara
|
|
81
81
|
when :hidden then return false if node.visible?
|
82
82
|
end
|
83
83
|
|
84
|
-
res =
|
84
|
+
res = node_filters.all? do |name, filter|
|
85
85
|
if options.has_key?(name)
|
86
86
|
filter.matches?(node, options[name])
|
87
87
|
elsif filter.default?
|
@@ -91,8 +91,16 @@ module Capybara
|
|
91
91
|
end
|
92
92
|
end
|
93
93
|
|
94
|
-
res &&= node.session
|
94
|
+
res &&= if node.respond_to?(:session)
|
95
|
+
node.session.using_wait_time(0){ @filter_block.call(node) }
|
96
|
+
else
|
97
|
+
@filter_block.call(node)
|
98
|
+
end unless @filter_block.nil?
|
99
|
+
|
95
100
|
res
|
101
|
+
|
102
|
+
rescue *(node.respond_to?(:session) ? node.session.driver.invalid_element_errors : [])
|
103
|
+
return false
|
96
104
|
end
|
97
105
|
|
98
106
|
def visible
|
@@ -114,16 +122,17 @@ module Capybara
|
|
114
122
|
|
115
123
|
def xpath(exact=nil)
|
116
124
|
exact = self.exact? if exact.nil?
|
117
|
-
expr =
|
118
|
-
|
125
|
+
expr = apply_expression_filters(@expression)
|
126
|
+
expr = if expr.respond_to?(:to_xpath) and exact
|
127
|
+
expr.to_xpath(:exact)
|
119
128
|
else
|
120
|
-
|
129
|
+
expr.to_s
|
121
130
|
end
|
122
131
|
filtered_xpath(expr)
|
123
132
|
end
|
124
133
|
|
125
134
|
def css
|
126
|
-
filtered_css(@expression)
|
135
|
+
filtered_css(apply_expression_filters(@expression))
|
127
136
|
end
|
128
137
|
|
129
138
|
# @api private
|
@@ -155,16 +164,22 @@ module Capybara
|
|
155
164
|
VALID_KEYS + custom_keys
|
156
165
|
end
|
157
166
|
|
158
|
-
def
|
167
|
+
def node_filters
|
159
168
|
if options.has_key?(:filter_set)
|
160
|
-
Capybara::Selector::FilterSet.all[options[:filter_set]].
|
169
|
+
::Capybara::Selector::FilterSet.all[options[:filter_set]].node_filters
|
161
170
|
else
|
162
|
-
@selector.
|
171
|
+
@selector.node_filters
|
163
172
|
end
|
164
173
|
end
|
165
174
|
|
175
|
+
def expression_filters
|
176
|
+
filters = @selector.expression_filters
|
177
|
+
filters.merge ::Capybara::Selector::FilterSet.all[options[:filter_set]].expression_filters if options.has_key?(:filter_set)
|
178
|
+
filters
|
179
|
+
end
|
180
|
+
|
166
181
|
def custom_keys
|
167
|
-
@custom_keys ||=
|
182
|
+
@custom_keys ||= node_filters.keys + expression_filters.keys
|
168
183
|
end
|
169
184
|
|
170
185
|
def assert_valid_keys
|
@@ -200,6 +215,18 @@ module Capybara
|
|
200
215
|
expr
|
201
216
|
end
|
202
217
|
|
218
|
+
def apply_expression_filters(expr)
|
219
|
+
expression_filters.inject(expr) do |memo, (name, ef)|
|
220
|
+
if options.has_key?(name)
|
221
|
+
ef.apply_filter(memo, options[name])
|
222
|
+
elsif ef.default?
|
223
|
+
ef.apply_filter(memo, ef.default)
|
224
|
+
else
|
225
|
+
memo
|
226
|
+
end
|
227
|
+
end
|
228
|
+
end
|
229
|
+
|
203
230
|
def warn_exact_usage
|
204
231
|
if options.has_key?(:exact) && !supports_exact?
|
205
232
|
warn "The :exact option only has an effect on queries using the XPath#is method. Using it with the query \"#{expression.to_s}\" has no effect."
|
@@ -7,7 +7,7 @@ module Capybara
|
|
7
7
|
@type = (args.first.is_a?(Symbol) || args.first.nil?) ? args.shift : nil
|
8
8
|
# @type = (Capybara.ignore_hidden_elements or Capybara.visible_text_only) ? :visible : :all if @type.nil?
|
9
9
|
@options = if args.last.is_a?(Hash) then args.pop.dup else {} end
|
10
|
-
|
10
|
+
super(@options)
|
11
11
|
|
12
12
|
@type = (session_options.ignore_hidden_elements or session_options.visible_text_only) ? :visible : :all if @type.nil?
|
13
13
|
|
@@ -15,6 +15,7 @@ class Capybara::RackTest::Driver < Capybara::Driver::Base
|
|
15
15
|
|
16
16
|
def initialize(app, options={})
|
17
17
|
raise ArgumentError, "rack-test requires a rack application, but none was given" unless app
|
18
|
+
@session = nil
|
18
19
|
@app = app
|
19
20
|
@options = DEFAULT_OPTIONS.merge(options)
|
20
21
|
end
|
@@ -19,14 +19,18 @@ module Capybara
|
|
19
19
|
end
|
20
20
|
|
21
21
|
module DSL
|
22
|
-
|
23
|
-
|
22
|
+
class <<self
|
23
|
+
remove_method :included
|
24
24
|
|
25
|
-
|
26
|
-
|
27
|
-
end
|
25
|
+
def included(base)
|
26
|
+
warn "including Capybara::DSL in the global scope is not recommended!" if base == Object
|
28
27
|
|
29
|
-
|
28
|
+
if defined?(::RSpec::Matchers) && base.include?(::RSpec::Matchers)
|
29
|
+
base.send(:include, ::Capybara::RSpecMatcherProxies)
|
30
|
+
end
|
31
|
+
|
32
|
+
super
|
33
|
+
end
|
30
34
|
end
|
31
35
|
end
|
32
36
|
end
|
@@ -44,6 +44,7 @@ module Capybara
|
|
44
44
|
end
|
45
45
|
|
46
46
|
def session_options
|
47
|
+
@context_el ||= nil
|
47
48
|
@context_el ? @context_el.session_options : Capybara.session_options
|
48
49
|
end
|
49
50
|
end
|
@@ -195,10 +196,14 @@ module Capybara
|
|
195
196
|
alias_method :failure_message_for_should_not, :failure_message_when_negated
|
196
197
|
end
|
197
198
|
|
199
|
+
# RSpec matcher for whether the element(s) matching a given selector exist
|
200
|
+
# See {Capybara::Node::Matcher#assert_selector}
|
198
201
|
def have_selector(*args, &optional_filter_block)
|
199
202
|
HaveSelector.new(*args, &optional_filter_block)
|
200
203
|
end
|
201
204
|
|
205
|
+
# RSpec matcher for whether the current element matches a given selector
|
206
|
+
# See {Capybara::Node::Matchers#assert_matches_selector}
|
202
207
|
def match_selector(*args, &optional_filter_block)
|
203
208
|
MatchSelector.new(*args, &optional_filter_block)
|
204
209
|
end
|
@@ -207,22 +212,30 @@ module Capybara
|
|
207
212
|
::RSpec::Matchers.define_negated_matcher :not_match_selector, :match_selector if defined?(::RSpec::Expectations::Version) && (Gem::Version.new(RSpec::Expectations::Version::STRING) >= Gem::Version.new('3.1'))
|
208
213
|
|
209
214
|
|
215
|
+
# RSpec matcher for whether elements(s) matching a given xpath selector exist
|
216
|
+
# See {Capybara::Node::Matchers#has_xpath?}
|
210
217
|
def have_xpath(xpath, options={}, &optional_filter_block)
|
211
218
|
HaveSelector.new(:xpath, xpath, options, &optional_filter_block)
|
212
219
|
end
|
213
220
|
|
221
|
+
# RSpec matcher for whether the current element matches a given xpath selector
|
214
222
|
def match_xpath(xpath, options={}, &optional_filter_block)
|
215
223
|
MatchSelector.new(:xpath, xpath, options, &optional_filter_block)
|
216
224
|
end
|
217
225
|
|
226
|
+
# RSpec matcher for whether elements(s) matching a given css selector exist
|
227
|
+
# See {Capybara::Node::Matchers#has_css?}
|
218
228
|
def have_css(css, options={}, &optional_filter_block)
|
219
229
|
HaveSelector.new(:css, css, options, &optional_filter_block)
|
220
230
|
end
|
221
231
|
|
232
|
+
# RSpec matcher for whether the current element matches a given css selector
|
222
233
|
def match_css(css, options={}, &optional_filter_block)
|
223
234
|
MatchSelector.new(:css, css, options, &optional_filter_block)
|
224
235
|
end
|
225
236
|
|
237
|
+
# RSpec matcher for text on the page
|
238
|
+
# See {Capybara::SessionMatchers#assert_text}
|
226
239
|
def have_text(*args)
|
227
240
|
HaveText.new(*args)
|
228
241
|
end
|
@@ -232,40 +245,56 @@ module Capybara
|
|
232
245
|
HaveTitle.new(title, options)
|
233
246
|
end
|
234
247
|
|
248
|
+
# RSpec matcher for the current path
|
249
|
+
# See {Capybara::SessionMatchers#assert_current_path}
|
235
250
|
def have_current_path(path, options = {})
|
236
251
|
HaveCurrentPath.new(path, options)
|
237
252
|
end
|
238
253
|
|
254
|
+
# RSpec matcher for links
|
255
|
+
# See {Capybara::Node::Matchers#has_link?}
|
239
256
|
def have_link(locator=nil, options={}, &optional_filter_block)
|
240
257
|
locator, options = nil, locator if locator.is_a? Hash
|
241
258
|
HaveSelector.new(:link, locator, options, &optional_filter_block)
|
242
259
|
end
|
243
260
|
|
261
|
+
# RSpec matcher for buttons
|
262
|
+
# See {Capybara::Node::Matchers#has_button?}
|
244
263
|
def have_button(locator=nil, options={}, &optional_filter_block)
|
245
264
|
locator, options = nil, locator if locator.is_a? Hash
|
246
265
|
HaveSelector.new(:button, locator, options, &optional_filter_block)
|
247
266
|
end
|
248
267
|
|
268
|
+
# RSpec matcher for links
|
269
|
+
# See {Capybara::Node::Matchers#has_field?}
|
249
270
|
def have_field(locator=nil, options={}, &optional_filter_block)
|
250
271
|
locator, options = nil, locator if locator.is_a? Hash
|
251
272
|
HaveSelector.new(:field, locator, options, &optional_filter_block)
|
252
273
|
end
|
253
274
|
|
275
|
+
# RSpec matcher for checked fields
|
276
|
+
# See {Capybara::Node::Matchers#has_checked_field?}
|
254
277
|
def have_checked_field(locator=nil, options={}, &optional_filter_block)
|
255
278
|
locator, options = nil, locator if locator.is_a? Hash
|
256
279
|
HaveSelector.new(:field, locator, options.merge(checked: true), &optional_filter_block)
|
257
280
|
end
|
258
281
|
|
282
|
+
# RSpec matcher for unchecked fields
|
283
|
+
# See {Capybara::Node::Matchers#has_unchecked_field?}
|
259
284
|
def have_unchecked_field(locator=nil, options={}, &optional_filter_block)
|
260
285
|
locator, options = nil, locator if locator.is_a? Hash
|
261
286
|
HaveSelector.new(:field, locator, options.merge(unchecked: true), &optional_filter_block)
|
262
287
|
end
|
263
288
|
|
289
|
+
# RSpec matcher for select elements
|
290
|
+
# See {Capybara::Node::Matchers#has_select?}
|
264
291
|
def have_select(locator=nil, options={}, &optional_filter_block)
|
265
292
|
locator, options = nil, locator if locator.is_a? Hash
|
266
293
|
HaveSelector.new(:select, locator, options, &optional_filter_block)
|
267
294
|
end
|
268
295
|
|
296
|
+
# RSpec matcher for table elements
|
297
|
+
# See {Capybara::Node::Matchers#has_table?}
|
269
298
|
def have_table(locator=nil, options={}, &optional_filter_block)
|
270
299
|
locator, options = nil, locator if locator.is_a? Hash
|
271
300
|
HaveSelector.new(:table, locator, options, &optional_filter_block)
|
data/lib/capybara/selector.rb
CHANGED
@@ -6,6 +6,9 @@ Capybara::Selector::FilterSet.add(:_field) do
|
|
6
6
|
filter(:disabled, :boolean, default: false, skip_if: :all) { |node, value| not(value ^ node.disabled?) }
|
7
7
|
filter(:multiple, :boolean) { |node, value| !(value ^ node.multiple?) }
|
8
8
|
|
9
|
+
expression_filter(:name) { |xpath, val| xpath[XPath.attr(:name).equals(val)] }
|
10
|
+
expression_filter(:placeholder) { |xpath, val| xpath[XPath.attr(:placeholder).equals(val)] }
|
11
|
+
|
9
12
|
describe do |options|
|
10
13
|
desc, states = String.new, []
|
11
14
|
states << 'checked' if options[:checked] || (options[:unchecked] == false)
|
@@ -65,21 +68,21 @@ end
|
|
65
68
|
# @filter [Boolean] :disabled Match disabled field?
|
66
69
|
# @filter [Boolean] :multiple Match fields that accept multiple values
|
67
70
|
Capybara.add_selector(:field) do
|
68
|
-
xpath
|
71
|
+
xpath do |locator, options|
|
69
72
|
xpath = XPath.descendant(:input, :textarea, :select)[~XPath.attr(:type).one_of('submit', 'image', 'hidden')]
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
73
|
+
locate_field(xpath, locator, options)
|
74
|
+
end
|
75
|
+
|
76
|
+
expression_filter(:type) do |expr, type|
|
77
|
+
type = type.to_s
|
78
|
+
if ['textarea', 'select'].include?(type)
|
79
|
+
expr.axis(:self, type.to_sym)
|
80
|
+
else
|
81
|
+
expr[XPath.attr(:type).equals(type)]
|
77
82
|
end
|
78
|
-
xpath=locate_field(xpath, locator, options)
|
79
|
-
xpath
|
80
83
|
end
|
81
84
|
|
82
|
-
filter_set(:_field) # checked/unchecked/disabled/multiple
|
85
|
+
filter_set(:_field) # checked/unchecked/disabled/multiple/name/placeholder
|
83
86
|
|
84
87
|
filter(:readonly, :boolean) { |node, value| not(value ^ node.readonly?) }
|
85
88
|
filter(:with) do |node, with|
|
@@ -87,7 +90,7 @@ Capybara.add_selector(:field) do
|
|
87
90
|
end
|
88
91
|
describe do |options|
|
89
92
|
desc = String.new
|
90
|
-
(expression_filters - [:type]).each { |ef| desc << " with #{ef} #{options[ef]}" if options.has_key?(ef) }
|
93
|
+
(expression_filters.keys - [:type]).each { |ef| desc << " with #{ef} #{options[ef]}" if options.has_key?(ef) }
|
91
94
|
desc << " of type #{options[:type].inspect}" if options[:type]
|
92
95
|
desc << " with value #{options[:with].to_s.inspect}" if options.has_key?(:with)
|
93
96
|
desc
|
@@ -107,7 +110,7 @@ end
|
|
107
110
|
Capybara.add_selector(:fieldset) do
|
108
111
|
xpath(:legend) do |locator, options|
|
109
112
|
xpath = XPath.descendant(:fieldset)
|
110
|
-
xpath = xpath[XPath.attr(:id).equals(locator.to_s)
|
113
|
+
xpath = xpath[XPath.attr(:id).equals(locator.to_s).or XPath.child(:legend)[XPath.string.n.is(locator.to_s)]] unless locator.nil?
|
111
114
|
xpath = xpath[XPath.child(:legend)[XPath.string.n.is(options[:legend])]] if options[:legend]
|
112
115
|
xpath
|
113
116
|
end
|
@@ -135,11 +138,11 @@ Capybara.add_selector(:link) do
|
|
135
138
|
end
|
136
139
|
unless locator.nil?
|
137
140
|
locator = locator.to_s
|
138
|
-
matchers = XPath.attr(:id).equals(locator)
|
139
|
-
XPath.string.n.is(locator)
|
140
|
-
XPath.attr(:title).is(locator)
|
141
|
-
XPath.descendant(:img)[XPath.attr(:alt).is(locator)]
|
142
|
-
matchers
|
141
|
+
matchers = XPath.attr(:id).equals(locator).or(
|
142
|
+
XPath.string.n.is(locator)).or(
|
143
|
+
XPath.attr(:title).is(locator)).or(
|
144
|
+
XPath.descendant(:img)[XPath.attr(:alt).is(locator)])
|
145
|
+
matchers = matchers.or XPath.attr(:'aria-label').is(locator) if options[:enable_aria_label]
|
143
146
|
xpath = xpath[matchers]
|
144
147
|
end
|
145
148
|
xpath = [:title].inject(xpath) { |memo, ef| memo[find_by_attr(ef, options[ef])] }
|
@@ -184,21 +187,21 @@ Capybara.add_selector(:button) do
|
|
184
187
|
|
185
188
|
unless locator.nil?
|
186
189
|
locator = locator.to_s
|
187
|
-
locator_matches = XPath.attr(:id).equals(locator)
|
188
|
-
locator_matches
|
190
|
+
locator_matches = XPath.attr(:id).equals(locator).or XPath.attr(:value).is(locator).or XPath.attr(:title).is(locator)
|
191
|
+
locator_matches = locator_matches.or XPath.attr(:'aria-label').is(locator) if options[:enable_aria_label]
|
189
192
|
|
190
193
|
input_btn_xpath = input_btn_xpath[locator_matches]
|
191
194
|
|
192
|
-
btn_xpath = btn_xpath[locator_matches
|
195
|
+
btn_xpath = btn_xpath[locator_matches.or XPath.string.n.is(locator).or XPath.descendant(:img)[XPath.attr(:alt).is(locator)]]
|
193
196
|
|
194
197
|
alt_matches = XPath.attr(:alt).is(locator)
|
195
|
-
alt_matches
|
198
|
+
alt_matches = alt_matches.or XPath.attr(:'aria-label').is(locator) if options[:enable_aria_label]
|
196
199
|
image_btn_xpath = image_btn_xpath[alt_matches]
|
197
200
|
end
|
198
201
|
|
199
|
-
res_xpath = input_btn_xpath
|
202
|
+
res_xpath = input_btn_xpath.union(btn_xpath).union(image_btn_xpath)
|
200
203
|
|
201
|
-
res_xpath = expression_filters.inject(res_xpath) { |memo, ef| memo[find_by_attr(ef, options[ef])] }
|
204
|
+
res_xpath = expression_filters.keys.inject(res_xpath) { |memo, ef| memo[find_by_attr(ef, options[ef])] }
|
202
205
|
|
203
206
|
res_xpath
|
204
207
|
end
|
@@ -220,7 +223,7 @@ end
|
|
220
223
|
Capybara.add_selector(:link_or_button) do
|
221
224
|
label "link or button"
|
222
225
|
xpath do |locator, options|
|
223
|
-
self.class.all.values_at(:link, :button).map {|selector| selector.xpath.call(locator, options)}.reduce(
|
226
|
+
self.class.all.values_at(:link, :button).map {|selector| selector.xpath.call(locator, options)}.reduce(:union)
|
224
227
|
end
|
225
228
|
|
226
229
|
filter(:disabled, :boolean, default: false, skip_if: :all) { |node, value| node.tag_name == "a" or not(value ^ node.disabled?) }
|
@@ -244,20 +247,21 @@ end
|
|
244
247
|
#
|
245
248
|
Capybara.add_selector(:fillable_field) do
|
246
249
|
label "field"
|
247
|
-
xpath
|
250
|
+
xpath do |locator, options|
|
248
251
|
xpath = XPath.descendant(:input, :textarea)[~XPath.attr(:type).one_of('submit', 'image', 'radio', 'checkbox', 'hidden', 'file')]
|
249
|
-
if options[:type]
|
250
|
-
type=options[:type].to_s
|
251
|
-
if ['textarea'].include?(type)
|
252
|
-
xpath = XPath.descendant(type.to_sym)
|
253
|
-
else
|
254
|
-
xpath = xpath[XPath.attr(:type).equals(type)]
|
255
|
-
end
|
256
|
-
end
|
257
252
|
locate_field(xpath, locator, options)
|
258
253
|
end
|
259
254
|
|
260
|
-
|
255
|
+
expression_filter(:type) do |expr, type|
|
256
|
+
type = type.to_s
|
257
|
+
if ['textarea'].include?(type)
|
258
|
+
expr.axis(:self, type.to_sym)
|
259
|
+
else
|
260
|
+
expr[XPath.attr(:type).equals(type)]
|
261
|
+
end
|
262
|
+
end
|
263
|
+
|
264
|
+
filter_set(:_field, [:disabled, :multiple, :name, :placeholder])
|
261
265
|
|
262
266
|
filter(:with) do |node, with|
|
263
267
|
with.is_a?(Regexp) ? node.value =~ with : node.value == with.to_s
|
@@ -286,12 +290,12 @@ end
|
|
286
290
|
#
|
287
291
|
Capybara.add_selector(:radio_button) do
|
288
292
|
label "radio button"
|
289
|
-
xpath
|
293
|
+
xpath do |locator, options|
|
290
294
|
xpath = XPath.descendant(:input)[XPath.attr(:type).equals('radio')]
|
291
295
|
locate_field(xpath, locator, options)
|
292
296
|
end
|
293
297
|
|
294
|
-
filter_set(:_field, [:checked, :unchecked, :disabled])
|
298
|
+
filter_set(:_field, [:checked, :unchecked, :disabled, :name])
|
295
299
|
|
296
300
|
filter(:option) { |node, value| node.value == value.to_s }
|
297
301
|
|
@@ -317,12 +321,12 @@ end
|
|
317
321
|
# @filter [String] :option Match the value
|
318
322
|
#
|
319
323
|
Capybara.add_selector(:checkbox) do
|
320
|
-
xpath
|
324
|
+
xpath do |locator, options|
|
321
325
|
xpath = XPath.descendant(:input)[XPath.attr(:type).equals('checkbox')]
|
322
326
|
locate_field(xpath, locator, options)
|
323
327
|
end
|
324
328
|
|
325
|
-
filter_set(:_field, [:checked, :unchecked, :disabled])
|
329
|
+
filter_set(:_field, [:checked, :unchecked, :disabled, :name])
|
326
330
|
|
327
331
|
filter(:option) { |node, value| node.value == value.to_s }
|
328
332
|
|
@@ -348,15 +352,16 @@ end
|
|
348
352
|
# @filter [Array<String>] :options Exact match options
|
349
353
|
# @filter [Array<String>] :with_options Partial match options
|
350
354
|
# @filter [String, Array<String>] :selected Match the selection(s)
|
355
|
+
# @filter [String, Array<String>] :with_selected Partial match the selection(s)
|
351
356
|
#
|
352
357
|
Capybara.add_selector(:select) do
|
353
358
|
label "select box"
|
354
|
-
xpath
|
359
|
+
xpath do |locator, options|
|
355
360
|
xpath = XPath.descendant(:select)
|
356
361
|
locate_field(xpath, locator, options)
|
357
362
|
end
|
358
363
|
|
359
|
-
filter_set(:_field, [:disabled, :multiple])
|
364
|
+
filter_set(:_field, [:disabled, :multiple, :name, :placeholder])
|
360
365
|
|
361
366
|
filter(:options) do |node, options|
|
362
367
|
if node.visible?
|
@@ -376,8 +381,13 @@ Capybara.add_selector(:select) do
|
|
376
381
|
end
|
377
382
|
|
378
383
|
filter(:selected) do |node, selected|
|
379
|
-
actual = node.all(:xpath, './/option', visible: false).select
|
380
|
-
|
384
|
+
actual = node.all(:xpath, './/option', visible: false).select(&:selected?).map { |option| option.text(:all) }
|
385
|
+
Array(selected).sort == actual.sort
|
386
|
+
end
|
387
|
+
|
388
|
+
filter(:with_selected) do |node, selected|
|
389
|
+
actual = node.all(:xpath, './/option', visible: false).select(&:selected?).map { |option| option.text(:all) }
|
390
|
+
(Array(selected) - actual).empty?
|
381
391
|
end
|
382
392
|
|
383
393
|
describe do |options|
|
@@ -385,6 +395,7 @@ Capybara.add_selector(:select) do
|
|
385
395
|
desc << " with options #{options[:options].inspect}" if options[:options]
|
386
396
|
desc << " with at least options #{options[:with_options].inspect}" if options[:with_options]
|
387
397
|
desc << " with #{options[:selected].inspect} selected" if options[:selected]
|
398
|
+
desc << " with at least #{options[:with_selected].inspect} selected" if options[:with_selected]
|
388
399
|
desc << describe_all_expression_filters(options)
|
389
400
|
desc
|
390
401
|
end
|
@@ -429,12 +440,12 @@ end
|
|
429
440
|
#
|
430
441
|
Capybara.add_selector(:file_field) do
|
431
442
|
label "file field"
|
432
|
-
xpath
|
443
|
+
xpath do |locator, options|
|
433
444
|
xpath = XPath.descendant(:input)[XPath.attr(:type).equals('file')]
|
434
445
|
locate_field(xpath, locator, options)
|
435
446
|
end
|
436
447
|
|
437
|
-
filter_set(:_field, [:disabled, :multiple])
|
448
|
+
filter_set(:_field, [:disabled, :multiple, :name])
|
438
449
|
|
439
450
|
describe do |options|
|
440
451
|
desc = String.new
|
@@ -454,7 +465,7 @@ Capybara.add_selector(:label) do
|
|
454
465
|
label "label"
|
455
466
|
xpath(:for) do |locator, options|
|
456
467
|
xpath = XPath.descendant(:label)
|
457
|
-
xpath = xpath[XPath.string.n.is(locator.to_s)
|
468
|
+
xpath = xpath[XPath.string.n.is(locator.to_s).or XPath.attr(:id).equals(locator.to_s)] unless locator.nil?
|
458
469
|
if options.has_key?(:for) && !options[:for].is_a?(Capybara::Node::Element)
|
459
470
|
xpath = xpath[XPath.attr(:for).equals(options[:for].to_s).or((~XPath.attr(:for)).and(XPath.descendant()[XPath.attr(:id).equals(options[:for].to_s)]))]
|
460
471
|
end
|
@@ -493,7 +504,7 @@ end
|
|
493
504
|
Capybara.add_selector(:table) do
|
494
505
|
xpath(:caption) do |locator, options|
|
495
506
|
xpath = XPath.descendant(:table)
|
496
|
-
xpath = xpath[XPath.attr(:id).equals(locator.to_s)
|
507
|
+
xpath = xpath[XPath.attr(:id).equals(locator.to_s).or XPath.descendant(:caption).is(locator.to_s)] unless locator.nil?
|
497
508
|
xpath = xpath[XPath.descendant(:caption).equals(options[:caption])] if options[:caption]
|
498
509
|
xpath
|
499
510
|
end
|
@@ -516,9 +527,9 @@ end
|
|
516
527
|
#
|
517
528
|
Capybara.add_selector(:frame) do
|
518
529
|
xpath(:name) do |locator, options|
|
519
|
-
xpath = XPath.descendant(:iframe)
|
520
|
-
xpath = xpath[XPath.attr(:id).equals(locator.to_s)
|
521
|
-
xpath = expression_filters.inject(xpath) { |memo, ef| memo[find_by_attr(ef, options[ef])] }
|
530
|
+
xpath = XPath.descendant(:iframe).union(XPath.descendant(:frame))
|
531
|
+
xpath = xpath[XPath.attr(:id).equals(locator.to_s).or XPath.attr(:name).equals(locator)] unless locator.nil?
|
532
|
+
xpath = expression_filters.keys.inject(xpath) { |memo, ef| memo[find_by_attr(ef, options[ef])] }
|
522
533
|
xpath
|
523
534
|
end
|
524
535
|
|