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
@@ -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
@@ -1,5 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
  require 'capybara/session/matchers'
3
+ require 'addressable/uri'
3
4
 
4
5
  module Capybara
5
6
 
@@ -125,7 +126,8 @@ module Capybara
125
126
  begin
126
127
  raise CapybaraError, "Your application server raised an error - It has been raised in your test code because Capybara.raise_server_errors == true"
127
128
  rescue CapybaraError
128
- raise @server.error.class, @server.error.message, @server.error.backtrace
129
+ #needed to get the cause set correctly in JRuby -- otherwise we could just do raise @server.error
130
+ raise @server.error, @server.error.message, @server.error.backtrace
129
131
  end
130
132
  end
131
133
  ensure
@@ -167,7 +169,11 @@ module Capybara
167
169
  # @return [String] Path of the current page, without any domain information
168
170
  #
169
171
  def current_path
170
- path = URI.parse(current_url).path
172
+ # Addressable parsing is more lenient than URI
173
+ uri = Addressable::URI.parse(current_url)
174
+ # Addressable doesn't support opaque URIs - we want nil here
175
+ return nil if uri.scheme == "about"
176
+ path = uri.path
171
177
  path if path and not path.empty?
172
178
  end
173
179
 
@@ -263,7 +269,7 @@ module Capybara
263
269
  # block, any command to Capybara will be handled as though it were scoped
264
270
  # to the given element.
265
271
  #
266
- # within(:xpath, '//div[@id="delivery-address"]') do
272
+ # within(:xpath, './/div[@id="delivery-address"]') do
267
273
  # fill_in('Street', with: '12 Main Street')
268
274
  # end
269
275
  #
@@ -587,11 +593,7 @@ module Capybara
587
593
  # @raise [Capybara::ModalNotFound] if modal dialog hasn't been found
588
594
  #
589
595
  def accept_alert(text_or_options=nil, options={}, &blk)
590
- text_or_options, options = nil, text_or_options if text_or_options.is_a?(Hash)
591
- options[:text] ||= text_or_options unless text_or_options.nil?
592
- options[:wait] ||= Capybara.default_max_wait_time
593
-
594
- driver.accept_modal(:alert, options, &blk)
596
+ accept_modal(:alert, text_or_options, options, &blk)
595
597
  end
596
598
 
597
599
  ##
@@ -601,11 +603,7 @@ module Capybara
601
603
  # @macro modal_params
602
604
  #
603
605
  def accept_confirm(text_or_options=nil, options={}, &blk)
604
- text_or_options, options = nil, text_or_options if text_or_options.is_a?(Hash)
605
- options[:text] ||= text_or_options unless text_or_options.nil?
606
- options[:wait] ||= Capybara.default_max_wait_time
607
-
608
- driver.accept_modal(:confirm, options, &blk)
606
+ accept_modal(:confirm, text_or_options, options, &blk)
609
607
  end
610
608
 
611
609
  ##
@@ -615,11 +613,7 @@ module Capybara
615
613
  # @macro modal_params
616
614
  #
617
615
  def dismiss_confirm(text_or_options=nil, options={}, &blk)
618
- text_or_options, options = nil, text_or_options if text_or_options.is_a?(Hash)
619
- options[:text] ||= text_or_options unless text_or_options.nil?
620
- options[:wait] ||= Capybara.default_max_wait_time
621
-
622
- driver.dismiss_modal(:confirm, options, &blk)
616
+ dismiss_modal(:confirm, text_or_options, options, &blk)
623
617
  end
624
618
 
625
619
  ##
@@ -630,11 +624,7 @@ module Capybara
630
624
  # @option options [String] :with Response to provide to the prompt
631
625
  #
632
626
  def accept_prompt(text_or_options=nil, options={}, &blk)
633
- text_or_options, options = nil, text_or_options if text_or_options.is_a?(Hash)
634
- options[:text] ||= text_or_options unless text_or_options.nil?
635
- options[:wait] ||= Capybara.default_max_wait_time
636
-
637
- driver.accept_modal(:prompt, options, &blk)
627
+ accept_modal(:prompt, text_or_options, options, &blk)
638
628
  end
639
629
 
640
630
  ##
@@ -644,11 +634,7 @@ module Capybara
644
634
  # @macro modal_params
645
635
  #
646
636
  def dismiss_prompt(text_or_options=nil, options={}, &blk)
