capybara 3.0.3 → 3.1.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 (82) hide show
  1. checksums.yaml +4 -4
  2. data/History.md +10 -0
  3. data/lib/capybara.rb +1 -1
  4. data/lib/capybara/helpers.rb +1 -1
  5. data/lib/capybara/minitest/spec.rb +2 -0
  6. data/lib/capybara/node/actions.rb +45 -5
  7. data/lib/capybara/queries/match_query.rb +2 -0
  8. data/lib/capybara/queries/selector_query.rb +3 -4
  9. data/lib/capybara/queries/text_query.rb +4 -5
  10. data/lib/capybara/rack_test/node.rb +6 -4
  11. data/lib/capybara/result.rb +1 -1
  12. data/lib/capybara/rspec/compound.rb +2 -0
  13. data/lib/capybara/selector.rb +72 -18
  14. data/lib/capybara/selector/css.rb +2 -0
  15. data/lib/capybara/selenium/driver.rb +14 -15
  16. data/lib/capybara/selenium/node.rb +23 -29
  17. data/lib/capybara/server.rb +29 -4
  18. data/lib/capybara/session.rb +12 -13
  19. data/lib/capybara/spec/session/all_spec.rb +6 -6
  20. data/lib/capybara/spec/session/{assert_current_path.rb → assert_current_path_spec.rb} +2 -3
  21. data/lib/capybara/spec/session/{assert_selector.rb → assert_selector_spec.rb} +0 -0
  22. data/lib/capybara/spec/session/{assert_text.rb → assert_text_spec.rb} +1 -1
  23. data/lib/capybara/spec/session/{assert_title.rb → assert_title_spec.rb} +0 -0
  24. data/lib/capybara/spec/session/check_spec.rb +6 -6
  25. data/lib/capybara/spec/session/click_link_or_button_spec.rb +0 -7
  26. data/lib/capybara/spec/session/current_url_spec.rb +6 -8
  27. data/lib/capybara/spec/session/element/{assert_match_selector.rb → assert_match_selector_spec.rb} +2 -0
  28. data/lib/capybara/spec/session/element/match_css_spec.rb +2 -0
  29. data/lib/capybara/spec/session/element/match_xpath_spec.rb +2 -0
  30. data/lib/capybara/spec/session/element/matches_selector_spec.rb +2 -0
  31. data/lib/capybara/spec/session/find_by_id_spec.rb +1 -1
  32. data/lib/capybara/spec/session/find_field_spec.rb +1 -1
  33. data/lib/capybara/spec/session/first_spec.rb +12 -12
  34. data/lib/capybara/spec/session/frame/frame_title_spec.rb +1 -1
  35. data/lib/capybara/spec/session/frame/frame_url_spec.rb +1 -1
  36. data/lib/capybara/spec/session/frame/switch_to_frame_spec.rb +2 -2
  37. data/lib/capybara/spec/session/frame/within_frame_spec.rb +1 -1
  38. data/lib/capybara/spec/session/has_button_spec.rb +5 -0
  39. data/lib/capybara/spec/session/has_current_path_spec.rb +2 -2
  40. data/lib/capybara/spec/session/has_field_spec.rb +1 -1
  41. data/lib/capybara/spec/session/has_none_selectors_spec.rb +2 -0
  42. data/lib/capybara/spec/session/has_selector_spec.rb +8 -0
  43. data/lib/capybara/spec/session/{headers.rb → headers_spec.rb} +0 -0
  44. data/lib/capybara/spec/session/node_spec.rb +4 -4
  45. data/lib/capybara/spec/session/reset_session_spec.rb +2 -2
  46. data/lib/capybara/spec/session/{response_code.rb → response_code_spec.rb} +0 -0
  47. data/lib/capybara/spec/session/save_and_open_screenshot_spec.rb +2 -1
  48. data/lib/capybara/spec/session/select_spec.rb +27 -1
  49. data/lib/capybara/spec/session/selectors_spec.rb +2 -0
  50. data/lib/capybara/spec/session/uncheck_spec.rb +3 -3
  51. data/lib/capybara/spec/session/visit_spec.rb +17 -10
  52. data/lib/capybara/spec/session/window/become_closed_spec.rb +3 -3
  53. data/lib/capybara/spec/session/window/current_window_spec.rb +2 -2
  54. data/lib/capybara/spec/session/window/open_new_window_spec.rb +3 -3
  55. data/lib/capybara/spec/session/window/switch_to_window_spec.rb +5 -5
  56. data/lib/capybara/spec/session/window/window_opened_by_spec.rb +3 -3
  57. data/lib/capybara/spec/session/window/window_spec.rb +24 -9
  58. data/lib/capybara/spec/session/window/windows_spec.rb +2 -2
  59. data/lib/capybara/spec/session/window/within_window_spec.rb +2 -2
  60. data/lib/capybara/spec/session/within_spec.rb +3 -3
  61. data/lib/capybara/spec/spec_helper.rb +7 -5
  62. data/lib/capybara/spec/views/form.erb +9 -0
  63. data/lib/capybara/version.rb +1 -1
  64. data/spec/basic_node_spec.rb +1 -1
  65. data/spec/capybara_spec.rb +4 -14
  66. data/spec/dsl_spec.rb +5 -3
  67. data/spec/fixtures/certificate.pem +25 -0
  68. data/spec/fixtures/key.pem +27 -0
  69. data/spec/fixtures/selenium_driver_rspec_failure.rb +2 -2
  70. data/spec/fixtures/selenium_driver_rspec_success.rb +2 -2
  71. data/spec/rack_test_spec.rb +8 -6
  72. data/spec/result_spec.rb +22 -2
  73. data/spec/rspec/features_spec.rb +4 -2
  74. data/spec/rspec/scenarios_spec.rb +1 -1
  75. data/spec/rspec/shared_spec_matchers.rb +7 -4
  76. data/spec/rspec/views_spec.rb +2 -1
  77. data/spec/rspec_spec.rb +80 -78
  78. data/spec/selenium_spec_marionette.rb +6 -4
  79. data/spec/server_spec.rb +40 -2
  80. data/spec/session_spec.rb +12 -4
  81. data/spec/shared_selenium_session.rb +106 -84
  82. metadata +11 -9
