capybara 3.14.0 → 3.15.0

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