647
- text_or_options, options = nil, text_or_options if text_or_options.is_a?(Hash)
648
- options[:text] ||= text_or_options unless text_or_options.nil?
649
- options[:wait] ||= Capybara.default_max_wait_time
650
-
651
- driver.dismiss_modal(:prompt, options, &blk)
637
+ dismiss_modal(:prompt, text_or_options, options, &blk)
652
638
  end
653
639
 
654
640
  ##
@@ -751,6 +737,21 @@ module Capybara
751
737
  end
752
738
 
753
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
+
754
755
 
755
756
  def open_file(path)
756
757
  begin
@@ -774,7 +775,7 @@ module Capybara
774
775
 
775
776
  def default_fn(extension)
776
777
  timestamp = Time.new.strftime("%Y%m%d%H%M%S")
777
- path = "capybara-#{timestamp}#{rand(10**10)}.#{extension}"
778
+ "capybara-#{timestamp}#{rand(10**10)}.#{extension}"
778
779
  end
779
780
 
780
781
  def scopes
@@ -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
@@ -52,6 +52,8 @@ Capybara::SpecHelper.spec '#match_selector?' do
52
52
  it 'should accept a custom filter block' do
53
53
  @session.visit('/form')
54
54
  cbox = @session.find(:css, '#form_pets_dog')
55
+ expect(cbox).to match_selector(:checkbox){ |node| node[:id] == "form_pets_dog"}
56
+ expect(cbox).not_to match_selector(:checkbox){ |node| node[:id] != "form_pets_dog"}
55
57
  expect(cbox.matches_selector?(:checkbox){ |node| node[:id] == "form_pets_dog"}).to be true
56
58
  expect(cbox.matches_selector?(:checkbox){ |node| node[:id] != "form_pets_dog"}).to be false
57
59
  end
@@ -181,4 +181,9 @@ Capybara::SpecHelper.spec "#fill_in" do
181
181
  end.to raise_error(Capybara::ElementNotFound)
182
182
  end
183
183
  end
184
+
185
+ it "should return the element filled in" do
186
+ el = @session.find(:fillable_field, 'form_first_name')
187
+ expect(@session.fill_in('form_first_name', with: 'Harry')).to eq el
188
+ end
184
189
  end
@@ -320,7 +320,7 @@ Capybara::SpecHelper.spec '#find' do
320
320
  end
321
321
  it "raises an error when there is a single inexact matches" do
322
322
  expect do
323
- result = @session.find(:xpath, XPath.descendant[XPath.attr(:class).is("almost_singular but")], match: :smart, exact: true)
323
+ @session.find(:xpath, XPath.descendant[XPath.attr(:class).is("almost_singular but")], match: :smart, exact: true)
324
324
  end.to raise_error(Capybara::ElementNotFound)
325
325
  end
326
326
  it "raises an error if there is no match" do
@@ -19,6 +19,11 @@ Capybara::SpecHelper.spec '#first' do
19
19
  expect(@session.first(@xpath).value).to eq('John')
20
20
  end
21
21
 
22
+ it "should warn when unused parameters are passed" do
23
+ expect_any_instance_of(Kernel).to receive(:warn).with(/Unused parameters passed.*unused text/)
24
+ @session.first(:css, '.header h2', 'unused text')
25
+ end
26
+
22
27
  context "with css selectors" do
23
28
  it "should find the first element using the given selector" do
24
29
  expect(@session.first(:css, 'h1').text).to eq('This is a test')
@@ -48,21 +53,26 @@ Capybara::SpecHelper.spec '#first' do
48
53
 
49
54
  it "should find nodes regardless of whether they are invisible when false" do
50
55
  expect(@session.first(:css, "a#invisible", visible: false)).not_to be_nil
56
+ expect(@session.first(:css, "a#invisible", visible: false, text: 'hidden link')).not_to be_nil
51
57
  expect(@session.first(:css, "a#visible", visible: false)).not_to be_nil
52
58
  end
53
59
 
54
60
  it "should find nodes regardless of whether they are invisible when :all" do
55
61
  expect(@session.first(:css, "a#invisible", visible: :all)).not_to be_nil
62
+ expect(@session.first(:css, "a#invisible", visible: :all, text: 'hidden link')).not_to be_nil
56
63
  expect(@session.first(:css, "a#visible", visible: :all)).not_to be_nil
57
64
  end
58
65
 
59
66
  it "should find only hidden nodes when :hidden" do
60
67
  expect(@session.first(:css, "a#invisible", visible: :hidden)).not_to be_nil