@@ -2,118 +2,120 @@
2
2
 
3
3
  require 'spec_helper'
4
4
 
5
- RSpec.describe 'capybara/rspec', type: :feature do
6
- it "should include Capybara in rspec" do
7
- visit('/foo')
8
- expect(page.body).to include('Another World')
9
- end
10
-
11
- it "should include RSpec matcher proxies" do
12
- expect(self.class.ancestors).to include Capybara::RSpecMatcherProxies
13
- end
14
-
15
- context "resetting session" do
16
- it "sets a cookie in one example..." do
17
- visit('/set_cookie')
18
- expect(page.body).to include('Cookie set to test_cookie')
19
- end
20
-
21
- it "...then it is not available in the next" do
22
- visit('/get_cookie')
23
- expect(page.body).not_to include('test_cookie')
24
- end
25
- end
26
-
27
- context "setting the current driver" do
28
- it "sets the current driver in one example..." do
29
- Capybara.current_driver = :selenium
5
+ RSpec.describe 'capybara/rspec' do
6
+ context "Feature", type: :feature do
7
+ it "should include Capybara in rspec" do
8
+ visit('/foo')
9
+ expect(page.body).to include('Another World')
30
10
  end
31
11
 
32
- it "...then it has returned to the default in the next example" do
33
- expect(Capybara.current_driver).to eq(:rack_test)
12
+ it "should include RSpec matcher proxies" do
13
+ expect(self.class.ancestors).to include Capybara::RSpecMatcherProxies
34
14
  end
35
- end
36
-
37
- it "switches to the javascript driver when giving it as metadata", js: true do
38
- expect(Capybara.current_driver).to eq(Capybara.javascript_driver)
39
- end
40
15
 
41
- it "switches to the given driver when giving it as metadata", driver: :culerity do
42
- expect(Capybara.current_driver).to eq(:culerity)
43
- end
16
+ context "resetting session" do
17
+ it "sets a cookie in one example..." do
18
+ visit('/set_cookie')
19
+ expect(page.body).to include('Cookie set to test_cookie')
20
+ end
44
21
 
45
- context "#all" do
46
- it "allows access to the Capybara finder" do
47
- visit('/with_html')
48
- found = all(:css, 'h2') { |element| element[:class] == 'head' }
49
- expect(found.size).to eq(5)
22
+ it "...then it is not available in the next" do
23
+ visit('/get_cookie')
24
+ expect(page.body).not_to include('test_cookie')
25
+ end
50
26
  end
51
27
 
52
- it "allows access to the RSpec matcher" do
53
- visit('/with_html')
54
- expect(%w[test1 test2]).to all(be_a(String))
55
- end
56
- end
28
+ context "setting the current driver" do
29
+ it "sets the current driver in one example..." do
30
+ Capybara.current_driver = :selenium
31
+ end
57
32
 
