capybara 2.10.1 → 2.11.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (54) hide show
  1. checksums.yaml +4 -4
  2. data/History.md +22 -0
  3. data/README.md +48 -29
  4. data/lib/capybara/node/actions.rb +39 -48
  5. data/lib/capybara/node/document.rb +4 -0
  6. data/lib/capybara/node/document_matchers.rb +13 -14
  7. data/lib/capybara/node/element.rb +21 -0
  8. data/lib/capybara/node/finders.rb +3 -3
  9. data/lib/capybara/node/matchers.rb +38 -31
  10. data/lib/capybara/node/simple.rb +3 -0
  11. data/lib/capybara/queries/selector_query.rb +4 -2
  12. data/lib/capybara/rack_test/node.rb +1 -1
  13. data/lib/capybara/result.rb +3 -1
  14. data/lib/capybara/rspec/matchers.rb +53 -95
  15. data/lib/capybara/selector/selector.rb +10 -5
  16. data/lib/capybara/selector.rb +7 -7
  17. data/lib/capybara/selenium/driver.rb +34 -7
  18. data/lib/capybara/selenium/node.rb +5 -1
  19. data/lib/capybara/session/matchers.rb +12 -14
  20. data/lib/capybara/session.rb +30 -29
  21. data/lib/capybara/spec/public/test.js +4 -0
  22. data/lib/capybara/spec/session/accept_alert_spec.rb +1 -1
  23. data/lib/capybara/spec/session/attach_file_spec.rb +1 -1
  24. data/lib/capybara/spec/session/check_spec.rb +8 -0
  25. data/lib/capybara/spec/session/choose_spec.rb +5 -0
  26. data/lib/capybara/spec/session/click_link_or_button_spec.rb +5 -0
  27. data/lib/capybara/spec/session/click_link_spec.rb +5 -0
  28. data/lib/capybara/spec/session/element/assert_match_selector.rb +5 -0
  29. data/lib/capybara/spec/session/element/match_css_spec.rb +6 -0
  30. data/lib/capybara/spec/session/element/matches_selector_spec.rb +2 -0
  31. data/lib/capybara/spec/session/fill_in_spec.rb +5 -0
  32. data/lib/capybara/spec/session/find_spec.rb +1 -1
  33. data/lib/capybara/spec/session/first_spec.rb +10 -0
  34. data/lib/capybara/spec/session/has_selector_spec.rb +11 -0
  35. data/lib/capybara/spec/session/node_spec.rb +12 -1
  36. data/lib/capybara/spec/session/text_spec.rb +3 -4
  37. data/lib/capybara/spec/test_app.rb +10 -1
  38. data/lib/capybara/spec/views/with_js.erb +3 -1
  39. data/lib/capybara/version.rb +1 -1
  40. data/lib/capybara/window.rb +18 -2
  41. data/lib/capybara.rb +8 -9
  42. data/spec/basic_node_spec.rb +1 -1
  43. data/spec/capybara_spec.rb +4 -1
  44. data/spec/fixtures/selenium_driver_rspec_failure.rb +4 -1
  45. data/spec/fixtures/selenium_driver_rspec_success.rb +4 -1
  46. data/spec/result_spec.rb +28 -2
  47. data/spec/rspec/{matchers_spec.rb → shared_spec_matchers.rb} +4 -3
  48. data/spec/selector_spec.rb +1 -1
  49. data/spec/selenium_spec_chrome.rb +36 -5
  50. data/spec/selenium_spec_firefox.rb +67 -0
  51. data/spec/selenium_spec_marionette.rb +114 -0
  52. data/spec/shared_selenium_session.rb +6 -4
  53. metadata +15 -8
  54. data/spec/selenium_firefox_spec.rb +0 -44
@@ -90,14 +90,11 @@ module Capybara
90
90
  # @raise [Capybara::ExpectationNotMet] If the selector does not exist
91
91
  #
92
92
  def assert_selector(*args, &optional_filter_block)
