capybara 2.18.0 → 3.0.0

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