capybara 3.30.0 → 3.33.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (96) hide show
  1. checksums.yaml +4 -4
  2. data/History.md +72 -0
  3. data/README.md +10 -3
  4. data/lib/capybara.rb +17 -7
  5. data/lib/capybara/cucumber.rb +1 -1
  6. data/lib/capybara/dsl.rb +10 -2
  7. data/lib/capybara/minitest.rb +232 -144
  8. data/lib/capybara/minitest/spec.rb +153 -97
  9. data/lib/capybara/node/actions.rb +16 -20
  10. data/lib/capybara/node/element.rb +13 -8
  11. data/lib/capybara/node/finders.rb +5 -1
  12. data/lib/capybara/node/matchers.rb +28 -21
  13. data/lib/capybara/node/simple.rb +1 -1
  14. data/lib/capybara/queries/base_query.rb +2 -1
  15. data/lib/capybara/queries/selector_query.rb +8 -1
  16. data/lib/capybara/queries/style_query.rb +1 -1
  17. data/lib/capybara/queries/text_query.rb +6 -0
  18. data/lib/capybara/rack_test/browser.rb +3 -1
  19. data/lib/capybara/rack_test/node.rb +34 -9
  20. data/lib/capybara/registration_container.rb +44 -0
  21. data/lib/capybara/registrations/servers.rb +1 -1
  22. data/lib/capybara/result.rb +29 -5
  23. data/lib/capybara/rspec/matcher_proxies.rb +4 -4
  24. data/lib/capybara/rspec/matchers.rb +27 -27
  25. data/lib/capybara/rspec/matchers/base.rb +12 -6
  26. data/lib/capybara/rspec/matchers/count_sugar.rb +2 -1
  27. data/lib/capybara/rspec/matchers/have_ancestor.rb +4 -3
  28. data/lib/capybara/rspec/matchers/have_current_path.rb +2 -2
  29. data/lib/capybara/rspec/matchers/have_selector.rb +15 -7
  30. data/lib/capybara/rspec/matchers/have_sibling.rb +3 -3
  31. data/lib/capybara/rspec/matchers/have_text.rb +3 -3
  32. data/lib/capybara/rspec/matchers/have_title.rb +2 -2
  33. data/lib/capybara/rspec/matchers/match_selector.rb +3 -3
  34. data/lib/capybara/rspec/matchers/match_style.rb +2 -2
  35. data/lib/capybara/rspec/matchers/spatial_sugar.rb +2 -1
  36. data/lib/capybara/selector.rb +12 -1
  37. data/lib/capybara/selector/definition.rb +5 -4
  38. data/lib/capybara/selector/definition/button.rb +1 -0
  39. data/lib/capybara/selector/definition/fillable_field.rb +1 -1
  40. data/lib/capybara/selector/definition/label.rb +1 -1
  41. data/lib/capybara/selector/definition/link.rb +8 -0
  42. data/lib/capybara/selector/definition/select.rb +31 -12
  43. data/lib/capybara/selector/definition/table.rb +1 -1
  44. data/lib/capybara/selector/selector.rb +4 -0
  45. data/lib/capybara/selenium/atoms/getAttribute.min.js +1 -1
  46. data/lib/capybara/selenium/atoms/src/getAttribute.js +1 -1
  47. data/lib/capybara/selenium/driver.rb +7 -4
  48. data/lib/capybara/selenium/driver_specializations/chrome_driver.rb +8 -10
  49. data/lib/capybara/selenium/driver_specializations/edge_driver.rb +7 -9
  50. data/lib/capybara/selenium/driver_specializations/firefox_driver.rb +2 -2
  51. data/lib/capybara/selenium/extensions/html5_drag.rb +24 -8
  52. data/lib/capybara/selenium/node.rb +92 -15
  53. data/lib/capybara/selenium/nodes/chrome_node.rb +4 -11
  54. data/lib/capybara/selenium/nodes/edge_node.rb +1 -1
  55. data/lib/capybara/selenium/nodes/firefox_node.rb +3 -3
  56. data/lib/capybara/selenium/patches/action_pauser.rb +26 -0
  57. data/lib/capybara/selenium/patches/logs.rb +3 -5
  58. data/lib/capybara/session.rb +33 -18
  59. data/lib/capybara/session/config.rb +3 -1
  60. data/lib/capybara/spec/public/test.js +58 -6
  61. data/lib/capybara/spec/session/all_spec.rb +45 -5
  62. data/lib/capybara/spec/session/assert_text_spec.rb +5 -5
  63. data/lib/capybara/spec/session/click_button_spec.rb +11 -0
  64. data/lib/capybara/spec/session/fill_in_spec.rb +29 -0
  65. data/lib/capybara/spec/session/find_spec.rb +11 -8
  66. data/lib/capybara/spec/session/has_button_spec.rb +16 -0
  67. data/lib/capybara/spec/session/has_css_spec.rb +12 -9
  68. data/lib/capybara/spec/session/has_current_path_spec.rb +2 -2
  69. data/lib/capybara/spec/session/has_field_spec.rb +16 -0
  70. data/lib/capybara/spec/session/has_select_spec.rb +32 -4
  71. data/lib/capybara/spec/session/has_selector_spec.rb +4 -4
  72. data/lib/capybara/spec/session/has_text_spec.rb +5 -1
  73. data/lib/capybara/spec/session/node_spec.rb +146 -30
  74. data/lib/capybara/spec/session/window/window_spec.rb +7 -7
  75. data/lib/capybara/spec/spec_helper.rb +2 -2
  76. data/lib/capybara/spec/test_app.rb +14 -18
  77. data/lib/capybara/spec/views/form.erb +13 -2
  78. data/lib/capybara/spec/views/with_dragula.erb +3 -1
  79. data/lib/capybara/spec/views/with_html.erb +2 -2
  80. data/lib/capybara/spec/views/with_js.erb +1 -0
  81. data/lib/capybara/version.rb +1 -1
  82. data/spec/capybara_spec.rb +1 -1
  83. data/spec/dsl_spec.rb +14 -1
  84. data/spec/minitest_spec.rb +1 -1
  85. data/spec/rack_test_spec.rb +13 -1
  86. data/spec/regexp_dissassembler_spec.rb +0 -4
  87. data/spec/result_spec.rb +40 -29
  88. data/spec/rspec/shared_spec_matchers.rb +65 -53
  89. data/spec/selector_spec.rb +1 -1
  90. data/spec/selenium_spec_chrome.rb +6 -3
  91. data/spec/selenium_spec_chrome_remote.rb +2 -0
  92. data/spec/server_spec.rb +41 -49
  93. data/spec/shared_selenium_node.rb +18 -0
  94. data/spec/shared_selenium_session.rb +25 -7
  95. data/spec/spec_helper.rb +1 -1
  96. metadata +5 -3
