capybara 2.10.2 → 2.11.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (53) hide show
  1. checksums.yaml +4 -4
  2. data/History.md +15 -0
  3. data/README.md +48 -29
  4. data/lib/capybara.rb +8 -9
  5. data/lib/capybara/node/actions.rb +39 -48
  6. data/lib/capybara/node/document.rb +4 -0
  7. data/lib/capybara/node/document_matchers.rb +13 -14
  8. data/lib/capybara/node/element.rb +21 -0
  9. data/lib/capybara/node/finders.rb +3 -3
  10. data/lib/capybara/node/matchers.rb +38 -31
  11. data/lib/capybara/node/simple.rb +3 -0
  12. data/lib/capybara/queries/selector_query.rb +4 -2
  13. data/lib/capybara/rack_test/node.rb +1 -1
  14. data/lib/capybara/result.rb +3 -1
  15. data/lib/capybara/rspec/matchers.rb +53 -95
  16. data/lib/capybara/selector.rb +7 -7
  17. data/lib/capybara/selector/selector.rb +10 -5
  18. data/lib/capybara/selenium/driver.rb +34 -7
  19. data/lib/capybara/selenium/node.rb +5 -1
  20. data/lib/capybara/session.rb +22 -27
  21. data/lib/capybara/session/matchers.rb +12 -14
  22. data/lib/capybara/spec/public/test.js +4 -0
  23. data/lib/capybara/spec/session/accept_alert_spec.rb +1 -1
  24. data/lib/capybara/spec/session/attach_file_spec.rb +1 -1
  25. data/lib/capybara/spec/session/check_spec.rb +8 -0
  26. data/lib/capybara/spec/session/choose_spec.rb +5 -0
  27. data/lib/capybara/spec/session/click_link_or_button_spec.rb +5 -0
  28. data/lib/capybara/spec/session/click_link_spec.rb +5 -0
  29. data/lib/capybara/spec/session/element/assert_match_selector.rb +5 -0
  30. data/lib/capybara/spec/session/element/match_css_spec.rb +6 -0
  31. data/lib/capybara/spec/session/element/matches_selector_spec.rb +2 -0
  32. data/lib/capybara/spec/session/fill_in_spec.rb +5 -0
  33. data/lib/capybara/spec/session/find_spec.rb +1 -1
  34. data/lib/capybara/spec/session/first_spec.rb +10 -0
  35. data/lib/capybara/spec/session/has_selector_spec.rb +11 -0
  36. data/lib/capybara/spec/session/node_spec.rb +3 -3
  37. data/lib/capybara/spec/session/text_spec.rb +3 -4
  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/spec/basic_node_spec.rb +1 -1
  42. data/spec/capybara_spec.rb +4 -1
  43. data/spec/fixtures/selenium_driver_rspec_failure.rb +4 -1
  44. data/spec/fixtures/selenium_driver_rspec_success.rb +4 -1
  45. data/spec/result_spec.rb +28 -2
  46. data/spec/rspec/{matchers_spec.rb → shared_spec_matchers.rb} +4 -3
  47. data/spec/selector_spec.rb +1 -1
  48. data/spec/selenium_spec_chrome.rb +36 -5
  49. data/spec/selenium_spec_firefox.rb +67 -0
  50. data/spec/selenium_spec_marionette.rb +114 -0
  51. data/spec/shared_selenium_session.rb +6 -4
  52. metadata +13 -6
  53. data/spec/selenium_firefox_spec.rb +0 -44
@@ -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
@@ -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
@@ -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"
@@ -269,7 +269,7 @@ module Capybara
269
269
  # block, any command to Capybara will be handled as though it were scoped
270
270
  # to the given element.
271
271
  #
272
- # within(:xpath, '//div[@id="delivery-address"]') do
272
+ # within(:xpath, './/div[@id="delivery-address"]') do
273
273
  # fill_in('Street', with: '12 Main Street')
274
274
  # end
275
275
  #
@@ -593,11 +593,7 @@ module Capybara
593
593
  # @raise [Capybara::ModalNotFound] if modal dialog hasn't been found
594
594
  #
595
595
  def accept_alert(text_or_options=nil, options={}, &blk)
596
- text_or_options, options = nil, text_or_options if text_or_options.is_a?(Hash)
597
- options[:text] ||= text_or_options unless text_or_options.nil?
598
- options[:wait] ||= Capybara.default_max_wait_time
599
-
600
- driver.accept_modal(:alert, options, &blk)
596
+ accept_modal(:alert, text_or_options, options, &blk)
601
597
  end
602
598
 
603
599
  ##
@@ -607,11 +603,7 @@ module Capybara
607
603
  # @macro modal_params
608
604
  #
609
605
  def accept_confirm(text_or_options=nil, options={}, &blk)
610
- text_or_options, options = nil, text_or_options if text_or_options.is_a?(Hash)
611
- options[:text] ||= text_or_options unless text_or_options.nil?
612
- options[:wait] ||= Capybara.default_max_wait_time
613
-
614
- driver.accept_modal(:confirm, options, &blk)
606
+ accept_modal(:confirm, text_or_options, options, &blk)
615
607
  end