68
+ expect(@session.first(:css, "a#invisible", visible: :hidden, text: 'hidden link')).not_to be_nil
69
+ expect(@session.first(:css, "a#invisible", visible: :hidden, text: 'not hidden link')).to be_nil
61
70
  expect(@session.first(:css, "a#visible", visible: :hidden)).to be_nil
62
71
  end
63
72
 
64
73
  it "should find only visible nodes when :visible" do
65
74
  expect(@session.first(:css, "a#invisible", visible: :visible)).to be_nil
75
+ expect(@session.first(:css, "a#invisible", visible: :visible, text: 'hidden link')).to be_nil
66
76
  expect(@session.first(:css, "a#visible", visible: :visible)).not_to be_nil
67
77
  end
68
78
 
@@ -29,6 +29,10 @@ Capybara::SpecHelper.spec '#has_selector?' do
29
29
  end
30
30
  end
31
31
 
32
+ it "should accept a filter block" do
33
+ expect(@session).to have_selector(:css, "a", count: 1) { |el| el[:id] == "foo" }
34
+ end
35
+
32
36
  context "with count" do
33
37
  it "should be true if the content is on the page the given number of times" do
34
38
  expect(@session).to have_selector("//p", count: 3)
@@ -105,6 +109,13 @@ Capybara::SpecHelper.spec '#has_no_selector?' do
105
109
  end
106
110
  end
107
111
 
112
+ it "should accept a filter block" do
113
+ if !defined?(::RSpec::Expectations::Version) || (Gem::Version.new(RSpec::Expectations::Version::STRING) < Gem::Version.new('3.0'))
114
+ skip "RSpec < 3 doesn't pass the block along to the matcher for the Builtin::Has matcher"
115
+ end
116
+ expect(@session).to have_no_selector(:css, "a#foo") { |el| el[:id] != "foo" }
117
+ end
118
+
108
119
  context "with count" do
109
120
  it "should be false if the content is on the page the given number of times" do
110
121
  expect(@session).not_to have_no_selector("//p", count: 3)
@@ -425,7 +425,18 @@ Capybara::SpecHelper.spec "node" do
425
425
  @session.find(:css, 'span')
426
426
  end.to raise_error(TestApp::TestAppError) do |e|
427
427
  expect(e.cause).to be_a Capybara::CapybaraError
428
- expect(e.cause.message).to match /Your application server raised an error/
428
+ expect(e.cause.message).to match(/Your application server raised an error/)
429
+ end
430
+ end
431
+
432
+ it "sets an explanatory exception as the cause of server exceptions with errors with initializers", requires: [:server, :js] do
433
+ skip "This version of ruby doesn't support exception causes" unless Exception.instance_methods.include? :cause
434
+ quietly { @session.visit("/other_error") }
435
+ expect do
436
+ @session.find(:css, 'span')
437
+ end.to raise_error(TestApp::TestAppOtherError) do |e|
438
+ expect(e.cause).to be_a Capybara::CapybaraError
439
+ expect(e.cause.message).to match(/Your application server raised an error/)
429
440
  end
430
441
  end
431
442
  end
@@ -35,7 +35,7 @@ Capybara::SpecHelper.spec '#text' do
35
35
  Capybara.ignore_hidden_elements = false
36
36
  expect(@session.find(:id, "hidden-text").text).to eq('Some of this text is')
37
37
  end
38
-
38
+
39
39
  it "ignores invisible text if ancestor is invisible" do
40
40
  @session.visit('/with_html')
41
41
  expect(@session.find(:id, "hidden_via_ancestor", visible: false).text).to eq('')
@@ -52,8 +52,7 @@ Capybara::SpecHelper.spec '#text' do
52
52
 
53
53
  it "should strip whitespace" do
54
54
  @session.visit('/with_html')
55
- n = @session.find(:css, '#second')
56
- expect(@session.find(:css, '#second').text).to match \
57
- /\ADuis aute .* text with whitespace .* est laborum\.\z/
55
+ @session.find(:css, '#second')
56
+ expect(@session.find(:css, '#second').text).to match(/\ADuis aute .* text with whitespace .* est laborum\.\z/)
58
57
  end
59
58
  end
@@ -6,7 +6,12 @@ require 'yaml'
6
6
 
7
7
  class TestApp < Sinatra::Base
8
8
  class TestAppError < StandardError; end
9
-
9
+ class TestAppOtherError < StandardError
10
+ def initialize(string1, msg)
11
+ @something = string1
12
+ @message = msg
13
+ end
14
+ end
10
15
  set :root, File.dirname(__FILE__)
