capybara 3.6.0 → 3.7.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (64) hide show
  1. checksums.yaml +4 -4
  2. data/History.md +16 -0
  3. data/README.md +5 -1
  4. data/lib/capybara.rb +2 -0
  5. data/lib/capybara/minitest/spec.rb +1 -1
  6. data/lib/capybara/node/actions.rb +34 -25
  7. data/lib/capybara/node/base.rb +15 -17
  8. data/lib/capybara/node/document_matchers.rb +1 -3
  9. data/lib/capybara/node/element.rb +11 -12
  10. data/lib/capybara/node/finders.rb +2 -1
  11. data/lib/capybara/node/simple.rb +13 -6
  12. data/lib/capybara/queries/base_query.rb +4 -4
  13. data/lib/capybara/queries/selector_query.rb +119 -94
  14. data/lib/capybara/queries/text_query.rb +2 -1
  15. data/lib/capybara/rack_test/form.rb +4 -4
  16. data/lib/capybara/rack_test/node.rb +5 -5
  17. data/lib/capybara/result.rb +23 -32
  18. data/lib/capybara/rspec/compound.rb +1 -1
  19. data/lib/capybara/rspec/matchers.rb +63 -61
  20. data/lib/capybara/selector.rb +28 -10
  21. data/lib/capybara/selector/css.rb +17 -17
  22. data/lib/capybara/selector/filter_set.rb +9 -9
  23. data/lib/capybara/selector/selector.rb +3 -4
  24. data/lib/capybara/selenium/driver.rb +73 -95
  25. data/lib/capybara/selenium/driver_specializations/chrome_driver.rb +4 -4
  26. data/lib/capybara/selenium/driver_specializations/marionette_driver.rb +9 -0
  27. data/lib/capybara/selenium/node.rb +127 -67
  28. data/lib/capybara/selenium/nodes/chrome_node.rb +3 -3
  29. data/lib/capybara/selenium/nodes/marionette_node.rb +14 -8
  30. data/lib/capybara/server.rb +2 -2
  31. data/lib/capybara/server/animation_disabler.rb +17 -3
  32. data/lib/capybara/server/middleware.rb +8 -4
  33. data/lib/capybara/session.rb +43 -37
  34. data/lib/capybara/session/config.rb +8 -6
  35. data/lib/capybara/spec/session/assert_text_spec.rb +14 -0
  36. data/lib/capybara/spec/session/attach_file_spec.rb +7 -0
  37. data/lib/capybara/spec/session/check_spec.rb +21 -0
  38. data/lib/capybara/spec/session/choose_spec.rb +15 -1
  39. data/lib/capybara/spec/session/fill_in_spec.rb +7 -0
  40. data/lib/capybara/spec/session/find_spec.rb +2 -1
  41. data/lib/capybara/spec/session/has_selector_spec.rb +18 -0
  42. data/lib/capybara/spec/session/has_text_spec.rb +14 -0
  43. data/lib/capybara/spec/session/node_spec.rb +2 -1
  44. data/lib/capybara/spec/session/reset_session_spec.rb +4 -4
  45. data/lib/capybara/spec/session/text_spec.rb +2 -1
  46. data/lib/capybara/spec/session/title_spec.rb +2 -1
  47. data/lib/capybara/spec/session/uncheck_spec.rb +8 -0
  48. data/lib/capybara/spec/session/within_spec.rb +2 -1
  49. data/lib/capybara/spec/spec_helper.rb +1 -32
  50. data/lib/capybara/spec/views/with_js.erb +3 -4
  51. data/lib/capybara/version.rb +1 -1
  52. data/spec/minitest_spec.rb +4 -0
  53. data/spec/minitest_spec_spec.rb +4 -0
  54. data/spec/rack_test_spec.rb +4 -4
  55. data/spec/rspec/shared_spec_matchers.rb +4 -2
  56. data/spec/selector_spec.rb +15 -1
  57. data/spec/selenium_spec_chrome.rb +1 -6
  58. data/spec/selenium_spec_chrome_remote.rb +1 -1
  59. data/spec/selenium_spec_firefox_remote.rb +2 -5
  60. data/spec/selenium_spec_ie.rb +41 -4
  61. data/spec/selenium_spec_marionette.rb +1 -25
  62. data/spec/shared_selenium_session.rb +74 -16
  63. data/spec/spec_helper.rb +41 -0
  64. metadata +2 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 1f61329ea428004635c5d6768afcdc61fcc22a2dd3a4cb0f14bde2d3b05102cb
