capybara 2.18.0 → 3.0.0.rc1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (168) hide show
  1. checksums.yaml +4 -4
  2. data/History.md +26 -1
  3. data/README.md +12 -12
  4. data/lib/capybara.rb +13 -25
  5. data/lib/capybara/config.rb +11 -57
  6. data/lib/capybara/cucumber.rb +2 -3
  7. data/lib/capybara/driver/base.rb +5 -16
  8. data/lib/capybara/driver/node.rb +5 -4
  9. data/lib/capybara/dsl.rb +1 -0
  10. data/lib/capybara/helpers.rb +16 -28
  11. data/lib/capybara/minitest.rb +139 -138
  12. data/lib/capybara/minitest/spec.rb +15 -14
  13. data/lib/capybara/node/actions.rb +59 -81
  14. data/lib/capybara/node/base.rb +11 -18
  15. data/lib/capybara/node/document.rb +2 -2
  16. data/lib/capybara/node/document_matchers.rb +8 -8
  17. data/lib/capybara/node/element.rb +30 -40
  18. data/lib/capybara/node/finders.rb +62 -70
  19. data/lib/capybara/node/matchers.rb +48 -71
  20. data/lib/capybara/node/simple.rb +11 -17
  21. data/lib/capybara/queries/ancestor_query.rb +4 -6
  22. data/lib/capybara/queries/base_query.rb +18 -17
  23. data/lib/capybara/queries/current_path_query.rb +8 -24
  24. data/lib/capybara/queries/match_query.rb +3 -7
  25. data/lib/capybara/queries/selector_query.rb +92 -95
  26. data/lib/capybara/queries/sibling_query.rb +4 -4
  27. data/lib/capybara/queries/text_query.rb +37 -34
  28. data/lib/capybara/queries/title_query.rb +8 -11
  29. data/lib/capybara/rack_test/browser.rb +15 -18
  30. data/lib/capybara/rack_test/css_handlers.rb +6 -4
  31. data/lib/capybara/rack_test/driver.rb +6 -10
  32. data/lib/capybara/rack_test/form.rb +50 -40
  33. data/lib/capybara/rack_test/node.rb +70 -56
  34. data/lib/capybara/rails.rb +2 -6
  35. data/lib/capybara/result.rb +22 -22
  36. data/lib/capybara/rspec.rb +5 -10
  37. data/lib/capybara/rspec/compound.rb +5 -10
  38. data/lib/capybara/rspec/features.rb +17 -48
  39. data/lib/capybara/rspec/matcher_proxies.rb +31 -15
  40. data/lib/capybara/rspec/matchers.rb +70 -60
  41. data/lib/capybara/selector.rb +129 -117
  42. data/lib/capybara/selector/css.rb +6 -11
  43. data/lib/capybara/selector/filter.rb +1 -17
  44. data/lib/capybara/selector/filter_set.rb +17 -14
  45. data/lib/capybara/selector/filters/base.rb +7 -6
  46. data/lib/capybara/selector/filters/expression_filter.rb +6 -23
  47. data/lib/capybara/selector/filters/node_filter.rb +2 -12
  48. data/lib/capybara/selector/selector.rb +27 -33
  49. data/lib/capybara/selenium/driver.rb +113 -127
  50. data/lib/capybara/selenium/node.rb +148 -113
  51. data/lib/capybara/server.rb +3 -2
  52. data/lib/capybara/session.rb +137 -223
  53. data/lib/capybara/session/config.rb +47 -67
  54. data/lib/capybara/session/matchers.rb +8 -7
  55. data/lib/capybara/spec/public/test.js +26 -4
  56. data/lib/capybara/spec/session/accept_alert_spec.rb +1 -0
  57. data/lib/capybara/spec/session/accept_confirm_spec.rb +3 -2
  58. data/lib/capybara/spec/session/accept_prompt_spec.rb +1 -0
  59. data/lib/capybara/spec/session/all_spec.rb +31 -18
  60. data/lib/capybara/spec/session/ancestor_spec.rb +2 -4
  61. data/lib/capybara/spec/session/assert_all_of_selectors_spec.rb +6 -5
  62. data/lib/capybara/spec/session/assert_current_path.rb +12 -11
  63. data/lib/capybara/spec/session/assert_selector.rb +1 -0
  64. data/lib/capybara/spec/session/assert_text.rb +18 -17
  65. data/lib/capybara/spec/session/assert_title.rb +1 -0
  66. data/lib/capybara/spec/session/attach_file_spec.rb +14 -13
  67. data/lib/capybara/spec/session/body_spec.rb +1 -0
  68. data/lib/capybara/spec/session/check_spec.rb +7 -6
  69. data/lib/capybara/spec/session/choose_spec.rb +5 -4
  70. data/lib/capybara/spec/session/click_button_spec.rb +20 -28
  71. data/lib/capybara/spec/session/click_link_or_button_spec.rb +8 -7
  72. data/lib/capybara/spec/session/click_link_spec.rb +8 -7
  73. data/lib/capybara/spec/session/current_scope_spec.rb +4 -3
  74. data/lib/capybara/spec/session/current_url_spec.rb +7 -6
  75. data/lib/capybara/spec/session/dismiss_confirm_spec.rb +1 -1
  76. data/lib/capybara/spec/session/dismiss_prompt_spec.rb +1 -0
  77. data/lib/capybara/spec/session/element/assert_match_selector.rb +1 -1
  78. data/lib/capybara/spec/session/element/match_xpath_spec.rb +1 -1
  79. data/lib/capybara/spec/session/element/matches_selector_spec.rb +5 -5
  80. data/lib/capybara/spec/session/evaluate_async_script_spec.rb +3 -2
  81. data/lib/capybara/spec/session/evaluate_script_spec.rb +4 -3
  82. data/lib/capybara/spec/session/execute_script_spec.rb +4 -3
  83. data/lib/capybara/spec/session/fill_in_spec.rb +6 -5
  84. data/lib/capybara/spec/session/find_button_spec.rb +4 -3
  85. data/lib/capybara/spec/session/find_by_id_spec.rb +2 -1
  86. data/lib/capybara/spec/session/find_field_spec.rb +8 -14
  87. data/lib/capybara/spec/session/find_link_spec.rb +6 -5
  88. data/lib/capybara/spec/session/find_spec.rb +37 -31
  89. data/lib/capybara/spec/session/first_spec.rb +60 -33
  90. data/lib/capybara/spec/session/frame/switch_to_frame_spec.rb +2 -1
  91. data/lib/capybara/spec/session/frame/within_frame_spec.rb +9 -16
  92. data/lib/capybara/spec/session/go_back_spec.rb +1 -0
  93. data/lib/capybara/spec/session/go_forward_spec.rb +1 -0
  94. data/lib/capybara/spec/session/has_all_selectors_spec.rb +15 -15
  95. data/lib/capybara/spec/session/has_button_spec.rb +2 -1
  96. data/lib/capybara/spec/session/has_css_spec.rb +3 -2
  97. data/lib/capybara/spec/session/has_current_path_spec.rb +12 -28
  98. data/lib/capybara/spec/session/has_field_spec.rb +4 -3
  99. data/lib/capybara/spec/session/has_link_spec.rb +1 -0
  100. data/lib/capybara/spec/session/has_none_selectors_spec.rb +17 -17
  101. data/lib/capybara/spec/session/has_select_spec.rb +30 -29
  102. data/lib/capybara/spec/session/has_selector_spec.rb +5 -4
  103. data/lib/capybara/spec/session/has_table_spec.rb +2 -1
  104. data/lib/capybara/spec/session/has_text_spec.rb +6 -5
  105. data/lib/capybara/spec/session/has_title_spec.rb +1 -0
  106. data/lib/capybara/spec/session/has_xpath_spec.rb +1 -0
  107. data/lib/capybara/spec/session/headers.rb +2 -1
  108. data/lib/capybara/spec/session/html_spec.rb +1 -0
  109. data/lib/capybara/spec/session/node_spec.rb +91 -56
  110. data/lib/capybara/spec/session/node_wrapper_spec.rb +36 -0
  111. data/lib/capybara/spec/session/refresh_spec.rb +4 -2
  112. data/lib/capybara/spec/session/reset_session_spec.rb +1 -0
  113. data/lib/capybara/spec/session/response_code.rb +1 -0
  114. data/lib/capybara/spec/session/save_and_open_page_spec.rb +1 -0
  115. data/lib/capybara/spec/session/save_and_open_screenshot_spec.rb +6 -11
  116. data/lib/capybara/spec/session/save_page_spec.rb +1 -17
  117. data/lib/capybara/spec/session/save_screenshot_spec.rb +1 -1
  118. data/lib/capybara/spec/session/select_spec.rb +20 -20
  119. data/lib/capybara/spec/session/selectors_spec.rb +2 -2
  120. data/lib/capybara/spec/session/sibling_spec.rb +1 -1
  121. data/lib/capybara/spec/session/text_spec.rb +1 -0
  122. data/lib/capybara/spec/session/title_spec.rb +1 -1
  123. data/lib/capybara/spec/session/uncheck_spec.rb +4 -3
  124. data/lib/capybara/spec/session/unselect_spec.rb +6 -5
  125. data/lib/capybara/spec/session/visit_spec.rb +9 -3
  126. data/lib/capybara/spec/session/window/become_closed_spec.rb +2 -1
  127. data/lib/capybara/spec/session/window/current_window_spec.rb +1 -0
  128. data/lib/capybara/spec/session/window/open_new_window_spec.rb +1 -0
  129. data/lib/capybara/spec/session/window/switch_to_window_spec.rb +2 -1
  130. data/lib/capybara/spec/session/window/window_opened_by_spec.rb +2 -1
  131. data/lib/capybara/spec/session/window/window_spec.rb +12 -12
  132. data/lib/capybara/spec/session/window/windows_spec.rb +2 -3
  133. data/lib/capybara/spec/session/window/within_window_spec.rb +13 -68
  134. data/lib/capybara/spec/session/within_spec.rb +1 -0
  135. data/lib/capybara/spec/spec_helper.rb +26 -18
  136. data/lib/capybara/spec/test_app.rb +8 -9
  137. data/lib/capybara/spec/views/form.erb +1 -0
  138. data/lib/capybara/spec/views/with_html.erb +3 -1
  139. data/lib/capybara/spec/views/within_frames.erb +4 -1
  140. data/lib/capybara/version.rb +2 -1
  141. data/lib/capybara/window.rb +6 -10
  142. data/spec/basic_node_spec.rb +1 -0
  143. data/spec/capybara_spec.rb +9 -32
  144. data/spec/dsl_spec.rb +5 -13
  145. data/spec/filter_set_spec.rb +5 -4
  146. data/spec/fixtures/selenium_driver_rspec_failure.rb +2 -1
  147. data/spec/fixtures/selenium_driver_rspec_success.rb +3 -2
  148. data/spec/minitest_spec.rb +4 -3
  149. data/spec/minitest_spec_spec.rb +3 -2
  150. data/spec/per_session_config_spec.rb +9 -8
  151. data/spec/rack_test_spec.rb +21 -20
  152. data/spec/result_spec.rb +17 -16
  153. data/spec/rspec/features_spec.rb +17 -14
  154. data/spec/rspec/scenarios_spec.rb +5 -7
  155. data/spec/rspec/shared_spec_matchers.rb +96 -99
  156. data/spec/rspec/views_spec.rb +2 -1
  157. data/spec/rspec_matchers_spec.rb +19 -2
  158. data/spec/rspec_spec.rb +11 -15
  159. data/spec/selector_spec.rb +5 -6
  160. data/spec/selenium_spec_chrome.rb +7 -4
  161. data/spec/selenium_spec_marionette.rb +26 -12
  162. data/spec/server_spec.rb +33 -33
  163. data/spec/session_spec.rb +2 -1
  164. data/spec/shared_selenium_session.rb +27 -21
  165. data/spec/spec_helper.rb +2 -5
  166. metadata +66 -87
  167. data/lib/capybara/query.rb +0 -7
  168. data/spec/selenium_spec_firefox.rb +0 -68