93
- query = Capybara::Queries::SelectorQuery.new(*args, &optional_filter_block)
94
- synchronize(query.wait) do
95
- result = query.resolve_for(self)
93
+ _verify_selector_result(args, optional_filter_block) do |result, query|
96
94
  unless result.matches_count? && ((!result.empty?) || query.expects_none?)
97
95
  raise Capybara::ExpectationNotMet, result.failure_message
98
96
  end
99
97
  end
100
- return true
101
98
  end
102
99
 
103
100
  ##
@@ -117,14 +114,11 @@ module Capybara
117
114
  # @raise [Capybara::ExpectationNotMet] If the selector exists
118
115
  #
119
116
  def assert_no_selector(*args, &optional_filter_block)
120
- query = Capybara::Queries::SelectorQuery.new(*args, &optional_filter_block)
121
- synchronize(query.wait) do
122
- result = query.resolve_for(self)
117
+ _verify_selector_result(args, optional_filter_block) do |result, query|
123
118
  if result.matches_count? && ((!result.empty?) || query.expects_none?)
124
119
  raise Capybara::ExpectationNotMet, result.negative_failure_message
125
120
  end
126
121
  end
127
- return true
128
122
  end
129
123
  alias_method :refute_selector, :assert_no_selector
130
124
 
@@ -333,7 +327,7 @@ module Capybara
333
327
  #
334
328
  def has_no_checked_field?(locator=nil, options={}, &optional_filter_block)
335
329
  locator, options = nil, locator if locator.is_a? Hash
336
- has_no_selector?(:field, locator, options.merge(checked: true))
330
+ has_no_selector?(:field, locator, options.merge(checked: true), &optional_filter_block)
337
331
  end
338
332
 
339
333
  ##
@@ -455,25 +449,15 @@ module Capybara
455
449
  # @raise [Capybara::ExpectationNotMet] If the selector does not match
456
450
  #
457
451
  def assert_matches_selector(*args, &optional_filter_block)
458
- query = Capybara::Queries::MatchQuery.new(*args, &optional_filter_block)
459
- synchronize(query.wait) do
460
- result = query.resolve_for(self.query_scope)
461
- unless result.include? self
462
- raise Capybara::ExpectationNotMet, "Item does not match the provided selector"
463
- end
452
+ _verify_match_result(args, optional_filter_block) do |result|
453
+ raise Capybara::ExpectationNotMet, "Item does not match the provided selector" unless result.include? self
464
454
  end
465
- return true
466
455
  end
467
456
 
468
457
  def assert_not_matches_selector(*args, &optional_filter_block)
469
- query = Capybara::Queries::MatchQuery.new(*args, &optional_filter_block)
470
- synchronize(query.wait) do
471
- result = query.resolve_for(self.query_scope)
472
- if result.include? self
473
- raise Capybara::ExpectationNotMet, 'Item matched the provided selector'
474
- end
458
+ _verify_match_result(args, optional_filter_block) do |result|
459
+ raise Capybara::ExpectationNotMet, 'Item matched the provided selector' if result.include? self
475
460
  end
476
- return true
477
461
  end
478
462
  alias_method :refute_matches_selector, :assert_not_matches_selector
479
463
 
@@ -573,14 +557,11 @@ module Capybara
573
557
  # @return [true]
574
558
  #
575
559
  def assert_text(*args)
576
- query = Capybara::Queries::TextQuery.new(*args)
577
- synchronize(query.wait) do
578
- count = query.resolve_for(self)
560
+ _verify_text(args) do |count, query|
579
561
  unless query.matches_count?(count) && ((count > 0) || query.expects_none?)
580
562
  raise Capybara::ExpectationNotMet, query.failure_message
581
563
  end
582
564
  end
583
- return true
584
565
  end
585
566
 
586
567
  ##
@@ -592,14 +573,11 @@ module Capybara
592
573
  # @return [true]
593
574
  #
594
575
  def assert_no_text(*args)
595
- query = Capybara::Queries::TextQuery.new(*args)
596
- synchronize(query.wait) do
597
- count = query.resolve_for(self)
576
+ _verify_text(args) do |count, query|
598
577
  if query.matches_count?(count) && ((count > 0) || query.expects_none?)