4
- data.tar.gz: d9d3de632455c5a3a8eecbef2c2fccc7b8d301bb589dcd7a5a21c719e639a5f5
3
+ metadata.gz: e081025be98ae57e3e43ef0fb2a156b3bfb3ff4d54f8f25bfc09a4bcff2fc59c
4
+ data.tar.gz: 95e8d6fffe004d4802cd0f97d3bddeae5394041dc774d341c3ad10a60d0b04eb
5
5
  SHA512:
6
- metadata.gz: 79e61c8494174a8006f158dc0b26d143a14525e4c0e77b4a1b5148f7f46c79207d4d5d89d0f5ef6f3ed3a8374bf2b54e915160770c6c254147c9aea52da48bf1
7
- data.tar.gz: b329ae727a2c0d47903f9c992e8acd9e7e26e3bb280751fb8f681cb67c29650ad11d002ad9bfae627c1e767a00483e97a74667e564d4cec0bae2c529afc27452
6
+ metadata.gz: 93e42ca9e9ad0bde5911fe2c7e86ad2d0ac1c40428dfcd256aa01b21b6a8c2e1755df018a272f1917e0628c5c7e1a68b35eb930c3abdb5ffec4e9079187ca5b8
7
+ data.tar.gz: c7faa43983241d170cf725c22fcc595d2fde4572a71abcf3271d32993bbb6e2874a96c74b850090089598d277b8793b072315fc1e34c9e7a876daeb64a007f7e
data/History.md CHANGED
@@ -1,3 +1,19 @@
1
+ # Version 3.7.0
2
+ Release date: 2018-09-02
3
+
4
+ ### Added
5
+
6
+ * `Capybara.disable_animation` can be set to a CSS selector to identify which elements will have animation disabled [Michael Glass]
7
+ * `Capybara.default_normalize_ws` option which sets whether or not text predicates and matchers (`has_text?`, `has_content?`, `assert_text`, etc) use `normalize_ws` option by default. Defaults to false. [Stegalin Ivan]
8
+ * Selector based predicates, matchers, and finders now support the `:normalize_ws` option for the `:text`/`:exact_text` filters. Defaults to the `Capybara.default_normalize_ws`setting above.
9
+ * Element `choose`/`check`/`uncheck`/`attach_file`/`fill_in` can now operate on the element they're called on or a descendant if no locator is passed.
10
+
11
+ ### Fixed
12
+
13
+ * All CSS styles applied by the `Element#attach_file` `:make_visible` option will now have `!important` priority set to ensure they override any other specified style.
14
+ * Firefox file inputs are only manually cleared when necessary.
15
+ *
16
+
1
17
  # Version 3.6.0
2
18
  Release date: 2018-08-14
3
19
 