@@ -1,4 +1,5 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  module Capybara
3
4
  module Queries
4
5
  class SiblingQuery < MatchQuery
@@ -7,7 +8,7 @@ module Capybara
7
8
  @sibling_node = node
8
9
  node.synchronize do
9
10
  match_results = super(node.session.current_scope, exact)
10
- node.all(:xpath, XPath.preceding_sibling.union(XPath.following_sibling)) do |el|
11
+ node.all(:xpath, XPath.preceding_sibling + XPath.following_sibling) do |el|
11
12
  match_results.include?(el)
12
13
  end
13
14
  end
@@ -15,9 +16,8 @@ module Capybara
15
16
 
16
17
  def description
17
18
  desc = super
18
- if @sibling_node && (sibling_query = @sibling_node.instance_variable_get(:@query))
19
- desc += " that is a sibling of #{sibling_query.description}"
20
- end
19
+ sibling_query = @sibling_node && @sibling_node.instance_variable_get(:@query)
20
+ desc += " that is a sibling of #{sibling_query.description}" if sibling_query
21
21
  desc
22
22
  end
23
23
  end
@@ -1,22 +1,26 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  module Capybara
3
4
  # @api private
4
5
  module Queries
5
6
  class TextQuery < BaseQuery
6
- def initialize(*args)
7
- @type = (args.first.is_a?(Symbol) || args.first.nil?) ? args.shift : nil
8
- # @type = (Capybara.ignore_hidden_elements or Capybara.visible_text_only) ? :visible : :all if @type.nil?
9
- @options = if args.last.is_a?(Hash) then args.pop.dup else {} end
7
+ def initialize(type = nil, expected_text, session_options:, **options) # rubocop:disable Style/OptionalArguments
8
+ @type = if type.nil?
9
+ Capybara.ignore_hidden_elements || Capybara.visible_text_only ? :visible : :all
10
+ else
11
+ type
12
+ end
13
+ @expected_text = if expected_text.is_a?(Regexp)
14
+ expected_text
15
+ else
16
+ Capybara::Helpers.normalize_whitespace(expected_text)
17
+ end
18
+ @options = options
10
19
  super(@options)