599
578
  raise Capybara::ExpectationNotMet, query.negative_failure_message
600
579
  end
601
580
  end
602
- return true
603
581
  end
604
582
 
605
583
  ##
@@ -645,6 +623,35 @@ module Capybara
645
623
  self.eql?(other) || (other.respond_to?(:base) && base == other.base)
646
624
  end
647
625
 
626
+ private
627
+
628
+ def _verify_selector_result(query_args, optional_filter_block, &result_block)
629
+ query = Capybara::Queries::SelectorQuery.new(*query_args, &optional_filter_block)
630
+ synchronize(query.wait) do
631
+ result = query.resolve_for(self)
632
+ result_block.call(result, query)
633
+ end
634
+ return true
635
+ end
636
+
637
+ def _verify_match_result(query_args, optional_filter_block, &result_block)
638
+ query = Capybara::Queries::MatchQuery.new(*query_args, &optional_filter_block)
639
+ synchronize(query.wait) do
640
+ result = query.resolve_for(self.query_scope)
641
+ result_block.call(result)
642
+ end
643
+ return true
644
+ end
645
+
646
+ def _verify_text(query_args)
647
+ query = Capybara::Queries::TextQuery.new(*query_args)
648
+ synchronize(query.wait) do
649
+ count = query.resolve_for(self)
650
+ yield(count, query)
651
+ end
652
+ return true
653
+ end
654
+
648
655
  end
649
656
  end
650
657
  end
@@ -148,6 +148,9 @@ module Capybara
148
148
  # no op
149
149
  end
150
150
 
151
+ ##
152
+ #
153
+ # @return [String] The title of the document
151
154
  def title
152
155
  if native.respond_to? :title
153
156
  native.title
@@ -53,7 +53,9 @@ module Capybara
53
53
  def matches_filters?(node)
54
54
  if options[:text]
55
55
  regexp = options[:text].is_a?(Regexp) ? options[:text] : Regexp.escape(options[:text].to_s)
56
- return false if not node.text(visible).match(regexp)
56
+ text_visible = visible
57
+ text_visible = :all if text_visible == :hidden
58
+ return false if not node.text(text_visible).match(regexp)
57
59
  end
58
60
 
59
61
  case visible
@@ -71,7 +73,7 @@ module Capybara
71
73
  end
72
74
  end
73
75
 
74
- res &&= @filter_block.call(node) unless @filter_block.nil?
76
+ res &&= Capybara.using_wait_time(0){ @filter_block.call(node)} unless @filter_block.nil?
75
77
  res
76
78
  end
77
79
 
@@ -152,7 +152,7 @@ private
152
152
  end
153
153
  end
154
154
 
155
- def set_radio(value)
155
+ def set_radio(_value)
156
156
  other_radios_xpath = XPath.generate { |x| x.anywhere(:input)[x.attr(:name).equals(self[:name])] }.to_s
157
157
  driver.dom.xpath(other_radios_xpath).each { |node| node.remove_attribute("checked") }
158
158
  native['checked'] = 'checked'
@@ -40,6 +40,8 @@ module Capybara
40
40
  alias :index :find_index
41
41
 
42
42
  def each(&block)
43
+ return enum_for(:each) unless block_given?
44
+
43
45
  @result_cache.each(&block)
44
46
  loop do
45
47
  next_result = @results_enum.next
@@ -137,7 +139,7 @@ module Capybara
137
139
 
138
140
  def lazy_select_elements(&block)
139
141
  if @elements.respond_to? :lazy #Ruby 2.0+
140
- @elements.lazy.select &block
142
+ @elements.lazy.select(&block)
141
143
  else
142
144
  Enumerator.new do |yielder|
143
145
  @elements.each do |val|
@@ -4,6 +4,8 @@ module Capybara
4
4
  class Matcher
5
5
  include ::RSpec::Matchers::Composable if defined?(::RSpec::Expectations::Version) && (Gem::Version.new(RSpec::Expectations::Version::STRING) >= Gem::Version.new('3.0'))
6
6
 