@@ -193,6 +193,17 @@ Capybara::SpecHelper.spec '#click_button' do
193
193
  end
194
194
  end
195
195
 
196
+ context 'when Capybara.enable_aria_role = true' do
197
+ it 'should click on a button role', requires: [:js] do
198
+ Capybara.enable_aria_role = true
199
+ @session.using_wait_time(1.5) do
200
+ @session.visit('/with_js')
201
+ @session.click_button('ARIA button')
202
+ expect(@session).to have_button('ARIA button has been clicked')
203
+ end
204
+ end
205
+ end
206
+
196
207
  context 'with fields associated with the form using the form attribute', requires: [:form_attribute] do
197
208
  let(:results) { extract_results(@session) }
198
209
 
@@ -47,6 +47,15 @@ Capybara::SpecHelper.spec '#fill_in' do
47
47
  expect(extract_results(@session)['description']).to eq('Texty text')
48
48
  end
49
49
 
50
+ it 'should fill in a textarea in a reasonable time by default' do
51
+ textarea = @session.find(:fillable_field, 'form[description]')
52
+ value = 'a' * 4000
53
+ start = Time.now
54
+ textarea.fill_in(with: value)
55
+ expect(Time.now.to_f).to be_within(0.25).of start.to_f
56
+ expect(textarea.value).to eq value
57
+ end
58
+
50
59
  it 'should fill in a password field by id' do
51
60
  @session.fill_in('form_password', with: 'supasikrit')
52
61
  @session.click_button('awesome')
@@ -95,6 +104,26 @@ Capybara::SpecHelper.spec '#fill_in' do
95
104
  expect(extract_results(@session)['html5_color']).to eq('#112233')
96
105
  end
97
106
 
