capybara 2.13.0 → 2.14.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 +82 -17
  3. data/README.md +29 -0
  4. data/lib/capybara.rb +81 -116
  5. data/lib/capybara/config.rb +121 -0
  6. data/lib/capybara/cucumber.rb +1 -0
  7. data/lib/capybara/driver/base.rb +6 -0
  8. data/lib/capybara/dsl.rb +1 -3
  9. data/lib/capybara/helpers.rb +3 -3
  10. data/lib/capybara/node/actions.rb +2 -2
  11. data/lib/capybara/node/base.rb +7 -2
  12. data/lib/capybara/node/element.rb +7 -1
  13. data/lib/capybara/node/finders.rb +13 -3
  14. data/lib/capybara/node/matchers.rb +15 -4
  15. data/lib/capybara/node/simple.rb +5 -0
  16. data/lib/capybara/queries/base_query.rb +8 -3
  17. data/lib/capybara/queries/selector_query.rb +11 -9
  18. data/lib/capybara/queries/text_query.rb +9 -4
  19. data/lib/capybara/rack_test/browser.rb +8 -5
  20. data/lib/capybara/rspec.rb +3 -1
  21. data/lib/capybara/rspec/matcher_proxies.rb +41 -0
  22. data/lib/capybara/rspec/matchers.rb +19 -5
  23. data/lib/capybara/selector.rb +13 -4
  24. data/lib/capybara/selector/selector.rb +3 -3
  25. data/lib/capybara/selenium/driver.rb +20 -6
  26. data/lib/capybara/selenium/node.rb +6 -2
  27. data/lib/capybara/server.rb +6 -5
  28. data/lib/capybara/session.rb +71 -14
  29. data/lib/capybara/session/config.rb +100 -0
  30. data/lib/capybara/spec/public/test.js +1 -1
  31. data/lib/capybara/spec/session/all_spec.rb +11 -0
  32. data/lib/capybara/spec/session/assert_all_of_selectors_spec.rb +24 -8
  33. data/lib/capybara/spec/session/fill_in_spec.rb +6 -0
  34. data/lib/capybara/spec/session/find_field_spec.rb +1 -0
  35. data/lib/capybara/spec/session/find_spec.rb +4 -3
  36. data/lib/capybara/spec/session/has_selector_spec.rb +1 -3
  37. data/lib/capybara/spec/session/node_spec.rb +23 -17
  38. data/lib/capybara/spec/session/reset_session_spec.rb +1 -1
  39. data/lib/capybara/spec/session/window/become_closed_spec.rb +4 -4
  40. data/lib/capybara/spec/spec_helper.rb +22 -0
  41. data/lib/capybara/spec/views/form.erb +6 -1
  42. data/lib/capybara/spec/views/with_html.erb +1 -0
  43. data/lib/capybara/version.rb +1 -1
  44. data/lib/capybara/window.rb +1 -1
  45. data/spec/capybara_spec.rb +14 -2
  46. data/spec/dsl_spec.rb +1 -0
  47. data/spec/per_session_config_spec.rb +67 -0
  48. data/spec/rspec/shared_spec_matchers.rb +2 -2
  49. data/spec/rspec/views_spec.rb +4 -0
  50. data/spec/rspec_spec.rb +77 -0
  51. data/spec/session_spec.rb +44 -0
  52. data/spec/shared_selenium_session.rb +9 -0
  53. data/spec/spec_helper.rb +4 -0
  54. metadata +7 -3
