capybara 3.37.1 → 3.39.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (101) hide show
  1. checksums.yaml +4 -4
  2. data/History.md +68 -4
  3. data/README.md +23 -11
  4. data/lib/capybara/helpers.rb +5 -1
  5. data/lib/capybara/node/actions.rb +4 -4
  6. data/lib/capybara/node/base.rb +2 -1
  7. data/lib/capybara/node/finders.rb +2 -0
  8. data/lib/capybara/node/whitespace_normalizer.rb +81 -0
  9. data/lib/capybara/queries/base_query.rb +2 -2
  10. data/lib/capybara/queries/selector_query.rb +4 -2
  11. data/lib/capybara/queries/text_query.rb +1 -1
  12. data/lib/capybara/rack_test/browser.rb +8 -2
  13. data/lib/capybara/rack_test/form.rb +29 -7
  14. data/lib/capybara/rack_test/node.rb +18 -15
  15. data/lib/capybara/registrations/drivers.rb +3 -3
  16. data/lib/capybara/registrations/servers.rb +30 -10
  17. data/lib/capybara/rspec/matcher_proxies.rb +3 -3
  18. data/lib/capybara/rspec/matchers/base.rb +8 -6
  19. data/lib/capybara/rspec/matchers/compound.rb +1 -1
  20. data/lib/capybara/selector/definition/link.rb +2 -1
  21. data/lib/capybara/selector/definition.rb +1 -1
  22. data/lib/capybara/selector/filter_set.rb +4 -5
  23. data/lib/capybara/selector/regexp_disassembler.rb +2 -5
  24. data/lib/capybara/selenium/driver.rb +6 -3
  25. data/lib/capybara/selenium/driver_specializations/edge_driver.rb +8 -4
  26. data/lib/capybara/selenium/driver_specializations/firefox_driver.rb +1 -1
  27. data/lib/capybara/selenium/extensions/html5_drag.rb +5 -4
  28. data/lib/capybara/selenium/logger_suppressor.rb +6 -2
  29. data/lib/capybara/selenium/node.rb +61 -26
  30. data/lib/capybara/selenium/nodes/chrome_node.rb +5 -1
  31. data/lib/capybara/selenium/nodes/edge_node.rb +24 -2
  32. data/lib/capybara/selenium/nodes/firefox_node.rb +2 -2
  33. data/lib/capybara/selenium/nodes/safari_node.rb +2 -2
  34. data/lib/capybara/selenium/patches/action_pauser.rb +3 -3
  35. data/lib/capybara/selenium/patches/atoms.rb +1 -1
  36. data/lib/capybara/selenium/patches/pause_duration_fix.rb +1 -1
  37. data/lib/capybara/server/animation_disabler.rb +21 -22
  38. data/lib/capybara/server/middleware.rb +1 -1
  39. data/lib/capybara/session/config.rb +3 -1
  40. data/lib/capybara/session.rb +11 -9
  41. data/lib/capybara/spec/public/test.js +4 -0
  42. data/lib/capybara/spec/session/all_spec.rb +1 -1
  43. data/lib/capybara/spec/session/attach_file_spec.rb +6 -0
  44. data/lib/capybara/spec/session/check_spec.rb +1 -0
  45. data/lib/capybara/spec/session/click_link_spec.rb +11 -0
  46. data/lib/capybara/spec/session/current_scope_spec.rb +1 -1
  47. data/lib/capybara/spec/session/fill_in_spec.rb +6 -0
  48. data/lib/capybara/spec/session/find_link_spec.rb +10 -0
  49. data/lib/capybara/spec/session/find_spec.rb +2 -2
  50. data/lib/capybara/spec/session/first_spec.rb +1 -1
  51. data/lib/capybara/spec/session/frame/within_frame_spec.rb +2 -0
  52. data/lib/capybara/spec/session/has_all_selectors_spec.rb +5 -5
  53. data/lib/capybara/spec/session/has_ancestor_spec.rb +2 -2
  54. data/lib/capybara/spec/session/has_any_selectors_spec.rb +2 -2
  55. data/lib/capybara/spec/session/has_button_spec.rb +6 -0
  56. data/lib/capybara/spec/session/has_current_path_spec.rb +1 -1
  57. data/lib/capybara/spec/session/has_link_spec.rb +10 -0
  58. data/lib/capybara/spec/session/has_none_selectors_spec.rb +7 -7
  59. data/lib/capybara/spec/session/has_select_spec.rb +6 -0
  60. data/lib/capybara/spec/session/has_text_spec.rb +4 -8
  61. data/lib/capybara/spec/session/matches_style_spec.rb +2 -0
  62. data/lib/capybara/spec/session/node_spec.rb +40 -1
  63. data/lib/capybara/spec/session/reset_session_spec.rb +13 -0
  64. data/lib/capybara/spec/session/scroll_spec.rb +3 -1
  65. data/lib/capybara/spec/session/window/windows_spec.rb +1 -1
  66. data/lib/capybara/spec/session/within_spec.rb +13 -0
  67. data/lib/capybara/spec/spec_helper.rb +8 -2
  68. data/lib/capybara/spec/test_app.rb +25 -6
  69. data/lib/capybara/spec/views/form.erb +17 -0
  70. data/lib/capybara/spec/views/with_html.erb +3 -3
  71. data/lib/capybara/spec/views/with_scope.erb +2 -2
  72. data/lib/capybara/version.rb +1 -1
  73. data/lib/capybara.rb +4 -2
  74. data/spec/capybara_spec.rb +12 -0
  75. data/spec/counter_spec.rb +35 -0
  76. data/spec/css_builder_spec.rb +1 -1
  77. data/spec/css_splitter_spec.rb +1 -1
  78. data/spec/dsl_spec.rb +2 -0
  79. data/spec/minitest_spec.rb +4 -0
  80. data/spec/minitest_spec_spec.rb +4 -0
  81. data/spec/per_session_config_spec.rb +1 -1
  82. data/spec/rack_test_spec.rb +10 -2
  83. data/spec/rspec/scenarios_spec.rb +1 -1
  84. data/spec/rspec/shared_spec_matchers.rb +1 -1
  85. data/spec/rspec_matchers_spec.rb +25 -0
  86. data/spec/rspec_spec.rb +2 -2
  87. data/spec/sauce_spec_chrome.rb +1 -1
  88. data/spec/selector_spec.rb +2 -2
  89. data/spec/selenium_spec_chrome.rb +7 -6
  90. data/spec/selenium_spec_chrome_remote.rb +9 -9
  91. data/spec/selenium_spec_edge.rb +12 -6
  92. data/spec/selenium_spec_firefox.rb +20 -8
  93. data/spec/selenium_spec_firefox_remote.rb +19 -4
  94. data/spec/selenium_spec_ie.rb +4 -2
  95. data/spec/selenium_spec_safari.rb +3 -1
  96. data/spec/server_spec.rb +2 -2
  97. data/spec/shared_selenium_session.rb +5 -5
  98. data/spec/spec_helper.rb +34 -1
  99. data/spec/whitespace_normalizer_spec.rb +54 -0
  100. data/spec/xpath_builder_spec.rb +1 -1
  101. metadata +5 -2