107
+ describe 'with input[type="range"]' do
108
+ it 'should set the range slider correctly' do
109
+ @session.fill_in('form_age', with: 51)
110
+ @session.click_button('awesome')
111
+ expect(extract_results(@session)['age'].to_f).to eq 51
112
+ end
113
+
114
+ it 'should set the range slider to valid values' do
115
+ @session.fill_in('form_age', with: '37.6')
116
+ @session.click_button('awesome')
117
+ expect(extract_results(@session)['age'].to_f).to eq 37.5
118
+ end
119
+
120
+ it 'should respect the range slider limits' do
121
+ @session.fill_in('form_age', with: '3')
122
+ @session.click_button('awesome')
123
+ expect(extract_results(@session)['age'].to_f).to eq 13
124
+ end
125
+ end
126
+
98
127
  it 'should fill in a field with a custom type' do
99
128
  @session.fill_in('Schmooo', with: 'Schmooo is the game')
100
129
  @session.click_button('awesome')
@@ -451,32 +451,35 @@ Capybara::SpecHelper.spec '#find' do
451
451
  context 'with spatial filters', requires: [:spatial] do
452
452
  before do
453
453
  @session.visit('/spatial')
454
- @center = @session.find(:css, 'div.center')
454
+ end
455
+
456
+ let :center do
457
+ @session.find(:css, 'div.center')
455
458
  end
456
459
 
457
460
  it 'should find an element above another element' do
458
- expect(@session.find(:css, 'div:not(.corner)', above: @center).text).to eq('2')
461
+ expect(@session.find(:css, 'div:not(.corner)', above: center).text).to eq('2')
459
462
  end
460
463
 
461
464
  it 'should find an element below another element' do
462
- expect(@session.find(:css, 'div:not(.corner):not(.footer)', below: @center).text).to eq('8')
465
+ expect(@session.find(:css, 'div:not(.corner):not(.footer)', below: center).text).to eq('8')
463
466
  end
464
467
 
465
468
  it 'should find an element left of another element' do
466
- expect(@session.find(:css, 'div:not(.corner)', left_of: @center).text).to eq('4')
469
+ expect(@session.find(:css, 'div:not(.corner)', left_of: center).text).to eq('4')
467
470
  end
468
471
 
469
472
  it 'should find an element right of another element' do
470
- expect(@session.find(:css, 'div:not(.corner)', right_of: @center).text).to eq('6')
473
+ expect(@session.find(:css, 'div:not(.corner)', right_of: center).text).to eq('6')
471
474
  end
472
475
 
473
476
  it 'should combine spatial filters' do
474
- expect(@session.find(:css, 'div', left_of: @center, above: @center).text).to eq('1')
475
- expect(@session.find(:css, 'div', right_of: @center, below: @center).text).to eq('9')
477
+ expect(@session.find(:css, 'div', left_of: center, above: center).text).to eq('1')
478
+ expect(@session.find(:css, 'div', right_of: center, below: center).text).to eq('9')
476
479
  end
477
480
 
478
481
  it 'should find an element "near" another element' do
479
- expect(@session.find(:css, 'div.distance', near: @center).text).to eq('2')
482
+ expect(@session.find(:css, 'div.distance', near: center).text).to eq('2')
480
483
  end
481
484
  end
482
485
 
@@ -39,6 +39,14 @@ Capybara::SpecHelper.spec '#has_button?' do
39
39
  expect(@session).to have_button('awe123', type: 'submit')
40
40
  expect(@session).not_to have_button('awe123', type: 'reset')
41
41
  end
42
+
43
+ it 'should be true for role=button when enable_aria_role: true' do
44
+ expect(@session).to have_button('ARIA button', enable_aria_role: true)
45
+ end
46
+
47
+ it 'should be false for role=button when enable_aria_role: false' do
48
+ expect(@session).not_to have_button('ARIA button', enable_aria_role: false)
49
+ end
42
50
  end
43
51
 
44
52
  Capybara::SpecHelper.spec '#has_no_button?' do
@@ -66,4 +74,12 @@ Capybara::SpecHelper.spec '#has_no_button?' do
66
74
  it 'should be false for disabled buttons if disabled: false' do
67
75
  expect(@session).to have_no_button('Disabled button', disabled: false)
68
76
  end