@@ -0,0 +1,100 @@
1
+ # frozen_string_literal: true
2
+ require 'delegate'
3
+
4
+ module Capybara
5
+ class SessionConfig
6
+ OPTIONS = [:always_include_port, :run_server, :default_selector, :default_max_wait_time, :ignore_hidden_elements,
7
+ :automatic_reload, :match, :exact, :exact_text, :raise_server_errors, :visible_text_only, :wait_on_first_by_default,
8
+ :automatic_label_click, :enable_aria_label, :save_path, :exact_options, :asset_host, :default_host, :app_host,
9
+ :save_and_open_page_path, :server_host, :server_port, :server_errors]
10
+
11
+ attr_accessor *OPTIONS
12
+
13
+ ##
14
+ #@!method always_include_port
15
+ # See {Capybara#configure}
16
+ #@!method run_server
17
+ # See {Capybara#configure}
18
+ #@!method default_selector
19
+ # See {Capybara#configure}
20
+ #@!method default_max_wait_time
21
+ # See {Capybara#configure}
22
+ #@!method ignore_hidden_elements
23
+ # See {Capybara#configure}
24
+ #@!method automatic_reload
25
+ # See {Capybara#configure}
26
+ #@!method match
27
+ # See {Capybara#configure}
28
+ #@!method exact
29
+ # See {Capybara#configure}
30
+ #@!method raise_server_errors
31
+ # See {Capybara#configure}
32
+ #@!method visible_text_only
33
+ # See {Capybara#configure}
34
+ #@!method wait_on_first_by_default
35
+ # See {Capybara#configure}
36
+ #@!method automatic_label_click
37
+ # See {Capybara#configure}
38
+ #@!method enable_aria_label
39
+ # See {Capybara#configure}
40
+ #@!method save_path
41
+ # See {Capybara#configure}
42
+ #@!method exact_options
43
+ # See {Capybara#configure}
44
+ #@!method asset_host
45
+ # See {Capybara#configure}
46
+ #@!method default_host
47
+ # See {Capybara#configure}
48
+ #@!method app_host
49
+ # See {Capybara#configure}
50
+ #@!method save_and_open_page_path
51
+ # See {Capybara#configure}
52
+ #@!method server_host
53
+ # See {Capybara#configure}
54
+ #@!method server_port
55
+ # See {Capybara#configure}
56
+ #@!method server_errors
57
+ # See {Capybara#configure}
58
+
59
+ ##
60
+ #
61
+ # @return [String] The IP address bound by default server
62
+ #
63
+ def server_host
64
+ @server_host || '127.0.0.1'
65
+ end
66
+
67
+ def server_errors=(errors)
68
+ (@server_errors ||= []).replace(errors.dup)
69
+ end
70
+
71
+ def app_host=(url)
72
+ 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)
73
+ @app_host = url
74
+ end
75
+
76
+ def default_host=(url)
77
+ 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)
78
+ @default_host = url
79
+ end
80
+
81
+ def save_and_open_page_path=(path)
82
+ warn "DEPRECATED: #save_and_open_page_path is deprecated, please use #save_path instead. \n"\
83
+ "Note: Behavior is slightly different with relative paths - see documentation" unless path.nil?
84
+ @save_and_open_page_path = path
85
+ end
86
+
87
+ def initialize_copy(other)
88
+ super
89
+ @server_errors = @server_errors.dup
90
+ end
91
+ end
92
+
93
+ class ReadOnlySessionConfig < SimpleDelegator
94
+ SessionConfig::OPTIONS.each do |m|
95
+ define_method "#{m}=" do |val|
96
+ raise "Per session settings are only supported when Capybara.threadsafe == true"
97
+ end
98
+ end
99
+ end
100
+ end
@@ -58,7 +58,7 @@ $(function() {
58
58
  $('#change-title').click(function() {
59
59
  setTimeout(function() {
60
60
  $('title').text('changed title')
61
- }, 250)
61
+ }, 400)
62
62
  });
