capybara 2.18.0 → 3.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (172) hide show
  1. checksums.yaml +4 -4
  2. data/History.md +55 -1
  3. data/README.md +18 -17
  4. data/lib/capybara/config.rb +11 -58
  5. data/lib/capybara/cucumber.rb +2 -3
  6. data/lib/capybara/driver/base.rb +15 -16
  7. data/lib/capybara/driver/node.rb +5 -4
  8. data/lib/capybara/dsl.rb +1 -0
  9. data/lib/capybara/helpers.rb +19 -29
  10. data/lib/capybara/minitest/spec.rb +15 -14
  11. data/lib/capybara/minitest.rb +139 -138
  12. data/lib/capybara/node/actions.rb +60 -81
  13. data/lib/capybara/node/base.rb +11 -18
  14. data/lib/capybara/node/document.rb +2 -2
  15. data/lib/capybara/node/document_matchers.rb +8 -8
  16. data/lib/capybara/node/element.rb +30 -40
  17. data/lib/capybara/node/finders.rb +62 -70
  18. data/lib/capybara/node/matchers.rb +50 -71
  19. data/lib/capybara/node/simple.rb +11 -17
  20. data/lib/capybara/queries/ancestor_query.rb +11 -7
  21. data/lib/capybara/queries/base_query.rb +22 -18
  22. data/lib/capybara/queries/current_path_query.rb +8 -24
  23. data/lib/capybara/queries/match_query.rb +3 -7
  24. data/lib/capybara/queries/selector_query.rb +92 -95
  25. data/lib/capybara/queries/sibling_query.rb +4 -4
  26. data/lib/capybara/queries/text_query.rb +35 -35
  27. data/lib/capybara/queries/title_query.rb +8 -11
  28. data/lib/capybara/rack_test/browser.rb +15 -18
  29. data/lib/capybara/rack_test/css_handlers.rb +6 -4
  30. data/lib/capybara/rack_test/driver.rb +6 -10
  31. data/lib/capybara/rack_test/form.rb +50 -40
  32. data/lib/capybara/rack_test/node.rb +93 -63
  33. data/lib/capybara/rails.rb +2 -6
  34. data/lib/capybara/result.rb +22 -22
  35. data/lib/capybara/rspec/compound.rb +5 -10
  36. data/lib/capybara/rspec/features.rb +17 -48
  37. data/lib/capybara/rspec/matcher_proxies.rb +31 -15
  38. data/lib/capybara/rspec/matchers.rb +70 -61
  39. data/lib/capybara/rspec.rb +5 -10
  40. data/lib/capybara/selector/css.rb +6 -11
  41. data/lib/capybara/selector/filter.rb +1 -17
  42. data/lib/capybara/selector/filter_set.rb +18 -15
  43. data/lib/capybara/selector/filters/base.rb +7 -6
  44. data/lib/capybara/selector/filters/expression_filter.rb +6 -23
  45. data/lib/capybara/selector/filters/node_filter.rb +2 -12
  46. data/lib/capybara/selector/selector.rb +28 -34
  47. data/lib/capybara/selector.rb +129 -117
  48. data/lib/capybara/selenium/driver.rb +131 -125
  49. data/lib/capybara/selenium/node.rb +197 -115
  50. data/lib/capybara/server.rb +3 -2
  51. data/lib/capybara/session/config.rb +47 -67
  52. data/lib/capybara/session/matchers.rb +8 -7
  53. data/lib/capybara/session.rb +138 -224
  54. data/lib/capybara/spec/public/test.js +25 -4
  55. data/lib/capybara/spec/session/accept_alert_spec.rb +1 -0
  56. data/lib/capybara/spec/session/accept_confirm_spec.rb +3 -2
  57. data/lib/capybara/spec/session/accept_prompt_spec.rb +1 -0
  58. data/lib/capybara/spec/session/all_spec.rb +31 -18
  59. data/lib/capybara/spec/session/ancestor_spec.rb +6 -8
  60. data/lib/capybara/spec/session/assert_all_of_selectors_spec.rb +6 -5
  61. data/lib/capybara/spec/session/assert_current_path.rb +12 -11
  62. data/lib/capybara/spec/session/assert_selector.rb +1 -0
  63. data/lib/capybara/spec/session/assert_text.rb +23 -23
  64. data/lib/capybara/spec/session/assert_title.rb +13 -3
  65. data/lib/capybara/spec/session/attach_file_spec.rb +51 -30
  66. data/lib/capybara/spec/session/body_spec.rb +1 -0
  67. data/lib/capybara/spec/session/check_spec.rb +7 -6
  68. data/lib/capybara/spec/session/choose_spec.rb +5 -4
  69. data/lib/capybara/spec/session/click_button_spec.rb +24 -32
  70. data/lib/capybara/spec/session/click_link_or_button_spec.rb +8 -7
  71. data/lib/capybara/spec/session/click_link_spec.rb +8 -7
  72. data/lib/capybara/spec/session/current_scope_spec.rb +4 -3
  73. data/lib/capybara/spec/session/current_url_spec.rb +17 -6
  74. data/lib/capybara/spec/session/dismiss_confirm_spec.rb +1 -1
  75. data/lib/capybara/spec/session/dismiss_prompt_spec.rb +1 -0
  76. data/lib/capybara/spec/session/element/assert_match_selector.rb +1 -1
  77. data/lib/capybara/spec/session/element/match_xpath_spec.rb +1 -1
  78. data/lib/capybara/spec/session/element/matches_selector_spec.rb +5 -5
  79. data/lib/capybara/spec/session/evaluate_async_script_spec.rb +3 -2
  80. data/lib/capybara/spec/session/evaluate_script_spec.rb +4 -3
  81. data/lib/capybara/spec/session/execute_script_spec.rb +4 -3
  82. data/lib/capybara/spec/session/fill_in_spec.rb +30 -5
  83. data/lib/capybara/spec/session/find_button_spec.rb +4 -3
  84. data/lib/capybara/spec/session/find_by_id_spec.rb +2 -1
  85. data/lib/capybara/spec/session/find_field_spec.rb +9 -15
  86. data/lib/capybara/spec/session/find_link_spec.rb +6 -5
  87. data/lib/capybara/spec/session/find_spec.rb +37 -31
  88. data/lib/capybara/spec/session/first_spec.rb +60 -33
  89. data/lib/capybara/spec/session/frame/frame_title_spec.rb +23 -0
  90. data/lib/capybara/spec/session/frame/frame_url_spec.rb +23 -0
  91. data/lib/capybara/spec/session/frame/switch_to_frame_spec.rb +2 -1
  92. data/lib/capybara/spec/session/frame/within_frame_spec.rb +9 -16
  93. data/lib/capybara/spec/session/go_back_spec.rb +1 -0
  94. data/lib/capybara/spec/session/go_forward_spec.rb +1 -0
  95. data/lib/capybara/spec/session/has_all_selectors_spec.rb +15 -15
  96. data/lib/capybara/spec/session/has_button_spec.rb +2 -1
  97. data/lib/capybara/spec/session/has_css_spec.rb +3 -2
  98. data/lib/capybara/spec/session/has_current_path_spec.rb +12 -28
  99. data/lib/capybara/spec/session/has_field_spec.rb +4 -3
  100. data/lib/capybara/spec/session/has_link_spec.rb +1 -0
  101. data/lib/capybara/spec/session/has_none_selectors_spec.rb +17 -17
  102. data/lib/capybara/spec/session/has_select_spec.rb +30 -29
  103. data/lib/capybara/spec/session/has_selector_spec.rb +5 -4
  104. data/lib/capybara/spec/session/has_table_spec.rb +2 -1
  105. data/lib/capybara/spec/session/has_text_spec.rb +9 -13
  106. data/lib/capybara/spec/session/has_title_spec.rb +1 -0
  107. data/lib/capybara/spec/session/has_xpath_spec.rb +1 -0
  108. data/lib/capybara/spec/session/headers.rb +2 -1
  109. data/lib/capybara/spec/session/html_spec.rb +1 -0
  110. data/lib/capybara/spec/session/node_spec.rb +91 -56
  111. data/lib/capybara/spec/session/node_wrapper_spec.rb +36 -0
  112. data/lib/capybara/spec/session/refresh_spec.rb +6 -2
  113. data/lib/capybara/spec/session/reset_session_spec.rb +19 -0
  114. data/lib/capybara/spec/session/response_code.rb +1 -0
  115. data/lib/capybara/spec/session/save_and_open_page_spec.rb +1 -0
  116. data/lib/capybara/spec/session/save_and_open_screenshot_spec.rb +6 -11
  117. data/lib/capybara/spec/session/save_page_spec.rb +1 -17
  118. data/lib/capybara/spec/session/save_screenshot_spec.rb +3 -3
  119. data/lib/capybara/spec/session/select_spec.rb +20 -20
  120. data/lib/capybara/spec/session/selectors_spec.rb +2 -2
  121. data/lib/capybara/spec/session/sibling_spec.rb +1 -1
  122. data/lib/capybara/spec/session/text_spec.rb +17 -3
  123. data/lib/capybara/spec/session/title_spec.rb +11 -1
  124. data/lib/capybara/spec/session/uncheck_spec.rb +4 -3
  125. data/lib/capybara/spec/session/unselect_spec.rb +6 -5
  126. data/lib/capybara/spec/session/visit_spec.rb +9 -3
  127. data/lib/capybara/spec/session/window/become_closed_spec.rb +2 -1
  128. data/lib/capybara/spec/session/window/current_window_spec.rb +1 -0
  129. data/lib/capybara/spec/session/window/open_new_window_spec.rb +1 -0
  130. data/lib/capybara/spec/session/window/switch_to_window_spec.rb +2 -1
  131. data/lib/capybara/spec/session/window/window_opened_by_spec.rb +2 -1
  132. data/lib/capybara/spec/session/window/window_spec.rb +12 -12
  133. data/lib/capybara/spec/session/window/windows_spec.rb +2 -3
  134. data/lib/capybara/spec/session/window/within_window_spec.rb +15 -71
  135. data/lib/capybara/spec/session/within_spec.rb +1 -0
  136. data/lib/capybara/spec/spec_helper.rb +34 -18
  137. data/lib/capybara/spec/test_app.rb +17 -9
  138. data/lib/capybara/spec/views/form.erb +7 -0
  139. data/lib/capybara/spec/views/with_html.erb +23 -1
  140. data/lib/capybara/spec/views/within_frames.erb +4 -1
  141. data/lib/capybara/version.rb +2 -1
  142. data/lib/capybara/window.rb +6 -10
  143. data/lib/capybara.rb +28 -25
  144. data/spec/basic_node_spec.rb +1 -0
  145. data/spec/capybara_spec.rb +11 -50
  146. data/spec/dsl_spec.rb +5 -13
  147. data/spec/filter_set_spec.rb +5 -4
  148. data/spec/fixtures/selenium_driver_rspec_failure.rb +2 -1
  149. data/spec/fixtures/selenium_driver_rspec_success.rb +3 -2
  150. data/spec/minitest_spec.rb +4 -3
  151. data/spec/minitest_spec_spec.rb +3 -2
  152. data/spec/per_session_config_spec.rb +9 -8
  153. data/spec/rack_test_spec.rb +21 -20
  154. data/spec/result_spec.rb +17 -16
  155. data/spec/rspec/features_spec.rb +17 -14
  156. data/spec/rspec/scenarios_spec.rb +5 -7
  157. data/spec/rspec/shared_spec_matchers.rb +96 -99
  158. data/spec/rspec/views_spec.rb +2 -1
  159. data/spec/rspec_matchers_spec.rb +18 -2
  160. data/spec/rspec_spec.rb +11 -15
  161. data/spec/selector_spec.rb +5 -6
  162. data/spec/selenium_spec_chrome.rb +9 -4
  163. data/spec/selenium_spec_edge.rb +27 -0
  164. data/spec/selenium_spec_ie.rb +31 -0
  165. data/spec/selenium_spec_marionette.rb +28 -12
  166. data/spec/server_spec.rb +33 -33
  167. data/spec/session_spec.rb +2 -1
  168. data/spec/shared_selenium_session.rb +36 -22
  169. data/spec/spec_helper.rb +3 -6
  170. metadata +68 -85
  171. data/lib/capybara/query.rb +0 -7
  172. data/spec/selenium_spec_firefox.rb +0 -68