77
+
78
+ it 'should be true for role=button when enable_aria_role: false' do
79
+ expect(@session).to have_no_button('ARIA button', enable_aria_role: false)
80
+ end
81
+
82
+ it 'should be false for role=button when enable_aria_role: true' do
83
+ expect(@session).not_to have_no_button('ARIA button', enable_aria_role: true)
84
+ end
69
85
  end
@@ -185,10 +185,10 @@ Capybara::SpecHelper.spec '#has_css?' do
185
185
  end
186
186
 
187
187
  it 'should be false when content occurs more times than given' do
188
- expect(@session).not_to have_css('h2.head', maximum: 4) # edge case
189
- expect(@session).not_to have_css('h2', maximum: 3)
188
+ # expect(@session).not_to have_css('h2.head', maximum: 4) # edge case
189
+ # expect(@session).not_to have_css('h2', maximum: 3)
190
190
  expect(@session).not_to have_css('h2').at_most(3).times
191
- expect(@session).not_to have_css('p', maximum: 1)
191
+ # expect(@session).not_to have_css('p', maximum: 1)
192
192
  end
193
193
 
194
194
  it 'should coerce maximum to an integer' do
@@ -234,18 +234,21 @@ Capybara::SpecHelper.spec '#has_css?' do
234
234
  context 'with spatial requirements', requires: [:spatial] do
235
235
  before do
236
236
  @session.visit('/spatial')
237
- @center = @session.find(:css, '.center')
237
+ end
238
+
239
+ let :center do
240
+ @session.find(:css, '.center')
238
241
  end
239
242
 
240
243
  it 'accepts spatial options' do
241
- expect(@session).to have_css('div', above: @center).thrice
242
- expect(@session).to have_css('div', above: @center, right_of: @center).once
244
+ expect(@session).to have_css('div', above: center).thrice
245
+ expect(@session).to have_css('div', above: center, right_of: center).once
243
246
  end
244
247
 
245
248
  it 'supports spatial sugar' do
246
- expect(@session).to have_css('div').left_of(@center).thrice
247
- expect(@session).to have_css('div').below(@center).right_of(@center).once
248
- expect(@session).to have_css('div').near(@center).exactly(8).times
249
+ expect(@session).to have_css('div').left_of(center).thrice
250
+ expect(@session).to have_css('div').below(center).right_of(center).once
251
+ expect(@session).to have_css('div').near(center).exactly(8).times
249
252
  end
250
253
  end
251
254
 
@@ -128,11 +128,11 @@ Capybara::SpecHelper.spec '#has_no_current_path?' do
128
128
  # Without ignore_query option
129
129
  expect do
130
130
  expect(@session).not_to have_current_path('/with_js')
131
- end. not_to raise_exception
131
+ end.not_to raise_exception
132
132
 
133
133
  # With ignore_query option
134
134
  expect do
135
135
  expect(@session).not_to have_current_path('/with_js', ignore_query: true)
136
- end. not_to raise_exception
136
+ end.not_to raise_exception
137
137
  end
138
138
  end
@@ -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,7 +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, {})
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
131
135
  end
132
136
 
133
137
  it 'should fail if passed without empty options' do
@@ -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
@@ -446,7 +446,7 @@ Capybara::SpecHelper.spec 'node' do
446
446
 
447
447
  it 'should work with Dragula' do
448
448
  @session.visit('/with_dragula')
449
- @session.within(:css, '#sortable') do
449
+ @session.within(:css, '#sortable.ready') do
450
450
  src = @session.find('div', text: 'Item 1')
451
451
  target = @session.find('div', text: 'Item 3')
452
452
  src.drag_to target
@@ -468,6 +468,50 @@ Capybara::SpecHelper.spec 'node' do
468
468
  end
469
469
  end
470
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
+
471
515
  context 'HTML5', requires: %i[js html5_drag] do
472
516
  it 'should HTML5 drag and drop an object' do
473
517
  @session.visit('/with_js')
@@ -499,8 +543,13 @@ Capybara::SpecHelper.spec 'node' do
499
543
  target = @session.find('//div[@id="drop_html5"]')
500
544
  element.drag_to(target)
501
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
+
502
551
  # 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)
552
+ drag_over_div = @session.first('//div[@class="log" and starts-with(text(), "DragOver")]')
504
553
  position = drag_over_div.text.sub('DragOver ', '')
