capybara 3.30.0 → 3.35.3

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