@@ -40,6 +40,7 @@ module Capybara
40
40
 
41
41
  NODE_METHODS = %i[
42
42
  all first attach_file text check choose scroll_to scroll_by
43
+ click double_click right_click
43
44
  click_link_or_button click_button click_link
44
45
  fill_in find find_all find_button find_by_id find_field find_link
45
46
  has_content? has_text? has_css? has_no_content? has_no_text?
@@ -58,7 +59,7 @@ module Capybara
58
59
  ].freeze
59
60
  SESSION_METHODS = %i[
60
61
  body html source current_url current_host current_path
61
- execute_script evaluate_script visit refresh go_back go_forward send_keys
62
+ execute_script evaluate_script evaluate_async_script visit refresh go_back go_forward send_keys
62
63
  within within_element within_fieldset within_table within_frame switch_to_frame
63
64
  current_window windows open_new_window switch_to_window within_window window_opened_by
64
65
  save_page save_and_open_page save_screenshot
@@ -129,6 +130,8 @@ module Capybara
129
130
  if @touched
130
131
  driver.reset!
131
132
  @touched = false
133
+ switch_to_frame(:top) rescue nil # rubocop:disable Style/RescueModifier
134
+ @scopes = [nil]
132
135
  end
133
136
  @server&.wait_for_pending_requests
134
137
  raise_server_error!
@@ -159,9 +162,8 @@ module Capybara
159
162
  if config.raise_server_errors
160
163
  raise CapybaraError, 'Your application server raised an error - It has been raised in your test code because Capybara.raise_server_errors == true'
161
164
  end
162
- rescue CapybaraError
163
- # needed to get the cause set correctly in JRuby -- otherwise we could just do raise @server.error
164
- raise @server.error, @server.error.message, @server.error.backtrace
165
+ rescue CapybaraError => capy_error # rubocop:disable Naming/RescuedExceptionsVariableName
166
+ raise @server.error, cause: capy_error
165
167
  ensure
166
168
  @server.reset_error!
167
169
  end
@@ -360,7 +362,7 @@ module Capybara
360
362
  new_scope = args.first.respond_to?(:to_capybara_node) ? args.first.to_capybara_node : find(*args, **kw_args)
361
363
  begin
362
364
  scopes.push(new_scope)
363
- yield if block_given?
365
+ yield new_scope if block_given?
364
366
  ensure
365
367
  scopes.pop
366
368
  end
@@ -409,7 +411,7 @@ module Capybara
409
411
  scopes.push(:frame)
410
412
  when :parent
411
413
  if scopes.last != :frame
412
- raise Capybara::ScopeError, "`switch_to_frame(:parent)` cannot be called from inside a descendant frame's "\
414
+ raise Capybara::ScopeError, "`switch_to_frame(:parent)` cannot be called from inside a descendant frame's " \
413
415
  '`within` block.'
414
416
  end
415
417
  scopes.pop
@@ -419,7 +421,7 @@ module Capybara
419
421
  top_level_scopes = [:frame, nil]
420
422
  if idx
421
423
  if scopes.slice(idx..).any? { |scope| !top_level_scopes.include?(scope) }
422
- raise Capybara::ScopeError, "`switch_to_frame(:top)` cannot be called from inside a descendant frame's "\
424
+ raise Capybara::ScopeError, "`switch_to_frame(:top)` cannot be called from inside a descendant frame's " \
423
425
  '`within` block.'
424
426
  end
425
427
  scopes.slice!(idx..)
@@ -511,7 +513,7 @@ module Capybara
511
513
  raise ArgumentError, '`switch_to_window`: either window or block should be provided' if !window && !window_locator
512
514
 
513
515
  unless scopes.last.nil?
514
- raise Capybara::ScopeError, '`switch_to_window` is not supposed to be invoked from '\
516
+ raise Capybara::ScopeError, '`switch_to_window` is not supposed to be invoked from ' \
515
517
  '`within` or `within_frame` blocks.'
516
518
  end
517
519
 
@@ -582,7 +584,7 @@ module Capybara
582
584
  synchronize_windows(options) do
583
585
  opened_handles = (driver.window_handles - old_handles)
584
586
  if opened_handles.size != 1
585
- raise Capybara::WindowError, 'block passed to #window_opened_by '\
587
+ raise Capybara::WindowError, 'block passed to #window_opened_by ' \
586
588
  "opened #{opened_handles.size} windows instead of 1"
587
589
  end
588
590
  Window.new(self, opened_handles.first)
@@ -44,6 +44,10 @@ $(function() {
44
44
  $(this).after('<div class="log">DragOver with client position: ' + ev.clientX + ',' + ev.clientY)
45
45
  if ($(this).hasClass('drop')) { ev.preventDefault(); }
46
46
  });
47
+ $('#drop_html5, #drop_html5_scroll').on('dragenter', function(ev){
48
+ $(this).after('<div class="log">DragEnter')
49
+ if ($(this).hasClass('drop')) { ev.preventDefault(); }
50
+ });
47
51
  $('#drop_html5, #drop_html5_scroll').on('dragleave', function(ev){
48
52
  $(this).after('<div class="log">DragLeave with client position: ' + ev.clientX + ',' + ev.clientY)
49
53
  if ($(this).hasClass('drop')) { ev.preventDefault(); }
@@ -31,7 +31,7 @@ Capybara::SpecHelper.spec '#all' do
31
31
  it 'should accept an XPath instance', :exact_false do
32
32
  @session.visit('/form')
33
33
  @xpath = Capybara::Selector.new(:fillable_field, config: {}, format: :xpath).call('Name')
34
- expect(@xpath).to be_a(::XPath::Union)
34
+ expect(@xpath).to be_a(XPath::Union)
35
35
  @result = @session.all(@xpath).map(&:value)
36
36
  expect(@result).to include('Smith', 'John', 'John Smith')
37
37
  end
@@ -55,6 +55,12 @@ Capybara::SpecHelper.spec '#attach_file' do
55
55
  expect(@session).to have_content('No file uploaded')
56
56
  end
57
57
 
58
+ it 'should send prior hidden field if no file submitted' do
59
+ @session.click_button('Upload Empty With Hidden')
60
+ expect(extract_results(@session)['document2']).to eq('hidden_field')
61
+ expect(extract_content_type(@session)).to start_with('multipart/form-data;')
62
+ end
63
+
58
64
  it 'should send content type text/plain when uploading a text file' do
59
65
  @session.attach_file 'Single Document', with_os_path_separators(test_file_path)
60
66
  @session.click_button 'Upload Single'
@@ -239,6 +239,7 @@ Capybara::SpecHelper.spec '#check' do
239
239
 
240
240
  context 'with allow_label_click options', requires: [:js] do
241
241
  it 'should allow offsets to click location on label' do
242
+ Capybara.w3c_click_offset = false
242
243
  expect(@session.find(:checkbox, 'form_cars_lotus', unchecked: true, visible: :hidden)).to be_truthy
243
244
  @session.check('form_cars_lotus', allow_label_click: { x: 90, y: 10 })
244
245
  @session.click_button('awesome')
@@ -131,6 +131,17 @@ Capybara::SpecHelper.spec '#click_link' do
131
131
  end
132
132
  end
133
133
 
134
+ context 'with :target option given' do
135
+ it 'should find links with valid target' do
136
+ @session.click_link('labore', target: '_self')
137
+ expect(@session).to have_content('Bar')
138
+ end
139
+
140
+ it "should raise error if link wasn't found" do
141
+ expect { @session.click_link('labore', target: '_blank') }.to raise_error(Capybara::ElementNotFound, /Unable to find link "labore"/)
142
+ end
143
+ end
144
+
134
145
  it 'should follow relative links' do
135
146
  @session.visit('/')
136
147
  @session.click_link('Relative')
@@ -7,7 +7,7 @@ Capybara::SpecHelper.spec '#current_scope' do
7
7
 
8
8
  context 'when not in a #within block' do
9
9
  it 'should return the document' do
10
- expect(@session.current_scope).to be_kind_of Capybara::Node::Document
10
+ expect(@session.current_scope).to be_a Capybara::Node::Document
11
11
  end
12
12
  end
13
13
 
@@ -98,6 +98,12 @@ Capybara::SpecHelper.spec '#fill_in' do
98
98
  expect(extract_results(@session)['description']).to eq("\r\nSome text\r\n")
99
99
  end
100
100
 
101
+ it 'should handle carriage returns with line feeds in a textarea correctly' do
102
+ @session.fill_in('form_description', with: "\r\nSome text\r\n")
103
+ @session.click_button('awesome')
104
+ expect(extract_results(@session)['description']).to eq("\r\nSome text\r\n")
105
+ end
106
+
101
107
  it 'should fill in a color field' do
102
108
  @session.fill_in('Html5 Color', with: '#112233')
103
109
  @session.click_button('html5_submit')
@@ -67,4 +67,14 @@ Capybara::SpecHelper.spec '#find_link' do
67
67
  expect { @session.find_link(download: 37) }.to raise_error ArgumentError
68
68
  end
69
69
  end
70
+
71
+ context 'with :target option' do
72
+ it 'should accept partial matches when false' do
73
+ expect(@session.find_link(target: '_self').text).to eq('labore')
74
+ end
75
+
76
+ it 'should not accept partial matches when true' do
77
+ expect { @session.find_link(target: '_blank') }.to raise_error Capybara::ElementNotFound
78
+ end
79
+ end
70
80
  end
@@ -236,7 +236,7 @@ Capybara::SpecHelper.spec '#find' do
236
236
  it 'should accept an XPath instance' do
237
237
  @session.visit('/form')
238
238
  @xpath = Capybara::Selector.new(:fillable_field, config: {}, format: :xpath).call('First Name')
239
- expect(@xpath).to be_a(::XPath::Union)
239
+ expect(@xpath).to be_a(XPath::Union)
240
240
  expect(@session.find(@xpath).value).to eq('John')
241
241
  end
242
242
 
@@ -528,7 +528,7 @@ Capybara::SpecHelper.spec '#find' do
528
528
  expect(@session.find(:link, 'test-foo')[:id]).to eq 'foo'
529
529
  end
530
530
  end
531
-
531
+
532
532
  it 'should warn if passed count options' do
533
533
  allow(Capybara::Helpers).to receive(:warn)
534
534
  @session.find('//h1', count: 44)
@@ -25,7 +25,7 @@ Capybara::SpecHelper.spec '#first' do
25
25
  it 'should accept an XPath instance' do
26
26
  @session.visit('/form')
27
27
  @xpath = Capybara::Selector.new(:fillable_field, config: {}, format: :xpath).call('First Name')
28
- expect(@xpath).to be_a(::XPath::Union)
28
+ expect(@xpath).to be_a(XPath::Union)
29
29
  expect(@session.first(@xpath).value).to eq('John')
30
30
  end
31
31
 
@@ -64,7 +64,9 @@ Capybara::SpecHelper.spec '#within_frame', requires: [:frames] do
64
64
  end
65
65
  expect do
66
66
  # Multiple frames in scope here
67
+ # rubocop:disable Style/Semicolon
67
68
  @session.within_frame { ; }
69
+ # rubocop:enable Style/Semicolon
68
70
  end.to raise_error Capybara::Ambiguous
69
71
  end
70
72
 
@@ -12,7 +12,7 @@ Capybara::SpecHelper.spec '#have_all_of_selectors' do
12
12
  it 'should be false if any of the given selectors are not on the page' do
13
13
  expect do
14
14
  expect(@session).to have_all_of_selectors(:css, 'p a#foo', 'h2#h2three', 'h2#h2one')
15
- end.to raise_error ::RSpec::Expectations::ExpectationNotMetError
15
+ end.to raise_error RSpec::Expectations::ExpectationNotMetError
16
16
  end
17
17
 
18
18
  it 'should use default selector' do
@@ -20,7 +20,7 @@ Capybara::SpecHelper.spec '#have_all_of_selectors' do
20
20
  expect(@session).to have_all_of_selectors('p a#foo', 'h2#h2two', 'h2#h2one')
21
21
  expect do
22
22
  expect(@session).to have_all_of_selectors('p a#foo', 'h2#h2three', 'h2#h2one')
23
- end.to raise_error ::RSpec::Expectations::ExpectationNotMetError
23
+ end.to raise_error RSpec::Expectations::ExpectationNotMetError
24
24
  end
25
25
 
26
26
  context 'should respect scopes' do
@@ -29,7 +29,7 @@ Capybara::SpecHelper.spec '#have_all_of_selectors' do
29
29
  expect(@session).to have_all_of_selectors(".//a[@id='foo']")
30
30
  expect do
31
31
  expect(@session).to have_all_of_selectors(".//a[@id='red']")
32
- end.to raise_error ::RSpec::Expectations::ExpectationNotMetError
32
+ end.to raise_error RSpec::Expectations::ExpectationNotMetError
33
33
  end
34
34
  end
35
35
 
@@ -38,7 +38,7 @@ Capybara::SpecHelper.spec '#have_all_of_selectors' do
38
38
  expect(el).to have_all_of_selectors(".//a[@id='foo']")
39
39
  expect do
40
40
  expect(el).to have_all_of_selectors(".//a[@id='red']")
41
- end.to raise_error ::RSpec::Expectations::ExpectationNotMetError
41
+ end.to raise_error RSpec::Expectations::ExpectationNotMetError
42
42
  end
43
43
  end
44
44
 
@@ -47,7 +47,7 @@ Capybara::SpecHelper.spec '#have_all_of_selectors' do
47
47
  expect(@session).to have_all_of_selectors(:field, 'normal', 'additional_newline', type: :textarea)
48
48
  expect do
49
49
  expect(@session).to have_all_of_selectors(:field, 'normal', 'test_field', 'additional_newline', type: :textarea)
50
- end.to raise_error ::RSpec::Expectations::ExpectationNotMetError
50
+ end.to raise_error RSpec::Expectations::ExpectationNotMetError
51
51
  end
52
52
  end
53
53
 
@@ -39,8 +39,8 @@ Capybara::SpecHelper.spec '#have_no_ancestor' do
39
39
  it 'should assert no matching ancestor' do
40
40
  el = @session.find(:css, '#ancestor1')
41
41
  expect(el).to have_no_ancestor(:css, '#child')
42
- expect(el).to have_no_ancestor(:css, '#ancestor1_sibiling')
42
+ expect(el).to have_no_ancestor(:css, '#ancestor1_sibling')
43
43
  expect(el).not_to have_ancestor(:css, '#child')
44
- expect(el).not_to have_ancestor(:css, '#ancestor1_sibiling')
44
+ expect(el).not_to have_ancestor(:css, '#ancestor1_sibling')
45
45
  end
46
46
  end
@@ -12,7 +12,7 @@ Capybara::SpecHelper.spec '#have_any_of_selectors' do
12
12
  it 'should be false if none of the given selectors are not on the page' do
13
13
  expect do
14
14
  expect(@session).to have_any_of_selectors(:css, 'span a#foo', 'h2#h2nope', 'h2#h2one_no')
15
- end.to raise_error ::RSpec::Expectations::ExpectationNotMetError
15
+ end.to raise_error RSpec::Expectations::ExpectationNotMetError
16
16
  end
17
17
 
18
18
  it 'should use default selector' do
@@ -20,7 +20,7 @@ Capybara::SpecHelper.spec '#have_any_of_selectors' do
20
20
  expect(@session).to have_any_of_selectors('p a#foo', 'h2#h2two', 'a#not_on_page')
21
21
  expect do
22
22
  expect(@session).to have_any_of_selectors('p a#blah', 'h2#h2three')
23
- end.to raise_error ::RSpec::Expectations::ExpectationNotMetError
23
+ end.to raise_error RSpec::Expectations::ExpectationNotMetError
24
24
  end
25
25
 
26
26
  it 'should be negateable' do
@@ -77,6 +77,12 @@ Capybara::SpecHelper.spec '#has_button?' do
77
77
  expect(@session).to have_button('A Button', focused: false)
78
78
  end
79
79
  end
80
+
81
+ it 'should raise an error if an invalid option is passed' do
82
+ expect do
83
+ expect(@session).to have_button('A Button', invalid: true)
84
+ end.to raise_error(ArgumentError, 'Invalid option(s) :invalid, should be one of :above, :below, :left_of, :right_of, :near, :count, :minimum, :maximum, :between, :text, :id, :class, :style, :visible, :obscured, :exact, :exact_text, :normalize_ws, :match, :wait, :filter_set, :focused, :disabled, :name, :value, :title, :type')
85
+ end
80
86
  end
81
87
 
82
88
  Capybara::SpecHelper.spec '#has_no_button?' do
@@ -75,7 +75,7 @@ Capybara::SpecHelper.spec '#has_current_path?' do
75
75
  @session.visit('/with_js?test=test')
76
76
  expect(@session).to have_current_path('/with_js?test=test')
77
77
  expect(@session).to have_current_path('/with_js', ignore_query: true)
78
- uri = ::Addressable::URI.parse(@session.current_url)
78
+ uri = Addressable::URI.parse(@session.current_url)
79
79
  uri.query = nil
80
80
  expect(@session).to have_current_path(uri.to_s, ignore_query: true)
81
81
  end
@@ -11,12 +11,14 @@ Capybara::SpecHelper.spec '#has_link?' do
11
11
  expect(@session).to have_link('A link', href: '/with_simple_html')
12
12
  expect(@session).to have_link(:'A link', href: :'/with_simple_html')
13
13
  expect(@session).to have_link('A link', href: %r{/with_simple_html})
14
+ expect(@session).to have_link('labore', target: '_self')
14
15
  end
15
16
 
16
17
  it 'should be false if the given link is not on the page' do
17
18
  expect(@session).not_to have_link('monkey')
18
19
  expect(@session).not_to have_link('A link', href: '/nonexistent-href')
19
20
  expect(@session).not_to have_link('A link', href: /nonexistent/)
21
+ expect(@session).not_to have_link('labore', target: '_blank')
20
22
  end
21
23
 
22
24
  it 'should notify if an invalid locator is specified' do
@@ -36,6 +38,12 @@ Capybara::SpecHelper.spec '#has_link?' do
36
38
  expect(@session).to have_link('labore', focused: false)
37
39
  end
38
40
  end
41
+
42
+ it 'should raise an error if an invalid option is passed' do
43
+ expect do
44
+ expect(@session).to have_link('labore', invalid: true)
45
+ end.to raise_error(ArgumentError, 'Invalid option(s) :invalid, should be one of :above, :below, :left_of, :right_of, :near, :count, :minimum, :maximum, :between, :text, :id, :class, :style, :visible, :obscured, :exact, :exact_text, :normalize_ws, :match, :wait, :filter_set, :focused, :href, :alt, :title, :target, :download')
46
+ end
39
47
  end
40
48
 
41
49
  Capybara::SpecHelper.spec '#has_no_link?' do
@@ -47,12 +55,14 @@ Capybara::SpecHelper.spec '#has_no_link?' do
47
55
  expect(@session).not_to have_no_link('foo')
48
56
  expect(@session).not_to have_no_link('awesome title')
49
57
  expect(@session).not_to have_no_link('A link', href: '/with_simple_html')
58
+ expect(@session).not_to have_no_link('labore', target: '_self')
50
59
  end
51
60
 
52
61
  it 'should be true if the given link is not on the page' do
53
62
  expect(@session).to have_no_link('monkey')
54
63
  expect(@session).to have_no_link('A link', href: '/nonexistent-href')
55
64
  expect(@session).to have_no_link('A link', href: %r{/nonexistent-href})
65
+ expect(@session).to have_no_link('labore', target: '_blank')
56
66
  end
57
67
 
58
68
  context 'with focused:', requires: [:active_element] do
@@ -8,10 +8,10 @@ Capybara::SpecHelper.spec '#have_none_of_selectors' do
8
8
  it 'should be false if any of the given locators are on the page' do
9
9
  expect do
10
10
  expect(@session).to have_none_of_selectors(:xpath, '//p', '//a')
11
- end.to raise_error ::RSpec::Expectations::ExpectationNotMetError
11
+ end.to raise_error RSpec::Expectations::ExpectationNotMetError
12
12
  expect do
13
13
  expect(@session).to have_none_of_selectors(:css, 'p a#foo')
14
- end.to raise_error ::RSpec::Expectations::ExpectationNotMetError
14
+ end.to raise_error RSpec::Expectations::ExpectationNotMetError
15
15
  end
16
16
 
17
17
  it 'should be true if none of the given locators are on the page' do
@@ -24,7 +24,7 @@ Capybara::SpecHelper.spec '#have_none_of_selectors' do
24
24
  expect(@session).to have_none_of_selectors('p a#doesnotexist', 'abbr')
25
25
  expect do
26
26
  expect(@session).to have_none_of_selectors('abbr', 'p a#foo')
27
- end.to raise_error ::RSpec::Expectations::ExpectationNotMetError
27
+ end.to raise_error RSpec::Expectations::ExpectationNotMetError
28
28
  end
29
29
 
30
30
  context 'should respect scopes' do
@@ -32,7 +32,7 @@ Capybara::SpecHelper.spec '#have_none_of_selectors' do
32
32
  @session.within "//p[@id='first']" do
33
33
  expect do
34
34
  expect(@session).to have_none_of_selectors(".//a[@id='foo']")
35
- end.to raise_error ::RSpec::Expectations::ExpectationNotMetError
35
+ end.to raise_error RSpec::Expectations::ExpectationNotMetError
36
36
  expect(@session).to have_none_of_selectors(".//a[@id='red']")
37
37
  end
38
38
  end
@@ -41,7 +41,7 @@ Capybara::SpecHelper.spec '#have_none_of_selectors' do
41
41
  el = @session.find "//p[@id='first']"
42
42
  expect do
43
43
  expect(el).to have_none_of_selectors(".//a[@id='foo']")
44
- end.to raise_error ::RSpec::Expectations::ExpectationNotMetError
44
+ end.to raise_error RSpec::Expectations::ExpectationNotMetError
45
45
  expect(el).to have_none_of_selectors(".//a[@id='red']")
46
46
  end
47
47
  end
@@ -50,14 +50,14 @@ Capybara::SpecHelper.spec '#have_none_of_selectors' do
50
50
  it 'should apply the options to all locators' do
51
51
  expect do
52
52
  expect(@session).to have_none_of_selectors('//p//a', text: 'Redirect')
53
- end.to raise_error ::RSpec::Expectations::ExpectationNotMetError
53
+ end.to raise_error RSpec::Expectations::ExpectationNotMetError
54
54
  expect(@session).to have_none_of_selectors('//p', text: 'Doesnotexist')
55
55
  end
56
56
 
57
57
  it 'should discard all matches where the given regexp is matched' do
58
58
  expect do
59
59
  expect(@session).to have_none_of_selectors('//p//a', text: /re[dab]i/i, count: 1)
60
- end.to raise_error ::RSpec::Expectations::ExpectationNotMetError
60
+ end.to raise_error RSpec::Expectations::ExpectationNotMetError
61
61
  expect(@session).to have_none_of_selectors('//p//a', text: /Red$/)
62
62
  end
63
63
  end
@@ -179,6 +179,12 @@ Capybara::SpecHelper.spec '#has_select?' do
179
179
  end
180
180
  end
181
181
 
182
+ it 'should raise an error if an invalid option is passed' do
183
+ expect do
184
+ expect(@session).to have_select('form_languages', invalid: true)
185
+ end.to raise_error(ArgumentError, 'Invalid option(s) :invalid, should be one of :above, :below, :left_of, :right_of, :near, :count, :minimum, :maximum, :between, :text, :id, :class, :style, :visible, :obscured, :exact, :exact_text, :normalize_ws, :match, :wait, :filter_set, :focused, :disabled, :name, :placeholder, :options, :enabled_options, :disabled_options, :selected, :with_selected, :multiple, :with_options')
186
+ end
187
+
182
188
  it 'should support locator-less usage' do
183
189
  expect(@session.has_select?(with_options: %w[Norway Sweden])).to be true
184
190
  expect(@session).to have_select(with_options: ['London'])
@@ -153,16 +153,12 @@ Capybara::SpecHelper.spec '#has_text?' do
153
153
  expect(@session).to have_text(42)
154
154
  end
155
155
 
156
- it 'should be true when passed nil' do
156
+ it 'should be true when passed nil, and warn about it' do
157
157
  # nil is converted to '' when to_s is invoked
158
158
  @session.visit('/with_html')
159
- expect(@session).to have_text(nil)
160
- end
161
-
162
- it 'should warn when passed nil' do
163
- @session.visit('/with_html')
164
- expect_any_instance_of(Kernel).to receive(:warn).with(/Checking for expected text of nil is confusing/) # rubocop:disable RSpec/AnyInstance
165
- expect(@session).to have_text(nil)
159
+ expect do
160
+ expect(@session).to have_text(nil)
161
+ end.to output(/Checking for expected text of nil is confusing/).to_stderr
166
162
  end
167
163
 
168
164
  it 'should wait for text to appear', requires: [:js] do
@@ -23,6 +23,7 @@ Capybara::SpecHelper.spec '#matches_style?', requires: [:css] do
23
23
  expect(@session.find(:css, '#first').matches_style?(display: /^in/)).to be false
24
24
  end
25
25
 
26
+ # rubocop:disable Capybara/MatchStyle
26
27
  it 'deprecated has_style?' do
27
28
  expect { have_style(display: /^bl/) }.to \
28
29
  output(/have_style is deprecated/).to_stderr
@@ -32,4 +33,5 @@ Capybara::SpecHelper.spec '#matches_style?', requires: [:css] do
32
33
  el.has_style?('display' => /^bl/)
33
34
  expect(Capybara::Helpers).to have_received(:warn)
34
35
  end
36
+ # rubocop:enable Capybara/MatchStyle
35
37
  end
@@ -157,6 +157,12 @@ Capybara::SpecHelper.spec 'node' do
157
157
  expect(@session.find(:css, '#existing_content_editable_child_parent').text).to eq("Some content\nWYSIWYG")
158
158
  end
159
159
  end
160
+
161
+ it 'should submit single text input forms if ended with \n' do
162
+ @session.visit('/form')
163
+ @session.find(:css, '#single_input').set("my entry\n")
164
+ expect(extract_results(@session)['single_input']).to eq('my entry')
165
+ end
160
166
  end
161
167
 
162
168
  describe '#tag_name' do
@@ -651,6 +657,16 @@ Capybara::SpecHelper.spec 'node' do
651
657
  expect(target).to have_text(%r{^HTML5 Dropped string: text/plain drag_html5-#{key}$}m, exact: true)
652
658
  end
653
659
  end
660
+
661
+ it 'should trigger a dragenter event, before the first dragover event' do
662
+ @session.visit('/with_js')
663
+ element = @session.find('//div[@id="drag_html5"]')
664
+ target = @session.find('//div[@id="drop_html5"]')
665
+ element.drag_to(target)
666
+
667
+ # Events are listed in reverse chronological order
668
+ expect(@session).to have_text(/DragOver.*DragEnter/m)
669
+ end
654
670
  end
655
671
  end
656
672
 
@@ -775,6 +791,7 @@ Capybara::SpecHelper.spec 'node' do
775
791
  end
776
792
 
777
793
  it 'should allow to adjust the click offset', requires: [:js] do
794
+ Capybara.w3c_click_offset = false
778
795
  @session.visit('with_js')
779
796
  @session.find(:css, '#click-test').click(x: 5, y: 5)
780
797
  link = @session.find(:link, 'has-been-clicked')
@@ -901,6 +918,7 @@ Capybara::SpecHelper.spec 'node' do
901
918
  end
902
919
 
903
920
  it 'should allow to adjust the offset', requires: [:js] do
921
+ Capybara.w3c_click_offset = false
904
922
  @session.visit('with_js')
905
923
  @session.find(:css, '#click-test').double_click(x: 10, y: 5)
906
924
  link = @session.find(:link, 'has-been-double-clicked')
@@ -987,6 +1005,7 @@ Capybara::SpecHelper.spec 'node' do
987
1005
  end
988
1006
 
989
1007
  it 'should allow to adjust the offset', requires: [:js] do
1008
+ Capybara.w3c_click_offset = false
990
1009
  @session.visit('with_js')
991
1010
  @session.find(:css, '#click-test').right_click(x: 10, y: 10)
992
1011
  link = @session.find(:link, 'has-been-right-clicked')
@@ -1181,7 +1200,7 @@ Capybara::SpecHelper.spec 'node' do
1181
1200
  @session.visit('/with_shadow')
1182
1201
  expect do
1183
1202
  shadow_root = @session.find(:css, '#shadow_host').shadow_root
1184
- expect(shadow_root).not_to be_nil
1203
+ expect(shadow_root).to be_a(Capybara::Node::Element)
1185
1204
  end.not_to raise_error
1186
1205
  end
1187
1206
 
@@ -1216,6 +1235,26 @@ Capybara::SpecHelper.spec 'node' do
1216
1235
  end.not_to raise_error
1217
1236
  expect(descendant).to have_checked_field('shadow_checkbox')
1218
1237
  end
1238
+
1239
+ it 'should produce error messages when failing' do
1240
+ @session.visit('/with_shadow')
1241
+ shadow_root = @session.find(:css, '#shadow_host').shadow_root
1242
+ expect do
1243
+ expect(shadow_root).to have_css('#shadow_content', text: 'Not in the document')
1244
+ end.to raise_error(/tag="ShadowRoot"/)
1245
+ end
1246
+
1247
+ it 'should get visible text' do
1248
+ @session.visit('/with_shadow')
1249
+ shadow_root = @session.find(:css, '#shadow_host').shadow_root
1250
+ expect(shadow_root).to have_text('some text scroll.html')
1251
+ end
1252
+
1253
+ it 'should get all text' do
1254
+ @session.visit('/with_shadow')
1255
+ shadow_root = @session.find(:css, '#shadow_host').shadow_root
1256
+ expect(shadow_root).to have_text(:all, 'some text scroll.html')
1257
+ end
1219
1258
  end
1220
1259
 
1221
1260
  describe '#reload', requires: [:js] do
@@ -95,6 +95,19 @@ Capybara::SpecHelper.spec '#reset_session!' do
95
95
  expect(@session.windows.size).to eq 1
96
96
  end
97
97
 
98
+ it 'does not block opening a new window after a frame was switched to and not switched back', requires: [:windows] do
99
+ @session.visit('/with_iframe?id=test_iframe&url=/')
100
+ @session.switch_to_frame(@session.find(:frame, 'test_iframe'))
101
+ within_window_test = lambda do
102
+ @session.within_window(@session.open_new_window) do
103
+ @session.visit('/')
104
+ end
105
+ end
106
+ expect(&within_window_test).to raise_error(Capybara::ScopeError)
107
+ @session.reset_session!
108
+ expect(&within_window_test).not_to raise_error
109
+ end
110
+
98
111
  context 'When reuse_server == false' do
99
112
  let!(:orig_reuse_server) { Capybara.reuse_server }
100
113
 
@@ -36,7 +36,9 @@ Capybara::SpecHelper.spec '#scroll_to', requires: [:scroll] do
36
36
  it 'can scroll the window to the vertical bottom' do
37
37
  @session.scroll_to :bottom
38
38
  max_scroll = @session.evaluate_script('document.documentElement.scrollHeight - document.documentElement.clientHeight')
39
- expect(@session.evaluate_script('[window.scrollX || window.pageXOffset, window.scrollY || window.pageYOffset]')).to eq [0, max_scroll]
39
+ scrolled_location_x, scrolled_location_y = @session.evaluate_script('[window.scrollX || window.pageXOffset, window.scrollY || window.pageYOffset]')
40
+ expect(scrolled_location_x).to be_within(0.5).of(0)
41
+ expect(scrolled_location_y).to be_within(0.5).of(max_scroll)
40
42
  end
41
43
 
42
44
  it 'can scroll the window to the vertical center' do
@@ -29,6 +29,6 @@ Capybara::SpecHelper.spec '#windows', requires: [:windows] do
29
29
  titles = @session.windows.map do |window|
30
30
  @session.within_window(window) { @session.title }
31
31
  end
32
- expect(titles).to match_array(['With Windows', 'Title of the first popup', 'Title of popup two'])
32
+ expect(titles).to match_array(['With Windows', 'Title of the first popup', 'Title of popup two']) # rubocop:disable RSpec/MatchArray
33
33
  end
34
34
  end
@@ -49,6 +49,19 @@ Capybara::SpecHelper.spec '#within' do
49
49
  expect { @session.text }.to raise_error(StandardError)
50
50
  end
51
51
  end
52
+
53
+ it 'should pass scope element to the block' do
54
+ @session.within(:css, '#another_foo') do |scope|
55
+ expect(scope).to match_css('#another_foo')
56
+ end
57
+ end
58
+
59
+ it 'should scope click', requires: [:js] do
60
+ @session.within(:css, '#another_foo') do |scope|
61
+ @session.click
62
+ expect(scope).to have_text('I was clicked')
63
+ end
64
+ end
52
65
  end
53
66
 
54
67
  context 'with XPath selector' do