7
+ attr_reader :failure_message, :failure_message_when_negated
8
+
7
9
  def wrap(actual)
8
10
  if actual.respond_to?("has_selector?")
9
11
  actual
@@ -11,10 +13,29 @@ module Capybara
11
13
  Capybara.string(actual.to_s)
12
14
  end
13
15
  end
16
+
17
+ # RSpec 2 compatibility:
18
+ def failure_message_for_should; failure_message end
19
+ def failure_message_for_should_not; failure_message_when_negated end
20
+
21
+ private
22
+
23
+ def wrap_matches?(actual)
24
+ yield(wrap(actual))
25
+ rescue Capybara::ExpectationNotMet => e
26
+ @failure_message = e.message
27
+ return false
28
+ end
29
+
30
+ def wrap_does_not_match?(actual)
31
+ yield(wrap(actual))
32
+ rescue Capybara::ExpectationNotMet => e
33
+ @failure_message_when_negated = e.message
34
+ return false
35
+ end
14
36
  end
15
37
 
16
38
  class HaveSelector < Matcher
17
- attr_reader :failure_message, :failure_message_when_negated
18
39
 
19
40
  def initialize(*args, &filter_block)
20
41
  @args = args
@@ -22,17 +43,11 @@ module Capybara
22
43
  end
23
44
 
24
45
  def matches?(actual)
25
- wrap(actual).assert_selector(*@args)
26
- rescue Capybara::ExpectationNotMet => e
27
- @failure_message = e.message
28
- return false
46
+ wrap_matches?(actual){ |el| el.assert_selector(*@args, &@filter_block) }
29
47
  end
30
48
 
31
49
  def does_not_match?(actual)
32
- wrap(actual).assert_no_selector(*@args)
33
- rescue Capybara::ExpectationNotMet => e
34
- @failure_message_when_negated = e.message
35
- return false
50
+ wrap_does_not_match?(actual){ |el| el.assert_no_selector(*@args, &@filter_block) }
36
51
  end
37
52
 
38
53
  def description
@@ -42,17 +57,29 @@ module Capybara
42
57
  def query
43
58
  @query ||= Capybara::Queries::SelectorQuery.new(*@args, &@filter_block)
44
59
  end
60
+ end
45
61
 
46
- # RSpec 2 compatibility:
47
- alias_method :failure_message_for_should, :failure_message
48
- alias_method :failure_message_for_should_not, :failure_message_when_negated
62
+ class MatchSelector < HaveSelector
63
+ def matches?(actual)
64
+ wrap_matches?(actual) { |el| el.assert_matches_selector(*@args, &@filter_block) }
65
+ end
66
+
67
+ def does_not_match?(actual)
68
+ wrap_does_not_match?(actual) { |el| el.assert_not_matches_selector(*@args, &@filter_block) }
69
+ end
70
+
71
+ def description
72
+ "match #{query.description}"
73
+ end
74
+
75
+ def query
76
+ @query ||= Capybara::Queries::MatchQuery.new(*@args)
77
+ end
49
78
  end
50
79
 
51
80
  class HaveText < Matcher
52
81
  attr_reader :type, :content, :options
53
82
 
54
- attr_reader :failure_message, :failure_message_when_negated
55
-
56
83
  def initialize(*args)
57
84
  @args = args.dup
58
85
 
@@ -63,17 +90,11 @@ module Capybara
63
90
  end
64
91
 
65
92
  def matches?(actual)
66
- wrap(actual).assert_text(*@args)
67
- rescue Capybara::ExpectationNotMet => e
68
- @failure_message = e.message
69
- return false
93
+ wrap_matches?(actual) { |el| el.assert_text(*@args) }
70
94
  end
71
95
 
72
96
  def does_not_match?(actual)
73
- wrap(actual).assert_no_text(*@args)
74
- rescue Capybara::ExpectationNotMet => e
75
- @failure_message_when_negated = e.message
76
- return false
97
+ wrap_does_not_match?(actual) { |el| el.assert_no_text(*@args) }
77
98
  end
78
99
 
79
100
  def description
