capybara 3.29.0 → 3.30.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 (79) hide show
  1. checksums.yaml +4 -4
  2. data/History.md +19 -1
  3. data/README.md +1 -1
  4. data/lib/capybara/config.rb +7 -3
  5. data/lib/capybara/helpers.rb +3 -1
  6. data/lib/capybara/node/actions.rb +23 -19
  7. data/lib/capybara/node/document.rb +2 -2
  8. data/lib/capybara/node/document_matchers.rb +3 -3
  9. data/lib/capybara/node/element.rb +10 -8
  10. data/lib/capybara/node/finders.rb +12 -10
  11. data/lib/capybara/node/matchers.rb +39 -33
  12. data/lib/capybara/node/simple.rb +3 -1
  13. data/lib/capybara/queries/ancestor_query.rb +1 -1
  14. data/lib/capybara/queries/selector_query.rb +17 -4
  15. data/lib/capybara/queries/sibling_query.rb +1 -1
  16. data/lib/capybara/rack_test/browser.rb +4 -1
  17. data/lib/capybara/rack_test/driver.rb +1 -1
  18. data/lib/capybara/rack_test/form.rb +1 -1
  19. data/lib/capybara/selector.rb +22 -16
  20. data/lib/capybara/selector/css.rb +1 -1
  21. data/lib/capybara/selector/definition.rb +2 -2
  22. data/lib/capybara/selector/definition/button.rb +7 -2
  23. data/lib/capybara/selector/definition/checkbox.rb +2 -2
  24. data/lib/capybara/selector/definition/css.rb +3 -1
  25. data/lib/capybara/selector/definition/datalist_input.rb +1 -1
  26. data/lib/capybara/selector/definition/datalist_option.rb +1 -1
  27. data/lib/capybara/selector/definition/element.rb +1 -1
  28. data/lib/capybara/selector/definition/field.rb +1 -1
  29. data/lib/capybara/selector/definition/file_field.rb +1 -1
  30. data/lib/capybara/selector/definition/fillable_field.rb +1 -1
  31. data/lib/capybara/selector/definition/label.rb +3 -1
  32. data/lib/capybara/selector/definition/radio_button.rb +2 -2
  33. data/lib/capybara/selector/definition/select.rb +1 -1
  34. data/lib/capybara/selector/definition/table.rb +5 -2
  35. data/lib/capybara/selector/filter_set.rb +11 -9
  36. data/lib/capybara/selector/filters/base.rb +6 -1
  37. data/lib/capybara/selector/filters/locator_filter.rb +1 -1
  38. data/lib/capybara/selector/selector.rb +4 -2
  39. data/lib/capybara/selenium/driver.rb +19 -11
  40. data/lib/capybara/selenium/driver_specializations/firefox_driver.rb +2 -2
  41. data/lib/capybara/selenium/extensions/html5_drag.rb +6 -5
  42. data/lib/capybara/selenium/node.rb +6 -4
  43. data/lib/capybara/selenium/nodes/chrome_node.rb +7 -3
  44. data/lib/capybara/selenium/nodes/edge_node.rb +3 -1
  45. data/lib/capybara/selenium/nodes/firefox_node.rb +1 -1
  46. data/lib/capybara/server.rb +15 -3
  47. data/lib/capybara/server/checker.rb +1 -1
  48. data/lib/capybara/server/middleware.rb +20 -10
  49. data/lib/capybara/session.rb +10 -8
  50. data/lib/capybara/session/config.rb +6 -2
  51. data/lib/capybara/session/matchers.rb +6 -6
  52. data/lib/capybara/spec/public/test.js +11 -0
  53. data/lib/capybara/spec/session/all_spec.rb +15 -0
  54. data/lib/capybara/spec/session/ancestor_spec.rb +5 -0
  55. data/lib/capybara/spec/session/assert_text_spec.rb +4 -0
  56. data/lib/capybara/spec/session/click_button_spec.rb +5 -0
  57. data/lib/capybara/spec/session/find_spec.rb +20 -0
  58. data/lib/capybara/spec/session/has_table_spec.rb +51 -5
  59. data/lib/capybara/spec/session/has_text_spec.rb +31 -0
  60. data/lib/capybara/spec/session/node_spec.rb +15 -0
  61. data/lib/capybara/spec/session/save_and_open_screenshot_spec.rb +2 -2
  62. data/lib/capybara/spec/session/save_screenshot_spec.rb +4 -4
  63. data/lib/capybara/spec/session/selectors_spec.rb +15 -2
  64. data/lib/capybara/spec/views/form.erb +5 -0
  65. data/lib/capybara/version.rb +1 -1
  66. data/spec/dsl_spec.rb +2 -2
  67. data/spec/minitest_spec_spec.rb +46 -46
  68. data/spec/regexp_dissassembler_spec.rb +45 -37
  69. data/spec/result_spec.rb +3 -3
  70. data/spec/rspec/features_spec.rb +1 -0
  71. data/spec/rspec/shared_spec_matchers.rb +3 -3
  72. data/spec/rspec_spec.rb +4 -4
  73. data/spec/selenium_spec_chrome.rb +3 -3
  74. data/spec/selenium_spec_firefox.rb +7 -2
  75. data/spec/server_spec.rb +42 -0
  76. data/spec/session_spec.rb +1 -1
  77. data/spec/shared_selenium_node.rb +3 -3
  78. data/spec/shared_selenium_session.rb +8 -7
  79. metadata +3 -3
