capybara 3.14.0 → 3.15.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 (46) hide show
  1. checksums.yaml +4 -4
  2. data/History.md +13 -0
  3. data/README.md +2 -1
  4. data/lib/capybara/node/actions.rb +37 -6
  5. data/lib/capybara/node/matchers.rb +10 -2
  6. data/lib/capybara/selector.rb +117 -1
  7. data/lib/capybara/selector/selector.rb +7 -0
  8. data/lib/capybara/selector/xpath_extensions.rb +9 -0
  9. data/lib/capybara/selenium/driver.rb +13 -2
  10. data/lib/capybara/selenium/driver_specializations/safari_driver.rb +15 -0
  11. data/lib/capybara/selenium/extensions/find.rb +2 -1
  12. data/lib/capybara/selenium/node.rb +1 -1
  13. data/lib/capybara/selenium/nodes/firefox_node.rb +1 -1
  14. data/lib/capybara/selenium/nodes/safari_node.rb +145 -0
  15. data/lib/capybara/spec/session/attach_file_spec.rb +46 -27
  16. data/lib/capybara/spec/session/click_button_spec.rb +65 -60
  17. data/lib/capybara/spec/session/element/matches_selector_spec.rb +40 -39
  18. data/lib/capybara/spec/session/fill_in_spec.rb +3 -3
  19. data/lib/capybara/spec/session/find_spec.rb +5 -0
  20. data/lib/capybara/spec/session/has_table_spec.rb +120 -0
  21. data/lib/capybara/spec/session/node_spec.rb +3 -3
  22. data/lib/capybara/spec/session/reset_session_spec.rb +8 -7
  23. data/lib/capybara/spec/session/window/become_closed_spec.rb +20 -17
  24. data/lib/capybara/spec/session/window/window_spec.rb +44 -48
  25. data/lib/capybara/spec/views/form.erb +5 -0
  26. data/lib/capybara/spec/views/tables.erb +67 -0
  27. data/lib/capybara/spec/views/with_html.erb +2 -2
  28. data/lib/capybara/spec/views/with_js.erb +1 -0
  29. data/lib/capybara/version.rb +1 -1
  30. data/spec/capybara_spec.rb +4 -4
  31. data/spec/css_builder_spec.rb +2 -0
  32. data/spec/dsl_spec.rb +13 -17
  33. data/spec/rack_test_spec.rb +77 -85
  34. data/spec/rspec/features_spec.rb +2 -0
  35. data/spec/rspec/shared_spec_matchers.rb +34 -35
  36. data/spec/rspec_spec.rb +11 -13
  37. data/spec/selector_spec.rb +31 -0
  38. data/spec/selenium_spec_chrome.rb +25 -25
  39. data/spec/selenium_spec_firefox.rb +62 -35
  40. data/spec/selenium_spec_firefox_remote.rb +2 -0
  41. data/spec/selenium_spec_safari.rb +148 -0
  42. data/spec/server_spec.rb +40 -44
  43. data/spec/shared_selenium_session.rb +27 -21
  44. data/spec/spec_helper.rb +4 -0
  45. data/spec/xpath_builder_spec.rb +2 -0
  46. metadata +7 -3
@@ -94,6 +94,11 @@
94
94
  <input type="file" name="form[image]" id="form_image"/>
95
95
  </p>
96
96
 
97
+ <p>
98
+ <label for="form_hidden_image">Hidden Image</label>
99
+ <input type="file" name="form[hidden_image]" id="form_hidden_image" style="display: none"/>
100
+ </p>
101
+
97
102
  <p>
98
103
  <input type="hidden" name="form[token]" value="12345" id="form_token"/>
99
104
  </p>
@@ -61,3 +61,70 @@
61
61
  </tr>
62
62
  </table>
63
63
  </form>
