capybara 3.0.3 → 3.1.0

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