58
- context "#within" do
59
- it "allows access to the Capybara scoper" do
60
- visit('/with_html')
61
- expect do
62
- within(:css, "#does_not_exist") { click_link "Go to simple" }
63
- end.to raise_error(Capybara::ElementNotFound)
33
+ it "...then it has returned to the default in the next example" do
34
+ expect(Capybara.current_driver).to eq(:rack_test)
35
+ end
64
36
  end
65
37
 
66
- it "allows access to the RSpec matcher" do
67
- visit('/with_html')
68
- # This reads terribly, but must call #within
69
- expect(find(:css, 'span.number').text.to_i).to within(1).of(41)
38
+ it "switches to the javascript driver when giving it as metadata", js: true do
39
+ expect(Capybara.current_driver).to eq(Capybara.javascript_driver)
70
40
  end
71
- end
72
- end
73
41
 
74
- RSpec.describe 'capybara/rspec', type: :other do
75
- context "when RSpec::Matchers is included after Capybara::DSL" do
76
- before do
77
- class DSLMatchersTest
78
- include Capybara::DSL
79
- include RSpec::Matchers
80
- end
81
-
82
- @test_class_instance = DSLMatchersTest.new
42
+ it "switches to the given driver when giving it as metadata", driver: :culerity do
43
+ expect(Capybara.current_driver).to eq(:culerity)
83
44
  end
84
45
 
85
46
  context "#all" do
86
47
  it "allows access to the Capybara finder" do
87
- @test_class_instance.visit('/with_html')
88
- expect(@test_class_instance.all(:css, 'h2.head').size).to eq(5)
48
+ visit('/with_html')
49
+ found = all(:css, 'h2') { |element| element[:class] == 'head' }
50
+ expect(found.size).to eq(5)
89
51
  end
90
52
 
91
53
  it "allows access to the RSpec matcher" do
92
- @test_class_instance.visit('/with_html')
93
- expect(%w[test1 test2]).to @test_class_instance.all(be_a(String))
54
+ visit('/with_html')
55
+ strings = %w[test1 test2]
56
+ expect(strings).to all(be_a(String))
94
57
  end
95
58
  end
96
59
 
97
60
  context "#within" do
98
61
  it "allows access to the Capybara scoper" do
99
- @test_class_instance.visit('/with_html')
62
+ visit('/with_html')
100
63
  expect do
101
- @test_class_instance.within(:css, "#does_not_exist") { @test_class_instance.click_link "Go to simple" }
64
+ within(:css, "#does_not_exist") { click_link "Go to simple" }
102
65
  end.to raise_error(Capybara::ElementNotFound)
103
66
  end
104
67
 
105
68
  it "allows access to the RSpec matcher" do
106
- @test_class_instance.visit('/with_html')
69
+ visit('/with_html')
107
70
  # This reads terribly, but must call #within
108
- expect(@test_class_instance.find(:css, 'span.number').text.to_i).to @test_class_instance.within(1).of(41)
71
+ expect(find(:css, 'span.number').text.to_i).to within(1).of(41)
109
72
  end
110
73
  end
111
74
  end
112
- end
113
75
 
114
- RSpec.describe 'capybara/rspec', type: :other do
115
- it "should not include Capybara" do
116
- expect { visit('/') }.to raise_error(NoMethodError)
76
+ context 'Type: Other', type: :other do
77
+ context "when RSpec::Matchers is included after Capybara::DSL" do
78
+ before do
79
+ class DSLMatchersTest
80
+ include Capybara::DSL
81
+ include RSpec::Matchers
82
+ end
83
+
84
+ @test_class_instance = DSLMatchersTest.new
85
+ end
86
+
87
+ context "#all" do
88
+ it "allows access to the Capybara finder" do
89
+ @test_class_instance.visit('/with_html')
90
+ expect(@test_class_instance.all(:css, 'h2.head').size).to eq(5)
91
+ end
92
+
93
+ it "allows access to the RSpec matcher" do
94
+ @test_class_instance.visit('/with_html')
95
+ strings = %w[test1 test2]
96
+ expect(strings).to @test_class_instance.all(be_a(String))
97
+ end
98
+ end
99
+
100
+ context "#within" do
101
+ it "allows access to the Capybara scoper" do
102
+ @test_class_instance.visit('/with_html')
103
+ expect do
104
+ @test_class_instance.within(:css, "#does_not_exist") { @test_class_instance.click_link "Go to simple" }
105
+ end.to raise_error(Capybara::ElementNotFound)
106
+ end
107
+
108
+ it "allows access to the RSpec matcher" do
109
+ @test_class_instance.visit('/with_html')
110
+ # This reads terribly, but must call #within
111
+ expect(@test_class_instance.find(:css, 'span.number').text.to_i).to @test_class_instance.within(1).of(41)
112
+ end
113
+ end
114
+ end
115
+
116
+ it "should not include Capybara" do
117
+ expect { visit('/') }.to raise_error(NoMethodError)
118
+ end
117
119
  end