64
+
65
+ <table>
66
+ <caption>Horizontal Headers</caption>
67
+ <thead>
68
+ <tr>
69
+ <th>First Name</th>
70
+ <th>Last Name</th>
71
+ <th>City</th>
72
+ </tr>
73
+ </thead>
74
+ <tbody>
75
+ <tr>
76
+ <td>Thomas</td>
77
+ <td>Walpole</td>
78
+ <td>Oceanside</td>
79
+ </tr>
80
+ <tr>
81
+ <td>Danilo</td>
82
+ <td>Wilkinson</td>
83
+ <td>Johnsonville</td>
84
+ </tr>
85
+ <tr>
86
+ <td>Vern</td>
87
+ <td>Konopelski</td>
88
+ <td>Everette</td>
89
+ </tr>
90
+ <tr>
91
+ <td>Ratke</td>
92
+ <td>Lawrence</td>
93
+ <td>East Sorayashire</td>
94
+ </tr>
95
+ <tr>
96
+ <td>Palmer</td>
97
+ <td>Sawayn</td>
98
+ <td>West Trinidad</td>
99
+ </tr>
100
+ </tbody>
101
+ </table>
102
+
103
+ <table>
104
+ <caption>Vertical Headers</caption>
105
+ <tr>
106
+ <th>First Name</th>
107
+ <td>Thomas</td>
108
+ <td>Danilo</td>
109
+ <td>Vern</td>
110
+ <td>Ratke</td>
111
+ <td>Palmer</td>
112
+ </tr>
113
+ <tr>
114
+ <th>Last Name</th>
115
+ <td>Walpole</td>
116
+ <td>Wilkinson</td>
117
+ <td>Konopelski</td>
118
+ <td>Lawrence</td>
119
+ <td>Sawayn</td>
120
+ </tr>
121
+ <tr>
122
+ <th>City</th>
123
+ <td>Oceanside</td>
124
+ <td>Johnsonville</td>
125
+ <td>Everette</td>
126
+ <td>East Sorayashire</td>
127
+ <td>West Trinidad</td>
128
+ </tr>
129
+ </table>
130
+
@@ -21,9 +21,9 @@
21
21
  Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod
22
22
  tempor incididunt ut <a href="/with_simple_html" title="awesome title" class="simple">labore</a>
23
23
  et dolore magna aliqua. Ut enim ad minim veniam,
24
- quis nostrud exercitation <a href="/foo" id="foo">ullamco</a> laboris nisi
24
+ quis nostrud exercitation <a href="/foo" id="foo" data-test-id="test-foo">ullamco</a> laboris nisi
25
25
  ut aliquip ex ea commodo consequat.
26
- <a href="/with_simple_html" aria-label="Go to simple"><img id="first_image" width="20" height="20" alt="awesome image" /></a>
26
+ <a href="/with_simple_html" aria-label="Go to simple" data-test-id="test-simple"><img id="first_image" width="20" height="20" alt="awesome image" src="data:image/gif;base64,R0lGODlhAQABAIAAAAUEBAAAACwAAAAAAQABAAACAkQBADs="/></a>
27
27
  </p>
28
28
 
29
29
  <p class="para" id="second" style="display: inline;">
@@ -137,6 +137,7 @@
137
137
 
138
138
  <p>
139
139
  <input type="file" id="hidden_file" style="opacity:0; display: none;">
140
+ <label for="hidden_file">Label for hidden file input</label>
140
141
  </p>
141
142
 
142
143
  <p>
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Capybara
4
- VERSION = '3.14.0'
4
+ VERSION = '3.15.0'
5
5
  end
@@ -4,12 +4,12 @@ require 'spec_helper'
4
4
 
5
5
  RSpec.describe Capybara do
6
6
  describe 'default_max_wait_time' do
7
- after do
8
- Capybara.default_max_wait_time = @previous_default_time
9
- end
7
+ before { @previous_default_time = Capybara.default_max_wait_time }
8
+
9
+ after { Capybara.default_max_wait_time = @previous_default_time } # rubocop:disable RSpec/InstanceVariable
10
10
 
11
11
  it 'should be changeable' do
12
- @previous_default_time = Capybara.default_max_wait_time
12
+ expect(Capybara.default_max_wait_time).not_to eq(5)
13
13
  Capybara.default_max_wait_time = 5
14
14
  expect(Capybara.default_max_wait_time).to eq(5)
15
15
  end
@@ -2,6 +2,7 @@
2
2
 
3
3
  require 'spec_helper'
4
4
 