data/README.md CHANGED
@@ -7,7 +7,7 @@
7
7
  [![SemVer](https://api.dependabot.com/badges/compatibility_score?dependency-name=capybara&package-manager=bundler&version-scheme=semver)](https://dependabot.com/compatibility-score.html?dependency-name=capybara&package-manager=bundler&version-scheme=semver)
8
8
 
9
9
  **Note** You are viewing the README for the development version of Capybara. If you are using the current release version
10
- you can find the README at https://github.com/teamcapybara/capybara/blob/3.6_stable/README.md
10
+ you can find the README at https://github.com/teamcapybara/capybara/blob/3.7_stable/README.md
11
11
 
12
12
 
13
13
  Capybara helps you test web applications by simulating how a real user would
@@ -409,6 +409,8 @@ and test server, see "Transactions and database setup" below.
409
409
 
410
410
  ### <a name="capybara-webkit"></a>Capybara-webkit
411
411
 
412
+ Note: `capybara-webkit` depends on QtWebkit which went EOL quite some time ago. There has been an attempt to revive the project but `capybara-webkit` is not yet (AFAIK) compatible with the revived version of QtWebKit (could be a good OSS project for someone) and as such is still limited to an old version of QtWebKit. This means its support for modern JS and CSS is severely limited. Y
413
+
412
414
  The [capybara-webkit driver](https://github.com/thoughtbot/capybara-webkit) is for true headless
413
415
  testing. It uses QtWebKit to start a rendering engine process. It can execute JavaScript as well.
414
416
  It is significantly faster than drivers like Selenium since it does not load an entire browser.
@@ -427,6 +429,8 @@ Capybara.javascript_driver = :webkit
427
429
 
428
430
  ### <a name="poltergeist"></a>Poltergeist
429
431
 
432
+ Note: `poltergeist` depends on PhantomJS for which active development ended quite some time ago (2.1.1). As such it is roughly equivalent to a 6-7 year old version of Safari, meaning lack of support for modern JS And CSS. If any effort to update PhantomJS succeeds in the future this situation could change.
433
+
430
434
  [Poltergeist](https://github.com/teampoltergeist/poltergeist) is another
431
435
  headless driver which integrates Capybara with
432
436
  [PhantomJS](http://phantomjs.org/). It is truly headless, so doesn't
@@ -85,6 +85,7 @@ module Capybara
85
85
  # [default_set_options = Hash] The default options passed to Node::set (Default: {})
86
86
  # [test_id = Symbol/String/nil] Optional attribute to match locator aginst with builtin selectors along with id (Default: nil)
87
87
  # [predicates_wait = Boolean] Whether Capybaras predicate matchers use waiting behavior by default (Default: true)
88
+ # [default_normalize_ws = Boolean] Whether text predicates and matchers use normalize whitespace behaviour (Default: false)
88
89
  #
89
90
  # === DSL Options
90
91
  #
@@ -486,6 +487,7 @@ Capybara.configure do |config|
486
487
  config.default_set_options = {}
487
488
  config.test_id = nil
488
489
  config.predicates_wait = true
490
+ config.default_normalize_ws = false
489
491
  end
490
492
 
491
493
  Capybara.register_driver :rack_test do |app|
@@ -169,7 +169,7 @@ module Capybara
169
169
  # Expectation that element has style
170
170
  #
171
171
  # @!method must_have_style
172
- # see {Capybara::SessionMatchers#assert_style}
172
+ # see {Capybara::Node::Matchers#assert_style}
173
173
  end
174
174
  end
175
175
  end
@@ -62,6 +62,7 @@ module Capybara
62
62
  #
63
63
  # Locate a text field or text area and fill it in with the given text
64
64
  # The field can be found via its name, id, Capybara.test_id attribute, or label text.
65
+ # If no locator is provided will operate on self or a descendant
65
66
  #
66
67
  # page.fill_in 'Name', with: 'Bob'
67
68
  #
@@ -82,6 +83,7 @@ module Capybara
82
83
  # @return [Capybara::Node::Element] The element filled_in
83
84
  def fill_in(locator = nil, with:, currently_with: nil, fill_options: {}, **find_options)
84
85
  find_options[:with] = currently_with if currently_with
86
+ find_options[:allow_self] = true if locator.nil?
85
87
  find(:fillable_field, locator, find_options).set(with, fill_options)
86
88
  end
87
89
 
@@ -90,8 +92,9 @@ module Capybara
90
92
 
91
93
  ##
92
94
  #
93
- # Find a radio button and mark it as checked. The radio button can be found
94
- # via name, id or label text.
95
+ # Find a descendant radio button and mark it as checked. The radio button can be found
96
+ # via name, id or label text. If no locator is provided this will match against self or
97
+ # a descendant.
95
98
  #
96
99
  # page.choose('Male')
97
100
  #
@@ -112,8 +115,9 @@ module Capybara
112
115
 
113
116
  ##
114
117
  #
115
- # Find a check box and mark it as checked. The check box can be found
116
- # via name, id or label text.
118
+ # Find a descendant check box and mark it as checked. The check box can be found
119
+ # via name, id or label text. If no locator is provided this will match against
120
+ # self or a descendant.
117
121
  #
118
122
  # page.check('German')
119
123
  #
@@ -135,8 +139,9 @@ module Capybara
135
139
 
136
140
  ##
137
141
  #
138
- # Find a check box and mark uncheck it. The check box can be found
139
- # via name, id or label text.
142
+ # Find a descendant check box and mark uncheck it. The check box can be found
143
+ # via name, id or label text. If no locator is provided this will match against
144
+ # self or a descendant.
140
145
  #
141
146
  # page.uncheck('German')
142
147
  #
@@ -170,7 +175,7 @@ module Capybara
170
175
  # @macro waiting_behavior
171
176
  #
172
177
  # @param value [String] Which option to select
173
- # @param from: [String] The id, Capybara.test_id atrtribute, name or label of the select box
178
+ # @param from [String] The id, Capybara.test_id atrtribute, name or label of the select box
174
179
  #
175
180
  # @return [Capybara::Node::Element] The option element selected
176
181
  def select(value = nil, from: nil, **options)
@@ -193,8 +198,8 @@ module Capybara
193
198
  #
194
199
  # @macro waiting_behavior
195
200
  #
196
- # @param value [String] Which option to unselect
197
- # @param from: [String] The id, Capybara.test_id attribute, name or label of the select box
201
+ # @param value [String] Which option to unselect
202
+ # @param from [String] The id, Capybara.test_id attribute, name or label of the select box
198
203
  #
199
204
  # @return [Capybara::Node::Element] The option element unselected
200
205
  def unselect(value = nil, from: nil, **options)
@@ -204,18 +209,19 @@ module Capybara
204
209
 
205
210
  ##
206
211
  #
207
- # Find a file field on the page and attach a file given its path. The file field can
212
+ # Find a descendant file field on the page and attach a file given its path. The file field can
208
213
  # be found via its name, id or label text. In the case of the file field being hidden for
209
214
  # styling reasons the `make_visible` option can be used to temporarily change the CSS of
210
- # the file field, attach the file, and then revert the CSS back to original.
215
+ # the file field, attach the file, and then revert the CSS back to original. If no locator is
216
+ # passed this will match self or a descendant.
211
217
  #
212
218
  # page.attach_file(locator, '/path/to/file.png')
213
219
  #
214
- # @overload attach_file([locator], path, **options)
220
+ # @overload attach_file([locator], paths, **options)
215
221
  # @macro waiting_behavior
216
222
  #
217
- # @param [String] locator Which field to attach the file to
218
- # @param [String] path The path of the file that will be attached, or an array of paths
223
+ # @param [String] locator Which field to attach the file to
224
+ # @param [String, Array<String>] paths The path(s) of the file(s) that will be attached
219
225
  #
220
226
  # @option options [Symbol] match (Capybara.match) The matching strategy to use (:one, :first, :prefer_exact, :smart).
221
227
  # @option options [Boolean] exact (Capybara.exact) Match the exact label name/contents or accept a partial match.
@@ -226,16 +232,17 @@ module Capybara
226
232
  # @option options [true, Hash] make_visible A Hash of CSS styles to change before attempting to attach the file, if `true` { opacity: 1, display: 'block', visibility: 'visible' } is used (may not be supported by all drivers)
227
233
  #
228
234
  # @return [Capybara::Node::Element] The file field element
229
- def attach_file(locator = nil, path, make_visible: nil, **options) # rubocop:disable Style/OptionalArguments
230
- Array(path).each do |p|
231
- raise Capybara::FileNotFound, "cannot attach file, #{p} does not exist" unless File.exist?(p.to_s)
235
+ def attach_file(locator = nil, paths, make_visible: nil, **options) # rubocop:disable Style/OptionalArguments
236
+ Array(paths).each do |path|
237
+ raise Capybara::FileNotFound, "cannot attach file, #{path} does not exist" unless File.exist?(path.to_s)
232
238
  end
239
+ options[:allow_self] = true if locator.nil?
233
240
  # Allow user to update the CSS style of the file input since they are so often hidden on a page
234
241
  if make_visible
235
242
  ff = find(:file_field, locator, options.merge(visible: :all))
236
- while_visible(ff, make_visible) { |el| el.set(path) }
243
+ while_visible(ff, make_visible) { |el| el.set(paths) }
237
244
  else
238
- find(:file_field, locator, options).set(path)
245
+ find(:file_field, locator, options).set(paths)
239
246
  end
240
247
  end
241
248
 
@@ -258,7 +265,7 @@ module Capybara
258
265
 
259
266
  def select_datalist_option(input, value)
260
267
  datalist_options = input.evaluate_script(DATALIST_OPTIONS_SCRIPT)
261
- option = datalist_options.find { |o| o.values_at('value', 'label').include?(value) }
268
+ option = datalist_options.find { |opt| opt.values_at('value', 'label').include?(value) }
262
269
  raise ::Capybara::ElementNotFound, %(Unable to find datalist option "#{value}") unless option
263
270
  input.set(option['value'])
264
271
  rescue ::Capybara::NotSupportedByDriverError
@@ -291,17 +298,19 @@ module Capybara
291
298
  end
292
299
 
293
300
  def _check_with_label(selector, checked, locator, allow_label_click: session_options.automatic_label_click, **options)
301
+ options[:allow_self] = true if locator.nil?
302
+
294
303
  synchronize(Capybara::Queries::BaseQuery.wait(options, session_options.default_max_wait_time)) do
295
304
  begin
296
305
  el = find(selector, locator, options)
297
306
  el.set(checked)
298
- rescue StandardError => e
299
- raise unless allow_label_click && catch_error?(e)
307
+ rescue StandardError => err
308
+ raise unless allow_label_click && catch_error?(err)
300
309
  begin
301
310
  el ||= find(selector, locator, options.merge(visible: :all))
302
- find(:label, for: el, visible: true).click unless el.checked? == checked
311
+ el.session.find(:label, for: el, visible: true).click unless el.checked? == checked
303
312
  rescue StandardError # swallow extra errors - raise original
304
- raise e
313
+ raise err
305
314
  end
306
315
  end
307
316
  end
@@ -312,7 +321,7 @@ module Capybara
312
321
  var css = arguments[0];
313
322
  for (var prop in css){
314
323
  if (css.hasOwnProperty(prop)) {
315
- this.style[prop] = css[prop]
324
+ this.style.setProperty(prop, css[prop], "important");
316
325
  }
317
326
  }
318
327
  JS
@@ -74,24 +74,22 @@ module Capybara
74
74
  # @raise [Capybara::FrozenInTime] If the return value of `Time.now` appears stuck
75
75
  #
76
76
  def synchronize(seconds = session_options.default_max_wait_time, errors: nil)
77
- if session.synchronized
77
+ return yield if session.synchronized
78
+
79
+ session.synchronized = true
80
+ timer = Capybara::Helpers.timer(expire_in: seconds)
81
+ begin
78
82
  yield
79
- else
80
- session.synchronized = true
81
- timer = Capybara::Helpers.timer(expire_in: seconds)
82
- begin
83
- yield
84
- rescue StandardError => e
85
- session.raise_server_error!
86
- raise e unless driver.wait? && catch_error?(e, errors)
87
- raise e if timer.expired?
88
- sleep(0.05)
89
- raise Capybara::FrozenInTime, 'Time appears to be frozen. Capybara does not work with libraries which freeze time, consider using time travelling instead' if timer.stalled?
90
- reload if session_options.automatic_reload
91
- retry
92
- ensure
93
- session.synchronized = false
94
- end
83
+ rescue StandardError => err
84
+ session.raise_server_error!
85
+ raise err unless driver.wait? && catch_error?(err, errors)
86
+ raise err if timer.expired?
87
+ sleep(0.05)
88
+ raise Capybara::FrozenInTime, 'Time appears to be frozen. Capybara does not work with libraries which freeze time, consider using time travelling instead' if timer.stalled?
89
+ reload if session_options.automatic_reload
90
+ retry
91
+ ensure
92
+ session.synchronized = false
95
93
  end
96
94
  end
97
95
 
@@ -55,9 +55,7 @@ module Capybara
55
55
 
56
56
  def _verify_title(title, options)
57
57
  query = Capybara::Queries::TitleQuery.new(title, options)
58
- synchronize(query.wait) do
59
- yield(query)
60
- end
58
+ synchronize(query.wait) { yield(query) }
61
59
  true
62
60
  end
63
61
  end
@@ -55,8 +55,8 @@ module Capybara
55
55
  #
56
56
  def text(type = nil, normalize_ws: false)
57
57
  type ||= :all unless session_options.ignore_hidden_elements || session_options.visible_text_only
58
- t = synchronize { type == :all ? base.all_text : base.visible_text }
59
- normalize_ws ? t.gsub(/[[:space:]]+/, ' ').strip : t
58
+ txt = synchronize { type == :all ? base.all_text : base.visible_text }
59
+ normalize_ws ? txt.gsub(/[[:space:]]+/, ' ').strip : txt
60
60
  end
61
61
 
62
62
  ##
@@ -78,19 +78,19 @@ module Capybara
78
78
  #
79
79
  # element.style('color', 'font-size') # => Computed values of CSS 'color' and 'font-size' styles
80
80
  #
81
- # @param [String] Names of the desired CSS properties
82
- # @return [Hash] Hash of the CSS property names to computed values
81
+ # @param [Array<String>] styles Names of the desired CSS properties
82
+ # @return [Hash] Hash of the CSS property names to computed values
83
83
  #
84
84
  def style(*styles)
85
85
  styles = styles.flatten.map(&:to_s)
86
86
  raise ArgumentError, 'You must specify at least one CSS style' if styles.empty?
87
87
  begin
88
88
  synchronize { base.style(styles) }
89
- rescue NotImplementedError => e
89
+ rescue NotImplementedError => err
90
90
  begin
91
91
  evaluate_script(STYLE_SCRIPT, *styles)
92
92
  rescue Capybara::NotSupportedByDriverError
93
- raise e
93
+ raise err
94
94
  end
95
95
  end
96
96
  end
@@ -108,7 +108,7 @@ module Capybara
108
108
  # Set the value of the form element to the given value.
109
109
  #
110
110
  # @param [String] value The new value
111
- # @param [Hash{}] options Driver specific options for how to set the value. Take default values from {Capybara#default_set_options}
111
+ # @param [Hash{}] options Driver specific options for how to set the value. Take default values from `Capybara#default_set_options` - See {Capybara::configure}
112
112
  #
113
113
  # @return [Capybara::Node::Element] The element
114
114
  def set(value, **options)
@@ -429,8 +429,8 @@ module Capybara
429
429
  begin
430
430
  reloaded = query_scope.reload.first(@query.name, @query.locator, @query.options)
431
431
  @base = reloaded.base if reloaded
432
- rescue StandardError => e
433
- raise e unless catch_error?(e)
432
+ rescue StandardError => err
433
+ raise err unless catch_error?(err)
434
434
  end
435
435
  end
436
436
  self
@@ -440,9 +440,8 @@ module Capybara
440
440
  %(#<Capybara::Node::Element tag="#{base.tag_name}" path="#{base.path}">)
441
441
  rescue NotSupportedByDriverError
442
442
  %(#<Capybara::Node::Element tag="#{base.tag_name}">)
443
- rescue StandardError => e
444
- raise unless session.driver.invalid_element_errors.any? { |et| e.is_a?(et) }
445
-
443
+ rescue StandardError => err
444
+ raise unless session.driver.invalid_element_errors.any? { |et| err.is_a?(et) }
446
445
  %(Obsolete #<Capybara::Node::Element>)
447
446
  end
448
447
 
@@ -223,6 +223,7 @@ module Capybara
223
223
  # @param [String] locator The locator for the specified selector
224
224
  # @option options [String, Regexp] text Only find elements which contain this text or match this regexp
225
225
  # @option options [String, Boolean] exact_text (Capybara.exact_text) When String the elements contained text must match exactly, when Boolean controls whether the :text option must match exactly
226
+ # @option options [Boolean] normalize_ws (Capybara.default_normalize_ws) Whether the `text`/`exact_text` options are compared against elment text with whitespace normalized or as returned by the driver
226
227
  # @option options [Boolean, Symbol] visible Only find elements with the specified visibility:
227
228
  # * true - only finds visible elements.
228
229
  # * false - finds invisible _and_ visible elements.
@@ -306,7 +307,7 @@ module Capybara
306
307
  end
307
308
 
308
309
  def options_include_minimum?(opts)
309
- %i[count minimum between].any? { |k| opts.key?(k) }
310
+ %i[count minimum between].any? { |key| opts.key?(key) }
310
311
  end
311
312
  end
312
313
  end
@@ -29,8 +29,8 @@ module Capybara
29
29
  # @return [String] The text of the element
30
30
  #
31
31
  def text(_type = nil, normalize_ws: false)
32
- t = native.text
33
- normalize_ws ? t.gsub(/[[:space:]]+/, ' ').strip : t
32
+ txt = native.text
33
+ normalize_ws ? txt.gsub(/[[:space:]]+/, ' ').strip : txt
34
34
  end
35
35
 
36
36
  ##
@@ -79,11 +79,11 @@ module Capybara
79
79
  if tag_name == 'textarea'
80
80
  native['_capybara_raw_value']
81
81
  elsif tag_name == 'select'
82
+ selected_options = find_xpath('.//option[@selected]')
82
83
  if multiple?
83
- native.xpath(".//option[@selected='selected']").map { |option| option[:value] || option.content }
84
+ selected_options.map(&method(:option_value))
84
85
  else
85
- option = native.xpath(".//option[@selected='selected']").first || native.xpath('.//option').first
86
- option[:value] || option.content if option
86
+ option_value(selected_options.first || find_xpath('.//option').first)
87
87
  end
88
88
  elsif tag_name == 'input' && %w[radio checkbox].include?(native[:type])
89
89
  native[:value] || 'on'
@@ -104,7 +104,7 @@ module Capybara
104
104
  return false if (tag_name == 'input') && (native[:type] == 'hidden')
105
105
 
106
106
  if check_ancestors
107
- !native.xpath("boolean(./ancestor-or-self::*[contains(@style, 'display:none') or contains(@style, 'display: none') or @hidden or name()='script' or name()='head'])")
107
+ !find_xpath("boolean(./ancestor-or-self::*[contains(@style, 'display:none') or contains(@style, 'display: none') or @hidden or name()='script' or name()='head'])")
108
108
  else
109
109
  # No need for an xpath if only checking the current element
110
110
  !(native.has_attribute?('hidden') || (native[:style] =~ /display:\s?none/) || %w[script head].include?(tag_name))
@@ -177,6 +177,13 @@ module Capybara
177
177
  def session_options
178
178
  Capybara.session_options
179
179
  end
180
+
181
+ private
182
+
183
+ def option_value(option)
184
+ return nil if option.nil?
185
+ option[:value] || option.content
186
+ end
180
187
  end
181
188
  end
182
189
  end
@@ -23,9 +23,9 @@ module Capybara
23
23
 
24
24
  def self.wait(options, default = Capybara.default_max_wait_time)
25
25
  # if no value or nil for the :wait option is passed it should default to the default
26
- w = options.fetch(:wait, nil)
27
- w = default if w.nil?
28
- w || 0
26
+ wait = options.fetch(:wait, nil)
27
+ wait = default if wait.nil?
28
+ wait || 0
29
29
  end
30
30
 
31
31
  ##
@@ -69,7 +69,7 @@ module Capybara
69
69
  private
70
70
 
71
71
  def count_specified?
72
- COUNT_KEYS.any? { |k| options.key? k }
72
+ COUNT_KEYS.any? { |key| options.key? key }
73
73
  end
74
74
 
75
75
  def count_message