capybara 3.16.1 → 3.33.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (197) hide show
  1. checksums.yaml +4 -4
  2. data/.yardopts +1 -0
  3. data/History.md +321 -0
  4. data/README.md +51 -60
  5. data/lib/capybara.rb +71 -114
  6. data/lib/capybara/config.rb +8 -5
  7. data/lib/capybara/cucumber.rb +1 -1
  8. data/lib/capybara/driver/node.rb +15 -3
  9. data/lib/capybara/dsl.rb +10 -2
  10. data/lib/capybara/helpers.rb +5 -3
  11. data/lib/capybara/minitest.rb +242 -141
  12. data/lib/capybara/minitest/spec.rb +159 -90
  13. data/lib/capybara/node/actions.rb +85 -74
  14. data/lib/capybara/node/base.rb +4 -4
  15. data/lib/capybara/node/document.rb +2 -2
  16. data/lib/capybara/node/document_matchers.rb +3 -3
  17. data/lib/capybara/node/element.rb +216 -117
  18. data/lib/capybara/node/finders.rb +65 -65
  19. data/lib/capybara/node/matchers.rb +228 -126
  20. data/lib/capybara/node/simple.rb +9 -4
  21. data/lib/capybara/queries/ancestor_query.rb +5 -7
  22. data/lib/capybara/queries/base_query.rb +2 -1
  23. data/lib/capybara/queries/current_path_query.rb +1 -1
  24. data/lib/capybara/queries/selector_query.rb +296 -30
  25. data/lib/capybara/queries/sibling_query.rb +5 -4
  26. data/lib/capybara/queries/style_query.rb +2 -2
  27. data/lib/capybara/queries/text_query.rb +13 -1
  28. data/lib/capybara/queries/title_query.rb +1 -1
  29. data/lib/capybara/rack_test/browser.rb +7 -2
  30. data/lib/capybara/rack_test/driver.rb +1 -1
  31. data/lib/capybara/rack_test/form.rb +1 -1
  32. data/lib/capybara/rack_test/node.rb +43 -7
  33. data/lib/capybara/registration_container.rb +44 -0
  34. data/lib/capybara/registrations/drivers.rb +36 -0
  35. data/lib/capybara/registrations/patches/puma_ssl.rb +27 -0
  36. data/lib/capybara/registrations/servers.rb +44 -0
  37. data/lib/capybara/result.rb +36 -8
  38. data/lib/capybara/rspec/matcher_proxies.rb +6 -4
  39. data/lib/capybara/rspec/matchers.rb +100 -63
  40. data/lib/capybara/rspec/matchers/base.rb +23 -10
  41. data/lib/capybara/rspec/matchers/count_sugar.rb +37 -0
  42. data/lib/capybara/rspec/matchers/have_ancestor.rb +28 -0
  43. data/lib/capybara/rspec/matchers/have_current_path.rb +2 -2
  44. data/lib/capybara/rspec/matchers/have_selector.rb +16 -8
  45. data/lib/capybara/rspec/matchers/have_sibling.rb +27 -0
  46. data/lib/capybara/rspec/matchers/have_text.rb +4 -4
  47. data/lib/capybara/rspec/matchers/have_title.rb +2 -2
  48. data/lib/capybara/rspec/matchers/match_selector.rb +3 -3
  49. data/lib/capybara/rspec/matchers/match_style.rb +2 -2
  50. data/lib/capybara/rspec/matchers/spatial_sugar.rb +39 -0
  51. data/lib/capybara/selector.rb +219 -588
  52. data/lib/capybara/selector/builders/css_builder.rb +10 -6
  53. data/lib/capybara/selector/builders/xpath_builder.rb +1 -1
  54. data/lib/capybara/selector/css.rb +4 -2
  55. data/lib/capybara/selector/definition.rb +277 -0
  56. data/lib/capybara/selector/definition/button.rb +52 -0
  57. data/lib/capybara/selector/definition/checkbox.rb +26 -0
  58. data/lib/capybara/selector/definition/css.rb +10 -0
  59. data/lib/capybara/selector/definition/datalist_input.rb +35 -0
  60. data/lib/capybara/selector/definition/datalist_option.rb +25 -0
  61. data/lib/capybara/selector/definition/element.rb +27 -0
  62. data/lib/capybara/selector/definition/field.rb +40 -0
  63. data/lib/capybara/selector/definition/fieldset.rb +14 -0
  64. data/lib/capybara/selector/definition/file_field.rb +13 -0
  65. data/lib/capybara/selector/definition/fillable_field.rb +33 -0
  66. data/lib/capybara/selector/definition/frame.rb +17 -0
  67. data/lib/capybara/selector/definition/id.rb +6 -0
  68. data/lib/capybara/selector/definition/label.rb +62 -0
  69. data/lib/capybara/selector/definition/link.rb +54 -0
  70. data/lib/capybara/selector/definition/link_or_button.rb +16 -0
  71. data/lib/capybara/selector/definition/option.rb +27 -0
  72. data/lib/capybara/selector/definition/radio_button.rb +27 -0
  73. data/lib/capybara/selector/definition/select.rb +81 -0
  74. data/lib/capybara/selector/definition/table.rb +109 -0
  75. data/lib/capybara/selector/definition/table_row.rb +21 -0
  76. data/lib/capybara/selector/definition/xpath.rb +5 -0
  77. data/lib/capybara/selector/filter_set.rb +13 -9
  78. data/lib/capybara/selector/filters/base.rb +11 -2
  79. data/lib/capybara/selector/filters/locator_filter.rb +13 -3
  80. data/lib/capybara/selector/regexp_disassembler.rb +9 -2
  81. data/lib/capybara/selector/selector.rb +43 -448
  82. data/lib/capybara/selenium/atoms/getAttribute.min.js +1 -0
  83. data/lib/capybara/selenium/atoms/isDisplayed.min.js +1 -0
  84. data/lib/capybara/selenium/atoms/src/getAttribute.js +161 -0
  85. data/lib/capybara/selenium/atoms/src/isDisplayed.js +454 -0
  86. data/lib/capybara/selenium/driver.rb +125 -56
  87. data/lib/capybara/selenium/driver_specializations/chrome_driver.rb +73 -17
  88. data/lib/capybara/selenium/driver_specializations/edge_driver.rb +124 -0
  89. data/lib/capybara/selenium/driver_specializations/firefox_driver.rb +41 -2
  90. data/lib/capybara/selenium/driver_specializations/internet_explorer_driver.rb +14 -1
  91. data/lib/capybara/selenium/driver_specializations/safari_driver.rb +14 -5
  92. data/lib/capybara/selenium/extensions/file_input_click_emulation.rb +34 -0
  93. data/lib/capybara/selenium/extensions/find.rb +67 -45
  94. data/lib/capybara/selenium/extensions/html5_drag.rb +152 -36
  95. data/lib/capybara/selenium/extensions/modifier_keys_stack.rb +28 -0
  96. data/lib/capybara/selenium/logger_suppressor.rb +34 -0
  97. data/lib/capybara/selenium/node.rb +227 -56
  98. data/lib/capybara/selenium/nodes/chrome_node.rb +93 -8
  99. data/lib/capybara/selenium/nodes/edge_node.rb +104 -0
  100. data/lib/capybara/selenium/nodes/firefox_node.rb +37 -59
  101. data/lib/capybara/selenium/nodes/ie_node.rb +22 -0
  102. data/lib/capybara/selenium/nodes/safari_node.rb +27 -54
  103. data/lib/capybara/selenium/patches/action_pauser.rb +26 -0
  104. data/lib/capybara/selenium/patches/atoms.rb +18 -0
  105. data/lib/capybara/selenium/patches/is_displayed.rb +16 -0
  106. data/lib/capybara/selenium/patches/logs.rb +45 -0
  107. data/lib/capybara/server.rb +19 -3
  108. data/lib/capybara/server/animation_disabler.rb +2 -2
  109. data/lib/capybara/server/checker.rb +6 -2
  110. data/lib/capybara/server/middleware.rb +23 -13
  111. data/lib/capybara/session.rb +124 -106
  112. data/lib/capybara/session/config.rb +12 -10
  113. data/lib/capybara/session/matchers.rb +6 -6
  114. data/lib/capybara/spec/public/offset.js +6 -0
  115. data/lib/capybara/spec/public/test.js +94 -5
  116. data/lib/capybara/spec/session/all_spec.rb +84 -6
  117. data/lib/capybara/spec/session/ancestor_spec.rb +5 -0
  118. data/lib/capybara/spec/session/assert_current_path_spec.rb +5 -2
  119. data/lib/capybara/spec/session/assert_text_spec.rb +9 -5
  120. data/lib/capybara/spec/session/attach_file_spec.rb +14 -6
  121. data/lib/capybara/spec/session/check_spec.rb +10 -4
  122. data/lib/capybara/spec/session/choose_spec.rb +8 -2
  123. data/lib/capybara/spec/session/click_button_spec.rb +44 -1
  124. data/lib/capybara/spec/session/click_link_spec.rb +11 -0
  125. data/lib/capybara/spec/session/evaluate_script_spec.rb +12 -0
  126. data/lib/capybara/spec/session/fill_in_spec.rb +37 -2
  127. data/lib/capybara/spec/session/find_spec.rb +60 -6
  128. data/lib/capybara/spec/session/first_spec.rb +1 -1
  129. data/lib/capybara/spec/session/frame/switch_to_frame_spec.rb +14 -1
  130. data/lib/capybara/spec/session/frame/within_frame_spec.rb +12 -1
  131. data/lib/capybara/spec/session/has_ancestor_spec.rb +46 -0
  132. data/lib/capybara/spec/session/has_button_spec.rb +16 -0
  133. data/lib/capybara/spec/session/has_css_spec.rb +35 -6
  134. data/lib/capybara/spec/session/has_current_path_spec.rb +6 -4
  135. data/lib/capybara/spec/session/has_field_spec.rb +34 -0
  136. data/lib/capybara/spec/session/has_select_spec.rb +32 -4
  137. data/lib/capybara/spec/session/has_selector_spec.rb +4 -4
  138. data/lib/capybara/spec/session/has_sibling_spec.rb +50 -0
  139. data/lib/capybara/spec/session/has_table_spec.rb +51 -5
  140. data/lib/capybara/spec/session/has_text_spec.rb +47 -0
  141. data/lib/capybara/spec/session/matches_style_spec.rb +2 -2
  142. data/lib/capybara/spec/session/node_spec.rb +574 -16
  143. data/lib/capybara/spec/session/save_and_open_screenshot_spec.rb +2 -2
  144. data/lib/capybara/spec/session/save_screenshot_spec.rb +4 -4
  145. data/lib/capybara/spec/session/scroll_spec.rb +1 -1
  146. data/lib/capybara/spec/session/select_spec.rb +5 -10
  147. data/lib/capybara/spec/session/selectors_spec.rb +24 -3
  148. data/lib/capybara/spec/session/uncheck_spec.rb +2 -2
  149. data/lib/capybara/spec/session/unselect_spec.rb +1 -1
  150. data/lib/capybara/spec/session/window/window_spec.rb +10 -9
  151. data/lib/capybara/spec/spec_helper.rb +7 -2
  152. data/lib/capybara/spec/test_app.rb +26 -21
  153. data/lib/capybara/spec/views/animated.erb +49 -0
  154. data/lib/capybara/spec/views/form.erb +25 -4
  155. data/lib/capybara/spec/views/frame_child.erb +2 -1
  156. data/lib/capybara/spec/views/frame_one.erb +1 -0
  157. data/lib/capybara/spec/views/obscured.erb +9 -9
  158. data/lib/capybara/spec/views/offset.erb +32 -0
  159. data/lib/capybara/spec/views/react.erb +45 -0
  160. data/lib/capybara/spec/views/spatial.erb +31 -0
  161. data/lib/capybara/spec/views/with_animation.erb +29 -1
  162. data/lib/capybara/spec/views/with_dragula.erb +24 -0
  163. data/lib/capybara/spec/views/with_html.erb +28 -2
  164. data/lib/capybara/spec/views/with_js.erb +2 -1
  165. data/lib/capybara/spec/views/with_jstree.erb +26 -0
  166. data/lib/capybara/spec/views/with_sortable_js.erb +21 -0
  167. data/lib/capybara/version.rb +1 -1
  168. data/lib/capybara/window.rb +10 -10
  169. data/spec/basic_node_spec.rb +6 -6
  170. data/spec/capybara_spec.rb +28 -28
  171. data/spec/dsl_spec.rb +16 -3
  172. data/spec/filter_set_spec.rb +5 -5
  173. data/spec/fixtures/selenium_driver_rspec_failure.rb +1 -1
  174. data/spec/fixtures/selenium_driver_rspec_success.rb +1 -1
  175. data/spec/minitest_spec.rb +12 -2
  176. data/spec/minitest_spec_spec.rb +56 -45
  177. data/spec/rack_test_spec.rb +25 -12
  178. data/spec/regexp_dissassembler_spec.rb +53 -39
  179. data/spec/result_spec.rb +50 -54
  180. data/spec/rspec/features_spec.rb +1 -0
  181. data/spec/rspec/shared_spec_matchers.rb +78 -62
  182. data/spec/rspec_spec.rb +5 -5
  183. data/spec/sauce_spec_chrome.rb +1 -0
  184. data/spec/selector_spec.rb +26 -16
  185. data/spec/selenium_spec_chrome.rb +84 -5
  186. data/spec/selenium_spec_chrome_remote.rb +23 -8
  187. data/spec/selenium_spec_edge.rb +23 -8
  188. data/spec/selenium_spec_firefox.rb +16 -21
  189. data/spec/selenium_spec_firefox_remote.rb +4 -13
  190. data/spec/selenium_spec_ie.rb +23 -15
  191. data/spec/selenium_spec_safari.rb +17 -17
  192. data/spec/server_spec.rb +87 -42
  193. data/spec/session_spec.rb +11 -4
  194. data/spec/shared_selenium_node.rb +83 -0
  195. data/spec/shared_selenium_session.rb +62 -72
  196. data/spec/spec_helper.rb +43 -5
  197. metadata +114 -16
