capybara 3.30.0 → 3.35.3

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 (146) hide show
  1. checksums.yaml +4 -4
  2. data/History.md +153 -13
  3. data/README.md +9 -4
  4. data/lib/capybara.rb +18 -8
  5. data/lib/capybara/config.rb +4 -6
  6. data/lib/capybara/cucumber.rb +1 -1
  7. data/lib/capybara/driver/base.rb +4 -0
  8. data/lib/capybara/dsl.rb +10 -2
  9. data/lib/capybara/helpers.rb +25 -1
  10. data/lib/capybara/minitest.rb +232 -144
  11. data/lib/capybara/minitest/spec.rb +156 -97
  12. data/lib/capybara/node/actions.rb +16 -21
  13. data/lib/capybara/node/base.rb +6 -6
  14. data/lib/capybara/node/element.rb +14 -13
  15. data/lib/capybara/node/finders.rb +12 -7
  16. data/lib/capybara/node/matchers.rb +36 -27
  17. data/lib/capybara/node/simple.rb +6 -2
  18. data/lib/capybara/queries/ancestor_query.rb +1 -1
  19. data/lib/capybara/queries/base_query.rb +2 -1
  20. data/lib/capybara/queries/current_path_query.rb +14 -4
  21. data/lib/capybara/queries/selector_query.rb +40 -18
  22. data/lib/capybara/queries/sibling_query.rb +1 -1
  23. data/lib/capybara/queries/style_query.rb +1 -1
  24. data/lib/capybara/queries/text_query.rb +7 -1
  25. data/lib/capybara/rack_test/browser.rb +9 -3
  26. data/lib/capybara/rack_test/driver.rb +1 -0
  27. data/lib/capybara/rack_test/form.rb +1 -1
  28. data/lib/capybara/rack_test/node.rb +35 -10
  29. data/lib/capybara/registration_container.rb +44 -0
  30. data/lib/capybara/registrations/drivers.rb +18 -12
  31. data/lib/capybara/registrations/patches/puma_ssl.rb +3 -1
  32. data/lib/capybara/registrations/servers.rb +3 -2
  33. data/lib/capybara/result.rb +35 -15
  34. data/lib/capybara/rspec.rb +2 -0
  35. data/lib/capybara/rspec/matcher_proxies.rb +5 -5
  36. data/lib/capybara/rspec/matchers.rb +33 -32
  37. data/lib/capybara/rspec/matchers/base.rb +12 -6
  38. data/lib/capybara/rspec/matchers/count_sugar.rb +2 -1
  39. data/lib/capybara/rspec/matchers/have_ancestor.rb +4 -3
  40. data/lib/capybara/rspec/matchers/have_current_path.rb +2 -2
  41. data/lib/capybara/rspec/matchers/have_selector.rb +15 -7
  42. data/lib/capybara/rspec/matchers/have_sibling.rb +3 -3
  43. data/lib/capybara/rspec/matchers/have_text.rb +3 -3
  44. data/lib/capybara/rspec/matchers/have_title.rb +2 -2
  45. data/lib/capybara/rspec/matchers/match_selector.rb +3 -3
  46. data/lib/capybara/rspec/matchers/match_style.rb +7 -2
  47. data/lib/capybara/rspec/matchers/spatial_sugar.rb +2 -1
  48. data/lib/capybara/selector.rb +14 -3
  49. data/lib/capybara/selector/builders/css_builder.rb +1 -1
  50. data/lib/capybara/selector/builders/xpath_builder.rb +3 -1
  51. data/lib/capybara/selector/definition.rb +11 -9
  52. data/lib/capybara/selector/definition/button.rb +26 -14
  53. data/lib/capybara/selector/definition/css.rb +1 -1
  54. data/lib/capybara/selector/definition/datalist_input.rb +1 -1
  55. data/lib/capybara/selector/definition/element.rb +2 -1
  56. data/lib/capybara/selector/definition/fillable_field.rb +1 -1
  57. data/lib/capybara/selector/definition/label.rb +2 -2
  58. data/lib/capybara/selector/definition/link.rb +8 -0
  59. data/lib/capybara/selector/definition/select.rb +32 -13
  60. data/lib/capybara/selector/definition/table.rb +1 -1
  61. data/lib/capybara/selector/definition/table_row.rb +2 -2
  62. data/lib/capybara/selector/filter_set.rb +2 -2
  63. data/lib/capybara/selector/selector.rb +9 -1
  64. data/lib/capybara/selenium/atoms/getAttribute.min.js +1 -1
  65. data/lib/capybara/selenium/atoms/src/getAttribute.js +1 -1
  66. data/lib/capybara/selenium/atoms/src/isDisplayed.js +1 -1
  67. data/lib/capybara/selenium/driver.rb +52 -7
  68. data/lib/capybara/selenium/driver_specializations/chrome_driver.rb +10 -12
  69. data/lib/capybara/selenium/driver_specializations/edge_driver.rb +9 -11
  70. data/lib/capybara/selenium/driver_specializations/firefox_driver.rb +3 -3
  71. data/lib/capybara/selenium/extensions/find.rb +4 -4
  72. data/lib/capybara/selenium/extensions/html5_drag.rb +24 -8
  73. data/lib/capybara/selenium/extensions/scroll.rb +8 -10
  74. data/lib/capybara/selenium/logger_suppressor.rb +8 -2
  75. data/lib/capybara/selenium/node.rb +96 -16
  76. data/lib/capybara/selenium/nodes/chrome_node.rb +27 -16
  77. data/lib/capybara/selenium/nodes/edge_node.rb +1 -1
  78. data/lib/capybara/selenium/nodes/firefox_node.rb +9 -4
  79. data/lib/capybara/selenium/nodes/safari_node.rb +1 -1
  80. data/lib/capybara/selenium/patches/action_pauser.rb +26 -0
  81. data/lib/capybara/selenium/patches/atoms.rb +4 -4
  82. data/lib/capybara/selenium/patches/logs.rb +7 -9
  83. data/lib/capybara/server/animation_disabler.rb +8 -3
  84. data/lib/capybara/server/middleware.rb +4 -2
  85. data/lib/capybara/session.rb +53 -29
  86. data/lib/capybara/session/config.rb +3 -1
  87. data/lib/capybara/session/matchers.rb +11 -11
  88. data/lib/capybara/spec/public/test.js +64 -7
  89. data/lib/capybara/spec/session/accept_alert_spec.rb +1 -1
  90. data/lib/capybara/spec/session/all_spec.rb +45 -5
  91. data/lib/capybara/spec/session/assert_text_spec.rb +5 -5
  92. data/lib/capybara/spec/session/check_spec.rb +6 -0
  93. data/lib/capybara/spec/session/click_button_spec.rb +11 -0
  94. data/lib/capybara/spec/session/click_link_or_button_spec.rb +9 -0
  95. data/lib/capybara/spec/session/current_url_spec.rb +11 -1
  96. data/lib/capybara/spec/session/fill_in_spec.rb +29 -0
  97. data/lib/capybara/spec/session/find_spec.rb +11 -8
  98. data/lib/capybara/spec/session/has_button_spec.rb +51 -0
  99. data/lib/capybara/spec/session/has_css_spec.rb +14 -10
  100. data/lib/capybara/spec/session/has_current_path_spec.rb +15 -2
  101. data/lib/capybara/spec/session/has_field_spec.rb +16 -0
  102. data/lib/capybara/spec/session/has_select_spec.rb +32 -4
  103. data/lib/capybara/spec/session/has_selector_spec.rb +4 -4
  104. data/lib/capybara/spec/session/has_text_spec.rb +5 -12
  105. data/lib/capybara/spec/session/html_spec.rb +1 -1
  106. data/lib/capybara/spec/session/matches_style_spec.rb +2 -2
  107. data/lib/capybara/spec/session/node_spec.rb +169 -33
  108. data/lib/capybara/spec/session/refresh_spec.rb +2 -1
  109. data/lib/capybara/spec/session/save_page_spec.rb +4 -4
  110. data/lib/capybara/spec/session/window/switch_to_window_spec.rb +1 -1
  111. data/lib/capybara/spec/session/window/window_opened_by_spec.rb +1 -1
  112. data/lib/capybara/spec/session/window/window_spec.rb +8 -8
  113. data/lib/capybara/spec/session/window/windows_spec.rb +1 -1
  114. data/lib/capybara/spec/spec_helper.rb +13 -14
  115. data/lib/capybara/spec/test_app.rb +23 -21
  116. data/lib/capybara/spec/views/form.erb +36 -3
  117. data/lib/capybara/spec/views/with_animation.erb +8 -0
  118. data/lib/capybara/spec/views/with_dragula.erb +3 -1
  119. data/lib/capybara/spec/views/with_html.erb +2 -2
  120. data/lib/capybara/spec/views/with_jquery_animation.erb +24 -0
  121. data/lib/capybara/spec/views/with_js.erb +3 -0
  122. data/lib/capybara/spec/views/with_sortable_js.erb +1 -1
  123. data/lib/capybara/version.rb +1 -1
  124. data/lib/capybara/window.rb +3 -7
  125. data/spec/basic_node_spec.rb +9 -8
  126. data/spec/capybara_spec.rb +1 -1
  127. data/spec/dsl_spec.rb +14 -1
  128. data/spec/fixtures/selenium_driver_rspec_success.rb +1 -1
  129. data/spec/minitest_spec.rb +3 -2
  130. data/spec/rack_test_spec.rb +28 -6
  131. data/spec/regexp_dissassembler_spec.rb +0 -4
  132. data/spec/result_spec.rb +40 -29
  133. data/spec/rspec/features_spec.rb +3 -1
  134. data/spec/rspec/scenarios_spec.rb +4 -0
  135. data/spec/rspec/shared_spec_matchers.rb +63 -51
  136. data/spec/rspec_spec.rb +4 -0
  137. data/spec/selector_spec.rb +17 -2
  138. data/spec/selenium_spec_chrome.rb +45 -21
  139. data/spec/selenium_spec_chrome_remote.rb +7 -1
  140. data/spec/selenium_spec_firefox.rb +15 -13
  141. data/spec/server_spec.rb +60 -49
  142. data/spec/shared_selenium_node.rb +18 -0
  143. data/spec/shared_selenium_session.rb +98 -7
  144. data/spec/spec_helper.rb +1 -1
  145. metadata +50 -14
  146. data/lib/capybara/spec/session/source_spec.rb +0 -0