@@ -79,14 +79,18 @@ module Capybara
79
79
 
80
80
  remove_method :app_host=
81
81
  def app_host=(url)
82
- raise ArgumentError, "Capybara.app_host should be set to a url (http://www.example.com). Attempted to set #{url.inspect}." unless url.nil? || url.match?(URI::DEFAULT_PARSER.make_regexp)
82
+ unless url.nil? || url.match?(URI::DEFAULT_PARSER.make_regexp)
83
+ raise ArgumentError, "Capybara.app_host should be set to a url (http://www.example.com). Attempted to set #{url.inspect}."
84
+ end
83
85
 
84
86
  @app_host = url
85
87
  end
86
88
 
87
89
  remove_method :default_host=
88
90
  def default_host=(url)
89
- raise ArgumentError, "Capybara.default_host should be set to a url (http://www.example.com). Attempted to set #{url.inspect}." unless url.nil? || url.match?(URI::DEFAULT_PARSER.make_regexp)
91
+ unless url.nil? || url.match?(URI::DEFAULT_PARSER.make_regexp)
92
+ raise ArgumentError, "Capybara.default_host should be set to a url (http://www.example.com). Attempted to set #{url.inspect}."
93
+ end
90
94
 
91
95
  @default_host = url
92
96
  end
@@ -20,7 +20,7 @@ module Capybara
20
20
  # @return [true]
21
21
  #
22
22
  def assert_current_path(path, **options)
23
- _verify_current_path(path, options) do |query|
23
+ _verify_current_path(path, **options) do |query|
24
24
  raise Capybara::ExpectationNotMet, query.failure_message unless query.resolves_for?(self)
25
25
  end
26
26
  end
@@ -36,7 +36,7 @@ module Capybara
36
36
  # @return [true]
37
37
  #
38
38
  def assert_no_current_path(path, **options)
39
- _verify_current_path(path, options) do |query|
39
+ _verify_current_path(path, **options) do |query|
40
40
  raise Capybara::ExpectationNotMet, query.negative_failure_message if query.resolves_for?(self)
41
41
  end
42
42
  end
@@ -51,7 +51,7 @@ module Capybara
51
51
  # @return [Boolean]
52
52
  #
53
53
  def has_current_path?(path, **options)
54
- make_predicate(options) { assert_current_path(path, options) }
54
+ make_predicate(options) { assert_current_path(path, **options) }
55
55
  end
56
56
 
57
57
  ##
@@ -64,13 +64,13 @@ module Capybara
64
64
  # @return [Boolean]
65
65
  #
66
66
  def has_no_current_path?(path, **options)
