capybara 2.14.0 → 2.14.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (55) hide show
  1. checksums.yaml +4 -4
  2. data/History.md +12 -0
  3. data/README.md +1 -1
  4. data/lib/capybara.rb +16 -15
  5. data/lib/capybara/minitest.rb +92 -113
  6. data/lib/capybara/minitest/spec.rb +12 -37
  7. data/lib/capybara/node/matchers.rb +6 -5
  8. data/lib/capybara/queries/base_query.rb +4 -0
  9. data/lib/capybara/queries/current_path_query.rb +1 -0
  10. data/lib/capybara/queries/selector_query.rb +39 -12
  11. data/lib/capybara/queries/text_query.rb +1 -1
  12. data/lib/capybara/queries/title_query.rb +1 -0
  13. data/lib/capybara/rack_test/driver.rb +1 -0
  14. data/lib/capybara/rspec/matcher_proxies.rb +10 -6
  15. data/lib/capybara/rspec/matchers.rb +29 -0
  16. data/lib/capybara/selector.rb +61 -50
  17. data/lib/capybara/selector/expression_filter.rb +40 -0
  18. data/lib/capybara/selector/filter_set.rb +22 -3
  19. data/lib/capybara/selector/selector.rb +33 -12
  20. data/lib/capybara/selenium/driver.rb +130 -25
  21. data/lib/capybara/selenium/node.rb +3 -3
  22. data/lib/capybara/session/config.rb +29 -23
  23. data/lib/capybara/session/matchers.rb +3 -0
  24. data/lib/capybara/spec/session/accept_alert_spec.rb +1 -1
  25. data/lib/capybara/spec/session/all_spec.rb +2 -1
  26. data/lib/capybara/spec/session/assert_selector.rb +1 -1
  27. data/lib/capybara/spec/session/assert_title.rb +22 -9
  28. data/lib/capybara/spec/session/dismiss_confirm_spec.rb +3 -3
  29. data/lib/capybara/spec/session/dismiss_prompt_spec.rb +1 -1
  30. data/lib/capybara/spec/session/find_spec.rb +3 -2
  31. data/lib/capybara/spec/session/first_spec.rb +10 -5
  32. data/lib/capybara/spec/session/has_css_spec.rb +11 -0
  33. data/lib/capybara/spec/session/has_current_path_spec.rb +5 -3
  34. data/lib/capybara/spec/session/has_select_spec.rb +62 -4
  35. data/lib/capybara/spec/session/has_text_spec.rb +5 -3
  36. data/lib/capybara/spec/session/has_title_spec.rb +4 -2
  37. data/lib/capybara/spec/session/has_xpath_spec.rb +5 -3
  38. data/lib/capybara/spec/session/node_spec.rb +8 -4
  39. data/lib/capybara/spec/session/window/become_closed_spec.rb +4 -4
  40. data/lib/capybara/spec/session/window/window_opened_by_spec.rb +4 -4
  41. data/lib/capybara/spec/spec_helper.rb +1 -1
  42. data/lib/capybara/spec/views/form.erb +22 -1
  43. data/lib/capybara/spec/views/with_html.erb +1 -1
  44. data/lib/capybara/version.rb +1 -1
  45. data/spec/capybara_spec.rb +16 -0
  46. data/spec/filter_set_spec.rb +28 -0
  47. data/spec/minitest_spec_spec.rb +4 -4
  48. data/spec/per_session_config_spec.rb +4 -4
  49. data/spec/result_spec.rb +20 -0
  50. data/spec/selector_spec.rb +2 -1
  51. data/spec/selenium_spec_chrome.rb +12 -1
  52. data/spec/selenium_spec_firefox.rb +2 -1
  53. data/spec/selenium_spec_marionette.rb +4 -3
  54. data/spec/shared_selenium_session.rb +14 -7
  55. metadata +18 -2
@@ -8,6 +8,10 @@ module Capybara
8
8
  attr_reader :options
9
9
  attr_writer :session_options
10
10
 