@@ -60,6 +60,22 @@ Capybara::SpecHelper.spec '#has_field' do
60
60
  end
61
61
  end
62
62
 
63
+ context 'with validation message', requires: [:html_validation] do
64
+ it 'should accept a regexp' do
65
+ @session.fill_in('form_zipcode', with: '1234')
66
+ expect(@session).to have_field('form_zipcode', validation_message: /match the requested format/)
67
+ expect(@session).not_to have_field('form_zipcode', validation_message: /random/)
68
+ end
69
+
70
+ it 'should accept a string' do
71
+ @session.fill_in('form_zipcode', with: '1234')
72
+ expect(@session).to have_field('form_zipcode', validation_message: 'Please match the requested format.')
73
+ expect(@session).not_to have_field('form_zipcode', validation_message: 'match the requested format.')
74
+ @session.fill_in('form_zipcode', with: '12345')
75
+ expect(@session).to have_field('form_zipcode', validation_message: '')
76
+ end
77
+ end
78
+
63
79
  context 'with type' do
64
80
  it 'should be true if a field with the given type is on the page' do
65
81
  expect(@session).to have_field('First Name', type: 'text')
@@ -63,7 +63,7 @@ Capybara::SpecHelper.spec '#has_select?' do
63
63
  end
64
64
 