5
+ # rubocop:disable RSpec/InstanceVariable
5
6
  RSpec.describe Capybara::Selector::CSSBuilder do
6
7
  let :builder do
7
8
  ::Capybara::Selector::CSSBuilder.new(@css)
@@ -97,3 +98,4 @@ RSpec.describe Capybara::Selector::CSSBuilder do
97
98
  end
98
99
  end
99
100
  end
101
+ # rubocop:enable RSpec/InstanceVariable
@@ -115,14 +115,11 @@ RSpec.describe Capybara::DSL do
115
115
  end
116
116
  end
117
117
 
118
+ # rubocop:disable RSpec/InstanceVariable
118
119
  describe '#using_wait_time' do
119
- before do
120
- @previous_wait_time = Capybara.default_max_wait_time
121
- end
120
+ before { @previous_wait_time = Capybara.default_max_wait_time }
122
121
 
123
- after do
124
- Capybara.default_max_wait_time = @previous_wait_time
125
- end
122
+ after { Capybara.default_max_wait_time = @previous_wait_time }
126
123
 
127
124
  it 'should switch the wait time and switch it back' do
128
125
  in_block = nil
@@ -142,6 +139,7 @@ RSpec.describe Capybara::DSL do
142
139
  expect(Capybara.default_max_wait_time).to eq(@previous_wait_time)
143
140
  end
144
141
  end
142
+ # rubocop:enable RSpec/InstanceVariable
145
143
 
146
144
  describe '#app' do
147
145
  it 'should be changeable' do
@@ -249,31 +247,29 @@ RSpec.describe Capybara::DSL do
249
247
  end
250
248
 
251
249
  describe 'the DSL' do
252
- before do
253
- @session = Class.new { include Capybara::DSL }.new
254
- end
250
+ let(:session) { Class.new { include Capybara::DSL }.new }
255
251
 
256
252
  it 'should be possible to include it in another class' do
257
- @session.visit('/with_html')
258
- @session.click_link('ullamco')
259
- expect(@session.body).to include('Another World')
253
+ session.visit('/with_html')
254
+ session.click_link('ullamco')
255
+ expect(session.body).to include('Another World')
260
256
  end
261
257
 
262
258
  it "should provide a 'page' shortcut for more expressive tests" do
263
- @session.page.visit('/with_html')
264
- @session.page.click_link('ullamco')
265
- expect(@session.page.body).to include('Another World')
259
+ session.page.visit('/with_html')
260
+ session.page.click_link('ullamco')
261
+ expect(session.page.body).to include('Another World')
266
262
  end
267
263
 
268
264
  it "should provide an 'using_session' shortcut" do
269
265
  allow(Capybara).to receive(:using_session)
270
- @session.using_session(:name)
266
+ session.using_session(:name)
271
267
  expect(Capybara).to have_received(:using_session).with(:name)
272
268
  end
273
269
 
274
270
  it "should provide a 'using_wait_time' shortcut" do
275
271
  allow(Capybara).to receive(:using_wait_time)
276
- @session.using_wait_time(6)
272
+ session.using_wait_time(6)
277
273
  expect(Capybara).to have_received(:using_wait_time).with(6)
278
274
  end
279
275
  end
@@ -37,55 +37,53 @@ end
37
37
 
38
38
  RSpec.describe Capybara::Session do # rubocop:disable RSpec/MultipleDescribes
39
39
  context 'with rack test driver' do
40
- before do
41
- @session = TestSessions::RackTest
42
- end
40
+ let(:session) { TestSessions::RackTest }
43
41
 
44
42
  describe '#driver' do
45
43
  it 'should be a rack test driver' do
46
- expect(@session.driver).to be_an_instance_of(Capybara::RackTest::Driver)
44
+ expect(session.driver).to be_an_instance_of(Capybara::RackTest::Driver)
47
45
  end
48
46
  end
49
47
 
50
48
  describe '#mode' do
51
49
  it 'should remember the mode' do
52
- expect(@session.mode).to eq(:rack_test)
50
+ expect(session.mode).to eq(:rack_test)
53
51
  end
54
52
  end
55
53
 
56
54
  describe '#click_link' do
57
55
  after do
58
- @session.driver.options[:respect_data_method] = false
56
+ session.driver.options[:respect_data_method] = false
59
57
  end