@@ -84,17 +105,11 @@ module Capybara
84
105
  content = Capybara::Helpers.normalize_whitespace(content) unless content.is_a? Regexp
85
106
  content.inspect
86
107
  end
87
-
88
- # RSpec 2 compatibility:
89
- alias_method :failure_message_for_should, :failure_message
90
- alias_method :failure_message_for_should_not, :failure_message_when_negated
91
108
  end
92
109
 
93
110
  class HaveTitle < Matcher
94
111
  attr_reader :title
95
112
 
96
- attr_reader :failure_message, :failure_message_when_negated
97
-
98
113
  def initialize(*args)
99
114
  @args = args
100
115
 
@@ -103,33 +118,21 @@ module Capybara
103
118
  end
104
119
 
105
120
  def matches?(actual)
106
- wrap(actual).assert_title(*@args)
107
- rescue Capybara::ExpectationNotMet => e
108
- @failure_message = e.message
109
- return false
121
+ wrap_matches?(actual) { |el| el.assert_title(*@args) }
110
122
  end
111
123
 
112
124
  def does_not_match?(actual)
113
- wrap(actual).assert_no_title(*@args)
114
- rescue Capybara::ExpectationNotMet => e
115
- @failure_message_when_negated = e.message
116
- return false
125
+ wrap_does_not_match?(actual) { |el| el.assert_no_title(*@args) }
117
126
  end
118
127
 
119
128
  def description
120
129
  "have title #{title.inspect}"
121
130
  end
122
-
123
- # RSpec 2 compatibility:
124
- alias_method :failure_message_for_should, :failure_message
125
- alias_method :failure_message_for_should_not, :failure_message_when_negated
126
131
  end
127
132
 
128
133
  class HaveCurrentPath < Matcher
129
134
  attr_reader :current_path
130
135
 
131
- attr_reader :failure_message, :failure_message_when_negated
132
-
133
136
  def initialize(*args)
134
137
  @args = args
135
138
 
@@ -138,26 +141,16 @@ module Capybara
138
141
  end
139
142
 
140
143
  def matches?(actual)
141
- wrap(actual).assert_current_path(*@args)
142
- rescue Capybara::ExpectationNotMet => e
143
- @failure_message = e.message
144
- return false
144
+ wrap_matches?(actual) { |el| el.assert_current_path(*@args) }
145
145
  end
146
146
 
147
147
  def does_not_match?(actual)
148
- wrap(actual).assert_no_current_path(*@args)
149
- rescue Capybara::ExpectationNotMet => e
150
- @failure_message_when_negated = e.message
151
- return false
148
+ wrap_does_not_match?(actual) { |el| el.assert_no_current_path(*@args) }
152
149
  end
153
150
 
154
151
  def description
155
152
  "have current path #{current_path.inspect}"
156
153
  end
157
-
158
- # RSpec 2 compatibility:
159
- alias_method :failure_message_for_should, :failure_message
160
- alias_method :failure_message_for_should_not, :failure_message_when_negated
161
154
  end
162
155
 
163
156
  class BecomeClosed
@@ -188,46 +181,12 @@ module Capybara
188
181
  alias_method :failure_message_for_should_not, :failure_message_when_negated
189
182
  end
190
183
 
191
- class MatchSelector < Matcher
192
- attr_reader :failure_message, :failure_message_when_negated
193
-
194
- def initialize(*args)
195
- @args = args
196
- end
197
-
198
- def matches?(actual)
199
- actual.assert_matches_selector(*@args)
200
- rescue Capybara::ExpectationNotMet => e
201
- @failure_message = e.message
202
- return false
203
- end
204
-
205
- def does_not_match?(actual)
206
- actual.assert_not_matches_selector(*@args)
207
- rescue Capybara::ExpectationNotMet => e
208
- @failure_message_when_negated = e.message
209
- return false
210
- end
211
-
212
- def description
213
- "match #{query.description}"
214
- end
215
-
216
- def query
217
- @query ||= Capybara::Queries::MatchQuery.new(*@args)
218
- end
219
-
220
- # RSpec 2 compatibility:
221
- alias_method :failure_message_for_should, :failure_message
222
- alias_method :failure_message_for_should_not, :failure_message_when_negated
223
- end
224
-
225
184
  def have_selector(*args, &optional_filter_block)