65
65
  it "should be true even when the selected option invisible, regardless of the select's visibility" do
66
- expect(@session).to have_select('Icecream', visible: false, selected: 'Chocolate')
66
+ expect(@session).to have_select('Icecream', visible: :hidden, selected: 'Chocolate')
67
67
  expect(@session).to have_select('Sorbet', selected: 'Vanilla')
68
68
  end
69
69
  end
@@ -88,7 +88,7 @@ Capybara::SpecHelper.spec '#has_select?' do
88
88
  end
89
89
 
90
90
  it "should be true even when the selected values are invisible, regardless of the select's visibility" do
91
- expect(@session).to have_select('Dessert', visible: false, with_options: %w[Pudding Tiramisu])
91
+ expect(@session).to have_select('Dessert', visible: :hidden, with_options: %w[Pudding Tiramisu])
92
92
  expect(@session).to have_select('Cake', with_selected: ['Chocolate Cake', 'Sponge Cake'])
93
93
  end
94
94
 
@@ -113,7 +113,35 @@ Capybara::SpecHelper.spec '#has_select?' do
113
113
  end
114
114
 
115
115
  it 'should be true even when the options are invisible, if the select itself is invisible' do
116
- expect(@session).to have_select('Icecream', visible: false, options: %w[Chocolate Vanilla Strawberry])
116
+ expect(@session).to have_select('Icecream', visible: :hidden, options: %w[Chocolate Vanilla Strawberry])
117
+ end
118
+ end
119
+
120
+ context 'with enabled options' do
121
+ it 'should be true if the listed options exist and are enabled' do
122
+ expect(@session).to have_select('form_title', enabled_options: %w[Mr Mrs Miss])
123
+ end
124
+
125
+ it 'should be false if the listed options do not exist' do
126
+ expect(@session).not_to have_select('form_title', enabled_options: ['Not there'])
127
+ end
128
+
129
+ it 'should be false if the listed option exists but is not enabled' do
130
+ expect(@session).not_to have_select('form_title', enabled_options: %w[Mr Mrs Miss Other])
131
+ end
132
+ end
133
+
134
+ context 'with disabled options' do
135
+ it 'should be true if the listed options exist and are disabled' do
136
+ expect(@session).to have_select('form_title', disabled_options: ['Other'])
137
+ end
138
+
139
+ it 'should be false if the listed options do not exist' do
140
+ expect(@session).not_to have_select('form_title', disabled_options: ['Not there'])
141
+ end
142
+
143
+ it 'should be false if the listed option exists but is not disabled' do
144
+ expect(@session).not_to have_select('form_title', disabled_options: %w[Other Mrs])
117
145
  end
