capybara 2.13.0 → 2.14.0

Sign up to get free protection for your applications and to get access to all the features.
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