11
+ def initialize(options)
12
+ @session_options = options.delete(:session_options)
13
+ end
14
+
11
15
  def session_options
12
16
  @session_options || Capybara.session_options
13
17
  end
@@ -6,6 +6,7 @@ module Capybara
6
6
  module Queries
7
7
  class CurrentPathQuery < BaseQuery
8
8
  def initialize(expected_path, options = {})
9
+ super(options)
9
10
  @expected_path = expected_path
10
11
  @options = {
11
12
  url: false,
@@ -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
- self.session_options = @options.delete(:session_options)
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 = query_filters.all? do |name, filter|
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.using_wait_time(0){ @filter_block.call(node)} unless @filter_block.nil?
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 = if @expression.respond_to?(:to_xpath) and exact
118
- @expression.to_xpath(:exact)
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
- @expression.to_s
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 query_filters
167
+ def node_filters
159
168
  if options.has_key?(:filter_set)
160
- Capybara::Selector::FilterSet.all[options[:filter_set]].filters
169
+ ::Capybara::Selector::FilterSet.all[options[:filter_set]].node_filters
161
170
  else
162
- @selector.custom_filters
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 ||= query_filters.keys + @selector.expression_filters
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
- self.session_options = @options.delete(:session_options)
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
 
@@ -6,6 +6,7 @@ module Capybara
6
6
  def initialize(expected_title, options = {})
7
7
  @expected_title = expected_title
8
8
  @options = options
9
+ super(@options)
9
10
  unless @expected_title.is_a?(Regexp)
10
11
  @expected_title = Capybara::Helpers.normalize_whitespace(@expected_title)
11
12
  end
@@ -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
- def self.included(base)
23
- warn "including Capybara::DSL in the global scope is not recommended!" if base == Object
22
+ class <<self
23
+ remove_method :included
24
24
 
25
- if defined?(::RSpec::Matchers) && base.include?(::RSpec::Matchers)
26
- base.send(:include, ::Capybara::RSpecMatcherProxies)
27
- end
25
+ def included(base)
26
+ warn "including Capybara::DSL in the global scope is not recommended!" if base == Object
28
27
 
29
- super
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)
@@ -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(:name, :placeholder, :type) do |locator, options|
71
+ xpath do |locator, options|
69
72
  xpath = XPath.descendant(:input, :textarea, :select)[~XPath.attr(:type).one_of('submit', 'image', 'hidden')]
70
- if options[:type]
71
- type=options[:type].to_s
72
- if ['textarea', 'select'].include?(type)
73
- xpath = XPath.descendant(type.to_sym)
74
- else
75
- xpath = xpath[XPath.attr(:type).equals(type)]
76
- end
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) | XPath.child(:legend)[XPath.string.n.is(locator.to_s)]] unless locator.nil?
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 |= XPath.attr(:'aria-label').is(locator) if options[:enable_aria_label]
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) | XPath.attr(:value).is(locator) | XPath.attr(:title).is(locator)
188
- locator_matches |= XPath.attr(:'aria-label').is(locator) if options[:enable_aria_label]
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 | XPath.string.n.is(locator) | XPath.descendant(:img)[XPath.attr(:alt).is(locator)]]
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 |= XPath.attr(:'aria-label').is(locator) if options[:enable_aria_label]
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 + btn_xpath + image_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(:name, :placeholder, :type) do |locator, options|
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
- filter_set(:_field, [:disabled, :multiple])
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(:name) do |locator, options|
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(:name) do |locator, options|
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(:name, :placeholder) do |locator, options|
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 { |option| option.selected? }.map { |option| option.text(:all) }
380
- [selected].flatten.sort == actual.sort
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(:name) do |locator, options|
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) | XPath.attr(:id).equals(locator.to_s)] unless locator.nil?
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) | XPath.descendant(:caption).is(locator.to_s)] unless locator.nil?
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) + XPath.descendant(:frame)
520
- xpath = xpath[XPath.attr(:id).equals(locator.to_s) | XPath.attr(:name).equals(locator)] unless locator.nil?
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