118
146
  end
119
147
 
@@ -130,7 +158,7 @@ Capybara::SpecHelper.spec '#has_select?' do
130
158
  end
131
159
 
132
160
  it 'should be true even when the options are invisible, if the select itself is invisible' do
133
- expect(@session).to have_select('Icecream', visible: false, with_options: %w[Vanilla Strawberry])
161
+ expect(@session).to have_select('Icecream', visible: :hidden, with_options: %w[Vanilla Strawberry])
134
162
  end
135
163
  end
136
164
 
@@ -61,12 +61,12 @@ Capybara::SpecHelper.spec '#has_selector?' do
61
61
  end
62
62
 
63
63
  it 'should respect visibility setting' do
64
- expect(@session).to have_selector(:id, 'hidden-text', text: 'Some of this text is hidden!', visible: false)
65
- expect(@session).not_to have_selector(:id, 'hidden-text', text: 'Some of this text is hidden!', visible: true)
64
+ expect(@session).to have_selector(:id, 'hidden-text', text: 'Some of this text is hidden!', visible: :all)
65
+ expect(@session).not_to have_selector(:id, 'hidden-text', text: 'Some of this text is hidden!', visible: :visible)
66
66
  Capybara.ignore_hidden_elements = false
67
- expect(@session).to have_selector(:id, 'hidden-text', text: 'Some of this text is hidden!', visible: false)
67
+ expect(@session).to have_selector(:id, 'hidden-text', text: 'Some of this text is hidden!', visible: :all)
68
68
  Capybara.visible_text_only = true
69
- expect(@session).not_to have_selector(:id, 'hidden-text', text: 'Some of this text is hidden!', visible: true)
69
+ expect(@session).not_to have_selector(:id, 'hidden-text', text: 'Some of this text is hidden!', visible: :visible)
70
70
  end
71
71
 
72
72
  it 'should discard all matches where the given regexp is not matched' do
@@ -127,18 +127,11 @@ Capybara::SpecHelper.spec '#has_text?' do
127
127
  def to_hash; { value: 'Other hash' } end
128
128
  end.new
129
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)
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
142
135
  end
143
136
  end
144
137
 
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- # Note: This file uses `sleep` to sync up parts of the tests. This is only implemented like this
3
+ # NOTE: This file uses `sleep` to sync up parts of the tests. This is only implemented like this
4
4
  # because of the methods being tested. In tests using Capybara this type of behavior should be implemented
5
5
  # using Capybara provided assertions with builtin waiting behavior.
6
6
 
@@ -28,8 +28,8 @@ Capybara::SpecHelper.spec '#matches_style?', requires: [:css] do
28
28
  output(/have_style is deprecated/).to_stderr
29
29
 
30
30
  el = @session.find(:css, '#first')
31
- allow(el).to receive(:warn).and_return(nil)
31
+ allow(Capybara::Helpers).to receive(:warn).and_return(nil)
32
32
  el.has_style?('display' => /^bl/)
33
- expect(el).to have_received(:warn)
33
+ expect(Capybara::Helpers).to have_received(:warn)
34
34
  end
35
35
  end
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- # Note: This file uses `sleep` to sync up parts of the tests. This is only implemented like this
3
+ # NOTE: This file uses `sleep` to sync up parts of the tests. This is only implemented like this
4
4
  # because of the methods being tested. In tests using Capybara this type of behavior should be implemented
5
5
  # using Capybara provided assertions with builtin waiting behavior.
6
6
 
@@ -270,13 +270,13 @@ Capybara::SpecHelper.spec 'node' do
270
270
  end
271
271
 
272
272
  it 'works when details is toggled open and closed' do
273
- @session.find(:css, '#closed_details').click
274
- expect(@session).to have_css('#closed_details *', visible: true, count: 5)
273
+ @session.find(:css, '#closed_details > summary').click
274
+ expect(@session).to have_css('#closed_details *', visible: :visible, count: 5)
275
275
  .and(have_no_css('#closed_details *', visible: :hidden))