226
185
  HaveSelector.new(*args, &optional_filter_block)
227
186
  end
228
187
 
229
- def match_selector(*args)
230
- MatchSelector.new(*args)
188
+ def match_selector(*args, &optional_filter_block)
189
+ MatchSelector.new(*args, &optional_filter_block)
231
190
  end
232
191
  # defined_negated_matcher was added in RSpec 3.1 - it's syntactic sugar only since a user can do
233
192
  # expect(page).not_to match_selector, so not sure we really need to support not_match_selector for prior to RSpec 3.1
@@ -238,16 +197,16 @@ module Capybara
238
197
  HaveSelector.new(:xpath, xpath, options, &optional_filter_block)
239
198
  end
240
199
 
241
- def match_xpath(xpath, options={})
242
- MatchSelector.new(:xpath, xpath, options)
200
+ def match_xpath(xpath, options={}, &optional_filter_block)
201
+ MatchSelector.new(:xpath, xpath, options, &optional_filter_block)
243
202
  end
244
203
 
245
204
  def have_css(css, options={}, &optional_filter_block)
246
205
  HaveSelector.new(:css, css, options, &optional_filter_block)
247
206
  end
248
207
 
249
- def match_css(css, options={})
250
- MatchSelector.new(:css, css, options)
208
+ def match_css(css, options={}, &optional_filter_block)
209
+ MatchSelector.new(:css, css, options, &optional_filter_block)
251
210
  end
252
211
 
253
212
  def have_text(*args)
@@ -307,6 +266,5 @@ module Capybara
307
266
  def become_closed(options = {})
308
267
  BecomeClosed.new(options)
309
268
  end
310
-
311
269
  end
312
270
  end
@@ -6,6 +6,7 @@ require 'xpath'
6
6
  #Patch XPath to allow a nil condition in where
7
7
  module XPath
8
8
  class Renderer
9
+ undef :where if method_defined?(:where)
9
10
  def where(on, condition)
10
11
  condition = condition.to_s
11
12
  if !condition.empty?
@@ -177,14 +178,14 @@ module Capybara
177
178
 
178
179
  def filter_set(name, filters_to_use = nil)
179
180
  f_set = FilterSet.all[name]
180
- f_set.filters.each do | name, filter |
181
- custom_filters[name] = filter if filters_to_use.nil? || filters_to_use.include?(name)
181
+ f_set.filters.each do |n, filter|
182
+ custom_filters[n] = filter if filters_to_use.nil? || filters_to_use.include?(n)
182
183
  end
183
- f_set.descriptions.each { |desc| @filter_set.describe &desc }
184
+ f_set.descriptions.each { |desc| @filter_set.describe(&desc) }
184
185
  end
185
186
 
186
187
  def describe &block
187
- @filter_set.describe &block
188
+ @filter_set.describe(&block)
188
189
  end
189
190
 
190
191
  ##
@@ -228,8 +229,12 @@ module Capybara
228
229
  locate_xpath
229
230
  end
230
231
 
232
+ def describe_all_expression_filters(opts={})
233
+ expression_filters.map { |ef| " with #{ef} #{opts[ef]}" if opts.has_key?(ef) }.join
234
+ end
235
+
231
236
  def find_by_attr(attribute, value)
232
- finder_name = "find_by_#{attribute.to_s}_attr"
237
+ finder_name = "find_by_#{attribute}_attr"
233
238
  if respond_to?(finder_name, true)
234
239
  send(finder_name, value)
235
240
  else
@@ -87,7 +87,7 @@ Capybara.add_selector(:field) do
87
87
  end
88
88
  describe do |options|
89
89
  desc = String.new
90
- (expression_filters - [:type]).each { |ef| desc << " with #{ef.to_s} #{options[ef]}" if options.has_key?(ef) }
90
+ (expression_filters - [:type]).each { |ef| desc << " with #{ef} #{options[ef]}" if options.has_key?(ef) }
91
91
  desc << " of type #{options[:type].inspect}" if options[:type]