20
+ self.session_options = session_options
11
21
 
12
- @type = (session_options.ignore_hidden_elements or session_options.visible_text_only) ? :visible : :all if @type.nil?
22
+ @search_regexp = Capybara::Helpers.to_regexp(@expected_text, exact: exact?)
13
23
 
14
- @expected_text = args.shift
15
- unless @expected_text.is_a?(Regexp)
16
- @expected_text = Capybara::Helpers.normalize_whitespace(@expected_text)
17
- end
18
- @search_regexp = Capybara::Helpers.to_regexp(@expected_text, nil, exact?)
19
- warn "Unused parameters passed to #{self.class.name} : #{args}" unless args.empty?
20
24
  assert_valid_keys
21
25
  end
22
26
 
@@ -42,48 +46,47 @@ module Capybara
42
46
  end
43
47
  end
44
48
 
45
- private
49
+ private
46
50
 
47
51
  def exact?
48
52
  options.fetch(:exact, session_options.exact_text)
49
53
  end
50
54
 
51
55
  def build_message(report_on_invisible)
52
- message = String.new()
56
+ message = "".dup
53
57
  unless (COUNT_KEYS & @options.keys).empty?
54
58
  message << " but found #{@count} #{Capybara::Helpers.declension('time', 'times', @count)}"