276
276
 
277
- @session.find(:css, '#closed_details').click
277
+ @session.find(:css, '#closed_details > summary').click
278
278
  descendants_css = '#closed_details > *:not(summary), #closed_details > *:not(summary) *'
279
- expect(@session).to have_no_css(descendants_css, visible: true)
279
+ expect(@session).to have_no_css(descendants_css, visible: :visible)
280
280
  .and(have_css(descendants_css, visible: :hidden, count: 3))
281
281
  end
282
282
  end
@@ -388,8 +388,8 @@ Capybara::SpecHelper.spec 'node' do
388
388
 
389
389
  describe '#==' do
390
390
  it 'preserve object identity' do
391
- expect(@session.find('//h1') == @session.find('//h1')).to be true # rubocop:disable Lint/UselessComparison
392
- expect(@session.find('//h1') === @session.find('//h1')).to be true # rubocop:disable Style/CaseEquality, Lint/UselessComparison
391
+ expect(@session.find('//h1') == @session.find('//h1')).to be true # rubocop:disable Lint/BinaryOperatorWithIdenticalOperands
392
+ expect(@session.find('//h1') === @session.find('//h1')).to be true # rubocop:disable Style/CaseEquality, Lint/BinaryOperatorWithIdenticalOperands
393
393
  expect(@session.find('//h1').eql?(@session.find('//h1'))).to be false
394
394
  end
395
395
 
@@ -409,6 +409,17 @@ Capybara::SpecHelper.spec 'node' do
409
409
  element = @session.find(:link, 'Second Link')
410
410
  expect(@session.find(:xpath, element.path)).to eq(element)
411
411
  end
412
+
413
+ it 'reports when element in shadow dom', requires: [:shadow_dom] do
414
+ @session.visit('/with_js')
415
+ shadow = @session.find(:css, '#shadow')
416
+ element = @session.evaluate_script(<<~JS, shadow)
417
+ (function(root){
418
+ return root.shadowRoot.querySelector('span');
419
+ })(arguments[0])
420
+ JS
421
+ expect(element.path).to eq '(: Shadow DOM element - no XPath :)'
422
+ end
412
423
  end
413
424
 
414
425
  describe '#trigger', requires: %i[js trigger] do
@@ -446,7 +457,7 @@ Capybara::SpecHelper.spec 'node' do
446
457
 
447
458
  it 'should work with Dragula' do
448
459
  @session.visit('/with_dragula')
449
- @session.within(:css, '#sortable') do
460
+ @session.within(:css, '#sortable.ready') do
450
461
  src = @session.find('div', text: 'Item 1')
451
462
  target = @session.find('div', text: 'Item 3')
452
463
  src.drag_to target
@@ -468,6 +479,50 @@ Capybara::SpecHelper.spec 'node' do
468
479
  end
469
480
  end
470
481
 
482
+ it 'should simulate a single held down modifier key' do
483
+ %I[
484
+ alt
485
+ ctrl
486
+ meta
487
+ shift
488
+ ].each do |modifier_key|
489
+ @session.visit('/with_js')
490
+
491
+ element = @session.find('//div[@id="drag"]')
492
+ target = @session.find('//div[@id="drop"]')
493
+
494
+ element.drag_to(target, drop_modifiers: modifier_key)
495
+ expect(@session).to have_css('div.drag_start', exact_text: 'Dragged!')
496
+ expect(@session).to have_xpath("//div[contains(., 'Dropped!-#{modifier_key}')]")
497
+ end
498
+ end
499
+
500
+ it 'should simulate multiple held down modifier keys' do
501
+ @session.visit('/with_js')
502
+
503
+ element = @session.find('//div[@id="drag"]')
504
+ target = @session.find('//div[@id="drop"]')
505
+
506
+ modifier_keys = %I[alt ctrl meta shift]
507
+
508
+ element.drag_to(target, drop_modifiers: modifier_keys)
509
+ expect(@session).to have_xpath("//div[contains(., 'Dropped!-#{modifier_keys.join('-')}')]")
510
+ end
511
+
512
+ it 'should support key aliases' do
513
+ { control: :ctrl,
514
+ command: :meta,
515
+ cmd: :meta }.each do |(key_alias, key)|
516
+ @session.visit('/with_js')
517
+
518
+ element = @session.find('//div[@id="drag"]')
519
+ target = @session.find('//div[@id="drop"]')
520
+
521
+ element.drag_to(target, drop_modifiers: [key_alias])
522
+ expect(target).to have_text("Dropped!-#{key}", exact: true)
523
+ end
524
+ end
525
+
471
526
  context 'HTML5', requires: %i[js html5_drag] do