67
- make_predicate(options) { assert_no_current_path(path, options) }
67
+ make_predicate(options) { assert_no_current_path(path, **options) }
68
68
  end
69
69
 
70
70
  private
71
71
 
72
- def _verify_current_path(path, options)
73
- query = Capybara::Queries::CurrentPathQuery.new(path, options)
72
+ def _verify_current_path(path, **options)
73
+ query = Capybara::Queries::CurrentPathQuery.new(path, **options)
74
74
  document.synchronize(query.wait) do
75
75
  yield(query)
76
76
  end
@@ -12,10 +12,21 @@ $(function() {
12
12
  $('#drag_html5, #drag_html5_scroll').on('dragstart', function(ev){
13
13
  ev.originalEvent.dataTransfer.setData("text", ev.target.id);
14
14
  });
15
+ $('#drag_html5, #drag_html5_scroll').on('dragend', function(ev){
16
+ $(this).after('<div class="log">DragEnd with client position: ' + ev.clientX + ',' + ev.clientY)
17
+ });
15
18
  $('#drop_html5, #drop_html5_scroll').on('dragover', function(ev){
16
19
  $(this).after('<div class="log">DragOver with client position: ' + ev.clientX + ',' + ev.clientY)
17
20
  if ($(this).hasClass('drop')) { ev.preventDefault(); }
18
21
  });
22
+ $('#drop_html5, #drop_html5_scroll').on('dragleave', function(ev){
23
+ $(this).after('<div class="log">DragLeave with client position: ' + ev.clientX + ',' + ev.clientY)
24
+ if ($(this).hasClass('drop')) { ev.preventDefault(); }
25
+ });
26
+ $('#drop_html5, #drop_html5_scroll').on('drop', function(ev){
27
+ $(this).after('<div class="log">Drop with client position: ' + ev.clientX + ',' + ev.clientY)
28
+ if ($(this).hasClass('drop')) { ev.preventDefault(); }
29
+ });
19
30
  $('#drop_html5, #drop_html5_scroll').on('drop', function(ev){
20
31
  ev.preventDefault();
21
32
  var oev = ev.originalEvent;
@@ -36,6 +36,13 @@ Capybara::SpecHelper.spec '#all' do
36
36
  expect(@result).to include('Smith', 'John', 'John Smith')
37
37
  end
38
38
 
39
+ it 'should allow reversing the order' do
40
+ @session.visit('/form')
41
+ fields = @session.all(:fillable_field, 'Name', exact: false).to_a
42
+ reverse_fields = @session.all(:fillable_field, 'Name', order: :reverse, exact: false).to_a
43
+ expect(fields).to eq(reverse_fields.reverse)
44
+ end
45
+
39
46
  it 'should raise an error when given invalid options' do
40
47
  expect { @session.all('//p', schmoo: 'foo') }.to raise_error(ArgumentError)
41
48
  end
@@ -128,6 +135,7 @@ Capybara::SpecHelper.spec '#all' do
128
135
  it 'should succeed when the number of elements founds matches the expectation' do
129
136
  expect { @session.all(:css, 'h1, p', count: 4) }.not_to raise_error
130
137
  end
138
+
131
139
  it 'should raise ExpectationNotMet when the number of elements founds does not match the expectation' do
132
140
  expect { @session.all(:css, 'h1, p', count: 5) }.to raise_error(Capybara::ExpectationNotMet)
133
141
  end
@@ -137,6 +145,7 @@ Capybara::SpecHelper.spec '#all' do
137
145
  it 'should succeed when the number of elements founds matches the expectation' do
138
146
  expect { @session.all(:css, 'h1, p', minimum: 0) }.not_to raise_error
139
147
  end
148
+
140
149
  it 'should raise ExpectationNotMet when the number of elements founds does not match the expectation' do
141
150
  expect { @session.all(:css, 'h1, p', minimum: 5) }.to raise_error(Capybara::ExpectationNotMet)
142
151
  end
@@ -146,6 +155,7 @@ Capybara::SpecHelper.spec '#all' do
146
155
  it 'should succeed when the number of elements founds matches the expectation' do
147
156
  expect { @session.all(:css, 'h1, p', maximum: 4) }.not_to raise_error
148
157
  end
158
+
149
159
  it 'should raise ExpectationNotMet when the number of elements founds does not match the expectation' do
150
160
  expect { @session.all(:css, 'h1, p', maximum: 0) }.to raise_error(Capybara::ExpectationNotMet)
151
161
  end
@@ -155,6 +165,7 @@ Capybara::SpecHelper.spec '#all' do
155
165
  it 'should succeed when the number of elements founds matches the expectation' do
156
166
  expect { @session.all(:css, 'h1, p', between: 2..7) }.not_to raise_error
157
167
  end
168
+
158
169
  it 'should raise ExpectationNotMet when the number of elements founds does not match the expectation' do
159
170
  expect { @session.all(:css, 'h1, p', between: 0..3) }.to raise_error(Capybara::ExpectationNotMet)
160
171
  end
@@ -175,6 +186,7 @@ Capybara::SpecHelper.spec '#all' do
175
186
  between: 0..3 }
176
187
  expect { @session.all(:css, 'h1, p', o) }.not_to raise_error
177
188
  end
189
+
178
190
  context 'with no :count expectation' do
179
191
  it 'fails if :minimum is not met' do
180
192
  o = { minimum: 5,
@@ -182,18 +194,21 @@ Capybara::SpecHelper.spec '#all' do
182
194
  between: 2..7 }
183
195
  expect { @session.all(:css, 'h1, p', o) }.to raise_error(Capybara::ExpectationNotMet)
184
196
  end
197
+
185
198
  it 'fails if :maximum is not met' do
186
199
  o = { minimum: 0,
187
200
  maximum: 0,
188
201
  between: 2..7 }
189
202
  expect { @session.all(:css, 'h1, p', o) }.to raise_error(Capybara::ExpectationNotMet)
190
203
  end
204
+
191
205
  it 'fails if :between is not met' do
192
206
  o = { minimum: 0,
193
207
  maximum: 4,
194
208
  between: 0..3 }
195
209
  expect { @session.all(:css, 'h1, p', o) }.to raise_error(Capybara::ExpectationNotMet)
196
210
  end
211
+
197
212
  it 'succeeds if all combineable expectations are met' do
198
213
  o = { minimum: 0,
199
214
  maximum: 4,
@@ -20,6 +20,11 @@ Capybara::SpecHelper.spec '#ancestor' do
20
20
  expect(el.ancestor('//div', text: "Ancestor\nAncestor\nAncestor")[:id]).to eq('ancestor3')
21
21
  end
22
22
 
23
+ it 'should find the closest ancestor' do
24
+ el = @session.find(:css, '#child')
25
+ expect(el.ancestor('.//div', order: :reverse, match: :first)[:id]).to eq('ancestor1')
26
+ end
27
+
23
28
  it 'should raise an error if there are multiple matches' do
24
29
  el = @session.find(:css, '#child')
25
30
  expect { el.ancestor('//div') }.to raise_error(Capybara::Ambiguous)
@@ -159,6 +159,7 @@ Capybara::SpecHelper.spec '#assert_text' do
159
159
  between: 0..4 }
160
160
  expect { @session.assert_text('Header', o) }.not_to raise_error
161
161
  end
162
+
162
163
  context 'with no :count expectation' do
163
164
  it 'fails if :minimum is not met' do
164
165
  o = { minimum: 6,
@@ -166,18 +167,21 @@ Capybara::SpecHelper.spec '#assert_text' do
166
167
  between: 2..7 }
167
168
  expect { @session.assert_text('Header', o) }.to raise_error(Capybara::ExpectationNotMet)
168
169
  end
170
+
169
171
  it 'fails if :maximum is not met' do
170
172
  o = { minimum: 0,
171
173
  maximum: 0,
172
174
  between: 2..7 }
173
175
  expect { @session.assert_text('Header', o) }.to raise_error(Capybara::ExpectationNotMet)
174
176
  end
177
+
175
178
  it 'fails if :between is not met' do
176
179
  o = { minimum: 0,
177
180
  maximum: 5,
178
181
  between: 0..4 }
179
182
  expect { @session.assert_text('Header', o) }.to raise_error(Capybara::ExpectationNotMet)
180
183
  end
184
+
181
185
  it 'succeeds if all combineable expectations are met' do
182
186
  o = { minimum: 0,
183
187
  maximum: 5,
@@ -186,6 +186,11 @@ Capybara::SpecHelper.spec '#click_button' do
186
186
  @session.click_button(name: 'form[awesome]')
187
187
  expect(extract_results(@session)['first_name']).to eq('John')
188
188
  end
189
+
190
+ it 'should submit by specific button name regex' do
191
+ @session.click_button(name: /form\[awes.*\]/)
192
+ expect(extract_results(@session)['first_name']).to eq('John')
193
+ end
189
194
  end
190
195
 
191
196
  context 'with fields associated with the form using the form attribute', requires: [:form_attribute] do
@@ -275,14 +275,17 @@ Capybara::SpecHelper.spec '#find' do
275
275
  @session.find(:css, '.multiple', match: :one)
276
276
  end.to raise_error(Capybara::Ambiguous)
277
277
  end
278
+
278
279
  it 'raises an error even if there the match is exact and the others are inexact' do
279
280
  expect do
280
281
  @session.find(:xpath, XPath.descendant[XPath.attr(:class).is('almost_singular')], exact: false, match: :one)
281
282
  end.to raise_error(Capybara::Ambiguous)
282
283
  end
284
+
283
285
  it 'returns the element if there is only one' do
284
286
  expect(@session.find(:css, '.singular', match: :one).text).to eq('singular')
285
287
  end
288
+
286
289
  it 'raises an error if there is no match' do
287
290
  expect do
288
291
  @session.find(:css, '.does-not-exist', match: :one)
@@ -294,6 +297,7 @@ Capybara::SpecHelper.spec '#find' do
294
297
  it 'returns the first matched element' do
295
298
  expect(@session.find(:css, '.multiple', match: :first).text).to eq('multiple one')
296
299
  end
300
+
297
301
  it 'raises an error if there is no match' do
298
302
  expect do
299
303
  @session.find(:css, '.does-not-exist', match: :first)
@@ -308,19 +312,23 @@ Capybara::SpecHelper.spec '#find' do
308
312
  @session.find(:xpath, XPath.descendant[XPath.attr(:class).is('multiple')], match: :smart, exact: false)
309
313
  end.to raise_error(Capybara::Ambiguous)
310
314
  end
315
+
311
316
  it 'finds a single exact match when there also are inexact matches' do
312
317
  result = @session.find(:xpath, XPath.descendant[XPath.attr(:class).is('almost_singular')], match: :smart, exact: false)
313
318
  expect(result.text).to eq('almost singular')
314
319
  end
320
+
315
321
  it 'raises an error when there are multiple inexact matches' do
316
322
  expect do
317
323
  @session.find(:xpath, XPath.descendant[XPath.attr(:class).is('almost_singul')], match: :smart, exact: false)
318
324
  end.to raise_error(Capybara::Ambiguous)
319
325
  end
326
+
320
327
  it 'finds a single inexact match' do
321
328
  result = @session.find(:xpath, XPath.descendant[XPath.attr(:class).is('almost_singular but')], match: :smart, exact: false)
322
329
  expect(result.text).to eq('almost singular but not quite')
323
330
  end
331
+
324
332
  it 'raises an error if there is no match' do
325
333
  expect do
326
334
  @session.find(:xpath, XPath.descendant[XPath.attr(:class).is('does-not-exist')], match: :smart, exact: false)
@@ -334,20 +342,24 @@ Capybara::SpecHelper.spec '#find' do
334
342
  @session.find(:xpath, XPath.descendant[XPath.attr(:class).is('multiple')], match: :smart, exact: true)
335
343
  end.to raise_error(Capybara::Ambiguous)
336
344
  end
345
+
337
346
  it 'finds a single exact match when there also are inexact matches' do
338
347
  result = @session.find(:xpath, XPath.descendant[XPath.attr(:class).is('almost_singular')], match: :smart, exact: true)
339
348
  expect(result.text).to eq('almost singular')
340
349
  end
350
+
341
351
  it 'raises an error when there are multiple inexact matches' do
342
352
  expect do
343
353
  @session.find(:xpath, XPath.descendant[XPath.attr(:class).is('almost_singul')], match: :smart, exact: true)
344
354
  end.to raise_error(Capybara::ElementNotFound)
345
355
  end
356
+
346
357
  it 'raises an error when there is a single inexact matches' do
347
358
  expect do
348
359
  @session.find(:xpath, XPath.descendant[XPath.attr(:class).is('almost_singular but')], match: :smart, exact: true)
349
360
  end.to raise_error(Capybara::ElementNotFound)
350
361
  end
362
+
351
363
  it 'raises an error if there is no match' do
352
364
  expect do
353
365
  @session.find(:xpath, XPath.descendant[XPath.attr(:class).is('does-not-exist')], match: :smart, exact: true)
@@ -362,18 +374,22 @@ Capybara::SpecHelper.spec '#find' do
362
374
  result = @session.find(:xpath, XPath.descendant[XPath.attr(:class).is('multiple')], match: :prefer_exact, exact: false)
363
375
  expect(result.text).to eq('multiple one')
364
376
  end
377
+
365
378
  it 'finds a single exact match when there also are inexact matches' do
366
379
  result = @session.find(:xpath, XPath.descendant[XPath.attr(:class).is('almost_singular')], match: :prefer_exact, exact: false)
367
380
  expect(result.text).to eq('almost singular')
368
381
  end
382
+
369
383
  it 'picks the first one when there are multiple inexact matches' do
370
384
  result = @session.find(:xpath, XPath.descendant[XPath.attr(:class).is('almost_singul')], match: :prefer_exact, exact: false)
371
385
  expect(result.text).to eq('almost singular but not quite')
372
386
  end
387
+
373
388
  it 'finds a single inexact match' do
374
389
  result = @session.find(:xpath, XPath.descendant[XPath.attr(:class).is('almost_singular but')], match: :prefer_exact, exact: false)
375
390
  expect(result.text).to eq('almost singular but not quite')
376
391
  end
392
+
377
393
  it 'raises an error if there is no match' do
378
394
  expect do
379
395
  @session.find(:xpath, XPath.descendant[XPath.attr(:class).is('does-not-exist')], match: :prefer_exact, exact: false)
@@ -386,20 +402,24 @@ Capybara::SpecHelper.spec '#find' do
386
402
  result = @session.find(:xpath, XPath.descendant[XPath.attr(:class).is('multiple')], match: :prefer_exact, exact: true)
387
403
  expect(result.text).to eq('multiple one')
388
404
  end
405
+
389
406
  it 'finds a single exact match when there also are inexact matches' do
390
407
  result = @session.find(:xpath, XPath.descendant[XPath.attr(:class).is('almost_singular')], match: :prefer_exact, exact: true)
391
408
  expect(result.text).to eq('almost singular')
392
409
  end
410
+
393
411
  it 'raises an error if there are multiple inexact matches' do
394
412
  expect do
395
413
  @session.find(:xpath, XPath.descendant[XPath.attr(:class).is('almost_singul')], match: :prefer_exact, exact: true)
396
414
  end.to raise_error(Capybara::ElementNotFound)
397
415
  end
416
+
398
417
  it 'raises an error if there is a single inexact match' do
399
418
  expect do
400
419
  @session.find(:xpath, XPath.descendant[XPath.attr(:class).is('almost_singular but')], match: :prefer_exact, exact: true)
401
420
  end.to raise_error(Capybara::ElementNotFound)
402
421
  end
422
+
403
423
  it 'raises an error if there is no match' do
404
424
  expect do
405
425
  @session.find(:xpath, XPath.descendant[XPath.attr(:class).is('does-not-exist')], match: :prefer_exact, exact: true)
@@ -143,10 +143,56 @@ Capybara::SpecHelper.spec '#has_no_table?' do
143
143
  ])
144
144
  end
145
145
 
146
- it 'should consider columns' do
147
- expect(@session).to have_no_table('Vertical Headers', with_cols:
148
- [
149
- { 'First Name' => 'Joe' }
150
- ])
146
+ context 'using :with_cols' do
147
+ it 'should consider a single column' do
148
+ expect(@session).to have_no_table('Vertical Headers', with_cols:
149
+ [
150
+ { 'First Name' => 'Joe' }
151
+ ])
152
+ end
153
+
154
+ it 'should be true even if the last column does exist' do
155
+ expect(@session).to have_no_table('Vertical Headers', with_cols:
156
+ [
157
+ {
158
+ 'First Name' => 'What?',
159
+ 'What?' => 'Walpole',
160
+ 'City' => 'Oceanside' # This line makes the example fail
161
+ }
162
+ ])
163
+ end
164
+
165
+ it 'should be true if none of the columns exist' do
166
+ expect(@session).to have_no_table('Vertical Headers', with_cols:
167
+ [
168
+ {
169
+ 'First Name' => 'What?',
170
+ 'What?' => 'Walpole',
171
+ 'City' => 'What?'
172
+ }
173
+ ])
174
+ end
175
+
176
+ it 'should be true if the first column does match' do
177
+ expect(@session).to have_no_table('Vertical Headers', with_cols:
178
+ [
179
+ {
180
+ 'First Name' => 'Thomas',
181
+ 'Last Name' => 'What',
182
+ 'City' => 'What'
183
+ }
184
+ ])
185
+ end
186
+
187
+ it 'should be true if none of the columns match' do
188
+ expect(@session).to have_no_table('Vertical Headers', with_cols:
189
+ [
190
+ {
191
+ 'First Name' => 'What',
192
+ 'Last Name' => 'What',
193
+ 'City' => 'What'
194
+ }
195
+ ])
196
+ end
151
197
  end
152
198
  end
@@ -111,6 +111,37 @@ 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
+ expect(@session).to have_text(:visible, with_to_hash, {})
131
+ end
132
+
133
+ it 'should fail if passed without empty options' do
134
+ with_to_hash = Class.new do
135
+ def to_s; '42' end
136
+ def to_hash; { blah: 'Other hash' } end
137
+ end.new
138
+ @session.visit('/with_html')
139
+ expect do
140
+ expect(@session).to have_text(:visible, with_to_hash)
141
+ end.to raise_error(ArgumentError)
142
+ end
143
+ end
144
+
114
145
  context 'with exact: true option' do
115
146
  it 'should be true if text matches exactly' do
116
147
  @session.visit('/with_html')
@@ -493,6 +493,21 @@ Capybara::SpecHelper.spec 'node' do
493
493
  expect(@session).to have_css('div.log', text: /DragOver with client position: [1-9]\d*,[1-9]\d*/, count: 2)
494
494
  end
495
495
 
496
+ it 'should preserve clientX/Y from last dragover event' do
497
+ @session.visit('/with_js')
498
+ element = @session.find('//div[@id="drag_html5"]')
499
+ target = @session.find('//div[@id="drop_html5"]')
500
+ element.drag_to(target)
501
+
502
+ # The first "DragOver" div is inserted by the last dragover event dispatched
503
+ drag_over_div = @session.find('//div[@class="log" and starts-with(text(), "DragOver")]', match: :first)
504
+ position = drag_over_div.text.sub('DragOver ', '')
505
+
506
+ expect(@session).to have_css('div.log', text: /DragLeave #{position}/, count: 1)
507
+ expect(@session).to have_css('div.log', text: /Drop #{position}/, count: 1)
508
+ expect(@session).to have_css('div.log', text: /DragEnd #{position}/, count: 1)
509
+ end
510
+
496
511
  it 'should not HTML5 drag and drop on a non HTML5 drop element' do
497
512
  @session.visit('/with_js')
498
513
  element = @session.find('//div[@id="drag_html5"]')