@@ -1,36 +1,24 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  module Capybara
3
4
  module Queries
4
5
  class SelectorQuery < Queries::BaseQuery
5
6
  attr_accessor :selector, :locator, :options, :expression, :find, :negative
6
7
 
7
- VALID_KEYS = COUNT_KEYS + [:text, :id, :class, :visible, :exact, :exact_text, :match, :wait, :filter_set]
8
- VALID_MATCH = [:first, :smart, :prefer_exact, :one]
8
+ VALID_KEYS = COUNT_KEYS + %i[text id class visible exact exact_text match wait filter_set]
9
+ VALID_MATCH = %i[first smart prefer_exact one].freeze
9
10
 
10
- def initialize(*args, &filter_block)
11
+ def initialize(*args, session_options:, **options, &filter_block)
11
12
  @resolved_node = nil
12
- @options = if args.last.is_a?(Hash) then args.pop.dup else {} end
13
+ @options = options.dup
13
14
  super(@options)
15
+ self.session_options = session_options
14
16
 
17
+ @selector = find_selector(args[0].is_a?(Symbol) ? args.shift : args[0])
18
+ @locator = args.shift
15
19
  @filter_block = filter_block
16
20
 
17
- if args[0].is_a?(Symbol)
18
- @selector = Selector.all.fetch(args.shift) do |selector_type|
19
- raise ArgumentError, "Unknown selector type (:#{selector_type})"
20
- end
21
- @locator = args.shift
22
- else
23
- @selector = Selector.all.values.find { |s| s.match?(args[0]) }
24
- @locator = args.shift
25
- end
26
- @selector ||= Selector.all[session_options.default_selector]
27
-
28
- warn "Unused parameters passed to #{self.class.name} : #{args}" unless args.empty?
29
-
30
- # for compatibility with Capybara 2.0
31
- if session_options.exact_options and @selector == Selector.all[:option]
32
- @options[:exact] = true
33
- end
21
+ raise ArgumentError, "Unused parameters passed to #{self.class.name} : #{args}" unless args.empty?
34
22
 