472
527
  it 'should HTML5 drag and drop an object' do
473
528
  @session.visit('/with_js')
@@ -499,8 +554,13 @@ Capybara::SpecHelper.spec 'node' do
499
554
  target = @session.find('//div[@id="drop_html5"]')
500
555
  element.drag_to(target)
501
556
 
557
+ conditions = %w[DragLeave Drop DragEnd].map do |text|
558
+ have_css('div.log', text: text)
559
+ end
560
+ expect(@session).to(conditions.reduce { |memo, cond| memo.and(cond) })
561
+
502
562
  # 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)
563
+ drag_over_div = @session.first('//div[@class="log" and starts-with(text(), "DragOver")]')
504
564
  position = drag_over_div.text.sub('DragOver ', '')
505
565
 
506
566
  expect(@session).to have_css('div.log', text: /DragLeave #{position}/, count: 1)
@@ -551,6 +611,46 @@ Capybara::SpecHelper.spec 'node' do
551
611
  source.drag_to target
552
612
  expect(@session).to have_xpath('//div[contains(., "HTML5 Dropped")]')
553
613
  end
614
+
615
+ it 'should simulate a single held down modifier key' do
616
+ %I[alt ctrl meta shift].each do |modifier_key|
617
+ @session.visit('/with_js')
618
+
619
+ element = @session.find('//div[@id="drag_html5"]')
620
+ target = @session.find('//div[@id="drop_html5"]')
621
+
622
+ element.drag_to(target, drop_modifiers: modifier_key)
623
+
624
+ expect(@session).to have_css('div.drag_start', exact_text: 'HTML5 Dragged!')
625
+ expect(@session).to have_xpath("//div[contains(., 'HTML5 Dropped string: text/plain drag_html5-#{modifier_key}')]")
626
+ end
627
+ end
628
+
629
+ it 'should simulate multiple held down modifier keys' do
630
+ @session.visit('/with_js')
631
+
632
+ element = @session.find('//div[@id="drag_html5"]')
633
+ target = @session.find('//div[@id="drop_html5"]')
634
+
635
+ modifier_keys = %I[alt ctrl meta shift]
636
+
637
+ element.drag_to(target, drop_modifiers: modifier_keys)
638
+ expect(@session).to have_xpath("//div[contains(., 'HTML5 Dropped string: text/plain drag_html5-#{modifier_keys.join('-')}')]")
639
+ end
640
+
641
+ it 'should support key aliases' do
642
+ { control: :ctrl,
643
+ command: :meta,
644
+ cmd: :meta }.each do |(key_alias, key)|
645
+ @session.visit('/with_js')
646
+
647
+ element = @session.find('//div[@id="drag_html5"]')
648
+ target = @session.find('//div[@id="drop_html5"]')
649
+
650
+ element.drag_to(target, drop_modifiers: [key_alias])
651
+ expect(target).to have_text(%r{^HTML5 Dropped string: text/plain drag_html5-#{key}$}m, exact: true)
652
+ end
653
+ end
554
654
  end
555
655
  end
556
656
 
@@ -582,6 +682,15 @@ Capybara::SpecHelper.spec 'node' do
582
682
  expect(@session).to have_xpath('//div[contains(., "HTML5 Dropped string: text/plain Some dropped text")]')
583
683
  end
584
684
 
685
+ it 'can drop a pathname' do
686
+ @session.visit('/with_js')
687
+ target = @session.find('//div[@id="drop_html5"]')
688
+ target.drop(
689
+ Pathname.new(with_os_path_separators(File.expand_path('../fixtures/capybara.jpg', File.dirname(__FILE__))))
690
+ )
691
+ expect(@session).to have_xpath('//div[contains(., "HTML5 Dropped file: capybara.jpg")]')
692
+ end
693
+
585
694
  it 'can drop multiple strings' do
586
695
  @session.visit('/with_js')
587
696
  target = @session.find('//div[@id="drop_html5"]')
@@ -669,7 +778,7 @@ Capybara::SpecHelper.spec 'node' do
669
778
  @session.visit('with_js')
670
779
  @session.find(:css, '#click-test').click(x: 5, y: 5)
671
780
  link = @session.find(:link, 'has-been-clicked')
672
- locations = link.text.match(/^Has been clicked at (?<x>[\d\.-]+),(?<y>[\d\.-]+)$/)
781
+ locations = link.text.match(/^Has been clicked at (?<x>[\d.-]+),(?<y>[\d.-]+)$/)
673
782
  # 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
674
783
  # integer/float conversions and rounding.
675
784
  expect(locations[:x].to_f).to be_within(1).of(5)
@@ -719,7 +828,10 @@ Capybara::SpecHelper.spec 'node' do
719
828
  context 'offset', requires: [:js] do
720
829
  before do
721
830
  @session.visit('/offset')
722
- @clicker = @session.find(:id, 'clicker')
831
+ end
832
+
833
+ let :clicker do
834
+ @session.find(:id, 'clicker')
723
835
  end
724
836
 
725
837
  context 'when w3c_click_offset is false' do
@@ -728,17 +840,17 @@ Capybara::SpecHelper.spec 'node' do
728
840
  end
729
841
 
730
842
  it 'should offset from top left of element' do
731
- @clicker.click(x: 10, y: 5)
843
+ clicker.click(x: 10, y: 5)
732
844
  expect(@session).to have_text(/clicked at 110,105/)
733
845
  end
734
846
 
735
847
  it 'should offset outside the element' do
736
- @clicker.click(x: -15, y: -10)
848
+ clicker.click(x: -15, y: -10)
737
849
  expect(@session).to have_text(/clicked at 85,90/)
738
850
  end
739
851
 
740
852
  it 'should default to click the middle' do
741
- @clicker.click
853
+ clicker.click
742
854
  expect(@session).to have_text(/clicked at 150,150/)
743
855
  end
744
856
  end
@@ -749,21 +861,30 @@ Capybara::SpecHelper.spec 'node' do
749
861
  end
750
862
 
751
863
  it 'should offset from center of element' do
752
- @clicker.click(x: 10, y: 5)
864
+ clicker.click(x: 10, y: 5)
753
865
  expect(@session).to have_text(/clicked at 160,155/)
754
866
  end
755
867
 
756
868
  it 'should offset outside from center of element' do
757
- @clicker.click(x: -65, y: -60)
869
+ clicker.click(x: -65, y: -60)
758
870
  expect(@session).to have_text(/clicked at 85,90/)
759
871
  end
760
872
 
761
873
  it 'should default to click the middle' do
762
- @clicker.click
874
+ clicker.click
763
875
  expect(@session).to have_text(/clicked at 150,150/)
764
876
  end
765
877
  end
766
878
  end
879
+
880
+ context 'delay', requires: [:js] do
881
+ it 'should delay the mouse up' do
882
+ @session.visit('with_js')
883
+ @session.find(:css, '#click-test').click(delay: 2)
884
+ delay = @session.evaluate_script('window.click_delay')
885
+ expect(delay).to be >= 2
886
+ end
887
+ end
767
888
  end
768
889
 
769
890
  describe '#double_click', requires: [:js] do
@@ -783,7 +904,7 @@ Capybara::SpecHelper.spec 'node' do
783
904
  @session.visit('with_js')
784
905
  @session.find(:css, '#click-test').double_click(x: 10, y: 5)
785
906
  link = @session.find(:link, 'has-been-double-clicked')
786
- locations = link.text.match(/^Has been double clicked at (?<x>[\d\.-]+),(?<y>[\d\.-]+)$/)
907
+ locations = link.text.match(/^Has been double clicked at (?<x>[\d.-]+),(?<y>[\d.-]+)$/)
787
908
  # Resulting click location should be very close to 10, 5 relative to top left corner of the element, but may not be exact due
788
909
  # to integer/float conversions and rounding.
789
910
  expect(locations[:x].to_f).to be_within(1).of(10)
@@ -802,7 +923,10 @@ Capybara::SpecHelper.spec 'node' do
802
923
  context 'offset', requires: [:js] do
803
924
  before do
804
925
  @session.visit('/offset')
805
- @clicker = @session.find(:id, 'clicker')
926
+ end
927
+
928
+ let :clicker do
929
+ @session.find(:id, 'clicker')
806
930
  end
807
931
 
808
932
  context 'when w3c_click_offset is false' do
@@ -811,17 +935,17 @@ Capybara::SpecHelper.spec 'node' do
811
935
  end
812
936
 
813
937
  it 'should offset from top left of element' do
814
- @clicker.double_click(x: 10, y: 5)
938
+ clicker.double_click(x: 10, y: 5)
815
939
  expect(@session).to have_text(/clicked at 110,105/)
816
940
  end
817
941
 
818
942
  it 'should offset outside the element' do
819
- @clicker.double_click(x: -15, y: -10)
943
+ clicker.double_click(x: -15, y: -10)
820
944
  expect(@session).to have_text(/clicked at 85,90/)
821
945
  end
822
946
 
823
947
  it 'should default to click the middle' do
824
- @clicker.double_click
948
+ clicker.double_click
825
949
  expect(@session).to have_text(/clicked at 150,150/)
826
950
  end
827
951
  end
@@ -832,17 +956,17 @@ Capybara::SpecHelper.spec 'node' do
832
956
  end
833
957
 
834
958
  it 'should offset from center of element' do
835
- @clicker.double_click(x: 10, y: 5)
959
+ clicker.double_click(x: 10, y: 5)
836
960
  expect(@session).to have_text(/clicked at 160,155/)
837
961
  end
838
962
 
839
963
  it 'should offset outside from center of element' do
840
- @clicker.double_click(x: -65, y: -60)
964
+ clicker.double_click(x: -65, y: -60)
841
965
  expect(@session).to have_text(/clicked at 85,90/)
842
966
  end
843
967
 
844
968
  it 'should default to click the middle' do
845
- @clicker.double_click
969
+ clicker.double_click
846
970
  expect(@session).to have_text(/clicked at 150,150/)
847
971
  end
848
972
  end
@@ -866,7 +990,7 @@ Capybara::SpecHelper.spec 'node' do
866
990
  @session.visit('with_js')
867
991
  @session.find(:css, '#click-test').right_click(x: 10, y: 10)
868
992
  link = @session.find(:link, 'has-been-right-clicked')
869
- locations = link.text.match(/^Has been right clicked at (?<x>[\d\.-]+),(?<y>[\d\.-]+)$/)
993
+ locations = link.text.match(/^Has been right clicked at (?<x>[\d.-]+),(?<y>[\d.-]+)$/)
870
994
  # Resulting click location should be very close to 10, 10 relative to top left corner of the element, but may not be exact due
871
995
  # to integer/float conversions and rounding
872
996
  expect(locations[:x].to_f).to be_within(1).of(10)
@@ -885,7 +1009,10 @@ Capybara::SpecHelper.spec 'node' do
885
1009
  context 'offset', requires: [:js] do
886
1010
  before do
887
1011
  @session.visit('/offset')
888
- @clicker = @session.find(:id, 'clicker')
1012
+ end
1013
+
1014
+ let :clicker do
1015
+ @session.find(:id, 'clicker')
889
1016
  end
890
1017
 
891
1018
  context 'when w3c_click_offset is false' do
@@ -894,17 +1021,17 @@ Capybara::SpecHelper.spec 'node' do
894
1021
  end
895
1022
 
896
1023
  it 'should offset from top left of element' do
897
- @clicker.right_click(x: 10, y: 5)
1024
+ clicker.right_click(x: 10, y: 5)
898
1025
  expect(@session).to have_text(/clicked at 110,105/)
899
1026
  end
900
1027
 
901
1028
  it 'should offset outside the element' do
902
- @clicker.right_click(x: -15, y: -10)
1029
+ clicker.right_click(x: -15, y: -10)
903
1030
  expect(@session).to have_text(/clicked at 85,90/)
904
1031
  end
905
1032
 
906
1033
  it 'should default to click the middle' do
907
- @clicker.right_click
1034
+ clicker.right_click
908
1035
  expect(@session).to have_text(/clicked at 150,150/)
909
1036
  end
910
1037
  end
@@ -915,21 +1042,30 @@ Capybara::SpecHelper.spec 'node' do
915
1042
  end
916
1043
 
917
1044
  it 'should offset from center of element' do
918
- @clicker.right_click(x: 10, y: 5)
1045
+ clicker.right_click(x: 10, y: 5)
919
1046
  expect(@session).to have_text(/clicked at 160,155/)
920
1047
  end
921
1048
 
922
1049
  it 'should offset outside from center of element' do
923
- @clicker.right_click(x: -65, y: -60)
1050
+ clicker.right_click(x: -65, y: -60)
924
1051
  expect(@session).to have_text(/clicked at 85,90/)
925
1052
  end
926
1053
 
927
1054
  it 'should default to click the middle' do
928
- @clicker.right_click
1055
+ clicker.right_click
929
1056
  expect(@session).to have_text(/clicked at 150,150/)
930
1057
  end
931
1058
  end
932
1059
  end
1060
+
1061
+ context 'delay', requires: [:js] do
1062
+ it 'should delay the mouse up' do
1063
+ @session.visit('with_js')
1064
+ @session.find(:css, '#click-test').right_click(delay: 2)
1065
+ delay = @session.evaluate_script('window.right_click_delay')
1066
+ expect(delay).to be >= 2
1067
+ end
1068
+ end
933
1069
  end
934
1070
 
935
1071
  describe '#send_keys', requires: [:send_keys] do