60
58
 
61
59
  it 'should use data-method if option is true' do
62
- @session.driver.options[:respect_data_method] = true
63
- @session.visit '/with_html'
64
- @session.click_link 'A link with data-method'
65
- expect(@session.html).to include('The requested object was deleted')
60
+ session.driver.options[:respect_data_method] = true
61
+ session.visit '/with_html'
62
+ session.click_link 'A link with data-method'
63
+ expect(session.html).to include('The requested object was deleted')
66
64
  end
67
65
 
68
66
  it 'should not use data-method if option is false' do
69
- @session.driver.options[:respect_data_method] = false
70
- @session.visit '/with_html'
71
- @session.click_link 'A link with data-method'
72
- expect(@session.html).to include('Not deleted')
67
+ session.driver.options[:respect_data_method] = false
68
+ session.visit '/with_html'
69
+ session.click_link 'A link with data-method'
70
+ expect(session.html).to include('Not deleted')
73
71
  end
74
72
 
75
73
  it "should use data-method if available even if it's capitalized" do
76
- @session.driver.options[:respect_data_method] = true
77
- @session.visit '/with_html'
78
- @session.click_link 'A link with capitalized data-method'
79
- expect(@session.html).to include('The requested object was deleted')
74
+ session.driver.options[:respect_data_method] = true
75
+ session.visit '/with_html'
76
+ session.click_link 'A link with capitalized data-method'
77
+ expect(session.html).to include('The requested object was deleted')
80
78
  end
81
79
  end
82
80
 
83
81
  describe '#fill_in' do
84
82
  it 'should warn that :fill_options are not supported' do
85
83
  allow_any_instance_of(Capybara::RackTest::Node).to receive(:warn)
86
- @session.visit '/with_html'
87
- field = @session.fill_in 'test_field', with: 'not_monkey', fill_options: { random: true }
88
- expect(@session).to have_field('test_field', with: 'not_monkey')
84
+ session.visit '/with_html'
85
+ field = session.fill_in 'test_field', with: 'not_monkey', fill_options: { random: true }
86
+ expect(session).to have_field('test_field', with: 'not_monkey')
89
87
  expect(field.base).to have_received(:warn).with("Options passed to Node#set but the RackTest driver doesn't support any - ignoring")
90
88
  end
91
89
  end
@@ -93,50 +91,50 @@ RSpec.describe Capybara::Session do # rubocop:disable RSpec/MultipleDescribes
93
91
  describe '#attach_file' do
94
92
  context 'with multipart form' do
95
93
  it 'should submit an empty form-data section if no file is submitted' do
96
- @session.visit('/form')
97
- @session.click_button('Upload Empty')
98
- expect(@session.html).to include('Successfully ignored empty file field.')
94
+ session.visit('/form')
95
+ session.click_button('Upload Empty')
96
+ expect(session.html).to include('Successfully ignored empty file field.')
99
97
  end
100
98
  end
101
99
 
102
100
  it 'should not submit an obsolete mime type' do
103
- @test_jpg_file_path = File.expand_path('fixtures/capybara.csv', File.dirname(__FILE__))
104
- @session.visit('/form')
105
- @session.attach_file 'form_document', @test_jpg_file_path
106
- @session.click_button('Upload Single')
107
- expect(@session).to have_content('Content-type: text/csv')
101
+ test_jpg_file_path = File.expand_path('fixtures/capybara.csv', File.dirname(__FILE__))
102
+ session.visit('/form')
103
+ session.attach_file 'form_document', test_jpg_file_path
104
+ session.click_button('Upload Single')
105
+ expect(session).to have_content('Content-type: text/csv')
108
106
  end
109
107
  end
110
108
 
111
109
  describe '#click' do
112
110
  context 'on a label' do
113
111
  it 'should toggle the associated checkbox' do