505
554
 
506
555
  expect(@session).to have_css('div.log', text: /DragLeave #{position}/, count: 1)
@@ -551,6 +600,46 @@ Capybara::SpecHelper.spec 'node' do
551
600
  source.drag_to target
552
601
  expect(@session).to have_xpath('//div[contains(., "HTML5 Dropped")]')
553
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
554
643
  end
555
644
  end
556
645
 
@@ -669,7 +758,7 @@ Capybara::SpecHelper.spec 'node' do
669
758
  @session.visit('with_js')
670
759
  @session.find(:css, '#click-test').click(x: 5, y: 5)
671
760
  link = @session.find(:link, 'has-been-clicked')
672
- 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.-]+)$/)
673
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
674
763
  # integer/float conversions and rounding.
675
764
  expect(locations[:x].to_f).to be_within(1).of(5)
@@ -719,7 +808,10 @@ Capybara::SpecHelper.spec 'node' do
719
808
  context 'offset', requires: [:js] do
720
809
  before do
721
810
  @session.visit('/offset')
722
- @clicker = @session.find(:id, 'clicker')
811
+ end
812
+
813
+ let :clicker do
814
+ @session.find(:id, 'clicker')
723
815
  end
724
816
 
725
817
  context 'when w3c_click_offset is false' do
@@ -728,17 +820,17 @@ Capybara::SpecHelper.spec 'node' do
728
820
  end
729
821
 
730
822
  it 'should offset from top left of element' do
731
- @clicker.click(x: 10, y: 5)
823
+ clicker.click(x: 10, y: 5)
732
824
  expect(@session).to have_text(/clicked at 110,105/)
733
825
  end
734
826
 
735
827
  it 'should offset outside the element' do
736
- @clicker.click(x: -15, y: -10)
828
+ clicker.click(x: -15, y: -10)
737
829
  expect(@session).to have_text(/clicked at 85,90/)
738
830
  end
739
831
 
740
832
  it 'should default to click the middle' do
741
- @clicker.click
833
+ clicker.click
742
834
  expect(@session).to have_text(/clicked at 150,150/)
743
835
  end
744
836
  end
@@ -749,21 +841,30 @@ Capybara::SpecHelper.spec 'node' do
749
841
  end
750
842
 
751
843
  it 'should offset from center of element' do
752
- @clicker.click(x: 10, y: 5)
844
+ clicker.click(x: 10, y: 5)
753
845
  expect(@session).to have_text(/clicked at 160,155/)
754
846
  end
755
847
 
756
848
  it 'should offset outside from center of element' do
757
- @clicker.click(x: -65, y: -60)
849
+ clicker.click(x: -65, y: -60)
758
850
  expect(@session).to have_text(/clicked at 85,90/)
759
851
  end
760
852
 
761
853
  it 'should default to click the middle' do
762
- @clicker.click
854
+ clicker.click
763
855
  expect(@session).to have_text(/clicked at 150,150/)
764
856
  end
765
857
  end
766
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
767
868
  end
768
869
 
769
870
  describe '#double_click', requires: [:js] do
@@ -783,7 +884,7 @@ Capybara::SpecHelper.spec 'node' do
783
884
  @session.visit('with_js')
784
885
  @session.find(:css, '#click-test').double_click(x: 10, y: 5)
785
886
  link = @session.find(:link, 'has-been-double-clicked')
786
- 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.-]+)$/)
787
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
788
889
  # to integer/float conversions and rounding.
789
890
  expect(locations[:x].to_f).to be_within(1).of(10)
@@ -802,7 +903,10 @@ Capybara::SpecHelper.spec 'node' do
802
903
  context 'offset', requires: [:js] do
803
904
  before do
804
905
  @session.visit('/offset')
805
- @clicker = @session.find(:id, 'clicker')
906
+ end
907
+
908
+ let :clicker do
909
+ @session.find(:id, 'clicker')
806
910
  end
807
911
 
808
912
  context 'when w3c_click_offset is false' do
@@ -811,17 +915,17 @@ Capybara::SpecHelper.spec 'node' do
811
915
  end
812
916
 
813
917
  it 'should offset from top left of element' do
814
- @clicker.double_click(x: 10, y: 5)
918
+ clicker.double_click(x: 10, y: 5)
815
919
  expect(@session).to have_text(/clicked at 110,105/)