616
608
 
617
609
  ##
@@ -621,11 +613,7 @@ module Capybara
621
613
  # @macro modal_params
622
614
  #
623
615
  def dismiss_confirm(text_or_options=nil, options={}, &blk)
624
- text_or_options, options = nil, text_or_options if text_or_options.is_a?(Hash)
625
- options[:text] ||= text_or_options unless text_or_options.nil?
626
- options[:wait] ||= Capybara.default_max_wait_time
627
-
628
- driver.dismiss_modal(:confirm, options, &blk)
616
+ dismiss_modal(:confirm, text_or_options, options, &blk)
629
617
  end
630
618
 
631
619
  ##
@@ -636,11 +624,7 @@ module Capybara
636
624
  # @option options [String] :with Response to provide to the prompt
637
625
  #
638
626
  def accept_prompt(text_or_options=nil, options={}, &blk)
639
- text_or_options, options = nil, text_or_options if text_or_options.is_a?(Hash)
640
- options[:text] ||= text_or_options unless text_or_options.nil?
641
- options[:wait] ||= Capybara.default_max_wait_time
642
-
643
- driver.accept_modal(:prompt, options, &blk)
627
+ accept_modal(:prompt, text_or_options, options, &blk)
644
628
  end
645
629
 
646
630
  ##
@@ -650,11 +634,7 @@ module Capybara
650
634
  # @macro modal_params
651
635
  #
652
636
  def dismiss_prompt(text_or_options=nil, options={}, &blk)
653
- text_or_options, options = nil, text_or_options if text_or_options.is_a?(Hash)
654
- options[:text] ||= text_or_options unless text_or_options.nil?
655
- options[:wait] ||= Capybara.default_max_wait_time
656
-
657
- driver.dismiss_modal(:prompt, options, &blk)
637
+ dismiss_modal(:prompt, text_or_options, options, &blk)
658
638
  end
659
639
 
660
640
  ##
@@ -757,6 +737,21 @@ module Capybara
757
737
  end
758
738
 
759
739
  private
740
+ def accept_modal(type, text_or_options, options, &blk)
741
+ driver.accept_modal(type, modal_options(text_or_options, options), &blk)
742
+ end
743
+
744
+ def dismiss_modal(type, text_or_options, options, &blk)
745
+ driver.dismiss_modal(type, modal_options(text_or_options, options), &blk)
746
+ end
747
+
748
+ def modal_options(text_or_options, options)
749
+ text_or_options, options = nil, text_or_options if text_or_options.is_a?(Hash)
750
+ options[:text] ||= text_or_options unless text_or_options.nil?
751
+ options[:wait] ||= Capybara.default_max_wait_time
752
+ options
753
+ end
754
+
760
755
 
761
756
  def open_file(path)
762
757
  begin
@@ -780,7 +775,7 @@ module Capybara
780
775
 
781
776
  def default_fn(extension)
782
777
  timestamp = Time.new.strftime("%Y%m%d%H%M%S")
783
- path = "capybara-#{timestamp}#{rand(10**10)}.#{extension}"
778
+ "capybara-#{timestamp}#{rand(10**10)}.#{extension}"
784
779
  end
785
780
 
786
781
  def scopes
@@ -17,13 +17,7 @@ module Capybara
17
17
  # @return [true]
18
18
  #
19
19
  def assert_current_path(path, options={})
20
- query = Capybara::Queries::CurrentPathQuery.new(path, options)
21
- document.synchronize(query.wait) do
22
- unless query.resolves_for?(self)
23
- raise Capybara::ExpectationNotMet, query.failure_message
24
- end
25
- end
26
- return true
20
+ _verify_current_path(path,options) { |query| raise Capybara::ExpectationNotMet, query.failure_message unless query.resolves_for?(self) }
27
21
  end
28
22
 
29
23
  ##
@@ -34,13 +28,7 @@ module Capybara
34
28
  # @return [true]
35
29
  #
36
30
  def assert_no_current_path(path, options={})
37
- query = Capybara::Queries::CurrentPathQuery.new(path, options)
38
- document.synchronize(query.wait) do
39
- if query.resolves_for?(self)
40
- raise Capybara::ExpectationNotMet, query.negative_failure_message
41
- end
42
- end
43
- return true
31
+ _verify_current_path(path,options) { |query| raise Capybara::ExpectationNotMet, query.negative_failure_message if query.resolves_for?(self) }
44
32
  end
45
33
 
46
34
  ##
@@ -66,5 +54,15 @@ module Capybara
66
54
  rescue Capybara::ExpectationNotMet
67
55
  return false
68
56
  end
57
+
58
+ private
59
+
60
+ def _verify_current_path(path, options)
61
+ query = Capybara::Queries::CurrentPathQuery.new(path, options)
62
+ document.synchronize(query.wait) do
63
+ yield(query)
64
+ end
65
+ return true
66
+ end
69
67
  end