114
- @session.visit('/form')
115
- expect(@session).to have_unchecked_field('form_pets_cat')
116
- @session.find(:label, 'Cat').click
117
- expect(@session).to have_checked_field('form_pets_cat')
118
- @session.find(:label, 'Cat').click
119
- expect(@session).to have_unchecked_field('form_pets_cat')
120
- @session.find(:label, 'McLaren').click
121
- expect(@session).to have_checked_field('form_cars_mclaren', visible: :hidden)
112
+ session.visit('/form')
113
+ expect(session).to have_unchecked_field('form_pets_cat')
114
+ session.find(:label, 'Cat').click
115
+ expect(session).to have_checked_field('form_pets_cat')
116
+ session.find(:label, 'Cat').click
117
+ expect(session).to have_unchecked_field('form_pets_cat')
118
+ session.find(:label, 'McLaren').click
119
+ expect(session).to have_checked_field('form_cars_mclaren', visible: :hidden)
122
120
  end
123
121
 
124
122
  it 'should toggle the associated radio' do
125
- @session.visit('/form')
126
- expect(@session).to have_unchecked_field('gender_male')
127
- @session.find(:label, 'Male').click
128
- expect(@session).to have_checked_field('gender_male')
129
- @session.find(:label, 'Female').click
130
- expect(@session).to have_unchecked_field('gender_male')
123
+ session.visit('/form')
124
+ expect(session).to have_unchecked_field('gender_male')
125
+ session.find(:label, 'Male').click
126
+ expect(session).to have_checked_field('gender_male')
127
+ session.find(:label, 'Female').click
128
+ expect(session).to have_unchecked_field('gender_male')
131
129
  end
132
130
  end
133
131
  end
134
132
 
135
133
  describe '#text' do
136
134
  it 'should return original text content for textareas' do
137
- @session.visit('/with_html')
138
- @session.find_field('normal', type: 'textarea', with: 'banana').set('hello')
139
- normal = @session.find(:css, '#normal')
135
+ session.visit('/with_html')
136
+ session.find_field('normal', type: 'textarea', with: 'banana').set('hello')
137
+ normal = session.find(:css, '#normal')
140
138
  expect(normal.value).to eq 'hello'
141
139
  expect(normal.text).to eq 'banana'
142
140
  end
@@ -144,8 +142,8 @@ RSpec.describe Capybara::Session do # rubocop:disable RSpec/MultipleDescribes
144
142
 
145
143
  describe '#style' do
146
144
  it 'should raise an error' do
147
- @session.visit('/with_html')
148
- el = @session.find(:css, '#first')
145
+ session.visit('/with_html')
146
+ el = session.find(:css, '#first')
149
147
  expect { el.style('display') }.to raise_error NotImplementedError, /not process CSS/
150
148
  end
151
149
  end
@@ -153,87 +151,81 @@ RSpec.describe Capybara::Session do # rubocop:disable RSpec/MultipleDescribes
153
151
  end
154
152
 
155
153
  RSpec.describe Capybara::RackTest::Driver do
156
- before do
157
- @driver = TestSessions::RackTest.driver
158
- end
154
+ let(:driver) { TestSessions::RackTest.driver }
159
155
 
160
156
  describe ':headers option' do
161
157
  it 'should always set headers' do
162
- @driver = Capybara::RackTest::Driver.new(TestApp, headers: { 'HTTP_FOO' => 'foobar' })
163
- @driver.visit('/get_header')
164
- expect(@driver.html).to include('foobar')
158
+ driver = Capybara::RackTest::Driver.new(TestApp, headers: { 'HTTP_FOO' => 'foobar' })
159
+ driver.visit('/get_header')
160
+ expect(driver.html).to include('foobar')
165
161
  end
166
162
 
167
163
  it 'should keep headers on link clicks' do
168
- @driver = Capybara::RackTest::Driver.new(TestApp, headers: { 'HTTP_FOO' => 'foobar' })
169
- @driver.visit('/header_links')
170
- @driver.find_xpath('.//a').first.click
171
- expect(@driver.html).to include('foobar')
164
+ driver = Capybara::RackTest::Driver.new(TestApp, headers: { 'HTTP_FOO' => 'foobar' })
165
+ driver.visit('/header_links')
166
+ driver.find_xpath('.//a').first.click
167
+ expect(driver.html).to include('foobar')
172
168
  end
173
169
 
174
170
  it 'should keep headers on form submit' do