118
120
  end
119
121
 
@@ -44,7 +44,7 @@ $stdout.puts `#{Selenium::WebDriver::Firefox.driver_path} --version` if ENV['CI'
44
44
 
45
45
  Capybara::SpecHelper.run_specs TestSessions::SeleniumMarionette, "selenium", capybara_skip: skipped_tests
46
46
 
47
- RSpec.describe "Capybara::Session with firefox" do
47
+ RSpec.describe "Capybara::Session with firefox" do # rubocop:disable RSpec/MultipleDescribes
48
48
  include Capybara::SpecHelper
49
49
  include_examples "Capybara::Session", TestSessions::SeleniumMarionette, :selenium_marionette
50
50
  include_examples Capybara::RSpecMatchers, TestSessions::SeleniumMarionette, :selenium_marionette
@@ -57,7 +57,7 @@ RSpec.describe Capybara::Selenium::Driver do
57
57
 
58
58
  describe '#quit' do
59
59
  it "should reset browser when quit" do
60
- expect(@driver.browser).to be
60
+ expect(@driver.browser).to be_truthy
61
61
  @driver.quit
62
62
  # access instance variable directly so we don't create a new browser instance
63
63
  expect(@driver.instance_variable_get(:@browser)).to be_nil
@@ -74,7 +74,7 @@ RSpec.describe Capybara::Selenium::Driver do
74
74
  end
75
75
 
76
76
  it "warns UnknownError returned during quit because the browser is probably already gone" do
77
- expect_any_instance_of(Capybara::Selenium::Driver).to receive(:warn).with(/random message/)
77
+ allow(@driver).to receive(:warn)
78
78
  allow(@driver.browser).to(
79
79
  receive(:quit)
80
80
  .and_raise(Selenium::WebDriver::Error::UnknownError, "random message")
@@ -82,10 +82,11 @@ RSpec.describe Capybara::Selenium::Driver do
82
82
 
83
83
  expect { @driver.quit }.not_to raise_error
84
84
  expect(@driver.instance_variable_get(:@browser)).to be_nil
85
+ expect(@driver).to have_received(:warn).with(/random message/)
85
86
  end
86
87
 
87
88
  it "ignores silenced UnknownError returned during quit because the browser is almost definitely already gone" do
88
- expect_any_instance_of(Capybara::Selenium::Driver).not_to receive(:warn)
89
+ allow(@driver).to receive(:warn)
89
90
  allow(@driver.browser).to(
90
91
  receive(:quit)
91
92
  .and_raise(Selenium::WebDriver::Error::UnknownError, "Error communicating with the remote browser")
@@ -93,6 +94,7 @@ RSpec.describe Capybara::Selenium::Driver do
93
94
 
94
95
  expect { @driver.quit }.not_to raise_error
95
96
  expect(@driver.instance_variable_get(:@browser)).to be_nil
97
+ expect(@driver).not_to have_received(:warn)
96
98
  end
97
99
  end
98
100
  end
@@ -51,7 +51,7 @@ RSpec.describe Capybara::Server do
51
51
 
52
52
  it "should use given port" do
53
53
  @app = proc { |_env| [200, {}, ["Hello Server!"]] }
54
- @server = Capybara::Server.new(@app, 22790).boot
54
+ @server = Capybara::Server.new(@app, port: 22790).boot
55
55
 
56
56
  @res = Net::HTTP.start(@server.host, 22790) { |http| http.get('/') }
57
57
  expect(@res.body).to include('Hello Server')
@@ -73,6 +73,28 @@ RSpec.describe Capybara::Server do
73
73
  expect(@res2.body).to include('Hello Second Server')
74
74
  end
75
75
 
76
+ it "should support SSL" do
77
+ begin
78
+ key = File.join(Dir.pwd, "spec", "fixtures", "key.pem")
79
+ cert = File.join(Dir.pwd, "spec", "fixtures", "certificate.pem")
80
+ Capybara.server = :puma, { Host: "ssl://#{Capybara.server_host}?key=#{key}&cert=#{cert}" }
81
+ app = proc { |_env| [200, {}, ['Hello SSL Server!']] }
82
+ server = Capybara::Server.new(app).boot
83
+
84
+ expect do
85
+ Net::HTTP.start(server.host, server.port) { |http| http.get('/__idntify__') }
86
+ end.to raise_error(EOFError)
87
+
88
+ res = Net::HTTP.start(server.host, server.port, use_ssl: true, verify_mode: OpenSSL::SSL::VERIFY_NONE) do |https|
89
+ https.get('/')
90
+ end
91
+
92
+ expect(res.body).to include('Hello SSL Server!')
93
+ ensure
94
+ Capybara.server = :default
95
+ end
96
+ end
97
+
76
98
  context "When Capybara.reuse_server is true" do
77
99
  before do
78
100
  @old_reuse_server = Capybara.reuse_server
@@ -189,10 +211,26 @@ RSpec.describe Capybara::Server do
189
211
  it "is not #responsive? when Net::HTTP raises a SystemCallError" do
190
212
  app = -> { [200, {}, ['Hello, world']] }
191
213
  server = Capybara::Server.new(app)
192
- expect(Net::HTTP).to receive(:start).and_raise(SystemCallError.allocate)
214
+ allow(Net::HTTP).to receive(:start).and_raise(SystemCallError.allocate)
193
215
  expect(server.responsive?).to eq false
194
216
  end
195
217
 
218
+ [EOFError, Net::ReadTimeout].each do |err|
219
+ it "should attempt an HTTPS connection if HTTP connection returns #{err}" do
220
+ app = -> { [200, {}, ['Hello, world']] }
221
+ ordered_errors = [Errno::ECONNREFUSED, err]
222
+ # allow(Net::HTTP).to receive(:start).with(anything, anything, hash_excluding(:use_ssl)).and_yield { raise Errno::ECONNREFUSED }
223
+ allow(Net::HTTP).to receive(:start).with(anything, anything, hash_excluding(:use_ssl)) do
224
+ raise ordered_errors.shift
225
+ end
226
+ response = Net::HTTPSuccess.allocate
227
+ allow(response).to receive(:body).and_return app.object_id.to_s
228
+ allow(Net::HTTP).to receive(:start).with(anything, anything, hash_including(use_ssl: true)).and_return(response).once
229
+ Capybara::Server.new(app).boot
230
+ expect(Net::HTTP).to have_received(:start).exactly(3).times
231
+ end
232
+ end
233
+
196
234
  def start_request(server, wait_time)
197
235
  # Start request, but don't wait for it to finish
198
236
  socket = TCPSocket.new(server.host, server.port)
@@ -3,10 +3,18 @@
3
3
  require 'spec_helper'
4
4
 
5
5
  RSpec.describe Capybara::Session do
6
- it "verifies a passed app is a rack app" do
7
- expect do
8
- Capybara::Session.new(:unknown, random: "hash")
9
- end.to raise_error TypeError, "The second parameter to Session::new should be a rack app if passed."
6
+ context "#new" do
7
+ it "should raise an error if passed non-existent driver" do
8
+ expect do
9
+ Capybara::Session.new(:quox, TestApp).driver
10
+ end.to raise_error(Capybara::DriverNotFoundError)
11
+ end
12
+
13
+ it "verifies a passed app is a rack app" do
14
+ expect do
15
+ Capybara::Session.new(:unknown, random: "hash")
16
+ end.to raise_error TypeError, "The second parameter to Session::new should be a rack app if passed."
17
+ end
10
18
  end
11
19
 
12
20
  context "current_driver" do
@@ -7,27 +7,23 @@ RSpec.shared_examples "Capybara::Session" do |session, mode|
7
7
  let(:session) { session }
8
8
 
9
9
  context 'with selenium driver' do
10
- before do
11
- @session = session
12
- end
13
-
14
10
  describe '#driver' do
15
11
  it "should be a selenium driver" do
16
- expect(@session.driver).to be_an_instance_of(Capybara::Selenium::Driver)
12
+ expect(session.driver).to be_an_instance_of(Capybara::Selenium::Driver)
17
13
  end
18
14
  end
19
15
 
20
16
  describe '#mode' do
21
17
  it "should remember the mode" do
22
- expect(@session.mode).to eq(mode)
18
+ expect(session.mode).to eq(mode)
23
19
  end
24
20
  end
25
21
 
26
22
  describe "#reset!" do
27
23
  it "freshly reset session should not be touched" do
28
- @session.instance_variable_set(:@touched, true)
29
- @session.reset!
30
- expect(@session.instance_variable_get(:@touched)).to eq false
24
+ session.instance_variable_set(:@touched, true)
25
+ session.reset!
26
+ expect(session.instance_variable_get(:@touched)).to eq false
31
27
  end
32
28
  end
33
29
 
@@ -35,7 +31,7 @@ RSpec.shared_examples "Capybara::Session" do |session, mode|
35
31
  before do
36
32
  @current_dir = Dir.getwd
37
33
  Dir.chdir(File.join(File.dirname(__FILE__), '..'))
38
- @env = { 'SELENIUM_BROWSER' => @session.driver.options[:browser].to_s }
34
+ @env = { 'SELENIUM_BROWSER' => session.driver.options[:browser].to_s }
39
35
  end
40
36
 
41
37
  after do
@@ -59,142 +55,167 @@ RSpec.shared_examples "Capybara::Session" do |session, mode|
59
55
 
60
56
  describe "#accept_alert", requires: [:modals] do
61
57
  it "supports a blockless mode" do
62
- @session.visit('/with_js')
63
- @session.click_link('Open alert')
64
- @session.accept_alert
65
- expect { @session.driver.browser.switch_to.alert }.to raise_error(@session.driver.send(:modal_error))
58
+ session.visit('/with_js')
59
+ session.click_link('Open alert')
60
+ session.accept_alert
61
+ expect { session.driver.browser.switch_to.alert }.to raise_error(session.driver.send(:modal_error))
66
62
  end
67
63
 
68
64
  it "can be called before visiting" do
69
- @session.accept_alert "Initial alert" do
70
- @session.visit('/initial_alert')
65
+ session.accept_alert "Initial alert" do
66
+ session.visit('/initial_alert')
71
67
  end
72
- expect(@session).to have_text('Initial alert page')
68
+ expect(session).to have_text('Initial alert page')
73
69
  end
74
70
  end
75
71
 
76
72
  context '#fill_in_with empty string and no options' do
77
73
  it 'should trigger change when clearing a field' do
78
- @session.visit('/with_js')
79
- @session.fill_in('with_change_event', with: '')
74
+ session.visit('/with_js')
75
+ session.fill_in('with_change_event', with: '')
80
76
  # click outside the field to trigger the change event
81
- @session.find(:css, 'body').click
82
- expect(@session).to have_selector(:css, '.change_event_triggered', match: :one)
77
+ session.find(:css, 'body').click
78
+ expect(session).to have_selector(:css, '.change_event_triggered', match: :one)
83
79
  end
84
80
  end
85
81
 
86
82
  context "#fill_in with { :clear => :backspace } fill_option", requires: [:js] do
87
83
  it 'should fill in a field, replacing an existing value' do
88
- @session.visit('/form')
89
- @session.fill_in('form_first_name',
90
- with: 'Harry',
91
- fill_options: { clear: :backspace })
92
- expect(@session.find(:fillable_field, 'form_first_name').value).to eq('Harry')
84
+ session.visit('/form')
85
+ session.fill_in('form_first_name',
86
+ with: 'Harry',
87
+ fill_options: { clear: :backspace })
88
+ expect(session.find(:fillable_field, 'form_first_name').value).to eq('Harry')
93
89
  end
94
90
 
95
91
  it 'should only trigger onchange once' do
96
- @session.visit('/with_js')
97
- @session.fill_in('with_change_event',
98
- with: 'some value',
99
- fill_options: { clear: :backspace })
92
+ session.visit('/with_js')
93
+ session.fill_in('with_change_event',
94
+ with: 'some value',
95
+ fill_options: { clear: :backspace })
100
96
  # click outside the field to trigger the change event
101
- @session.find(:css, 'body').click
102
- expect(@session.find(:css, '.change_event_triggered', match: :one)).to have_text 'some value'
97
+ session.find(:css, 'body').click
98
+ expect(session.find(:css, '.change_event_triggered', match: :one)).to have_text 'some value'
103
99
  end
104
100
 
105
101
  it 'should trigger change when clearing field' do
106
- @session.visit('/with_js')
107
- @session.fill_in('with_change_event',
108
- with: '',
109
- fill_options: { clear: :backspace })
102
+ session.visit('/with_js')
103
+ session.fill_in('with_change_event',
104
+ with: '',
105
+ fill_options: { clear: :backspace })
110
106
  # click outside the field to trigger the change event
111
- @session.find(:css, 'body').click
112
- expect(@session).to have_selector(:css, '.change_event_triggered', match: :one)
107
+ session.find(:css, 'body').click
108
+ expect(session).to have_selector(:css, '.change_event_triggered', match: :one)
113
109
  end
114
110
 
115
111
  it 'should trigger input event field_value.length times' do
116
- @session.visit('/with_js')
117
- @session.fill_in('with_change_event',
118
- with: '',
119
- fill_options: { clear: :backspace })
112
+ session.visit('/with_js')
113
+ session.fill_in('with_change_event',
114
+ with: '',
115
+ fill_options: { clear: :backspace })
120
116
  # click outside the field to trigger the change event
121
- @session.find(:css, 'body').click
122
- expect(@session).to have_xpath('//p[@class="input_event_triggered"]', count: 13)
117
+ session.find(:css, 'body').click
118
+ expect(session).to have_xpath('//p[@class="input_event_triggered"]', count: 13)
123
119
  end
124
120
  end
125
121
 
126
122
  context "#fill_in with { clear: :none } fill_options" do
127
123
  it 'should append to content in a field' do
128
- @session.visit('/form')
129
- @session.fill_in('form_first_name',
130
- with: 'Harry',
131
- fill_options: { clear: :none })
132
- expect(@session.find(:fillable_field, 'form_first_name').value).to eq('JohnHarry')
124
+ session.visit('/form')
125
+ session.fill_in('form_first_name',
126
+ with: 'Harry',
127
+ fill_options: { clear: :none })
128
+ expect(session.find(:fillable_field, 'form_first_name').value).to eq('JohnHarry')
129
+ end
130
+ end
131
+
132
+ context '#fill_in with Date' do
133
+ before do
134
+ session.visit('/form')
135
+ session.execute_script <<-JS
136
+ window.capybara_formDateFiredEvents = [];
137
+ ['focus', 'input', 'change'].forEach(function(eventType) {
138
+ document.getElementById('form_date')
139
+ .addEventListener(eventType, function() { window.capybara_formDateFiredEvents.push(eventType); });
140
+ });
141
+ JS
142
+ # work around weird FF issue where it would create an extra focus issue in some cases
143
+ session.find(:css, 'body').click
144
+ end
145
+
146
+ it "should generate standard events on changing value" do
147
+ session.fill_in('form_date', with: Date.today)
148
+ expect(session.evaluate_script('window.capybara_formDateFiredEvents')).to eq %w[focus input change]
149
+ end
150
+
151
+ it "should not generate input and change events if the value is not changed" do
152
+ session.fill_in('form_date', with: Date.today)
153
+ session.fill_in('form_date', with: Date.today)
154
+ # Chrome adds an extra focus for some reason - ok for now
155
+ expect(session.evaluate_script('window.capybara_formDateFiredEvents')).to eq(%w[focus input change])
133
156
  end
134
157
  end
135
158
 
136
159
  context "#fill_in with { clear: Array } fill_options" do
137
160
  it 'should pass the array through to the element' do
138
- pending "selenium-webdriver/geckodriver doesn't support complex sets of characters" if marionette?(@session)
161
+ pending "selenium-webdriver/geckodriver doesn't support complex sets of characters" if marionette?(session)
139
162
  # this is mainly for use with [[:control, 'a'], :backspace] - however since that is platform dependant I'm testing with something less useful
140
- @session.visit('/form')
141
- @session.fill_in('form_first_name',
142
- with: 'Harry',
143
- fill_options: { clear: [[:shift, 'abc'], :backspace] })
144
- expect(@session.find(:fillable_field, 'form_first_name').value).to eq('JohnABHarry')
163
+ session.visit('/form')
164
+ session.fill_in('form_first_name',
165
+ with: 'Harry',
166
+ fill_options: { clear: [[:shift, 'abc'], :backspace] })
167
+ expect(session.find(:fillable_field, 'form_first_name').value).to eq('JohnABHarry')
145
168
  end
146
169
  end
147
170
 
148
171
  describe "#path" do
149
172
  it "returns xpath" do
150
173
  # this is here because it is testing for an XPath that is specific to the algorithm used in the selenium driver
151
- @session.visit('/path')
152
- element = @session.find(:link, 'Second Link')
174
+ session.visit('/path')
175
+ element = session.find(:link, 'Second Link')
153
176
  expect(element.path).to eq('/html/body/div[2]/a[1]')
154
177
  end
155
178
  end
156
179
 
157
180
  describe "all with disappearing elements" do
158
181
  it "ignores stale elements in results" do
159
- @session.visit('/path')
160
- elements = @session.all(:link) { |_node| raise Selenium::WebDriver::Error::StaleElementReferenceError }
182
+ session.visit('/path')
183
+ elements = session.all(:link) { |_node| raise Selenium::WebDriver::Error::StaleElementReferenceError }
161
184
  expect(elements.size).to eq 0
162
185
  end
163
186
  end
164
187
 
165
188
  describe "#evaluate_script" do
166
189
  it "can return an element" do
167
- @session.visit('/form')
168
- element = @session.evaluate_script("document.getElementById('form_title')")
169
- expect(element).to eq @session.find(:id, 'form_title')
190
+ session.visit('/form')
191
+ element = session.evaluate_script("document.getElementById('form_title')")
192
+ expect(element).to eq session.find(:id, 'form_title')
170
193
  end
171
194
 
172
195
  it "can return arrays of nested elements" do
173
- @session.visit('/form')
174
- elements = @session.evaluate_script('document.querySelectorAll("#form_city option")')
175
- elements.each do |el|
176
- expect(el).to be_instance_of Capybara::Node::Element
177
- end
178
- expect(elements).to eq @session.find(:css, '#form_city').all(:css, 'option').to_a
196
+ session.visit('/form')
197
+ elements = session.evaluate_script('document.querySelectorAll("#form_city option")')
198
+ expect(elements).to all(be_instance_of Capybara::Node::Element)
199
+ expect(elements).to eq session.find(:css, '#form_city').all(:css, 'option').to_a
179
200
  end
180
201
 
181
202
  it "can return hashes with elements" do
182
- @session.visit('/form')
183
- result = @session.evaluate_script("{ a: document.getElementById('form_title'), b: {c: document.querySelectorAll('#form_city option')}}")
203
+ session.visit('/form')
204
+ result = session.evaluate_script("{ a: document.getElementById('form_title'), b: {c: document.querySelectorAll('#form_city option')}}")
184
205
  expect(result).to eq(
185
- 'a' => @session.find(:id, 'form_title'),
206
+ 'a' => session.find(:id, 'form_title'),
186
207
  'b' => {
187
- 'c' => @session.find(:css, '#form_city').all(:css, 'option').to_a
208
+ 'c' => session.find(:css, '#form_city').all(:css, 'option').to_a
188
209
  }
189
210
  )
190
211
  end
191
212
 
192
213
  describe "#evaluate_async_script" do
193
214
  it "will timeout if the script takes too long" do
194
- @session.visit('/with_js')
215
+ session.visit('/with_js')
195
216
  expect do
196
- @session.using_wait_time(1) do
197
- @session.evaluate_async_script("var cb = arguments[0]; setTimeout(function(){ cb(null) }, 3000)")
217
+ session.using_wait_time(1) do
218
+ session.evaluate_async_script("var cb = arguments[0]; setTimeout(function(){ cb(null) }, 3000)")
198
219
  end
199
220
  end.to raise_error Selenium::WebDriver::Error::ScriptTimeoutError
200
221
  end
@@ -203,27 +224,28 @@ RSpec.shared_examples "Capybara::Session" do |session, mode|
203
224
 
204
225
  describe "Element#inspect" do
205
226
  it "outputs obsolete elements" do
206
- @session.visit('/form')
207
- el = @session.find(:button, 'Click me!').click
208
- expect(@session).to have_no_button('Click me!')
209
- expect(el).not_to receive(:synchronize)
227
+ session.visit('/form')
228
+ el = session.find(:button, 'Click me!').click
229
+ expect(session).to have_no_button('Click me!')
230
+ allow(el).to receive(:synchronize)
210
231
  expect(el.inspect).to eq "Obsolete #<Capybara::Node::Element>"
232
+ expect(el).not_to have_received(:synchronize)
211
233
  end
212
234
  end
213
235
 
214
236
  describe "Element#click" do
215
237
  it "should handle fixed headers/footers" do
216
- @session.visit('/with_fixed_header_footer')
217
- # @session.click_link('Go to root')
218
- @session.find(:link, 'Go to root').click
219
- expect(@session).to have_current_path('/')
238
+ session.visit('/with_fixed_header_footer')
239
+ # session.click_link('Go to root')
240
+ session.find(:link, 'Go to root').click
241
+ expect(session).to have_current_path('/')
220
242
  end
221
243
  end
222
244
 
223
245
  context "Windows" do
224
246
  it "can't close the primary window" do
225
247
  expect do
226
- @session.current_window.close
248
+ session.current_window.close
227
249
  end.to raise_error(ArgumentError, 'Not allowed to close the primary window')
228
250
  end
229
251
  end