70
68
  end
@@ -123,4 +123,8 @@ $(function() {
123
123
  input.disabled = true;
124
124
  }, 500)
125
125
  })
126
+ $('#set-storage').click(function(e){
127
+ sessionStorage.setItem('session', 'session_value');
128
+ localStorage.setItem('local', 'local value');
129
+ })
126
130
  });
@@ -26,7 +26,7 @@ Capybara::SpecHelper.spec '#accept_alert', requires: [:modals] do
26
26
  end
27
27
 
28
28
  it "should accept the alert if the text matches a regexp" do
29
- @session.accept_alert /op.{2}ed/ do
29
+ @session.accept_alert(/op.{2}ed/) do
30
30
  @session.click_link('Open alert')
31
31
  end
32
32
  expect(@session).to have_xpath("//a[@id='open-alert' and @opened='true']")
@@ -65,7 +65,7 @@ Capybara::SpecHelper.spec "#attach_file" do
65
65
  end
66
66
 
67
67
  it "should not break when using HTML5 multiple file input uploading multiple files" do
68
- pending "Selenium is buggy on this, see http://code.google.com/p/selenium/issues/detail?id=2239" if @session.respond_to?(:mode) && @session.mode.to_s =~ /^selenium/
68
+ pending "Selenium is buggy on this, see http://code.google.com/p/selenium/issues/detail?id=2239" if @session.respond_to?(:mode) && @session.mode.to_s =~ /^selenium_(firefox|marionette)/
69
69
  @session.attach_file "Multiple Documents", [@test_file_path, @another_test_file_path]
70
70
  @session.click_button('Upload Multiple')
71
71
  expect(@session.body).to include("2 | ")#number of files
@@ -169,6 +169,14 @@ Capybara::SpecHelper.spec "#check" do
169
169
  expect(extract_results(@session)['cars']).to include('tesla')
170
170
  end
171
171
 
172
+ it "should not wait the full time if label can be clicked" do
173
+ expect(@session.find(:checkbox, 'form_cars_tesla', unchecked: true, visible: :hidden)).to be
174
+ start_time = Time.now
175
+ @session.check('form_cars_tesla', allow_label_click: true, wait: 10)
176
+ end_time = Time.now
177
+ expect(end_time - start_time).to be < 10
178
+ end
179
+
172
180
  it "should check via the label if input is moved off the left edge of the page" do
173
181
  expect(@session.find(:checkbox, 'form_cars_pagani', unchecked: true, visible: :all)).to be
174
182
  @session.check('form_cars_pagani', allow_label_click: true)
@@ -86,4 +86,9 @@ Capybara::SpecHelper.spec "#choose" do
86
86
  end
87
87
  end
88
88
  end
89
+
90
+ it "should return the chosen radio button" do
91
+ el = @session.find(:radio_button, 'gender_male')
92
+ expect(@session.choose("gender_male")).to eq el
93
+ end
89
94
  end
@@ -108,6 +108,11 @@ Capybara::SpecHelper.spec '#click_link_or_button' do
108
108
  @session.click_link_or_button('Disabled button', disabled: false)
109
109
  end.to raise_error(Capybara::ElementNotFound)
110
110
  end
111
+ end
111
112
 
113
+ it "should return the element clicked" do
114
+ @session.visit('/with_html')
115
+ link = @session.find(:link, 'Blank Anchor')
116
+ expect(@session.click_link_or_button('Blank Anchor')).to eq link
112
117
  end
113
118
  end
@@ -186,4 +186,9 @@ Capybara::SpecHelper.spec '#click_link' do
186
186
  expect(@session).to have_content('Another World')
187
187
  end
188
188
  end
189
+
190
+ it "should return element clicked" do
191
+ el = @session.find(:link, 'Normal Anchor')
192
+ expect(@session.click_link('Normal Anchor')).to eq el
193
+ end
189
194
  end
@@ -28,4 +28,9 @@ Capybara::SpecHelper.spec '#assert_matches_selector' do
28
28
  it "should not accept count options" do
29
29
  expect { @element.assert_matches_selector(:css, '.number', count: 1) }.to raise_error(ArgumentError)
30
30
  end
31
+
32
+ it "should accept a filter block" do
33
+ @element.assert_matches_selector(:css, 'span') { |el| el[:class] == "number" }
34
+ @element.assert_not_matches_selector(:css,'span') { |el| el[:class] == "not_number" }
35
+ end
31
36
  end
@@ -14,4 +14,10 @@ Capybara::SpecHelper.spec '#match_css?' do
14
14
  expect(@element).not_to match_css("p a#doesnotexist")
15
15
  expect(@element).not_to match_css("p.nosuchclass")
16
16
  end
17
+
18
+ it "should accept an optional filter block" do
19
+ # This would be better done with
20
+ expect(@element).to match_css('span') { |el| el[:class] == "number" }
21
+ expect(@element).not_to match_css('span') { |el| el[:class] == "not_number" }
22
+ end
17
23
  end