175
- @driver = Capybara::RackTest::Driver.new(TestApp, headers: { 'HTTP_FOO' => 'foobar' })
176
- @driver.visit('/header_links')
177
- @driver.find_xpath('.//input').first.click
178
- expect(@driver.html).to include('foobar')
171
+ driver = Capybara::RackTest::Driver.new(TestApp, headers: { 'HTTP_FOO' => 'foobar' })
172
+ driver.visit('/header_links')
173
+ driver.find_xpath('.//input').first.click
174
+ expect(driver.html).to include('foobar')
179
175
  end
180
176
 
181
177
  it 'should keep headers on redirects' do
182
- @driver = Capybara::RackTest::Driver.new(TestApp, headers: { 'HTTP_FOO' => 'foobar' })
183
- @driver.visit('/get_header_via_redirect')
184
- expect(@driver.html).to include('foobar')
178
+ driver = Capybara::RackTest::Driver.new(TestApp, headers: { 'HTTP_FOO' => 'foobar' })
179
+ driver.visit('/get_header_via_redirect')
180
+ expect(driver.html).to include('foobar')
185
181
  end
186
182
  end
187
183
 
188
184
  describe ':follow_redirects option' do
189
185
  it 'defaults to following redirects' do
190
- @driver = Capybara::RackTest::Driver.new(TestApp)
186
+ driver = Capybara::RackTest::Driver.new(TestApp)
191
187
 
192
- @driver.visit('/redirect')
193
- expect(@driver.response.header['Location']).to be_nil
194
- expect(@driver.current_url).to match %r{/landed$}
188
+ driver.visit('/redirect')
189
+ expect(driver.response.header['Location']).to be_nil
190
+ expect(driver.current_url).to match %r{/landed$}
195
191
  end
196
192
 
197
193
  it 'is possible to not follow redirects' do
198
- @driver = Capybara::RackTest::Driver.new(TestApp, follow_redirects: false)
194
+ driver = Capybara::RackTest::Driver.new(TestApp, follow_redirects: false)
199
195
 
200
- @driver.visit('/redirect')
201
- expect(@driver.response.header['Location']).to match %r{/redirect_again$}
202
- expect(@driver.current_url).to match %r{/redirect$}
196
+ driver.visit('/redirect')
197
+ expect(driver.response.header['Location']).to match %r{/redirect_again$}
198
+ expect(driver.current_url).to match %r{/redirect$}
203
199
  end
204
200
  end
205
201
 
206
202
  describe ':redirect_limit option' do
207
203
  context 'with default redirect limit' do
208
- before do
209
- @driver = Capybara::RackTest::Driver.new(TestApp)
210
- end
204
+ let(:driver) { Capybara::RackTest::Driver.new(TestApp) }
211
205
 
212
206
  it 'should follow 5 redirects' do
213
- @driver.visit('/redirect/5/times')
214
- expect(@driver.html).to include('redirection complete')
207
+ driver.visit('/redirect/5/times')
208
+ expect(driver.html).to include('redirection complete')
215
209
  end
216
210
 
217
211
  it 'should not follow more than 6 redirects' do
218
212
  expect do
219
- @driver.visit('/redirect/6/times')
213
+ driver.visit('/redirect/6/times')
220
214
  end.to raise_error(Capybara::InfiniteRedirectError)
221
215
  end
222
216
  end
223
217
 
224
218
  context 'with 21 redirect limit' do
225
- before do
226
- @driver = Capybara::RackTest::Driver.new(TestApp, redirect_limit: 21)
227
- end
219
+ let(:driver) { Capybara::RackTest::Driver.new(TestApp, redirect_limit: 21) }
228
220
 
229
221
  it 'should follow 21 redirects' do
230
- @driver.visit('/redirect/21/times')
231
- expect(@driver.html).to include('redirection complete')
222
+ driver.visit('/redirect/21/times')
223
+ expect(driver.html).to include('redirection complete')
232
224
  end
233
225
 
234
226
  it 'should not follow more than 21 redirects' do
235
227
  expect do
236
- @driver.visit('/redirect/22/times')
228
+ driver.visit('/redirect/22/times')
237
229
  end.to raise_error(Capybara::InfiniteRedirectError)
238
230
  end
239
231
  end