35
23
  @expression = @selector.call(@locator, @options.merge(enable_aria_label: session_options.enable_aria_label))
36
24
 
@@ -40,10 +28,10 @@ module Capybara
40
28
  end
41
29
 
42
30
  def name; selector.name; end
43
- def label; selector.label or selector.name; end
31
+ def label; selector.label || selector.name; end
44
32
 
45
33
  def description
46
- @description = String.new()
34
+ @description = "".dup
47
35
  @description << "visible " if visible == :visible
48
36
  @description << "non-visible " if visible == :hidden
49
37
  @description << "#{label} #{locator.inspect}"
@@ -58,80 +46,39 @@ module Capybara
58
46
  end
59
47
 
60
48
  def matches_filters?(node)
61
- if options[:text]
62
- regexp = if options[:text].is_a?(Regexp)
63
- options[:text]
64
- else
65
- if exact_text == true
66
- /\A#{Regexp.escape(options[:text].to_s)}\z/
67
- else
68
- Regexp.escape(options[:text].to_s)
69
- end
70
- end
71
- text_visible = visible
72
- text_visible = :all if text_visible == :hidden
73
- return false if not node.text(text_visible).match(regexp)
74
- end
75
-
76
- if exact_text.is_a?(String)
77
- regexp = /\A#{Regexp.escape(options[:exact_text])}\z/
78
- text_visible = visible
79
- text_visible = :all if text_visible == :hidden
80
- return false if not node.text(text_visible).match(regexp)
81
- end
49
+ return false if options[:text] && !matches_text_filter(node, options[:text])
50
+ return false if exact_text.is_a?(String) && !matches_exact_text_filter(node, exact_text)
82
51
 