55
59
  end
56
60
  message << " in #{@actual_text.inspect}"
57
61
 
58
62
  details_message = []
63
+ details_message << case_insensitive_message if @node and !@expected_text.is_a? Regexp
64
+ details_message << invisible_message if @node and check_visible_text? and report_on_invisible
65
+ details_message.compact!
59
66
 
60
- if @node and !@expected_text.is_a? Regexp
61
- insensitive_regexp = Capybara::Helpers.to_regexp(@expected_text, Regexp::IGNORECASE)
62
- insensitive_count = @actual_text.scan(insensitive_regexp).size
63
- if insensitive_count != @count
64
- details_message << "it was found #{insensitive_count} #{Capybara::Helpers.declension("time", "times", insensitive_count)} using a case insensitive search"
65
- end
66
- end
67
+ message << ". (However, #{details_message.join(' and ')}.)" unless details_message.empty?
68
+ message
69
+ end
67
70
 
68
- if @node and check_visible_text? and report_on_invisible
69
- begin
70
- invisible_text = text(@node, :all)
71
- invisible_count = invisible_text.scan(@search_regexp).size
72
- if invisible_count != @count
73
- details_message << "it was found #{invisible_count} #{Capybara::Helpers.declension("time", "times", invisible_count)} including non-visible text"
74
- end
75
- rescue
76
- # An error getting the non-visible text (if element goes out of scope) should not affect the response
77
- end
71
+ def case_insensitive_message
72
+ insensitive_regexp = Capybara::Helpers.to_regexp(@expected_text, options: Regexp::IGNORECASE)
73
+ insensitive_count = @actual_text.scan(insensitive_regexp).size
74
+ if insensitive_count != @count
75
+ "it was found #{insensitive_count} #{Capybara::Helpers.declension("time", "times", insensitive_count)} using a case insensitive search"
78
76
  end
77
+ end
79
78
 
80
- message << ". (However, #{details_message.join(' and ')}.)" unless details_message.empty?
81
-
82
- message
79
+ def invisible_message
80
+ invisible_text = text(@node, :all)
81
+ invisible_count = invisible_text.scan(@search_regexp).size
82
+ if invisible_count != @count
83
+ "it was found #{invisible_count} #{Capybara::Helpers.declension("time", "times", invisible_count)} including non-visible text"
84
+ end
85
+ rescue # An error getting the non-visible text (if element goes out of scope) should not affect the response
83
86
  end
84
87
 
85
88
  def valid_keys
86
- COUNT_KEYS + [:wait, :exact]
89
+ COUNT_KEYS + %i[wait exact]
87
90
  end
88
91
 
89
92
  def check_visible_text?
@@ -1,22 +1,19 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  module Capybara
3
4
  # @api private
4
5
  module Queries
5
6
  class TitleQuery < BaseQuery
6
- def initialize(expected_title, options = {})
7
- @expected_title = expected_title
7
+ def initialize(expected_title, **options)
8
+ @expected_title = expected_title.is_a?(Regexp) ? expected_title : Capybara::Helpers.normalize_whitespace(expected_title)
8
9
  @options = options
9
10
  super(@options)
10
- unless @expected_title.is_a?(Regexp)
11
- @expected_title = Capybara::Helpers.normalize_whitespace(@expected_title)
12
- end
13
- @search_regexp = Capybara::Helpers.to_regexp(@expected_title, nil, options.fetch(:exact, false))
11
+ @search_regexp = Capybara::Helpers.to_regexp(@expected_title, exact: options.fetch(:exact, false))
14
12
  assert_valid_keys
15
13
  end
16
14
 
17
15
  def resolves_for?(node)
18
- @actual_title = node.title
19
- @actual_title.match(@search_regexp)
16
+ (@actual_title = node.title).match(@search_regexp)
20
17
  end
21
18
 
22
19
  def failure_message
@@ -27,15 +24,15 @@ module Capybara
27
24
  failure_message_helper(' not')
28
25
  end
29
26
 
30
- private
27
+ private
31
28
 
32
29
  def failure_message_helper(negated = '')
33
- verb = (@expected_title.is_a?(Regexp))? 'match' : 'include'
30
+ verb = @expected_title.is_a?(Regexp) ? 'match' : 'include'
34
31
  "expected #{@actual_title.inspect}#{negated} to #{verb} #{@expected_title.inspect}"
35
32
  end
36
33
 
37
34
  def valid_keys
38
- [:wait, :exact]
35
+ %i[wait exact]
39
36
  end
40
37
  end
41
38
  end