92
92
  desc << " with value #{options[:with].to_s.inspect}" if options.has_key?(:with)
93
93
  desc
@@ -196,7 +196,7 @@ Capybara.add_selector(:button) do
196
196
  describe do |options|
197
197
  desc = String.new
198
198
  desc << " that is disabled" if options[:disabled] == true
199
- expression_filters.each { |ef| desc << " with #{ef.to_s} #{options[ef]}" if options.has_key?(ef) }
199
+ desc << describe_all_expression_filters(options)
200
200
  desc
201
201
  end
202
202
  end
@@ -244,7 +244,7 @@ Capybara.add_selector(:fillable_field) do
244
244
 
245
245
  describe do |options|
246
246
  desc = String.new
247
- expression_filters.each { |ef| desc << " with #{ef.to_s} #{options[ef]}" if options.has_key?(ef) }
247
+ desc << describe_all_expression_filters(options)
248
248
  desc << " with value #{options[:with].to_s.inspect}" if options.has_key?(:with)
249
249
  desc
250
250
  end
@@ -277,7 +277,7 @@ Capybara.add_selector(:radio_button) do
277
277
  describe do |options|
278
278
  desc = String.new
279
279
  desc << " with value #{options[:option].inspect}" if options[:option]
280
- expression_filters.each { |ef| desc << " with #{ef.to_s} #{options[ef]}" if options.has_key?(ef) }
280
+ desc << describe_all_expression_filters(options)
281
281
  desc
282
282
  end
283
283
  end
@@ -308,7 +308,7 @@ Capybara.add_selector(:checkbox) do
308
308
  describe do |options|
309
309
  desc = String.new
310
310
  desc << " with value #{options[:option].inspect}" if options[:option]
311
- expression_filters.each { |ef| desc << " with #{ef.to_s} #{options[ef]}" if options.has_key?(ef) }
311
+ desc << describe_all_expression_filters(options)
312
312
  desc
313
313
  end
314
314
  end
@@ -364,7 +364,7 @@ Capybara.add_selector(:select) do
364
364
  desc << " with options #{options[:options].inspect}" if options[:options]
365
365
  desc << " with at least options #{options[:with_options].inspect}" if options[:with_options]
366
366
  desc << " with #{options[:selected].inspect} selected" if options[:selected]
367
- expression_filters.each { |ef| desc << " with #{ef.to_s} #{options[ef]}" if options.has_key?(ef) }
367
+ desc << describe_all_expression_filters(options)
368
368
  desc
369
369
  end
370
370
  end
@@ -417,7 +417,7 @@ Capybara.add_selector(:file_field) do
417
417
 
418
418
  describe do |options|
419
419
  desc = String.new
420
- expression_filters.each { |ef| desc << " with #{ef.to_s} #{options[ef]}" if options.has_key?(ef) }
420
+ desc << describe_all_expression_filters(options)
421
421
  desc
422
422
  end
423
423
  end
@@ -4,9 +4,11 @@ require "uri"
4
4
  class Capybara::Selenium::Driver < Capybara::Driver::Base
5
5
 
6
6
  DEFAULT_OPTIONS = {
7
- :browser => :firefox
7
+ :browser => :firefox,
8
+ clear_local_storage: false,
9
+ clear_session_storage: false
8
10
  }
9
- SPECIAL_OPTIONS = [:browser]
11
+ SPECIAL_OPTIONS = [:browser, :clear_local_storage, :clear_session_storage]
10
12
 
11
13
  attr_reader :app, :options
12
14
 
@@ -17,7 +19,7 @@ class Capybara::Selenium::Driver < Capybara::Driver::Base
17
19
  options[:desired_capabilities].merge!({ unexpectedAlertBehaviour: "ignore" })
18
20
  end
19
21
 
20
- @browser = Selenium::WebDriver.for(options[:browser], options.reject { |key,val| SPECIAL_OPTIONS.include?(key) })
22
+ @browser = Selenium::WebDriver.for(options[:browser], options.reject { |key,_val| SPECIAL_OPTIONS.include?(key) })
21
23
 