83
52
  case visible
84
- when :visible then return false unless node.visible?
85
- when :hidden then return false if node.visible?
53
+ when :visible then return false unless node.visible?
54
+ when :hidden then return false if node.visible?
86
55
  end
87
56
 
88
- res = node_filters.all? do |name, filter|
89
- if options.has_key?(name)
90
- filter.matches?(node, options[name])
91
- elsif filter.default?
92
- filter.matches?(node, filter.default)
93
- else
94
- true
95
- end
96
- end
97
-
98
- res &&= if node.respond_to?(:session)
99
- node.session.using_wait_time(0){ @filter_block.call(node) }
100
- else
101
- @filter_block.call(node)
102
- end unless @filter_block.nil?
103
-
104
- res
105
-
57
+ matches_node_filters?(node) && matches_filter_block?(node)
106
58
  rescue *(node.respond_to?(:session) ? node.session.driver.invalid_element_errors : [])
107
59
  return false
108
60
  end
109
61
 
110
62
  def visible
111
- case (vis = options.fetch(:visible){ @selector.default_visibility(session_options.ignore_hidden_elements) })
112
- when true then :visible
113
- when false then :all
114
- else vis
63
+ case (vis = options.fetch(:visible) { @selector.default_visibility(session_options.ignore_hidden_elements) })
64
+ when true then :visible
65
+ when false then :all
66
+ else vis
115
67
  end