@@ -1,4 +1,5 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  class Capybara::RackTest::Browser
3
4
  include ::Rack::Test::Methods
4
5
 
@@ -17,7 +18,7 @@ class Capybara::RackTest::Browser
17
18
  driver.options
18
19
  end
19
20
 
20
- def visit(path, attributes = {})
21
+ def visit(path, **attributes)
21
22
  reset_host!
22
23
  process_and_follow_redirects(:get, path, attributes)
23
24
  end
@@ -28,23 +29,24 @@ class Capybara::RackTest::Browser
28
29
  end
29
30
 
30
31
  def submit(method, path, attributes)
31
- path = request_path if not path or path.empty?
32
- process_and_follow_redirects(method, path, attributes, {'HTTP_REFERER' => current_url})
32
+ path = request_path if path.nil? || path.empty?
33
+ process_and_follow_redirects(method, path, attributes, 'HTTP_REFERER' => current_url)
33
34
  end
34
35
 
35
- def follow(method, path, attributes = {})
36
+ def follow(method, path, **attributes)
36
37
  return if path.gsub(/^#{Regexp.escape(request_path)}/, '').start_with?('#') || path.downcase.start_with?('javascript:')
37
- process_and_follow_redirects(method, path, attributes, {'HTTP_REFERER' => current_url})
38
+ process_and_follow_redirects(method, path, attributes, 'HTTP_REFERER' => current_url)
38
39
  end
39
40
 
40
41
  def process_and_follow_redirects(method, path, attributes = {}, env = {})
41
42
  process(method, path, attributes, env)
42
- if driver.follow_redirects?
43
- driver.redirect_limit.times do
44
- process(:get, last_response["Location"], {}, env) if last_response.redirect?
45
- end
46
- raise Capybara::InfiniteRedirectError, "redirected more than #{driver.redirect_limit} times, check for infinite redirects." if last_response.redirect?
43
+
44
+ return unless driver.follow_redirects?
45
+
46
+ driver.redirect_limit.times do
47
+ process(:get, last_response["Location"], {}, env) if last_response.redirect?
47
48
  end
49
+ raise Capybara::InfiniteRedirectError, "redirected more than #{driver.redirect_limit} times, check for infinite redirects." if last_response.redirect?
48
50
  end
49
51
 
50
52
  def process(method, path, attributes = {}, env = {})
@@ -55,7 +57,7 @@ class Capybara::RackTest::Browser
55
57
  else
56
58
  new_uri.path = request_path if path.start_with?("?")
57
59
  new_uri.path = "/" if new_uri.path.empty?
58
- new_uri.path = request_path.sub(%r(/[^/]*$), '/') + new_uri.path unless new_uri.path.start_with?('/')
60
+ new_uri.path = request_path.sub(%r{/[^/]*$}, '/') + new_uri.path unless new_uri.path.start_with?('/')
59
61
  end
60
62
  new_uri.scheme ||= @current_scheme
61
63
  new_uri.host ||= @current_host
@@ -91,7 +93,7 @@ class Capybara::RackTest::Browser
91
93
  end
92
94
 
93
95
  def find(format, selector)
94
- if format==:css
96
+ if format == :css
95
97
  dom.css(selector, Capybara::RackTest::CSSHandlers.new)
96
98
  else
97
99
  dom.xpath(selector)
@@ -105,12 +107,7 @@ class Capybara::RackTest::Browser
105
107
  end
106
108
 
107
109
  def title
108
- if dom.respond_to? :title
109
- dom.title
110
- else
111
- #old versions of nokogiri don't have #title - remove in 3.0
112
- dom.xpath('/html/head/title | /html/title').first.text
113
- end
110
+ dom.title
114
111
  end
115
112
 
116
113
  protected
@@ -1,11 +1,13 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  class Capybara::RackTest::CSSHandlers < BasicObject
3
4
  include ::Kernel
4
-
5
- def disabled list
5
+
6
+ def disabled(list)
6
7
  list.find_all { |node| node.has_attribute? 'disabled' }
7
- end
8
- def enabled list
8
+ end
9
+
10
+ def enabled(list)
9
11
  list.find_all { |node| !node.has_attribute? 'disabled' }
10
12
  end
11
13
  end
@@ -1,4 +1,5 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  require 'rack/test'
3
4
  require 'rack/utils'
4
5
  require 'mini_mime'
@@ -10,10 +11,10 @@ class Capybara::RackTest::Driver < Capybara::Driver::Base
10
11
  respect_data_method: false,
11
12
  follow_redirects: true,
12
13
  redirect_limit: 5
13
- }
14
+ }.freeze
14
15
  attr_reader :app, :options
15
16
 
16
- def initialize(app, options={})
17
+ def initialize(app, **options)
17
18
  raise ArgumentError, "rack-test requires a rack application, but none was given" unless app
18
19
  @session = nil
19
20
  @app = app
@@ -40,7 +41,7 @@ class Capybara::RackTest::Driver < Capybara::Driver::Base
40
41
  browser.last_request
41
42
  end
42
43
 