@@ -111,6 +111,41 @@ Capybara::SpecHelper.spec '#has_text?' do
111
111
  expect(@session).not_to have_text(/xxxxyzzz/)
112
112
  end
113
113
 
114
+ context 'with object implementing to_s and to_hash' do
115
+ it 'should work if the object is passed alone' do
116
+ with_to_hash = Class.new do
117
+ def to_s; '42' end
118
+ def to_hash; { value: 'Other hash' } end
119
+ end.new
120
+ @session.visit('/with_html')
121
+ expect(@session).to have_text(with_to_hash)
122
+ end
123
+
124
+ it 'should work if passed with empty options' do
125
+ with_to_hash = Class.new do
126
+ def to_s; '42' end
127
+ def to_hash; { value: 'Other hash' } end
128
+ end.new
129
+ @session.visit('/with_html')
130
+ if RUBY_VERSION >= '2.7'
131
+ expect(@session).to have_text(:visible, with_to_hash, **{})
132
+ else
133
+ expect(@session).to have_text(:visible, with_to_hash, {})
134
+ end
135
+ end
136
+
137
+ it 'should fail if passed without empty options' do
138
+ with_to_hash = Class.new do
139
+ def to_s; '42' end
140
+ def to_hash; { blah: 'Other hash' } end
141
+ end.new
142
+ @session.visit('/with_html')
143
+ expect do
144
+ expect(@session).to have_text(:visible, with_to_hash)
145
+ end.to raise_error(ArgumentError)
146
+ end
147
+ end
148
+
114
149
  context 'with exact: true option' do