116
68
  end
117
69
 
118
70
  def exact?
119
- return false if !supports_exact?
120
- options.fetch(:exact, session_options.exact)
71
+ supports_exact? ? options.fetch(:exact, session_options.exact) : false
121
72
  end
122
73
 
123
74
  def match
124
75
  options.fetch(:match, session_options.match)
125
76
  end
126
77
 
127
- def xpath(exact=nil)
128
- exact = self.exact? if exact.nil?
78
+ def xpath(exact = nil)
79
+ exact = exact? if exact.nil?
129
80
  expr = apply_expression_filters(@expression)
130
- expr = if expr.respond_to?(:to_xpath) and exact
131
- expr.to_xpath(:exact)
132
- else
133
- expr.to_s
134
- end
81
+ expr = exact ? expr.to_xpath(:exact) : expr.to_s if expr.respond_to?(:to_xpath)
135
82
  filtered_xpath(expr)
136
83
  end
137
84
 
@@ -144,9 +91,9 @@ module Capybara
144
91
  @resolved_node = node
145
92
  node.synchronize do
146
93
  children = if selector.format == :css
147
- node.find_css(self.css)
94
+ node.find_css(css)
148
95
  else
149
- node.find_xpath(self.xpath(exact))
96
+ node.find_xpath(xpath(exact))
150
97
  end.map do |child|
151
98
  if node.is_a?(Capybara::Node::Base)
152
99
  Capybara::Node::Element.new(node.session, child, node, self)
@@ -163,14 +110,45 @@ module Capybara
163
110
  @expression.respond_to? :to_xpath
164
111
  end
165
112
 
166
- private
113
+ private
114
+
115
+ def find_selector(locator)
116
+ selector = if locator.is_a?(Symbol)
117
+ Selector.all.fetch(locator) { |sel_type| raise ArgumentError, "Unknown selector type (:#{sel_type})" }
118
+ else
119
+ Selector.all.values.find { |s| s.match?(locator) }
120
+ end
121
+ selector || Selector.all[session_options.default_selector]
122
+ end
167
123
 
168
124
  def valid_keys
169
125
  VALID_KEYS + custom_keys
170
126
  end
171
127
 
128
+ def matches_node_filters?(node)
129
+ node_filters.all? do |name, filter|
130
+ if options.key?(name)
131
+ filter.matches?(node, options[name])
132
+ elsif filter.default?
133
+ filter.matches?(node, filter.default)
134
+ else
135
+ true
136
+ end
137
+ end
138
+ end
139
+
140
+ def matches_filter_block?(node)
141
+ return true unless @filter_block
142
+
143
+ if node.respond_to?(:session)
144
+ node.session.using_wait_time(0) { @filter_block.call(node) }
145
+ else
146
+ @filter_block.call(node)
147
+ end
148
+ end
149
+
172
150
  def node_filters