63
63
  $('#click-test').on({
64
64
  dblclick: function() {
@@ -66,6 +66,17 @@ Capybara::SpecHelper.spec "#all" do
66
66
  Capybara.ignore_hidden_elements = false
67
67
  expect(@session.all(:css, "a.simple").size).to eq(2)
68
68
  end
69
+
70
+ context "with per session config", requires: [:psc] do
71
+ it "should use the sessions ignore_hidden_elements", psc: true do
72
+ Capybara.ignore_hidden_elements = true
73
+ @session.config.ignore_hidden_elements = false
74
+ expect(Capybara.ignore_hidden_elements).to eq(true)
75
+ expect(@session.all(:css, "a.simple").size).to eq(2)
76
+ @session.config.ignore_hidden_elements = true
77
+ expect(@session.all(:css, "a.simple").size).to eq(1)
78
+ end
79
+ end
69
80
  end
70
81
 
71
82
  context 'with element count filters' do
@@ -18,10 +18,18 @@ Capybara::SpecHelper.spec '#assert_all_of_selectors' do
18
18
  @session.assert_all_of_selectors("p a#foo", "h2#h2two", "h2#h2one" )
19
19
  end
20
20
 
21
- it "should respect scopes" do
22
- @session.within "//p[@id='first']" do
23
- @session.assert_all_of_selectors(".//a[@id='foo']")
24
- expect { @session.assert_all_of_selectors(".//a[@id='red']") }.to raise_error(Capybara::ElementNotFound)
21
+ context "should respect scopes" do
22
+ it "when used with `within`" do
23
+ @session.within "//p[@id='first']" do
24
+ @session.assert_all_of_selectors(".//a[@id='foo']")
25
+ expect { @session.assert_all_of_selectors(".//a[@id='red']") }.to raise_error(Capybara::ElementNotFound)
26
+ end
27
+ end
28
+
29
+ it "when called on elements" do
30
+ el = @session.find "//p[@id='first']"
31
+ el.assert_all_of_selectors(".//a[@id='foo']")
32
+ expect { el.assert_all_of_selectors(".//a[@id='red']") }.to raise_error(Capybara::ElementNotFound)
25
33
  end
26
34
  end
27
35
 
@@ -65,10 +73,18 @@ Capybara::SpecHelper.spec '#assert_none_of_selectors' do
65
73
  expect { @session.assert_none_of_selectors("abbr", "p a#foo") }.to raise_error(Capybara::ElementNotFound)
66
74
  end
67
75
 
68
- it "should respect scopes" do
69
- @session.within "//p[@id='first']" do
70
- expect { @session.assert_none_of_selectors(".//a[@id='foo']") }.to raise_error(Capybara::ElementNotFound)
71
- @session.assert_none_of_selectors(".//a[@id='red']")
76
+ context "should respect scopes" do
77
+ it "when used with `within`" do
78
+ @session.within "//p[@id='first']" do
79
+ expect { @session.assert_none_of_selectors(".//a[@id='foo']") }.to raise_error(Capybara::ElementNotFound)
80
+ @session.assert_none_of_selectors(".//a[@id='red']")
81
+ end
82
+ end
83
+
84
+ it "when called on an element" do
85
+ el = @session.find "//p[@id='first']"
86
+ expect { el.assert_none_of_selectors(".//a[@id='foo']") }.to raise_error(Capybara::ElementNotFound)
87
+ el.assert_none_of_selectors(".//a[@id='red']")
72
88
  end
73
89
  end
74
90
 
@@ -106,6 +106,12 @@ Capybara::SpecHelper.spec "#fill_in" do
106
106
  expect(extract_results(@session)['first_name']).to eq('Thomas')
107
107
  end
108
108
 
109
+ it "should fill in a field based on type" do
110
+ @session.fill_in(type: 'schmooo', with: 'Schmooo for all')
111
+ @session.click_button('awesome')
112
+ expect(extract_results(@session)['schmooo']).to eq('Schmooo for all')
113
+ end
114
+
109
115
  it "should throw an exception if a hash containing 'with' is not provided" do
110
116
  expect {@session.fill_in 'Name', 'ignu'}.to raise_error(RuntimeError, /with/)
111
117
  end
@@ -8,6 +8,7 @@ Capybara::SpecHelper.spec '#find_field' do
8
8
  expect(@session.find_field('Dog').value).to eq('dog')
9
9
  expect(@session.find_field('form_description').text).to eq('Descriptive text goes here')
10
10
  expect(@session.find_field('Region')[:name]).to eq('form[region]')
11
+ expect(@session.find_field('With Asterisk*')).to be
11
12
  end
12
13
 
13
14
  context "aria_label attribute with Capybara.enable_aria_label" do
@@ -421,8 +421,9 @@ Capybara::SpecHelper.spec '#find' do
421
421
  end
422
422
  end
423
423
 
424
- it "should warn if selector type is unknown" do
425
- expect_any_instance_of(Kernel).to receive(:warn).with(/^Unknown selector type/)
426
- @session.find(:unknown, '//h1')
424
+ it "should raise if selector type is unknown" do
425
+ expect do
426
+ @session.find(:unknown, '//h1')
427
+ end.to raise_error(ArgumentError)
427
428
  end
428
429
  end
@@ -131,9 +131,7 @@ Capybara::SpecHelper.spec '#has_no_selector?' do
131
131
  end
132
132
 
133
133
  it "should accept a filter block" do
134
- if !defined?(::RSpec::Expectations::Version) || (Gem::Version.new(RSpec::Expectations::Version::STRING) < Gem::Version.new('3.0'))
135
- skip "RSpec < 3 doesn't pass the block along to the matcher for the Builtin::Has matcher"
136
- end
134
+ skip "RSpec < 3 doesn't pass the block along to the matcher for the Builtin::Has matcher" if rspec2?
137
135
  expect(@session).to have_no_selector(:css, "a#foo") { |el| el[:id] != "foo" }
138
136
  end
139
137
 
@@ -122,23 +122,25 @@ Capybara::SpecHelper.spec "node" do
122
122
  expect(@session.first('//textarea[@readonly]').set('changed')).to raise_error(Capybara::ReadOnlyElementError)
123
123
  end if Capybara::VERSION.to_f > 3.0
124
124
 
125
- it 'should allow me to change the contents of a contenteditable element', requires: [:js] do
126
- @session.visit('/with_js')
127
- @session.find(:css,'#existing_content_editable').set('WYSIWYG')
128
- expect(@session.find(:css,'#existing_content_editable').text).to eq('WYSIWYG')
129
- end
125
+ context "with a contenteditable element", requires: [:js] do
126
+ it 'should allow me to change the contents' do
127
+ @session.visit('/with_js')
128
+ @session.find(:css,'#existing_content_editable').set('WYSIWYG')
129
+ expect(@session.find(:css,'#existing_content_editable').text).to eq('WYSIWYG')
130
+ end
130
131
 
131
- it 'should allow me to set the contents of a contenteditable element', requires: [:js] do
132
- @session.visit('/with_js')
133
- @session.find(:css,'#blank_content_editable').set('WYSIWYG')
134
- expect(@session.find(:css,'#blank_content_editable').text).to eq('WYSIWYG')
135
- end
132
+ it 'should allow me to set the contents' do
133
+ @session.visit('/with_js')
134
+ @session.find(:css,'#blank_content_editable').set('WYSIWYG')
135
+ expect(@session.find(:css,'#blank_content_editable').text).to eq('WYSIWYG')
136
+ end
136
137
 
137
- it 'should allow me to change the contents of a contenteditable elements child', requires: [:js] do
138
- @session.visit('/with_js')
139
- @session.find(:css,'#existing_content_editable_child').set('WYSIWYG')
140
- expect(@session.find(:css,'#existing_content_editable_child').text).to eq('WYSIWYG')
141
- expect(@session.find(:css,'#existing_content_editable_child_parent').text).to eq('Some content WYSIWYG')
138
+ it 'should allow me to change the contents of a child element' do
139
+ @session.visit('/with_js')
140
+ @session.find(:css,'#existing_content_editable_child').set('WYSIWYG')
141
+ expect(@session.find(:css,'#existing_content_editable_child').text).to eq('WYSIWYG')
142
+ expect(@session.find(:css,'#existing_content_editable_child_parent').text).to eq('Some content WYSIWYG')
143
+ end
142
144
  end
143
145
  end
144
146
 
@@ -289,7 +291,6 @@ Capybara::SpecHelper.spec "node" do
289
291
 
290
292
  describe '#drag_to', requires: [:js, :drag] do
291
293
  it "should drag and drop an object" do
292
- pending "selenium-webdriver/geckodriver doesn't support mouse move_to" if marionette?(@session)
293
294
  @session.visit('/with_js')
294
295
  element = @session.find('//div[@id="drag"]')
295
296
  target = @session.find('//div[@id="drop"]')
@@ -300,7 +301,6 @@ Capybara::SpecHelper.spec "node" do
300
301
 
301
302
  describe '#hover', requires: [:hover] do
302
303
  it "should allow hovering on an element" do
303
- pending "selenium-webdriver/geckodriver doesn't support mouse move_to" if marionette?(@session)
304
304
  @session.visit('/with_hover')
305
305
  expect(@session.find(:css,'.hidden_until_hover', visible: false)).not_to be_visible
306
306
  @session.find(:css,'.wrapper').hover
@@ -314,6 +314,12 @@ Capybara::SpecHelper.spec "node" do
314
314
  expect(@session.current_url).to match(%r{/with_html$})
315
315
  end
316
316
 
317
+ it "should go to the same page if href is blank" do
318
+ @session.find(:css, '#link_blank_href').click
319
+ sleep 1
320
+ expect(@session).to have_current_path('/with_html')
321
+ end
322
+
317
323
  it "should be able to check a checkbox" do
318
324
  @session.visit('form')
319
325
  cbox = @session.find(:checkbox, 'form_terms_of_use')
@@ -88,7 +88,7 @@ Capybara::SpecHelper.spec '#reset_session!' do
88
88
  end
89
89
 
90
90
  it "raises configured errors caught inside the server", requires: [:server] do
91
- prev_errors = Capybara.server_errors
91
+ prev_errors = Capybara.server_errors.dup
92
92
 
93
93
  Capybara.server_errors = [LoadError]
94
94
  quietly { @session.visit("/error") }
@@ -60,18 +60,18 @@ Capybara::SpecHelper.spec '#become_closed', requires: [:windows, :js] do
60
60
  end
61
61
 
62
62
  context 'with not_to' do
63
- it 'should raise error if default_max_wait_time is more than timeout' do
63
+ it "should not raise error if window doesn't close before default_max_wait_time" do
64
64
  @session.within_window @other_window do
65
- @session.execute_script('setTimeout(function(){ window.close(); }, 700);')
65
+ @session.execute_script('setTimeout(function(){ window.close(); }, 1000);')
66
66
  end
67
- Capybara.using_wait_time 0.4 do
67
+ Capybara.using_wait_time 0.3 do
68
68
  expect do
69
69
  expect(@other_window).not_to become_closed
70
70
  end
71
71
  end
72
72
  end
73
73
 
74
- it 'should raise error if default_max_wait_time is more than timeout' do
74
+ it 'should raise error if window closes before default_max_wait_time' do
75
75
  @session.within_window @other_window do
76
76
  @session.execute_script('setTimeout(function(){ window.close(); }, 700);')
77
77
  end
@@ -39,6 +39,7 @@ module Capybara
39
39
  Capybara.match = :smart
40
40
  Capybara.wait_on_first_by_default = false
41
41
  Capybara.enable_aria_label = false
42
+ reset_threadsafe
42
43
  end
43
44
 
44
45
  def filter(requires, metadata)
@@ -64,9 +65,19 @@ module Capybara
64
65
  before do
65
66
  @session = session
66
67
  end
68
+
67
69
  after do
68
70
  @session.reset_session!
69
71
  end
72
+
73
+ before :each, psc: true do
74
+ SpecHelper.reset_threadsafe(true, @session)
75
+ end
76
+
77
+ after psc: true do
78
+ SpecHelper.reset_threadsafe(false, @session)
79
+ end
80
+
70
81
  specs.each do |spec_name, spec_options, block|
71
82
  describe spec_name, spec_options do
72
83
  class_eval(&block)
@@ -74,6 +85,13 @@ module Capybara
74
85
  end
75
86
  end
76
87
  end
88
+
89
+ def reset_threadsafe(bool = false, session = nil)
90
+ Capybara::Session.class_variable_set(:@@instance_created, false) # Work around limit on when threadsafe can be changed
91
+ Capybara.threadsafe = bool
92
+ session = session.current_session if session.respond_to?(:current_session)
93
+ session.instance_variable_set(:@config, nil) if session
94
+ end
77
95
  end # class << self
78
96
 
79
97
  def silence_stream(stream)
@@ -101,6 +119,10 @@ module Capybara
101
119
  def marionette?(session)
102
120
  session.driver.respond_to?(:marionette?) && session.driver.marionette?
103
121
  end
122
+
123
+ def rspec2?
124
+ !defined?(::RSpec::Expectations::Version) || (Gem::Version.new(RSpec::Expectations::Version::STRING) < Gem::Version.new('3.0'))
125
+ end
104
126
  end
105
127
  end
106
128
 
@@ -586,6 +586,11 @@ New line after and before textarea tag
586
586
  </label>
587
587
 
588
588
  <label>Confusion
589
- <textarea id="confusion_textarea" class="confusion confusion-textarea"/>
589
+ <textarea id="confusion_textarea" class="confusion confusion-textarea"></textarea>
590
590
  </label>
591
591
 
592
+ <p>
593
+ <label for="asterisk_input">With Asterisk<abbr title="required">*</abbr></label>
594
+ <input id="asterisk_input" type="number"value="2016"/>
595
+ </p>
596
+
@@ -119,4 +119,5 @@ banana</textarea>
119
119
 
120
120
  <div>
121
121
  <a id="link_placeholder">No href</a>
122
+ <a id="link_blank_href" href="">Blank href</a>
122
123
  </div>
@@ -1,4 +1,4 @@
1
1
  # frozen_string_literal: true
2
2
  module Capybara
3
- VERSION = '2.13.0'
3
+ VERSION = '2.14.0'
4
4
  end
@@ -115,7 +115,7 @@ module Capybara
115
115
 
116
116
  private
117
117
 
118
- def wait_for_stable_size(seconds=Capybara.default_max_wait_time)
118
+ def wait_for_stable_size(seconds=session.config.default_max_wait_time)
119
119
  res = yield if block_given?
120
120
  prev_size = size
121
121
  start_time = Capybara::Helpers.monotonic_time
@@ -14,8 +14,8 @@ RSpec.describe Capybara do
14
14
  end
15
15
 
16
16
  it "should be accesible as the deprecated default_wait_time" do
17
- expect(Capybara).to receive(:warn).ordered.with('DEPRECATED: #default_wait_time= is deprecated, please use #default_max_wait_time= instead')
18
- expect(Capybara).to receive(:warn).ordered.with('DEPRECATED: #default_wait_time is deprecated, please use #default_max_wait_time instead')
17
+ expect(Capybara.send(:config)).to receive(:warn).ordered.with('DEPRECATED: #default_wait_time= is deprecated, please use #default_max_wait_time= instead')
18
+ expect(Capybara.send(:config)).to receive(:warn).ordered.with('DEPRECATED: #default_wait_time is deprecated, please use #default_max_wait_time instead')
19
19
  @previous_default_time = Capybara.default_max_wait_time
20
20
  Capybara.default_wait_time = 5
21
21
  expect(Capybara.default_wait_time).to eq(5)
@@ -114,6 +114,18 @@ RSpec.describe Capybara do
114
114
  expect { Capybara.default_host = "http://www.example.com" }.not_to raise_error
115
115
  end
116
116
  end
117
+
118
+ describe "configure" do
119
+ it 'deprecates calling non configuration option methods in configure' do
120
+ expect_any_instance_of(Kernel).to receive(:warn).
121
+ with('Calling register_driver from Capybara.configure is deprecated - please call it on Capybara directly ( Capybara.register_driver(...) )')
122
+ Capybara.configure do |config|
123
+ config.register_driver(:random_name) do
124
+ #just a random block
125
+ end
126
+ end
127
+ end
128
+ end
117
129
  end
118
130
 
119
131
  RSpec.describe Capybara::Session do
@@ -16,6 +16,7 @@ Capybara::SpecHelper.run_specs TestClass.new, "DSL", capybara_skip: [
16
16
  :server,
17
17
  :hover,
18
18
  :about_scheme,
19
+ :psc
19
20
  ]
20
21
 
21
22
  RSpec.describe Capybara::DSL do