115
150
  it 'should be true if text matches exactly' do
116
151
  @session.visit('/with_html')
@@ -139,6 +174,12 @@ Capybara::SpecHelper.spec '#has_text?' do
139
174
  expect(@session).to have_text(nil)
140
175
  end
141
176
 
177
+ it 'should warn when passed nil' do
178
+ @session.visit('/with_html')
179
+ expect_any_instance_of(Kernel).to receive(:warn).with(/Checking for expected text of nil is confusing/) # rubocop:disable RSpec/AnyInstance
180
+ expect(@session).to have_text(nil)
181
+ end
182
+
142
183
  it 'should wait for text to appear', requires: [:js] do
143
184
  Capybara.using_wait_time(3) do
144
185
  @session.visit('/with_js')
@@ -166,12 +207,14 @@ Capybara::SpecHelper.spec '#has_text?' do
166
207
  it 'should be true if the text occurs the given number of times' do
167
208
  @session.visit('/with_count')
168
209
  expect(@session).to have_text('count', count: 2)
210
+ expect(@session).to have_text('count').exactly(2).times
169
211
  end
170
212
 
171
213
  it 'should be false if the text occurs a different number of times than the given' do
172
214
  @session.visit('/with_count')
173
215
  expect(@session).not_to have_text('count', count: 0)
174
216
  expect(@session).not_to have_text('count', count: 1)
217
+ expect(@session).not_to have_text('count').once
175
218
  expect(@session).not_to have_text(/count/, count: 3)
176
219
  end
177
220
 
@@ -186,12 +229,14 @@ Capybara::SpecHelper.spec '#has_text?' do
186
229
  it 'should be true when text occurs same or fewer times than given' do
187
230
  @session.visit('/with_count')
188
231
  expect(@session).to have_text('count', maximum: 2)
232
+ expect(@session).to have_text('count').at_most(2).times
189
233
  expect(@session).to have_text(/count/, maximum: 3)
190
234
  end
191
235
 
192
236
  it 'should be false when text occurs more times than given' do
193
237
  @session.visit('/with_count')
194
238
  expect(@session).not_to have_text('count', maximum: 1)
239
+ expect(@session).not_to have_text('count').at_most(1).times
195
240
  expect(@session).not_to have_text('count', maximum: 0)
196
241
  end
197
242
 
@@ -206,12 +251,14 @@ Capybara::SpecHelper.spec '#has_text?' do
206
251
  it 'should be true when text occurs same or more times than given' do
207
252
  @session.visit('/with_count')
208
253
  expect(@session).to have_text('count', minimum: 2)
254
+ expect(@session).to have_text('count').at_least(2).times
209
255
  expect(@session).to have_text(/count/, minimum: 0)
210
256
  end
211
257
 
212
258
  it 'should be false when text occurs fewer times than given' do
213
259
  @session.visit('/with_count')
214
260
  expect(@session).not_to have_text('count', minimum: 3)
261
+ expect(@session).not_to have_text('count').at_least(3).times
215
262
  end
216
263
 
217
264
  it 'should coerce minimum to an integer' do
@@ -24,8 +24,8 @@ Capybara::SpecHelper.spec '#matches_style?', requires: [:css] do
24
24
  end
25
25
 
26
26
  it 'deprecated has_style?' do
27
- expect_any_instance_of(Kernel).to receive(:warn).once
28
- have_style(display: /^bl/)
27
+ expect { have_style(display: /^bl/) }.to \
28
+ output(/have_style is deprecated/).to_stderr
29
29
 
30
30
  el = @session.find(:css, '#first')
31
31
  allow(el).to receive(:warn).and_return(nil)