173
- if options.has_key?(:filter_set)
151
+ if options.key?(:filter_set)
174
152
  ::Capybara::Selector::FilterSet.all[options[:filter_set]].node_filters
175
153
  else
176
154
  @selector.node_filters
@@ -179,7 +157,7 @@ module Capybara
179
157
 
180
158
  def expression_filters
181
159
  filters = @selector.expression_filters
182
- filters.merge ::Capybara::Selector::FilterSet.all[options[:filter_set]].expression_filters if options.has_key?(:filter_set)
160
+ filters.merge ::Capybara::Selector::FilterSet.all[options[:filter_set]].expression_filters if options.key?(:filter_set)
183
161
  filters
184
162
  end
185
163
 
@@ -195,13 +173,13 @@ module Capybara
195
173
  end
196
174
 
197
175
  def filtered_xpath(expr)
198
- if options.has_key?(:id) || options.has_key?(:class)
176
+ if options.key?(:id) || options.key?(:class)
199
177
  expr = "(#{expr})"
200
- expr = "#{expr}[#{XPath.attr(:id) == options[:id]}]" if options.has_key?(:id) && !custom_keys.include?(:id)
201
- if options.has_key?(:class) && !custom_keys.include?(:class)
178
+ expr = "#{expr}[#{XPath.attr(:id) == options[:id]}]" if options.key?(:id) && !custom_keys.include?(:id)
179
+ if options.key?(:class) && !custom_keys.include?(:class)
202
180
  class_xpath = Array(options[:class]).map do |klass|
203
- "contains(concat(' ',normalize-space(@class),' '),' #{klass} ')"
204
- end.join(" and ")
181
+ XPath.attr(:class).contains_word(klass)
182
+ end.reduce(:&)
205
183
  expr = "#{expr}[#{class_xpath}]"
206
184
  end
207
185
  end
@@ -209,12 +187,12 @@ module Capybara
209
187
  end
210
188
 
211
189
  def filtered_css(expr)
212
- if options.has_key?(:id) || options.has_key?(:class)
190
+ if options.key?(:id) || options.key?(:class)
213
191
  css_selectors = expr.split(',').map(&:rstrip)
214
192
  expr = css_selectors.map do |sel|
215
- sel += "##{Capybara::Selector::CSS.escape(options[:id])}" if options.has_key?(:id) && !custom_keys.include?(:id)
216
- sel += Array(options[:class]).map { |k| ".#{Capybara::Selector::CSS.escape(k)}"}.join if options.has_key?(:class) && !custom_keys.include?(:class)
217
- sel
193
+ sel += "##{Capybara::Selector::CSS.escape(options[:id])}" if options.key?(:id) && !custom_keys.include?(:id)
194
+ sel += Array(options[:class]).map { |k| ".#{Capybara::Selector::CSS.escape(k)}" }.join if options.key?(:class) && !custom_keys.include?(:class)
195
+ sel
218
196
  end.join(", ")
219
197
  end
220
198
  expr
@@ -222,7 +200,7 @@ module Capybara
222
200
 
223
201
  def apply_expression_filters(expr)
224
202
  expression_filters.inject(expr) do |memo, (name, ef)|
225
- if options.has_key?(name)
203
+ if options.key?(name)
226
204
  ef.apply_filter(memo, options[name])
227
205
  elsif ef.default?
228
206
  ef.apply_filter(memo, ef.default)
@@ -233,9 +211,8 @@ module Capybara
233
211
  end
234
212
 
235
213
  def warn_exact_usage
236
- if options.has_key?(:exact) && !supports_exact?
237
- warn "The :exact option only has an effect on queries using the XPath#is method. Using it with the query \"#{expression}\" has no effect."
238
- end
214
+ return unless options.key?(:exact) && !supports_exact?
215
+ warn "The :exact option only has an effect on queries using the XPath#is method. Using it with the query \"#{expression}\" has no effect."
239
216
  end
240
217
 
241
218
  def exact_text
@@ -246,6 +223,26 @@ module Capybara
246
223
  @resolved_node && !(@resolved_node.is_a?(::Capybara::Node::Document) ||
247
224
  (@resolved_node.is_a?(::Capybara::Node::Simple) && @resolved_node.path == '/'))