43
- def visit(path, attributes = {})
44
+ def visit(path, **attributes)
44
45
  browser.visit(path, attributes)
45
46
  end
46
47
 
@@ -52,7 +53,7 @@ class Capybara::RackTest::Driver < Capybara::Driver::Base
52
53
  browser.submit(method, path, attributes)
53
54
  end
54
55
 
55
- def follow(method, path, attributes = {})
56
+ def follow(method, path, **attributes)
56
57
  browser.follow(method, path, attributes)
57
58
  end
58
59
 
@@ -73,7 +74,7 @@ class Capybara::RackTest::Driver < Capybara::Driver::Base
73
74
  end
74
75
 
75
76
  def find_css(selector)
76
- browser.find(:css,selector)
77
+ browser.find(:css, selector)
77
78
  end
78
79
 
79
80
  def html
@@ -92,11 +93,6 @@ class Capybara::RackTest::Driver < Capybara::Driver::Base
92
93
  @browser = nil
93
94
  end
94
95
 
95
- # @deprecated This method is being removed
96
- def browser_initialized?
97
- super && !@browser.nil?
98
- end
99
-
100
96
  def get(*args, &block); browser.get(*args, &block); end
101
97
  def post(*args, &block); browser.post(*args, &block); end
102
98
  def put(*args, &block); browser.put(*args, &block); end
@@ -1,4 +1,5 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  class Capybara::RackTest::Form < Capybara::RackTest::Node
3
4
  # This only needs to inherit from Rack::Test::UploadedFile because Rack::Test checks for
4
5
  # the class specifically when determining whether to construct the request as multipart.
@@ -20,53 +21,21 @@ class Capybara::RackTest::Form < Capybara::RackTest::Node
20
21
  def params(button)
21
22
  params = make_params
22
23
 
23
- form_element_types=[:input, :select, :textarea]
24
- form_elements_xpath=XPath.generate do |x|
25
- xpath=x.descendant(*form_element_types).where(~x.attr(:form))
26
- xpath=xpath.union(x.anywhere(*form_element_types).where(x.attr(:form) == native[:id])) if native[:id]
27
- xpath.where(~x.attr(:disabled))
24
+ form_element_types = %i[input select textarea]
25
+ form_elements_xpath = XPath.generate do |x|
26
+ xpath = x.descendant(*form_element_types).where(!x.attr(:form))
27
+ xpath += x.anywhere(*form_element_types).where(x.attr(:form) == native[:id]) if native[:id]
28
+ xpath.where(!x.attr(:disabled))
28
29
  end.to_s
29
30
 
30
31
  native.xpath(form_elements_xpath).map do |field|
31
32
  case field.name
32
33
  when 'input'
33
- if %w(radio checkbox).include? field['type']
34
- if field['checked']
35
- node=Capybara::RackTest::Node.new(self.driver, field)
36
- merge_param!(params, field['name'].to_s, node.value.to_s)
37
- end
38
- elsif %w(submit image).include? field['type']
39
- # TO DO identify the click button here (in document order, rather
40
- # than leaving until the end of the params)
41
- elsif field['type'] =='file'
42
- if multipart?
43
- file = \
44
- if (value = field['value']).to_s.empty?
45
- NilUploadedFile.new
46
- else
47
- mime_info = MiniMime.lookup_by_filename(value)
48
- Rack::Test::UploadedFile.new(value, (mime_info && mime_info.content_type).to_s)
49
- end
50
- merge_param!(params, field['name'].to_s, file)
51
- else
52
- merge_param!(params, field['name'].to_s, File.basename(field['value'].to_s))
53
- end
54
- else
55
- merge_param!(params, field['name'].to_s, field['value'].to_s)
56
- end
34
+ add_input_param(field, params)
57
35
  when 'select'
58
- if field['multiple'] == 'multiple'
59
- options = field.xpath(".//option[@selected]")
60
- options.each do |option|
61
- merge_param!(params, field['name'].to_s, (option['value'] || option.text).to_s)
62
- end
63
- else
64
- option = field.xpath(".//option[@selected]").first
65
- option ||= field.xpath('.//option').first
66
- merge_param!(params, field['name'].to_s, (option['value'] || option.text).to_s) if option
67
- end
36
+ add_select_param(field, params)
68
37
  when 'textarea'
69
- merge_param!(params, field['name'].to_s, field['_capybara_raw_value'].to_s.gsub(/\n/, "\r\n"))
38
+ add_textarea_param(field, params)
70
39
  end
71
40
  end
72
41
  merge_param!(params, button[:name], button[:value] || "") if button[:name]
@@ -111,4 +80,45 @@ private
111
80
  ParamsHash.new
112
81
  end
113
82
  end