@@ -109,12 +109,24 @@ Capybara::SpecHelper.spec 'node' do
109
109
  expect(@session.first('//input').value).to eq('')
110
110
  end
111
111
 
112
- it 'should raise if the text field is readonly' do
113
- expect { @session.first('//input[@readonly]').set('changed') }.to raise_error(Capybara::ReadOnlyElementError)
114
- end
112
+ if ENV['CAPYBARA_THOROUGH']
113
+ it 'should raise if the text field is readonly' do
114
+ expect { @session.first('//input[@readonly]').set('changed') }.to raise_error(Capybara::ReadOnlyElementError)
115
+ end
116
+
117
+ it 'should raise if the textarea is readonly' do
118
+ expect { @session.first('//textarea[@readonly]').set('changed') }.to raise_error(Capybara::ReadOnlyElementError)
119
+ end
120
+ else
121
+ it 'should not change if the text field is readonly' do
122
+ @session.first('//input[@readonly]').set('changed')
123
+ expect(@session.first('//input[@readonly]').value).to eq 'should not change'
124
+ end
115
125
 
116
- it 'should raise if the textarea is readonly' do
117
- expect { @session.first('//textarea[@readonly]').set('changed') }.to raise_error(Capybara::ReadOnlyElementError)
126
+ it 'should not change if the textarea is readonly' do
127
+ @session.first('//textarea[@readonly]').set('changed')
128
+ expect(@session.first('//textarea[@readonly]').value).to eq 'textarea should not change'
129
+ end
118
130
  end
119
131
 
120
132
  it 'should use global default options' do
@@ -183,7 +195,7 @@ Capybara::SpecHelper.spec 'node' do
183
195
 
184
196
  it 'should see a disabled fieldset as disabled' do
185
197
  @session.visit('/form')
186
- expect(@session.find(:css, '#form_disabled_fieldset')).to be_disabled
198
+ expect(@session.find(:xpath, './/fieldset[@id="form_disabled_fieldset"]')).to be_disabled
187
199
  end
188
200
 
189
201
  context 'in a disabled fieldset' do
@@ -221,8 +233,9 @@ Capybara::SpecHelper.spec 'node' do
221
233
  end
222
234
 
223
235
  describe '#visible?' do
236
+ before { Capybara.ignore_hidden_elements = false }
237
+
224
238
  it 'should extract node visibility' do
225
- Capybara.ignore_hidden_elements = false
226
239
  expect(@session.first('//a')).to be_visible
227
240
 
228
241
  expect(@session.find('//div[@id="hidden"]')).not_to be_visible
@@ -232,11 +245,113 @@ Capybara::SpecHelper.spec 'node' do
232
245
  expect(@session.find('//input[@id="hidden_input"]')).not_to be_visible
233
246
  end
234
247
 
248
+ it 'template elements should not be visible' do
249
+ expect(@session.find('//template')).not_to be_visible
250
+ end
251
+
235
252
  it 'should be boolean' do
236
- Capybara.ignore_hidden_elements = false
237
253
  expect(@session.first('//a').visible?).to be true
238
254
  expect(@session.find('//div[@id="hidden"]').visible?).to be false
239
255
  end
256
+
257
+ it 'closed details > summary elements and descendants should be visible' do
258
+ expect(@session.find(:css, '#closed_details summary')).to be_visible
259
+ expect(@session.find(:css, '#closed_details summary h6')).to be_visible
260
+ end
261
+
262
+ it 'details non-summary descendants should be non-visible when closed' do
263
+ descendants = @session.all(:css, '#closed_details > *:not(summary), #closed_details > *:not(summary) *', minimum: 2)
264
+ expect(descendants).not_to include(be_visible)
265
+ end
266
+
267
+ it 'deatils descendants should be visible when open' do
268
+ descendants = @session.all(:css, '#open_details *')
269
+ expect(descendants).to all(be_visible)
270
+ end
271
+
272
+ it 'works when details is toggled open and closed' do
273
+ @session.find(:css, '#closed_details > summary').click
274
+ expect(@session).to have_css('#closed_details *', visible: :visible, count: 5)
275
+ .and(have_no_css('#closed_details *', visible: :hidden))
276
+
277
+ @session.find(:css, '#closed_details > summary').click
278
+ descendants_css = '#closed_details > *:not(summary), #closed_details > *:not(summary) *'
279
+ expect(@session).to have_no_css(descendants_css, visible: :visible)
280
+ .and(have_css(descendants_css, visible: :hidden, count: 3))
281
+ end
282
+ end
283
+
284
+ describe '#obscured?', requires: [:css] do
285
+ it 'should see non visible elements as obscured' do
286
+ Capybara.ignore_hidden_elements = false
287
+ expect(@session.find('//div[@id="hidden"]')).to be_obscured
288
+ expect(@session.find('//div[@id="hidden_via_ancestor"]')).to be_obscured
289
+ expect(@session.find('//div[@id="hidden_attr"]')).to be_obscured
290
+ expect(@session.find('//a[@id="hidden_attr_via_ancestor"]')).to be_obscured
291
+ expect(@session.find('//input[@id="hidden_input"]')).to be_obscured
292
+ end
293
+
294
+ it 'should see non-overlapped elements as not obscured' do
295
+ @session.visit('/obscured')
296
+ expect(@session.find(:css, '#cover')).not_to be_obscured
297
+ end
298
+
299
+ it 'should see elements only overlapped by descendants as not obscured' do
300
+ expect(@session.first(:css, 'p:not(.para)')).not_to be_obscured
301
+ end
302
+
303
+ it 'should see elements outside the viewport as obscured' do
304
+ @session.visit('/obscured')
305
+ off = @session.find(:css, '#offscreen')
306
+ off_wrapper = @session.find(:css, '#offscreen_wrapper')
307
+ expect(off).to be_obscured
308
+ expect(off_wrapper).to be_obscured
309
+ @session.scroll_to(off_wrapper)
310
+ expect(off_wrapper).not_to be_obscured
311
+ expect(off).to be_obscured
312
+ off_wrapper.scroll_to(off)
313
+ expect(off).not_to be_obscured
314
+ expect(off_wrapper).not_to be_obscured
315
+ end
316
+
317
+ it 'should see overlapped elements as obscured' do
318
+ @session.visit('/obscured')
319
+ expect(@session.find(:css, '#obscured')).to be_obscured
320
+ end
321
+
322
+ it 'should be boolean' do
323
+ Capybara.ignore_hidden_elements = false
324
+ expect(@session.first('//a').obscured?).to be false
325
+ expect(@session.find('//div[@id="hidden"]').obscured?).to be true
326
+ end
327
+
328
+ it 'should work in frames' do
329
+ @session.visit('/obscured')
330
+ frame = @session.find(:css, '#frameOne')
331
+ @session.within_frame(frame) do
332
+ div = @session.find(:css, '#divInFrameOne')
333
+ expect(div).to be_obscured
334
+ @session.scroll_to div
335
+ expect(div).not_to be_obscured
336
+ end
337
+ end
338
+
339
+ it 'should work in nested iframes' do
340
+ @session.visit('/obscured')
341
+ frame = @session.find(:css, '#nestedFrames')
342
+ @session.within_frame(frame) do
343
+ @session.within_frame(:css, '#childFrame') do
344
+ gcframe = @session.find(:css, '#grandchildFrame2')
345
+ @session.within_frame(gcframe) do
346
+ expect(@session.find(:css, '#divInFrameTwo')).to be_obscured
347
+ end
348
+ @session.scroll_to(gcframe)
349
+ @session.within_frame(gcframe) do
350
+ expect(@session.find(:css, '#divInFrameTwo')).not_to be_obscured
351
+ end
352
+ end
353
+ end
354
+ end
240
355
  end