11
16
  set :static, true
12
17
  set :raise_errors, true
@@ -125,6 +130,10 @@ class TestApp < Sinatra::Base
125
130
  raise TestAppError, "some error"
126
131
  end
127
132
 
133
+ get '/other_error' do
134
+ raise TestAppOtherError.new("something", "other error")
135
+ end
136
+
128
137
  get '/load_error' do
129
138
  raise LoadError
130
139
  end
@@ -97,7 +97,9 @@
97
97
  <p>
98
98
  <input id="disable-on-click"/>
99
99
  </p>
100
-
100
+ <p>
101
+ <input id="set-storage"/>
102
+ </p>
101
103
  <p>
102
104
  <a href="#" id="delayed-page-change">Change page</a>
103
105
  <a href="/with_html?options[]=things">Non-escaped query options</a>
@@ -1,4 +1,4 @@
1
1
  # frozen_string_literal: true
2
2
  module Capybara
3
- VERSION = '2.10.1'
3
+ VERSION = '2.11.0'
4
4
  end
@@ -85,7 +85,7 @@ module Capybara
85
85
  # @param height [String] the new window height in pixels
86
86
  #
87
87
  def resize_to(width, height)
88
- @driver.resize_window_to(handle, width, height)
88
+ wait_for_stable_size { @driver.resize_window_to(handle, width, height) }
89
89
  end
90
90
 
91
91
  ##
@@ -97,7 +97,7 @@ module Capybara
97
97
  # @macro about_current
98
98
  #
99
99
  def maximize
100
- @driver.maximize_window(handle)
100
+ wait_for_stable_size { @driver.maximize_window(handle) }
101
101
  end
102
102
 
103
103
  def eql?(other)
@@ -115,6 +115,22 @@ module Capybara
115
115
 
116
116
  private
117
117
 
118
+ def wait_for_stable_size(seconds=Capybara.default_max_wait_time)
119
+ res = yield if block_given?
120
+ prev_size = size
121
+ start_time = Capybara::Helpers.monotonic_time
122
+ begin
123
+ sleep 0.05
124
+ cur_size = size
125
+ return res if cur_size == prev_size
126
+ prev_size = cur_size
127
+ end while (Capybara::Helpers.monotonic_time - start_time) < seconds
128
+ #TODO raise error in 3.0
129
+ #raise Capybara::WindowError, "Window size not stable."
130
+ warn "Window size not stable in #{seconds} seconds. This will raise an exception in a future version of Capybara"
131
+ return res
132
+ end
133
+
118
134
  def raise_unless_current(what)
119
135
  unless current?
120
136
  raise Capybara::WindowError, "#{what} not current window is not possible."
data/lib/capybara.rb CHANGED
@@ -25,7 +25,8 @@ module Capybara
25
25
  attr_accessor :server_port, :exact, :match, :exact_options, :visible_text_only, :enable_aria_label
26
26
  attr_accessor :default_selector, :default_max_wait_time, :ignore_hidden_elements
27
27
  attr_accessor :save_path, :wait_on_first_by_default, :automatic_label_click, :automatic_reload
28
- attr_accessor :reuse_server, :raise_server_errors, :server_errors
28
+ attr_reader :reuse_server
29
+ attr_accessor :raise_server_errors, :server_errors
29
30
  attr_writer :default_driver, :current_driver, :javascript_driver, :session_name, :server_host
30
31
  attr_reader :save_and_open_page_path
31
32
  attr_accessor :app
@@ -331,7 +332,7 @@ module Capybara
331
332
  #
332
333
  def reset_sessions!
333
334
  #reset in reverse so sessions that started servers are reset last
334
- session_pool.reverse_each { |mode, session| session.reset! }
335
+ session_pool.reverse_each { |_mode, session| session.reset! }
335
336
  end
336
337
  alias_method :reset!, :reset_sessions!
337
338
 
@@ -391,12 +392,12 @@ module Capybara
391
392
  end
392
393
 
393
394
  def app_host=(url)
394
- raise ArgumentError.new("Capybara.app_host should be set to a url (http://www.example.com)") unless url.nil? || (url =~ URI::regexp)
395
+ raise ArgumentError.new("Capybara.app_host should be set to a url (http://www.example.com)") unless url.nil? || (url =~ URI::Parser.new.make_regexp)
395
396
  @app_host = url
396
397
  end
397
398
 