22
24
  main = Process.pid
23
25
  at_exit do
@@ -92,7 +94,7 @@ class Capybara::Selenium::Driver < Capybara::Driver::Base
92
94
  browser.execute_script "return #{script}"
93
95
  end
94
96
 
95
- def save_screenshot(path, options={})
97
+ def save_screenshot(path, _options={})
96
98
  browser.save_screenshot(path)
97
99
  end
98
100
 
@@ -107,6 +109,20 @@ class Capybara::Selenium::Driver < Capybara::Driver::Base
107
109
  # can trigger an endless series of unload modals
108
110
  begin
109
111
  @browser.manage.delete_all_cookies
112
+ if options[:clear_session_storage]
113
+ if @browser.respond_to? :session_storage
114
+ @browser.session_storage.clear
115
+ else
116
+ warn "sessionStorage clear requested but is not available for this driver"
117
+ end
118
+ end
119
+ if options[:clear_local_storage]
120
+ if @browser.respond_to? :local_storage
121
+ @browser.local_storage.clear
122
+ else
123
+ warn "localStorage clear requested but is not available for this driver"
124
+ end
125
+ end
110
126
  rescue Selenium::WebDriver::Error::UnhandledError
111
127
  # delete_all_cookies fails when we've previously gone
112
128
  # to about:blank, so we rescue this error and do nothing
@@ -220,8 +236,7 @@ class Capybara::Selenium::Driver < Capybara::Driver::Base
220
236
  browser.switch_to.window(handle) { yield }
221
237
  end
222
238
 
223
- def accept_modal(type, options={}, &blk)
224
- options = options.dup
239
+ def accept_modal(_type, options={})
225
240
  yield if block_given?
226
241
  modal = find_modal(options)
227
242
  modal.send_keys options[:with] if options[:with]
@@ -230,7 +245,7 @@ class Capybara::Selenium::Driver < Capybara::Driver::Base
230
245
  message
231
246
  end
232
247
 
233
- def dismiss_modal(type, options={}, &blk)
248
+ def dismiss_modal(_type, options={})
234
249
  yield if block_given?
235
250
  modal = find_modal(options)
236
251
  message = modal.text
@@ -242,6 +257,11 @@ class Capybara::Selenium::Driver < Capybara::Driver::Base
242
257
  @browser.quit if @browser
243
258
  rescue Errno::ECONNREFUSED
244
259
  # Browser must have already gone
260
+ rescue Selenium::WebDriver::Error::UnknownError => e
261
+ unless silenced_unknown_error_message?(e.message) # Most likely already gone
262
+ # probably already gone but not sure - so warn
263
+ warn "Ignoring Selenium UnknownError during driver quit: #{e.message}"
264
+ end
245
265
  ensure
246
266
  @browser = nil
247
267
  end
@@ -293,4 +313,11 @@ class Capybara::Selenium::Driver < Capybara::Driver::Base
293
313
  end
294
314
  end
295
315
 
316
+ def silenced_unknown_error_message?(msg)
317
+ silenced_unknown_error_messages.any? { |r| msg =~ r }
318
+ end
319
+
320
+ def silenced_unknown_error_messages
321
+ [ /Error communicating with the remote browser/ ]
322
+ end
296
323
  end
@@ -47,7 +47,11 @@ class Capybara::Selenium::Node < Capybara::Driver::Node
47
47
  click if value ^ native.attribute('checked').to_s.eql?("true")
48
48
  elsif tag_name == 'input' and type == 'file'
49
49
  path_names = value.to_s.empty? ? [] : value
50
- native.send_keys(*path_names)
50
+ if driver.options[:browser].to_s == "chrome"
51
+ native.send_keys(Array(path_names).join("\n"))
52
+ else
53
+ native.send_keys(*path_names)
54
+ end
51
55
  elsif tag_name == 'textarea' or tag_name == 'input'
52
56
  if readonly?
53
57
  warn "Attempt to set readonly element with value: #{value} \n *This will raise an exception in a future version of Capybara"