241
356
 
242
357
  describe '#checked?' do
@@ -328,6 +443,241 @@ Capybara::SpecHelper.spec 'node' do
328
443
  link.drag_to target
329
444
  expect(@session).to have_xpath('//div[contains(., "Dropped!")]')
330
445
  end
446
+
447
+ it 'should work with Dragula' do
448
+ @session.visit('/with_dragula')
449
+ @session.within(:css, '#sortable.ready') do
450
+ src = @session.find('div', text: 'Item 1')
451
+ target = @session.find('div', text: 'Item 3')
452
+ src.drag_to target
453
+ expect(@session).to have_content(/Item 2.*Item 1/, normalize_ws: true)
454
+ end
455
+ end
456
+
457
+ it 'should work with jsTree' do
458
+ @session.visit('/with_jstree')
459
+ @session.within(:css, '#container') do
460
+ @session.assert_text(/A.*B.*C/m)
461
+ source = @session.find(:css, '#j1_1_anchor')
462
+ target = @session.find(:css, '#j1_2_anchor')
463
+
464
+ source.drag_to(target)
465
+
466
+ @session.assert_no_text(/A.*B.*C/m)
467
+ @session.assert_text(/B.*C/m)
468
+ end
469
+ end
470
+
471
+ it 'should simulate a single held down modifier key' do
472
+ %I[
473
+ alt
474
+ ctrl
475
+ meta
476
+ shift
477
+ ].each do |modifier_key|
478
+ @session.visit('/with_js')
479
+
480
+ element = @session.find('//div[@id="drag"]')
481
+ target = @session.find('//div[@id="drop"]')
482
+
483
+ element.drag_to(target, drop_modifiers: modifier_key)
484
+ expect(@session).to have_css('div.drag_start', exact_text: 'Dragged!')
485
+ expect(@session).to have_xpath("//div[contains(., 'Dropped!-#{modifier_key}')]")
486
+ end
487
+ end
488
+
489
+ it 'should simulate multiple held down modifier keys' do
490
+ @session.visit('/with_js')
491
+
492
+ element = @session.find('//div[@id="drag"]')
493
+ target = @session.find('//div[@id="drop"]')
494
+
495
+ modifier_keys = %I[alt ctrl meta shift]
496
+
497
+ element.drag_to(target, drop_modifiers: modifier_keys)
498
+ expect(@session).to have_xpath("//div[contains(., 'Dropped!-#{modifier_keys.join('-')}')]")
499
+ end
500
+
501
+ it 'should support key aliases' do
502
+ { control: :ctrl,
503
+ command: :meta,
504
+ cmd: :meta }.each do |(key_alias, key)|
505
+ @session.visit('/with_js')
506
+
507
+ element = @session.find('//div[@id="drag"]')
508
+ target = @session.find('//div[@id="drop"]')
509
+
510
+ element.drag_to(target, drop_modifiers: [key_alias])
511
+ expect(target).to have_text("Dropped!-#{key}", exact: true)
512
+ end
513
+ end
514
+
515
+ context 'HTML5', requires: %i[js html5_drag] do
516
+ it 'should HTML5 drag and drop an object' do
517
+ @session.visit('/with_js')
518
+ element = @session.find('//div[@id="drag_html5"]')
519
+ target = @session.find('//div[@id="drop_html5"]')
520
+ element.drag_to(target)
521
+ expect(@session).to have_xpath('//div[contains(., "HTML5 Dropped string: text/plain drag_html5")]')
522
+ end
523
+
524
+ it 'should HTML5 drag and drop an object child' do
525
+ @session.visit('/with_js')
526
+ element = @session.find('//div[@id="drag_html5"]/p')
527
+ target = @session.find('//div[@id="drop_html5"]')
528
+ element.drag_to(target)
529
+ expect(@session).to have_xpath('//div[contains(., "HTML5 Dropped string: text/plain drag_html5")]')
530
+ end
531
+
532
+ it 'should set clientX/Y in dragover events' do
533
+ @session.visit('/with_js')
534
+ element = @session.find('//div[@id="drag_html5"]')
535
+ target = @session.find('//div[@id="drop_html5"]')
536
+ element.drag_to(target)
537
+ expect(@session).to have_css('div.log', text: /DragOver with client position: [1-9]\d*,[1-9]\d*/, count: 2)
538
+ end
539
+
540
+ it 'should preserve clientX/Y from last dragover event' do
541
+ @session.visit('/with_js')
542
+ element = @session.find('//div[@id="drag_html5"]')
543
+ target = @session.find('//div[@id="drop_html5"]')
544
+ element.drag_to(target)
545
+
546
+ conditions = %w[DragLeave Drop DragEnd].map do |text|
547
+ have_css('div.log', text: text)
548
+ end
549
+ expect(@session).to(conditions.reduce { |memo, cond| memo.and(cond) })
550
+
551
+ # The first "DragOver" div is inserted by the last dragover event dispatched
552
+ drag_over_div = @session.first('//div[@class="log" and starts-with(text(), "DragOver")]')
553
+ position = drag_over_div.text.sub('DragOver ', '')
554
+
555
+ expect(@session).to have_css('div.log', text: /DragLeave #{position}/, count: 1)
556
+ expect(@session).to have_css('div.log', text: /Drop #{position}/, count: 1)
557
+ expect(@session).to have_css('div.log', text: /DragEnd #{position}/, count: 1)
558
+ end
559
+
560
+ it 'should not HTML5 drag and drop on a non HTML5 drop element' do
561
+ @session.visit('/with_js')
562
+ element = @session.find('//div[@id="drag_html5"]')
563
+ target = @session.find('//div[@id="drop_html5"]')
564
+ target.execute_script("$(this).removeClass('drop');")
565
+ element.drag_to(target)
566
+ sleep 1
567
+ expect(@session).not_to have_xpath('//div[contains(., "HTML5 Dropped")]')
568
+ end
569
+
570
+ it 'should HTML5 drag and drop when scrolling needed' do
571
+ @session.visit('/with_js')
572
+ element = @session.find('//div[@id="drag_html5_scroll"]')
573
+ target = @session.find('//div[@id="drop_html5_scroll"]')
574
+ element.drag_to(target)
575
+ expect(@session).to have_xpath('//div[contains(., "HTML5 Dropped string: text/plain drag_html5_scroll")]')
576
+ end
577
+
578
+ it 'should drag HTML5 default draggable elements' do
579
+ @session.visit('/with_js')
580
+ link = @session.find_link('drag_link_html5')
581
+ target = @session.find(:id, 'drop_html5')
582
+ link.drag_to target
583
+ expect(@session).to have_xpath('//div[contains(., "HTML5 Dropped")]')
584
+ end
585
+
586
+ it 'should work with SortableJS' do
587
+ @session.visit('/with_sortable_js')
588
+ @session.within(:css, '#sortable') do
589
+ src = @session.find('div', text: 'Item 1')
590
+ target = @session.find('div', text: 'Item 3')
591
+ src.drag_to target
592
+ expect(@session).to have_content(/Item 3.*Item 1/, normalize_ws: true)
593
+ end
594
+ end
595
+
596
+ it 'should drag HTML5 default draggable element child' do
597
+ @session.visit('/with_js')
598
+ source = @session.find_link('drag_link_html5').find(:css, 'p')
599
+ target = @session.find(:id, 'drop_html5')
600
+ source.drag_to target
601
+ expect(@session).to have_xpath('//div[contains(., "HTML5 Dropped")]')
602
+ end
603
+
604
+ it 'should simulate a single held down modifier key' do
605
+ %I[alt ctrl meta shift].each do |modifier_key|
606
+ @session.visit('/with_js')
607
+
608
+ element = @session.find('//div[@id="drag_html5"]')
609
+ target = @session.find('//div[@id="drop_html5"]')
610
+
611
+ element.drag_to(target, drop_modifiers: modifier_key)
612
+
613
+ expect(@session).to have_css('div.drag_start', exact_text: 'HTML5 Dragged!')
614
+ expect(@session).to have_xpath("//div[contains(., 'HTML5 Dropped string: text/plain drag_html5-#{modifier_key}')]")
615
+ end
616
+ end
617
+
618
+ it 'should simulate multiple held down modifier keys' do
619
+ @session.visit('/with_js')
620
+
621
+ element = @session.find('//div[@id="drag_html5"]')
622
+ target = @session.find('//div[@id="drop_html5"]')
623
+
624
+ modifier_keys = %I[alt ctrl meta shift]
625
+
626
+ element.drag_to(target, drop_modifiers: modifier_keys)
627
+ expect(@session).to have_xpath("//div[contains(., 'HTML5 Dropped string: text/plain drag_html5-#{modifier_keys.join('-')}')]")
628
+ end
629
+
630
+ it 'should support key aliases' do
631
+ { control: :ctrl,
632
+ command: :meta,
633
+ cmd: :meta }.each do |(key_alias, key)|
634
+ @session.visit('/with_js')
635
+
636
+ element = @session.find('//div[@id="drag_html5"]')
637
+ target = @session.find('//div[@id="drop_html5"]')
638
+
639
+ element.drag_to(target, drop_modifiers: [key_alias])
640
+ expect(target).to have_text(%r{^HTML5 Dropped string: text/plain drag_html5-#{key}$}m, exact: true)
641
+ end
642
+ end
643
+ end
644
+ end
645
+
646
+ describe 'Element#drop', requires: %i[js html5_drag] do
647
+ it 'can drop a file' do
648
+ @session.visit('/with_js')
649
+ target = @session.find('//div[@id="drop_html5"]')
650
+ target.drop(
651
+ with_os_path_separators(File.expand_path('../fixtures/capybara.jpg', File.dirname(__FILE__)))
652
+ )
653
+ expect(@session).to have_xpath('//div[contains(., "HTML5 Dropped file: capybara.jpg")]')
654
+ end
655
+
656
+ it 'can drop multiple files' do
657
+ @session.visit('/with_js')
658
+ target = @session.find('//div[@id="drop_html5"]')
659
+ target.drop(
660
+ with_os_path_separators(File.expand_path('../fixtures/capybara.jpg', File.dirname(__FILE__))),
661
+ with_os_path_separators(File.expand_path('../fixtures/test_file.txt', File.dirname(__FILE__)))
662
+ )
663
+ expect(@session).to have_xpath('//div[contains(., "HTML5 Dropped file: capybara.jpg")]')
664
+ expect(@session).to have_xpath('//div[contains(., "HTML5 Dropped file: test_file.txt")]')
665
+ end
666
+
667
+ it 'can drop strings' do
668
+ @session.visit('/with_js')
669
+ target = @session.find('//div[@id="drop_html5"]')
670
+ target.drop('text/plain' => 'Some dropped text')
671
+ expect(@session).to have_xpath('//div[contains(., "HTML5 Dropped string: text/plain Some dropped text")]')
672
+ end
673
+
674
+ it 'can drop multiple strings' do
675
+ @session.visit('/with_js')
676
+ target = @session.find('//div[@id="drop_html5"]')
677
+ target.drop('text/plain' => 'Some dropped text', 'text/url' => 'http://www.google.com')
678
+ expect(@session).to have_xpath('//div[contains(., "HTML5 Dropped string: text/plain Some dropped text")]')
679
+ expect(@session).to have_xpath('//div[contains(., "HTML5 Dropped string: text/url http://www.google.com")]')
680
+ end
331
681
  end
332
682
 
333
683
  describe '#hover', requires: [:hover] do
@@ -408,7 +758,7 @@ Capybara::SpecHelper.spec 'node' do
408
758
  @session.visit('with_js')
409
759
  @session.find(:css, '#click-test').click(x: 5, y: 5)
410
760
  link = @session.find(:link, 'has-been-clicked')
411
- locations = link.text.match(/^Has been clicked at (?<x>[\d\.-]+),(?<y>[\d\.-]+)$/)
761
+ locations = link.text.match(/^Has been clicked at (?<x>[\d.-]+),(?<y>[\d.-]+)$/)
412
762
  # Resulting click location should be very close to 0, 0 relative to top left corner of the element, but may not be exact due to
413
763
  # integer/float conversions and rounding.
414
764
  expect(locations[:x].to_f).to be_within(1).of(5)
@@ -429,7 +779,7 @@ Capybara::SpecHelper.spec 'node' do
429
779
  end
430
780
 
431
781
  it 'should retry clicking', requires: [:js] do
432
- @session.visit('/obscured')
782
+ @session.visit('/animated')
433
783
  obscured = @session.find(:css, '#obscured')
434
784
  @session.execute_script <<~JS
435
785
  setTimeout(function(){ $('#cover').hide(); }, 700)
@@ -438,7 +788,7 @@ Capybara::SpecHelper.spec 'node' do
438
788
  end
439
789
 
440
790
  it 'should allow to retry longer', requires: [:js] do
441
- @session.visit('/obscured')
791
+ @session.visit('/animated')
442
792
  obscured = @session.find(:css, '#obscured')
443
793
  @session.execute_script <<~JS
444
794
  setTimeout(function(){ $('#cover').hide(); }, 3000)
@@ -447,13 +797,74 @@ Capybara::SpecHelper.spec 'node' do
447
797
  end
448
798
 
449
799
  it 'should not retry clicking when wait is disabled', requires: [:js] do
450
- @session.visit('/obscured')
800
+ @session.visit('/animated')
451
801
  obscured = @session.find(:css, '#obscured')
452
802
  @session.execute_script <<~JS
453
803
  setTimeout(function(){ $('#cover').hide(); }, 2000)
454
804
  JS
455
805
  expect { obscured.click(wait: 0) }.to(raise_error { |e| expect(e).to be_an_invalid_element_error(@session) })
456
806
  end
807
+
808
+ context 'offset', requires: [:js] do
809
+ before do
810
+ @session.visit('/offset')
811
+ end
812
+
813
+ let :clicker do
814
+ @session.find(:id, 'clicker')
815
+ end
816
+
817
+ context 'when w3c_click_offset is false' do
818
+ before do
819
+ Capybara.w3c_click_offset = false
820
+ end
821
+
822
+ it 'should offset from top left of element' do
823
+ clicker.click(x: 10, y: 5)
824
+ expect(@session).to have_text(/clicked at 110,105/)
825
+ end
826
+
827
+ it 'should offset outside the element' do
828
+ clicker.click(x: -15, y: -10)
829
+ expect(@session).to have_text(/clicked at 85,90/)
830
+ end
831
+
832
+ it 'should default to click the middle' do
833
+ clicker.click
834
+ expect(@session).to have_text(/clicked at 150,150/)
835
+ end
836
+ end
837
+
838
+ context 'when w3c_click_offset is true' do
839
+ before do
840
+ Capybara.w3c_click_offset = true
841
+ end
842
+
843
+ it 'should offset from center of element' do
844
+ clicker.click(x: 10, y: 5)
845
+ expect(@session).to have_text(/clicked at 160,155/)
846
+ end
847
+
848
+ it 'should offset outside from center of element' do
849
+ clicker.click(x: -65, y: -60)
850
+ expect(@session).to have_text(/clicked at 85,90/)
851
+ end
852
+
853
+ it 'should default to click the middle' do
854
+ clicker.click
855
+ expect(@session).to have_text(/clicked at 150,150/)
856
+ end
857
+ end
858
+ end
859
+
860
+ context 'delay', requires: [:js] do
861
+ it 'should delay the mouse up' do
862
+ @session.visit('with_js')
863
+ @session.find(:css, '#click-test').click(delay: 2)
864
+ delay = @session.evaluate_script('window.click_delay')
865
+ expect(delay).to be >= 2
866
+ end
867
+ end
457
868
  end
458
869
 
459
870
  describe '#double_click', requires: [:js] do
@@ -473,7 +884,7 @@ Capybara::SpecHelper.spec 'node' do
473
884
  @session.visit('with_js')
474
885
  @session.find(:css, '#click-test').double_click(x: 10, y: 5)
475
886
  link = @session.find(:link, 'has-been-double-clicked')
476
- locations = link.text.match(/^Has been double clicked at (?<x>[\d\.-]+),(?<y>[\d\.-]+)$/)
887
+ locations = link.text.match(/^Has been double clicked at (?<x>[\d.-]+),(?<y>[\d.-]+)$/)
477
888
  # Resulting click location should be very close to 10, 5 relative to top left corner of the element, but may not be exact due
478
889
  # to integer/float conversions and rounding.
479
890
  expect(locations[:x].to_f).to be_within(1).of(10)
@@ -481,13 +892,65 @@ Capybara::SpecHelper.spec 'node' do
481
892
  end
482
893
 
483
894
  it 'should retry clicking', requires: [:js] do
484
- @session.visit('/obscured')
895
+ @session.visit('/animated')
485
896
  obscured = @session.find(:css, '#obscured')
486
897
  @session.execute_script <<~JS
487
898
  setTimeout(function(){ $('#cover').hide(); }, 700)
488
899
  JS
489
900
  expect { obscured.double_click }.not_to raise_error
490
901
  end
902
+
903
+ context 'offset', requires: [:js] do
904
+ before do
905
+ @session.visit('/offset')
906
+ end
907
+
908
+ let :clicker do
909
+ @session.find(:id, 'clicker')
910
+ end
911
+
912
+ context 'when w3c_click_offset is false' do
913
+ before do
914
+ Capybara.w3c_click_offset = false
915
+ end
916
+
917
+ it 'should offset from top left of element' do
918
+ clicker.double_click(x: 10, y: 5)
919
+ expect(@session).to have_text(/clicked at 110,105/)
920
+ end
921
+
922
+ it 'should offset outside the element' do
923
+ clicker.double_click(x: -15, y: -10)
924
+ expect(@session).to have_text(/clicked at 85,90/)
925
+ end
926
+
927
+ it 'should default to click the middle' do
928
+ clicker.double_click
929
+ expect(@session).to have_text(/clicked at 150,150/)
930
+ end
931
+ end
932
+
933
+ context 'when w3c_click_offset is true' do
934
+ before do
935
+ Capybara.w3c_click_offset = true
936
+ end
937
+
938
+ it 'should offset from center of element' do
939
+ clicker.double_click(x: 10, y: 5)
940
+ expect(@session).to have_text(/clicked at 160,155/)
941
+ end
942
+
943
+ it 'should offset outside from center of element' do
944
+ clicker.double_click(x: -65, y: -60)
945
+ expect(@session).to have_text(/clicked at 85,90/)
946
+ end
947
+
948
+ it 'should default to click the middle' do
949
+ clicker.double_click
950
+ expect(@session).to have_text(/clicked at 150,150/)
951
+ end
952
+ end
953
+ end
491
954
  end
492
955
 
493
956
  describe '#right_click', requires: [:js] do
@@ -507,7 +970,7 @@ Capybara::SpecHelper.spec 'node' do
507
970
  @session.visit('with_js')
508
971
  @session.find(:css, '#click-test').right_click(x: 10, y: 10)
509
972
  link = @session.find(:link, 'has-been-right-clicked')
510
- locations = link.text.match(/^Has been right clicked at (?<x>[\d\.-]+),(?<y>[\d\.-]+)$/)
973
+ locations = link.text.match(/^Has been right clicked at (?<x>[\d.-]+),(?<y>[\d.-]+)$/)
511
974
  # Resulting click location should be very close to 10, 10 relative to top left corner of the element, but may not be exact due
512
975
  # to integer/float conversions and rounding
513
976
  expect(locations[:x].to_f).to be_within(1).of(10)
@@ -515,13 +978,74 @@ Capybara::SpecHelper.spec 'node' do
515
978
  end
516
979
 
517
980
  it 'should retry clicking', requires: [:js] do
518
- @session.visit('/obscured')
981
+ @session.visit('/animated')
519
982
  obscured = @session.find(:css, '#obscured')
520
983
  @session.execute_script <<~JS
521
984
  setTimeout(function(){ $('#cover').hide(); }, 700)
522
985
  JS
523
986
  expect { obscured.right_click }.not_to raise_error
524
987
  end
988
+
989
+ context 'offset', requires: [:js] do
990
+ before do
991
+ @session.visit('/offset')
992
+ end
993
+
994
+ let :clicker do
995
+ @session.find(:id, 'clicker')
996
+ end
997
+
998
+ context 'when w3c_click_offset is false' do
999
+ before do
1000
+ Capybara.w3c_click_offset = false
1001
+ end
1002
+
1003
+ it 'should offset from top left of element' do
1004
+ clicker.right_click(x: 10, y: 5)
1005
+ expect(@session).to have_text(/clicked at 110,105/)
1006
+ end
1007
+
1008
+ it 'should offset outside the element' do
1009
+ clicker.right_click(x: -15, y: -10)
1010
+ expect(@session).to have_text(/clicked at 85,90/)
1011
+ end
1012
+
1013
+ it 'should default to click the middle' do
1014
+ clicker.right_click
1015
+ expect(@session).to have_text(/clicked at 150,150/)
1016
+ end
1017
+ end
1018
+
1019
+ context 'when w3c_click_offset is true' do
1020
+ before do
1021
+ Capybara.w3c_click_offset = true
1022
+ end
1023
+
1024
+ it 'should offset from center of element' do
1025
+ clicker.right_click(x: 10, y: 5)
1026
+ expect(@session).to have_text(/clicked at 160,155/)
1027
+ end
1028
+
1029
+ it 'should offset outside from center of element' do
1030
+ clicker.right_click(x: -65, y: -60)
1031
+ expect(@session).to have_text(/clicked at 85,90/)
1032
+ end
1033
+
1034
+ it 'should default to click the middle' do
1035
+ clicker.right_click
1036
+ expect(@session).to have_text(/clicked at 150,150/)
1037
+ end
1038
+ end
1039
+ end
1040
+
1041
+ context 'delay', requires: [:js] do
1042
+ it 'should delay the mouse up' do
1043
+ @session.visit('with_js')
1044
+ @session.find(:css, '#click-test').right_click(delay: 2)
1045
+ delay = @session.evaluate_script('window.right_click_delay')
1046
+ expect(delay).to be >= 2
1047
+ end
1048
+ end
525
1049
  end
526
1050
 
527
1051
  describe '#send_keys', requires: [:send_keys] do
@@ -603,6 +1127,18 @@ Capybara::SpecHelper.spec 'node' do
603
1127
  expect(el).to be_instance_of(Capybara::Node::Element)
604
1128
  expect(el).to eq(change)
605
1129
  end
1130
+
1131
+ it 'should support multiple statements via IIFE' do
1132
+ @session.visit('/with_js')
1133
+ change = @session.find(:css, '#change') # ensure page has loaded and element is available
1134
+ res = change.evaluate_script(<<~JS, 3)
1135
+ (function(n){
1136
+ var el = this;
1137
+ return [el, n];
1138
+ }).apply(this, arguments)
1139
+ JS
1140
+ expect(res).to eq [change, 3]
1141
+ end
606
1142
  end
607
1143
 
608
1144
  describe '#evaluate_async_script', requires: %i[js es_args] do
@@ -621,6 +1157,28 @@ Capybara::SpecHelper.spec 'node' do
621
1157
  end
622
1158
 
623
1159
  describe '#reload', requires: [:js] do
1160
+ it 'should reload elements found via ancestor with CSS' do
1161
+ @session.visit('/with_js')
1162
+ node = @session.find(:css, '#reload-me em').ancestor(:css, 'div')
1163
+ node.reload
1164
+ expect(node[:id]).to eq 'reload-me'
1165
+ end
1166
+
1167
+ it 'should reload elements found via ancestor with XPath' do
1168
+ @session.visit('/with_js')
1169
+ node = @session.find(:css, '#reload-me em').ancestor(:xpath, './/div')
1170
+ node.reload
1171
+ expect(node[:id]).to eq 'reload-me'
1172
+ end
1173
+
1174
+ it 'should reload elements found via sibling' do
1175
+ @session.visit('/with_js')
1176
+ node = @session.find(:css, '#the-list li', text: 'Item 1').sibling(:css, 'li')
1177
+ expect(node.text).to eq 'Item 2'
1178
+ node.reload
1179
+ expect(node.text).to eq 'Item 2'
1180
+ end
1181
+
624
1182
  context 'without automatic reload' do
625
1183
  before { Capybara.automatic_reload = false }
626
1184