398
399
  def default_host=(url)
399
- raise ArgumentError.new("Capybara.default_host should be set to a url (http://www.example.com)") unless url.nil? || (url =~ URI::regexp)
400
+ raise ArgumentError.new("Capybara.default_host should be set to a url (http://www.example.com)") unless url.nil? || (url =~ URI::Parser.new.make_regexp)
400
401
  @default_host = url
401
402
  end
402
403
 
@@ -469,7 +470,7 @@ module Capybara
469
470
  require 'capybara/selenium/driver'
470
471
  end
471
472
 
472
- Capybara.register_server :default do |app, port, host|
473
+ Capybara.register_server :default do |app, port, _host|
473
474
  Capybara.run_default_server(app, port)
474
475
  end
475
476
 
@@ -479,10 +480,8 @@ Capybara.register_server :webrick do |app, port, host|
479
480
  end
480
481
 
481
482
  Capybara.register_server :puma do |app, port, host|
482
- require 'puma'
483
- Puma::Server.new(app).tap do |s|
484
- s.add_tcp_listener host, port
485
- end.run.join
483
+ require 'rack/handler/puma'
484
+ Rack::Handler::Puma.run(app, Host: host, Port: port, Threads: "0:4")
486
485
  end
487
486
 
488
487
  Capybara.configure do |config|
@@ -52,7 +52,7 @@ RSpec.describe Capybara do
52
52
 
53
53
  it "allows using custom matchers" do
54
54
  Capybara.add_selector :lifeform do
55
- xpath { |name| "//option[contains(.,'#{name}')]" }
55
+ xpath { |name| ".//option[contains(.,'#{name}')]" }
56
56
  end
57
57
  expect(string).to have_selector(:id, "page")
58
58
  expect(string).not_to have_selector(:id, 'does-not-exist')
@@ -46,11 +46,14 @@ RSpec.describe Capybara do
46
46
  end
47
47
 
48
48
  it "should add a new server" do
49
+ skip "JRuby fails this because of path issues to geckodriver I think. Its tested in other runs - not worth figuring out at this time" if RUBY_PLATFORM == 'java'
50
+
51
+ require 'rack/handler/webrick'
49
52
  Capybara.register_server :blob do |app, port, host|
50
53
  Rack::Handler::WEBrick.run(app, Host: host, Port: port, AccessLog: [], Logger: WEBrick::Log::new(nil, 0))
51
54
  end
52
55
  Capybara.server = :blob
53
- session = Capybara::Session.new(:selenium, TestApp.dup)
56
+ session = Capybara::Session.new(:selenium, TestApp.new)
54
57
  session.visit('/')
55
58
  expect(session.body).to include("Hello world!")
56
59
  end
@@ -1,9 +1,12 @@
1
1
  # frozen_string_literal: true
2
2
  require 'spec_helper'
3
+ require 'selenium-webdriver'
3
4
 
4
5
  RSpec.describe Capybara::Selenium::Driver do
5
6
  it "should exit with a non-zero exit status" do
6
- browser = Capybara::Selenium::Driver.new(TestApp, browser: (ENV['SELENIUM_BROWSER'] || :firefox).to_sym).browser
7
+ options = { browser: (ENV['SELENIUM_BROWSER'] || :firefox).to_sym }
8
+ options[:desired_capabilities] = Selenium::WebDriver::Remote::Capabilities.firefox(marionette: false) if ENV['LEGACY_FIREFOX']
9
+ browser = Capybara::Selenium::Driver.new(TestApp, options).browser
7
10
  expect(true).to eq(false)
8
11
  end
9
12
  end
@@ -1,9 +1,12 @@
1
1
  # frozen_string_literal: true
2
2
  require 'spec_helper'
3
+ require 'selenium-webdriver'
3
4
 
4
5
  RSpec.describe Capybara::Selenium::Driver do
5
6
  it "should exit with a zero exit status" do
6
- browser = Capybara::Selenium::Driver.new(TestApp, browser: (ENV['SELENIUM_BROWSER'] || :firefox).to_sym).browser
7
+ options = { browser: (ENV['SELENIUM_BROWSER'] || :firefox).to_sym }
8
+ options[:desired_capabilities] = Selenium::WebDriver::Remote::Capabilities.firefox(marionette: false) if ENV['LEGACY_FIREFOX']
9
+ browser = Capybara::Selenium::Driver.new(TestApp, options ).browser
7
10
  expect(true).to eq(true)
8
11
  end
9
12
  end