816
920
  end
817
921
 
818
922
  it 'should offset outside the element' do
819
- @clicker.double_click(x: -15, y: -10)
923
+ clicker.double_click(x: -15, y: -10)
820
924
  expect(@session).to have_text(/clicked at 85,90/)
821
925
  end
822
926
 
823
927
  it 'should default to click the middle' do
824
- @clicker.double_click
928
+ clicker.double_click
825
929
  expect(@session).to have_text(/clicked at 150,150/)
826
930
  end
827
931
  end
@@ -832,17 +936,17 @@ Capybara::SpecHelper.spec 'node' do
832
936
  end
833
937
 
834
938
  it 'should offset from center of element' do
835
- @clicker.double_click(x: 10, y: 5)
939
+ clicker.double_click(x: 10, y: 5)
836
940
  expect(@session).to have_text(/clicked at 160,155/)
837
941
  end
838
942
 
839
943
  it 'should offset outside from center of element' do
840
- @clicker.double_click(x: -65, y: -60)
944
+ clicker.double_click(x: -65, y: -60)
841
945
  expect(@session).to have_text(/clicked at 85,90/)
842
946
  end
843
947
 
844
948
  it 'should default to click the middle' do
845
- @clicker.double_click
949
+ clicker.double_click
846
950
  expect(@session).to have_text(/clicked at 150,150/)
847
951
  end
848
952
  end
@@ -866,7 +970,7 @@ Capybara::SpecHelper.spec 'node' do
866
970
  @session.visit('with_js')
867
971
  @session.find(:css, '#click-test').right_click(x: 10, y: 10)
868
972
  link = @session.find(:link, 'has-been-right-clicked')
869
- 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.-]+)$/)
870
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
871
975
  # to integer/float conversions and rounding
872
976
  expect(locations[:x].to_f).to be_within(1).of(10)
@@ -885,7 +989,10 @@ Capybara::SpecHelper.spec 'node' do
885
989
  context 'offset', requires: [:js] do
886
990
  before do
887
991
  @session.visit('/offset')
888
- @clicker = @session.find(:id, 'clicker')
992
+ end
993
+
994
+ let :clicker do
995
+ @session.find(:id, 'clicker')
889
996
  end
890
997
 
891
998
  context 'when w3c_click_offset is false' do
@@ -894,17 +1001,17 @@ Capybara::SpecHelper.spec 'node' do
894
1001
  end
895
1002
 
896
1003
  it 'should offset from top left of element' do
897
- @clicker.right_click(x: 10, y: 5)
1004
+ clicker.right_click(x: 10, y: 5)
898
1005
  expect(@session).to have_text(/clicked at 110,105/)
899
1006
  end
900
1007
 
901
1008
  it 'should offset outside the element' do
902
- @clicker.right_click(x: -15, y: -10)
1009
+ clicker.right_click(x: -15, y: -10)
903
1010
  expect(@session).to have_text(/clicked at 85,90/)
904
1011
  end
905
1012
 
906
1013
  it 'should default to click the middle' do
907
- @clicker.right_click
1014
+ clicker.right_click
908
1015
  expect(@session).to have_text(/clicked at 150,150/)
909
1016
  end
910
1017
  end
@@ -915,21 +1022,30 @@ Capybara::SpecHelper.spec 'node' do
915
1022
  end
916
1023
 
917
1024
  it 'should offset from center of element' do
918
- @clicker.right_click(x: 10, y: 5)
1025
+ clicker.right_click(x: 10, y: 5)
919
1026
  expect(@session).to have_text(/clicked at 160,155/)
920
1027
  end
921
1028
 
922
1029
  it 'should offset outside from center of element' do
923
- @clicker.right_click(x: -65, y: -60)
1030
+ clicker.right_click(x: -65, y: -60)
924
1031
  expect(@session).to have_text(/clicked at 85,90/)
925
1032
  end
926
1033
 
927
1034
  it 'should default to click the middle' do
928
- @clicker.right_click
1035
+ clicker.right_click
929
1036
  expect(@session).to have_text(/clicked at 150,150/)
930
1037
  end
931
1038
  end
932
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
933
1049
  end
934
1050
 
935
1051
  describe '#send_keys', requires: [:send_keys] do