83
+
84
+ def add_input_param(field, params)
85
+ if %w[radio checkbox].include? field['type']
86
+ if field['checked']
87
+ node = Capybara::RackTest::Node.new(driver, field)
88
+ merge_param!(params, field['name'].to_s, node.value.to_s)
89
+ end
90
+ elsif %w[submit image].include? field['type']
91
+ # TODO: identify the click button here (in document order, rather
92
+ # than leaving until the end of the params)
93
+ elsif field['type'] == 'file'
94
+ if multipart?
95
+ file = if (value = field['value']).to_s.empty?
96
+ NilUploadedFile.new
97
+ else
98
+ mime_info = MiniMime.lookup_by_filename(value)
99
+ Rack::Test::UploadedFile.new(value, (mime_info && mime_info.content_type).to_s)
100
+ end
101
+ merge_param!(params, field['name'].to_s, file)
102
+ else
103
+ merge_param!(params, field['name'].to_s, File.basename(field['value'].to_s))
104
+ end
105
+ else
106
+ merge_param!(params, field['name'].to_s, field['value'].to_s)
107
+ end
108
+ end
109
+
110
+ def add_select_param(field, params)
111
+ if field['multiple'] == 'multiple'
112
+ field.xpath(".//option[@selected]").each do |option|
113
+ merge_param!(params, field['name'].to_s, (option['value'] || option.text).to_s)
114
+ end
115
+ else
116
+ option = field.xpath('.//option[@selected]').first || field.xpath('.//option').first
117
+ merge_param!(params, field['name'].to_s, (option['value'] || option.text).to_s) if option
118
+ end
119
+ end
120
+
121
+ def add_textarea_param(field, params)
122
+ merge_param!(params, field['name'].to_s, field['_capybara_raw_value'].to_s.gsub(/\n/, "\r\n"))
123
+ end
114
124
  end
@@ -1,4 +1,5 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  class Capybara::RackTest::Node < Capybara::Driver::Node
3
4
  def all_text
4
5
  Capybara::Helpers.normalize_whitespace(native.text)
@@ -16,64 +17,45 @@ class Capybara::RackTest::Node < Capybara::Driver::Node
16
17
  string_node.value
17
18
  end
18
19
 
19
- def set(value)
20
- return if disabled?
21
- if readonly?
22
- warn "Attempt to set readonly element with value: #{value} \n * This will raise an exception in a future version of Capybara"
23
- return
24
- end
20
+ def set(value, **options)
21
+ return if disabled? || readonly?
22
+
23
+ warn "Options passed to Node#set but the RackTest driver doesn't support any - ignoring" unless options.empty?
25
24
 
26
- if (Array === value) && !multiple?
27
- raise TypeError.new "Value cannot be an Array when 'multiple' attribute is not present. Not a #{value.class}"
25
+ if value.is_a?(Array) && !multiple?
26
+ raise TypeError, "Value cannot be an Array when 'multiple' attribute is not present. Not a #{value.class}"
28
27
  end
29
28
 
30
- if radio?
31
- set_radio(value)
32
- elsif checkbox?
33
- set_checkbox(value)
34
- elsif input_field?
35
- set_input(value)
36
- elsif textarea?
37
- native['_capybara_raw_value'] = value.to_s
29
+ if radio? then set_radio(value)
30
+ elsif checkbox? then set_checkbox(value)
31
+ elsif input_field? then set_input(value)
32
+ elsif textarea? then native['_capybara_raw_value'] = value.to_s
38
33
  end
39
34
  end
40
35
 
41
36
  def select_option
42
37
  return if disabled?
43
- if select_node['multiple'] != 'multiple'
44
- select_node.find_xpath(".//option[@selected]").each { |node| node.native.remove_attribute("selected") }
45
- end
38
+ deselect_options unless select_node.multiple?
46
39
  native["selected"] = 'selected'
47
40
  end
48
41
 
49
42
  def unselect_option
50
- if select_node['multiple'] != 'multiple'
51
- raise Capybara::UnselectNotAllowed, "Cannot unselect option from single select box."
52
- end
43
+ raise Capybara::UnselectNotAllowed, "Cannot unselect option from single select box." unless select_node.multiple?
53
44
  native.remove_attribute('selected')
54
45
  end
55
46
 
56
- def click
57
- if tag_name == 'a' && !self[:href].nil?
58
- method = self["data-method"] if driver.options[:respect_data_method]
59
- method ||= :get
60
- driver.follow(method, self[:href].to_s)
61
- elsif (tag_name == 'input' and %w(submit image).include?(type)) or
62
- ((tag_name == 'button') and type.nil? or type == "submit")
47
+ def click(keys = [], offset = {})
48
+ raise ArgumentError, "The RackTest driver does not support click options" unless keys.empty? && offset.empty?
49
+
50
+ if link?
51
+ follow_link
52
+ elsif submits?
63
53
  associated_form = form
64
54
  Capybara::RackTest::Form.new(driver, associated_form).submit(self) if associated_form
65
- elsif (tag_name == 'input' and %w(checkbox radio).include?(type))
55
+ elsif checkable?
66
56
  set(!checked?)
67
- elsif (tag_name == 'label')
68
- labelled_control = if native[:for]
69
- find_xpath("//input[@id='#{native[:for]}']").first
70
- else
71
- find_xpath(".//input").first
72
- end
73
-
74
- if labelled_control && (labelled_control.checkbox? || labelled_control.radio?)
75
- labelled_control.set(!labelled_control.checked?)
76
- end
57
+ elsif tag_name == 'label'
58
+ click_label
77
59
  end