248
225
  end
226
+
227
+ def matches_text_filter(node, text_option)
228
+ regexp = if text_option.is_a?(Regexp)
229
+ text_option
230
+ elsif exact_text == true
231
+ /\A#{Regexp.escape(text_option.to_s)}\z/
232
+ else
233
+ Regexp.escape(text_option.to_s)
234
+ end
235
+ text_visible = visible
236
+ text_visible = :all if text_visible == :hidden
237
+ node.text(text_visible).match(regexp)
238
+ end
239
+
240
+ def matches_exact_text_filter(node, exact_text_option)
241
+ regexp = /\A#{Regexp.escape(exact_text_option)}\z/
242
+ text_visible = visible
243
+ text_visible = :all if text_visible == :hidden
244
+ node.text(text_visible).match(regexp)
245
+ end
249
246
  end
250
247
  end
251
248
  end
@@ -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,23 @@
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
+
14
+ @expected_text = expected_text.is_a?(Regexp) ? expected_text : expected_text.to_s
15
+ @options = options
10
16
  super(@options)
17
+ self.session_options = session_options
11
18
 
12
- @type = (session_options.ignore_hidden_elements or session_options.visible_text_only) ? :visible : :all if @type.nil?
19
+ @search_regexp = Capybara::Helpers.to_regexp(@expected_text, exact: exact?)
13
20
 
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
21
  assert_valid_keys
21
22
  end
22
23
 
@@ -42,48 +43,47 @@ module Capybara
42
43
  end
43
44
  end
44
45
 
45
- private
46
+ private
46
47
 
47
48
  def exact?
48
49
  options.fetch(:exact, session_options.exact_text)
49
50
  end
50
51
 
51
52
  def build_message(report_on_invisible)
52
- message = String.new()
53
+ message = "".dup
53
54
  unless (COUNT_KEYS & @options.keys).empty?
54
55
  message << " but found #{@count} #{Capybara::Helpers.declension('time', 'times', @count)}"
55
56
  end
56
57
  message << " in #{@actual_text.inspect}"
57
58
 
58
59
  details_message = []
60
+ details_message << case_insensitive_message if @node and !@expected_text.is_a? Regexp
61
+ details_message << invisible_message if @node and check_visible_text? and report_on_invisible
62
+ details_message.compact!
59
63
 
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
64
+ message << ". (However, #{details_message.join(' and ')}.)" unless details_message.empty?
65
+ message
66
+ end
67
67
 
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
68
+ def case_insensitive_message
69
+ insensitive_regexp = Capybara::Helpers.to_regexp(@expected_text, options: Regexp::IGNORECASE)
70
+ insensitive_count = @actual_text.scan(insensitive_regexp).size
71
+ if insensitive_count != @count
72
+ "it was found #{insensitive_count} #{Capybara::Helpers.declension("time", "times", insensitive_count)} using a case insensitive search"
78
73
  end
74
+ end
79
75
 
80
- message << ". (However, #{details_message.join(' and ')}.)" unless details_message.empty?
81
-
82
- message
76
+ def invisible_message
77
+ invisible_text = text(@node, :all)
78
+ invisible_count = invisible_text.scan(@search_regexp).size
79
+ if invisible_count != @count
80
+ "it was found #{invisible_count} #{Capybara::Helpers.declension("time", "times", invisible_count)} including non-visible text"
81
+ end
82
+ rescue # An error getting the non-visible text (if element goes out of scope) should not affect the response
83
83
  end
84
84
 
85
85
  def valid_keys
86
- COUNT_KEYS + [:wait, :exact]
86
+ COUNT_KEYS + %i[wait exact]
87
87
  end
88
88
 
89
89
  def check_visible_text?
@@ -91,7 +91,7 @@ module Capybara
91
91
  end
92
92
 
93
93
  def text(node, query_type)
94
- Capybara::Helpers.normalize_whitespace(node.text(query_type))
94
+ node.text(query_type)
95
95
  end
96
96
  end
97
97
  end
@@ -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 : expected_title.to_s
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, all_whitespace: true, 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