78
60
  end
79
61
 
@@ -94,10 +76,12 @@ class Capybara::RackTest::Node < Capybara::Driver::Node
94
76
  end
95
77
 
96
78
  def disabled?
97
- if %w(option optgroup).include? tag_name
98
- string_node.disabled? || find_xpath("parent::*[self::optgroup or self::select]")[0].disabled?
79
+ return true if string_node.disabled?
80
+
81
+ if %w[option optgroup].include? tag_name
82
+ find_xpath("parent::*[self::optgroup or self::select]")[0].disabled?
99
83
  else
100
- string_node.disabled? || !find_xpath("parent::fieldset[@disabled] | ancestor::*[not(self::legend) or preceding-sibling::legend][parent::fieldset[@disabled]]").empty?
84
+ !find_xpath("parent::fieldset[@disabled] | ancestor::*[not(self::legend) or preceding-sibling::legend][parent::fieldset[@disabled]]").empty?
101
85
  end
102
86
  end
103
87
 
@@ -135,6 +119,10 @@ protected
135
119
 
136
120
  private
137
121
 
122
+ def deselect_options
123
+ select_node.find_xpath(".//option[@selected]").each { |node| node.native.remove_attribute("selected") }
124
+ end
125
+
138
126
  def string_node
139
127
  @string_node ||= Capybara::Node::Simple.new(native)
140
128
  end
@@ -150,19 +138,19 @@ private
150
138
 
151
139
  def form
152
140
  if native[:form]
153
- native.xpath("//form[@id='#{native[:form]}']").first
141
+ native.xpath("//form[@id='#{native[:form]}']")
154
142
  else
155
- native.ancestors('form').first
156
- end
143
+ native.ancestors('form')
144
+ end.first
157
145
  end
158
146
 
159
- def set_radio(_value)
160
- other_radios_xpath = XPath.generate { |x| x.anywhere(:input)[x.attr(:name).equals(self[:name])] }.to_s
147
+ def set_radio(_value) # rubocop:disable Naming/AccessorMethodName
148
+ other_radios_xpath = XPath.generate { |x| x.anywhere(:input)[x.attr(:name) == self[:name]] }.to_s
161
149
  driver.dom.xpath(other_radios_xpath).each { |node| node.remove_attribute("checked") }
162
150
  native['checked'] = 'checked'
163
151
  end
164
152
 
165
- def set_checkbox(value)
153
+ def set_checkbox(value) # rubocop:disable Naming/AccessorMethodName
166
154
  if value && !native['checked']
167
155
  native['checked'] = 'checked'
168
156
  elsif !value && native['checked']
@@ -170,13 +158,13 @@ private
170
158
  end
171
159
  end
172
160
 
173
- def set_input(value)
161
+ def set_input(value) # rubocop:disable Naming/AccessorMethodName
174
162
  if text_or_password? && attribute_is_not_blank?(:maxlength)
175
163
  # Browser behavior for maxlength="0" is inconsistent, so we stick with
176
164
  # Firefox, allowing no input
177
165
  value = value.to_s[0...self[:maxlength].to_i]
178
166
  end
179
- if Array === value #Assert multiple attribute is present
167
+ if value.is_a?(Array) # Assert multiple attribute is present
180
168
  value.each do |v|
181
169
  new_native = native.clone
182
170
  new_native.remove_attribute('value')
@@ -185,11 +173,7 @@ private
185
173
  end
186
174
  native.remove
187
175
  else
188
- if readonly?
189
- warn "Attempt to set readonly element with value: #{value} \n *This will raise an exception in a future version of Capybara"
190
- else
191
- native['value'] = value.to_s
192
- end
176
+ native['value'] = value.to_s
193
177
  end
194
178
  end
195
179
 
@@ -197,6 +181,36 @@ private
197
181
  self[attribute] && !self[attribute].empty?
198
182
  end
199
183
 
184
+ def follow_link
185
+ method = self["data-method"] if driver.options[:respect_data_method]
186
+ method ||= :get
187
+ driver.follow(method, self[:href].to_s)
188
+ end
189
+
190
+ def click_label
191
+ labelled_control = if native[:for]
192
+ find_xpath("//input[@id='#{native[:for]}']")
193
+ else
194
+ find_xpath(".//input")
195
+ end.first
196
+
197
+ if labelled_control && (labelled_control.checkbox? || labelled_control.radio?)
198
+ labelled_control.set(!labelled_control.checked?)
199
+ end
200
+ end
201
+
202
+ def link?
203
+ tag_name == 'a' && !self[:href].nil?
204
+ end
205
+
206
+ def submits?
207
+ (tag_name == 'input' and %w[submit image].include?(type)) || (tag_name == 'button' and [nil, "submit"].include?(type))
208
+ end
209
+
210
+ def checkable?
211
+ tag_name == 'input' and %w[checkbox radio].include?(type)
212
+ end
213
+
200
214
  protected
201
215
 
202
216
  def checkbox?