capybara 3.18.0 → 3.19.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (71) hide show
  1. checksums.yaml +4 -4
  2. data/History.md +16 -0
  3. data/README.md +14 -44
  4. data/lib/capybara/node/actions.rb +2 -2
  5. data/lib/capybara/node/element.rb +3 -5
  6. data/lib/capybara/queries/selector_query.rb +30 -11
  7. data/lib/capybara/rack_test/node.rb +1 -1
  8. data/lib/capybara/result.rb +2 -0
  9. data/lib/capybara/rspec/matcher_proxies.rb +2 -0
  10. data/lib/capybara/rspec/matchers/base.rb +2 -2
  11. data/lib/capybara/rspec/matchers/count_sugar.rb +36 -0
  12. data/lib/capybara/rspec/matchers/have_selector.rb +3 -0
  13. data/lib/capybara/rspec/matchers/have_text.rb +3 -0
  14. data/lib/capybara/selector.rb +196 -599
  15. data/lib/capybara/selector/css.rb +2 -0
  16. data/lib/capybara/selector/definition.rb +276 -0
  17. data/lib/capybara/selector/definition/button.rb +46 -0
  18. data/lib/capybara/selector/definition/checkbox.rb +23 -0
  19. data/lib/capybara/selector/definition/css.rb +5 -0
  20. data/lib/capybara/selector/definition/datalist_input.rb +35 -0
  21. data/lib/capybara/selector/definition/datalist_option.rb +25 -0
  22. data/lib/capybara/selector/definition/element.rb +27 -0
  23. data/lib/capybara/selector/definition/field.rb +40 -0
  24. data/lib/capybara/selector/definition/fieldset.rb +14 -0
  25. data/lib/capybara/selector/definition/file_field.rb +13 -0
  26. data/lib/capybara/selector/definition/fillable_field.rb +33 -0
  27. data/lib/capybara/selector/definition/frame.rb +17 -0
  28. data/lib/capybara/selector/definition/id.rb +6 -0
  29. data/lib/capybara/selector/definition/label.rb +43 -0
  30. data/lib/capybara/selector/definition/link.rb +45 -0
  31. data/lib/capybara/selector/definition/link_or_button.rb +16 -0
  32. data/lib/capybara/selector/definition/option.rb +27 -0
  33. data/lib/capybara/selector/definition/radio_button.rb +24 -0
  34. data/lib/capybara/selector/definition/select.rb +62 -0
  35. data/lib/capybara/selector/definition/table.rb +106 -0
  36. data/lib/capybara/selector/definition/table_row.rb +21 -0
  37. data/lib/capybara/selector/definition/xpath.rb +5 -0
  38. data/lib/capybara/selector/filters/base.rb +4 -0
  39. data/lib/capybara/selector/filters/locator_filter.rb +12 -2
  40. data/lib/capybara/selector/selector.rb +40 -452
  41. data/lib/capybara/selenium/driver.rb +4 -10
  42. data/lib/capybara/selenium/driver_specializations/chrome_driver.rb +3 -9
  43. data/lib/capybara/selenium/driver_specializations/internet_explorer_driver.rb +8 -0
  44. data/lib/capybara/selenium/extensions/find.rb +1 -1
  45. data/lib/capybara/selenium/logger_suppressor.rb +5 -0
  46. data/lib/capybara/selenium/node.rb +19 -13
  47. data/lib/capybara/selenium/nodes/chrome_node.rb +30 -0
  48. data/lib/capybara/selenium/nodes/firefox_node.rb +14 -12
  49. data/lib/capybara/selenium/nodes/ie_node.rb +11 -0
  50. data/lib/capybara/selenium/nodes/safari_node.rb +7 -12
  51. data/lib/capybara/server/checker.rb +7 -3
  52. data/lib/capybara/session.rb +2 -2
  53. data/lib/capybara/spec/session/all_spec.rb +1 -1
  54. data/lib/capybara/spec/session/find_spec.rb +1 -1
  55. data/lib/capybara/spec/session/first_spec.rb +1 -1
  56. data/lib/capybara/spec/session/has_css_spec.rb +7 -0
  57. data/lib/capybara/spec/session/has_text_spec.rb +6 -0
  58. data/lib/capybara/spec/session/save_screenshot_spec.rb +11 -0
  59. data/lib/capybara/spec/session/select_spec.rb +0 -5
  60. data/lib/capybara/spec/test_app.rb +8 -3
  61. data/lib/capybara/version.rb +1 -1
  62. data/lib/capybara/window.rb +1 -1
  63. data/spec/minitest_spec_spec.rb +1 -0
  64. data/spec/selector_spec.rb +12 -6
  65. data/spec/selenium_spec_firefox.rb +0 -3
  66. data/spec/selenium_spec_firefox_remote.rb +0 -3
  67. data/spec/selenium_spec_ie.rb +3 -1
  68. data/spec/server_spec.rb +1 -1
  69. data/spec/shared_selenium_session.rb +1 -1
  70. data/spec/spec_helper.rb +9 -2
  71. metadata +54 -2
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ Capybara.add_selector(:table_row, locator_type: [Array, Hash]) do
4
+ xpath do |locator|
5
+ xpath = XPath.descendant(:tr)
6
+ if locator.is_a? Hash
7
+ locator.reduce(xpath) do |xp, (header, cell)|
8
+ header_xp = XPath.ancestor(:table)[1].descendant(:tr)[1].descendant(:th)[XPath.string.n.is(header)]
9
+ cell_xp = XPath.descendant(:td)[
10
+ XPath.string.n.is(cell) & XPath.position.equals(header_xp.preceding_sibling.count.plus(1))
11
+ ]
12
+ xp[cell_xp]
13
+ end
14
+ else
15
+ initial_td = XPath.descendant(:td)[XPath.string.n.is(locator.shift)]
16
+ tds = locator.reverse.map { |cell| XPath.following_sibling(:td)[XPath.string.n.is(cell)] }
17
+ .reduce { |xp, cell| xp[cell] }
18
+ xpath[initial_td[tds]]
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ Capybara.add_selector(:xpath, locator_type: [:to_xpath, String], raw_locator: true) do
4
+ xpath { |xpath| xpath }
5
+ end
@@ -24,6 +24,10 @@ module Capybara
24
24
  @options.key?(:skip_if) && value == @options[:skip_if]
25
25
  end
26
26
 
27
+ def format
28
+ @options[:format]
29
+ end
30
+
27
31
  def matcher?
28
32
  !@matcher.nil?
29
33
  end
@@ -10,8 +10,18 @@ module Capybara
10
10
  super(nil, nil, block, options)
11
11
  end
12
12
 
13
- def matches?(node, value, context = nil)
14
- super(node, nil, value, context)
13
+ def matches?(node, value, context = nil, exact:)
14
+ apply(node, value, true, context, exact: exact, format: context&.default_format)
15
+ rescue Capybara::ElementNotFound
16
+ false
17
+ end
18
+
19
+ private
20
+
21
+ def apply(subject, value, skip_value, ctx, **options)
22
+ return skip_value if skip?(value)
23
+
24
+ filter_context(ctx).instance_exec(subject, value, **options, &@block)
15
25
  end
16
26
  end
17
27
  end
@@ -1,191 +1,10 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- # rubocop:disable Style/AsciiComments
4
-
5
- require 'capybara/selector/filter_set'
6
- require 'capybara/selector/css'
7
- require 'capybara/selector/regexp_disassembler'
8
- require 'capybara/selector/builders/xpath_builder'
9
- require 'capybara/selector/builders/css_builder'
10
-
11
3
  module Capybara
12
- #
13
- # ## Built-in Selectors
14
- #
15
- # * **:xpath** - Select elements by XPath expression
16
- # * Locator: An XPath expression
17
- #
18
- # * **:css** - Select elements by CSS selector
19
- # * Locator: A CSS selector
20
- #
21
- # * **:id** - Select element by id
22
- # * Locator: (String, Regexp, XPath::Expression) The id of the element to match
23
- #
24
- # * **:field** - Select field elements (input [not of type submit, image, or hidden], textarea, select)
25
- # * Locator: Matches against the id, Capybara.test_id attribute, name, or placeholder
26
- # * Filters:
27
- # * :id (String, Regexp, XPath::Expression) — Matches the id attribute
28
- # * :name (String) — Matches the name attribute
29
- # * :placeholder (String) — Matches the placeholder attribute
30
- # * :type (String) — Matches the type attribute of the field or element type for 'textarea' and 'select'
31
- # * :readonly (Boolean)
32
- # * :with (String) — Matches the current value of the field
33
- # * :class (String, Array<String>, Regexp, XPath::Expression) — Matches the class(es) provided
34
- # * :checked (Boolean) — Match checked fields?
35
- # * :unchecked (Boolean) — Match unchecked fields?
36
- # * :disabled (Boolean) — Match disabled field?
37
- # * :multiple (Boolean) — Match fields that accept multiple values
38
- # * :style (String, Regexp, Hash)
39
- #
40
- # * **:fieldset** - Select fieldset elements
41
- # * Locator: Matches id or contents of wrapped legend
42
- # * Filters:
43
- # * :id (String, Regexp, XPath::Expression) — Matches id attribute
44
- # * :legend (String) — Matches contents of wrapped legend
45
- # * :class (String, Array<String>, Regexp, XPath::Expression) — Matches the class(es) provided
46
- # * :style (String, Regexp, Hash)
47
- #
48
- # * **:link** - Find links ( <a> elements with an href attribute )
49
- # * Locator: Matches the id or title attributes, or the string content of the link, or the alt attribute of a contained img element
50
- # * Filters:
51
- # * :id (String, Regexp, XPath::Expression) — Matches the id attribute
52
- # * :title (String) — Matches the title attribute
53
- # * :alt (String) — Matches the alt attribute of a contained img element
54
- # * :class (String, Array<String>, Regexp, XPath::Expression) — Matches the class(es) provided
55
- # * :href (String, Regexp, nil) — Matches the normalized href of the link, if nil will find <a> elements with no href attribute
56
- # * :style (String, Regexp, Hash)
57
- #
58
- # * **:button** - Find buttons ( input [of type submit, reset, image, button] or button elements )
59
- # * Locator: Matches the id, Capybara.test_id attribute, value, or title attributes, string content of a button, or the alt attribute of an image type button or of a descendant image of a button
60
- # * Filters:
61
- # * :id (String, Regexp, XPath::Expression) — Matches the id attribute
62
- # * :name (String) - Matches the name attribute
63
- # * :title (String) — Matches the title attribute
64
- # * :class (String, Array<String>, Regexp, XPath::Expression) — Matches the class(es) provided
65
- # * :value (String) — Matches the value of an input button
66
- # * :type
67
- # * :style (String, Regexp, Hash)
68
- #
69
- # * **:link_or_button** - Find links or buttons
70
- # * Locator: See :link and :button selectors
71
- #
72
- # * **:fillable_field** - Find text fillable fields ( textarea, input [not of type submit, image, radio, checkbox, hidden, file] )
73
- # * Locator: Matches against the id, Capybara.test_id attribute, name, or placeholder
74
- # * Filters:
75
- # * :id (String, Regexp, XPath::Expression) — Matches the id attribute
76
- # * :name (String) — Matches the name attribute
77
- # * :placeholder (String) — Matches the placeholder attribute
78
- # * :with (String) — Matches the current value of the field
79
- # * :type (String) — Matches the type attribute of the field or element type for 'textarea'
80
- # * :class (String, Array<String>, Regexp, XPath::Expression) — Matches the class(es) provided
81
- # * :disabled (Boolean) — Match disabled field?
82
- # * :multiple (Boolean) — Match fields that accept multiple values
83
- # * :style (String, Regexp, Hash)
84
- #
85
- # * **:radio_button** - Find radio buttons
86
- # * Locator: Match id, Capybara.test_id attribute, name, or associated label text
87
- # * Filters:
88
- # * :id (String, Regexp, XPath::Expression) — Matches the id attribute
89
- # * :name (String) — Matches the name attribute
90
- # * :class (String, Array<String>, Regexp, XPath::Expression) — Matches the class(es) provided
91
- # * :checked (Boolean) — Match checked fields?
92
- # * :unchecked (Boolean) — Match unchecked fields?
93
- # * :disabled (Boolean) — Match disabled field?
94
- # * :option (String) — Match the value
95
- # * :style (String, Regexp, Hash)
96
- #
97
- # * **:checkbox** - Find checkboxes
98
- # * Locator: Match id, Capybara.test_id attribute, name, or associated label text
99
- # * Filters:
100
- # * *:id (String, Regexp, XPath::Expression) — Matches the id attribute
101
- # * *:name (String) — Matches the name attribute
102
- # * *:class (String, Array<String>, Regexp, XPath::Expression) — Matches the class(es) provided
103
- # * *:checked (Boolean) — Match checked fields?
104
- # * *:unchecked (Boolean) — Match unchecked fields?
105
- # * *:disabled (Boolean) — Match disabled field?
106
- # * *:option (String) — Match the value
107
- # * :style (String, Regexp, Hash)
108
- #
109
- # * **:select** - Find select elements
110
- # * Locator: Match id, Capybara.test_id attribute, name, placeholder, or associated label text
111
- # * Filters:
112
- # * :id (String, Regexp, XPath::Expression) — Matches the id attribute
113
- # * :name (String) — Matches the name attribute
114
- # * :placeholder (String) — Matches the placeholder attribute
115
- # * :class (String, Array<String>, Regexp, XPath::Expression) — Matches the class(es) provided
116
- # * :disabled (Boolean) — Match disabled field?
117
- # * :multiple (Boolean) — Match fields that accept multiple values
118
- # * :options (Array<String>) — Exact match options
119
- # * :with_options (Array<String>) — Partial match options
120
- # * :selected (String, Array<String>) — Match the selection(s)
121
- # * :with_selected (String, Array<String>) — Partial match the selection(s)
122
- # * :style (String, Regexp, Hash)
123
- #
124
- # * **:option** - Find option elements
125
- # * Locator: Match text of option
126
- # * Filters:
127
- # * :disabled (Boolean) — Match disabled option
128
- # * :selected (Boolean) — Match selected option
129
- #
130
- # * **:datalist_input**
131
- # * Locator:
132
- # * Filters:
133
- # * :disabled
134
- # * :name
135
- # * :placeholder
136
- #
137
- # * **:datalist_option**
138
- # * Locator:
139
- #
140
- # * **:file_field** - Find file input elements
141
- # * Locator: Match id, Capybara.test_id attribute, name, or associated label text
142
- # * Filters:
143
- # * :id (String, Regexp, XPath::Expression) — Matches the id attribute
144
- # * :name (String) — Matches the name attribute
145
- # * :class (String, Array<String>, Regexp, XPath::Expression) — Matches the class(es) provided
146
- # * :disabled (Boolean) — Match disabled field?
147
- # * :multiple (Boolean) — Match field that accepts multiple values
148
- # * :style (String, Regexp, Hash)
149
- #
150
- # * **:label** - Find label elements
151
- # * Locator: Match id or text contents
152
- # * Filters:
153
- # * :for (Element, String) — The element or id of the element associated with the label
154
- #
155
- # * **:table** - Find table elements
156
- # * Locator: id or caption text of table
157
- # * Filters:
158
- # * :id (String, Regexp, XPath::Expression) — Match id attribute of table
159
- # * :caption (String) — Match text of associated caption
160
- # * :class ((String, Array<String>, Regexp, XPath::Expression) — Matches the class(es) provided
161
- # * :style (String, Regexp, Hash)
162
- # * :with_rows (Array<Array<String>>, Array<Hash<String, String>>) - Partial match <td> data - visibility of <td> elements is not considered
163
- # * :rows (Array<Array<String>>) — Match all <td>s - visibility of <td> elements is not considered
164
- # * :with_cols (Array<Array<String>>, Array<Hash<String, String>>) - Partial match <td> data - visibility of <td> elements is not considered
165
- # * :cols (Array<Array<String>>) — Match all <td>s - visibility of <td> elements is not considered
166
- #
167
- # * **:table_row** - Find table row
168
- # * Locator: Array<String>, Hash<String,String> table row <td> contents - visibility of <td> elements is not considered
169
- #
170
- # * **:frame** - Find frame/iframe elements
171
- # * Locator: Match id or name
172
- # * Filters:
173
- # * :id (String, Regexp, XPath::Expression) — Match id attribute
174
- # * :name (String) — Match name attribute
175
- # * :class (String, Array<String>, Regexp, XPath::Expression) — Matches the class(es) provided
176
- # * :style (String, Regexp, Hash)
177
- #
178
- # * **:element**
179
- # * Locator: Type of element ('div', 'a', etc) - if not specified defaults to '*'
180
- # * Filters: Matches on any element attribute
181
- #
182
- class Selector
183
- attr_reader :name, :format
184
- extend Forwardable
185
-
4
+ class Selector < SimpleDelegator
186
5
  class << self
187
6
  def all
188
- @selectors ||= {} # rubocop:disable Naming/MemoizedInstanceVariableName
7
+ @definitions ||= {} # rubocop:disable Naming/MemoizedInstanceVariableName
189
8
  end
190
9
 
191
10
  def [](name)
@@ -193,7 +12,7 @@ module Capybara
193
12
  end
194
13
 
195
14
  def add(name, **options, &block)
196
- all[name.to_sym] = Capybara::Selector.new(name.to_sym, **options, &block)
15
+ all[name.to_sym] = Definition.new(name.to_sym, **options, &block)
197
16
  end
198
17
 
199
18
  def update(name, &block)
@@ -209,226 +28,57 @@ module Capybara
209
28
  end
210
29
  end
211
30
 
212
- def initialize(name, locator_type: nil, raw_locator: false, &block)
213
- @name = name
214
- @filter_set = FilterSet.add(name) {}
215
- @match = nil
216
- @label = nil
217
- @failure_message = nil
218
- @format = nil
219
- @expression = nil
220
- @expression_filters = {}
221
- @locator_filter = nil
222
- @default_visibility = nil
223
- @locator_type = locator_type
224
- @raw_locator = raw_locator
225
- @config = {
226
- enable_aria_label: false,
227
- test_id: nil
228
- }
229
- instance_eval(&block)
230
- end
231
-
232
- def custom_filters
233
- warn "Deprecated: Selector#custom_filters is not valid when same named expression and node filter exist - don't use"
234
- node_filters.merge(expression_filters).freeze
235
- end
31
+ attr_reader :errors
236
32
 
237
- def node_filters
238
- @filter_set.node_filters
33
+ def initialize(definition, config:, format:)
34
+ definition = self.class[definition] unless definition.is_a? Definition
35
+ super(definition)
36
+ @definition = definition
37
+ @config = config
38
+ @format = format
39
+ @errors = []
239
40
  end
240
41
 
241
- def expression_filters
242
- @filter_set.expression_filters
42
+ def format
43
+ @format || @definition.default_format
243
44
  end
45
+ alias_method :current_format, :format
244
46
 
245
- ##
246
- #
247
- # Define a selector by an xpath expression
248
- #
249
- # @overload xpath(*expression_filters, &block)
250
- # @param [Array<Symbol>] expression_filters ([]) Names of filters that are implemented via this expression, if not specified the names of any keyword parameters in the block will be used
251
- # @yield [locator, options] The block to use to generate the XPath expression
252
- # @yieldparam [String] locator The locator string passed to the query
253
- # @yieldparam [Hash] options The options hash passed to the query
254
- # @yieldreturn [#to_xpath, #to_s] An object that can produce an xpath expression
255
- #
256
- # @overload xpath()
257
- # @return [#call] The block that will be called to generate the XPath expression
258
- #
259
- def xpath(*allowed_filters, &block)
260
- expression(:xpath, allowed_filters, &block)
261
- end
262
-
263
- ##
264
- #
265
- # Define a selector by a CSS selector
266
- #
267
- # @overload css(*expression_filters, &block)
268
- # @param [Array<Symbol>] expression_filters ([]) Names of filters that can be implemented via this CSS selector
269
- # @yield [locator, options] The block to use to generate the CSS selector
270
- # @yieldparam [String] locator The locator string passed to the query
271
- # @yieldparam [Hash] options The options hash passed to the query
272
- # @yieldreturn [#to_s] An object that can produce a CSS selector
273
- #
274
- # @overload css()
275
- # @return [#call] The block that will be called to generate the CSS selector
276
- #
277
- def css(*allowed_filters, &block)
278
- expression(:css, allowed_filters, &block)
279
- end
280
-
281
- ##
282
- #
283
- # Automatic selector detection
284
- #
285
- # @yield [locator] This block takes the passed in locator string and returns whether or not it matches the selector
286
- # @yieldparam [String], locator The locator string used to determin if it matches the selector
287
- # @yieldreturn [Boolean] Whether this selector matches the locator string
288
- # @return [#call] The block that will be used to detect selector match
289
- #
290
- def match(&block)
291
- @match = block if block
292
- @match
47
+ def enable_aria_label
48
+ @config[:enable_aria_label]
293
49
  end
294
50
 
295
- ##
296
- #
297
- # Set/get a descriptive label for the selector
298
- #
299
- # @overload label(label)
300
- # @param [String] label A descriptive label for this selector - used in error messages
301
- # @overload label()
302
- # @return [String] The currently set label
303
- #
304
- def label(label = nil)
305
- @label = label if label
306
- @label
51
+ def test_id
52
+ @config[:test_id]
307
53
  end
308
54
 
309
- ##
310
- #
311
- # Description of the selector
312
- #
313
- # @!method description(options)
314
- # @param [Hash] options The options of the query used to generate the description
315
- # @return [String] Description of the selector when used with the options passed
316
- def_delegator :@filter_set, :description
317
-
318
- def call(locator, selector_config: {}, **options)
319
- @config.merge! selector_config
55
+ def call(locator, **options)
320
56
  if format
321
- @expression.call(locator, options)
57
+ raise ArgumentError, "Selector #{@name} does not support #{format}" unless expressions.key?(format)
58
+
59
+ instance_exec(locator, options, &expressions[format])
322
60
  else
323
61
  warn 'Selector has no format'
324
62
  end
325
63
  ensure
326
- warn "Locator #{locator.inspect} must #{locator_description}. This will raise an error in a future version of Capybara." unless locator_valid?(locator)
327
- end
328
-
329
- ##
330
- #
331
- # Should this selector be used for the passed in locator
332
- #
333
- # This is used by the automatic selector selection mechanism when no selector type is passed to a selector query
334
- #
335
- # @param [String] locator The locator passed to the query
336
- # @return [Boolean] Whether or not to use this selector
337
- #
338
- def match?(locator)
339
- @match&.call(locator)
340
- end
341
-
342
- ##
343
- #
344
- # Define a node filter for use with this selector
345
- #
346
- # @!method node_filter(name, *types, options={}, &block)
347
- # @param [Symbol, Regexp] name The filter name
348
- # @param [Array<Symbol>] types The types of the filter - currently valid types are [:boolean]
349
- # @param [Hash] options ({}) Options of the filter
350
- # @option options [Array<>] :valid_values Valid values for this filter
351
- # @option options :default The default value of the filter (if any)
352
- # @option options :skip_if Value of the filter that will cause it to be skipped
353
- # @option options [Regexp] :matcher (nil) A Regexp used to check whether a specific option is handled by this filter. If not provided the filter will be used for options matching the filter name.
354
- #
355
- # If a Symbol is passed for the name the block should accept | node, option_value |, while if a Regexp
356
- # is passed for the name the block should accept | node, option_name, option_value |. In either case
357
- # the block should return `true` if the node passes the filer or `false` if it doesn't
358
-
359
- # @!method filter
360
- # See {Selector#node_filter}
361
-
362
- ##
363
- #
364
- # Define an expression filter for use with this selector
365
- #
366
- # @!method expression_filter(name, *types, matcher: nil, **options, &block)
367
- # @param [Symbol, Regexp] name The filter name
368
- # @param [Regexp] matcher (nil) A Regexp used to check whether a specific option is handled by this filter
369
- # @param [Array<Symbol>] types The types of the filter - currently valid types are [:boolean]
370
- # @param [Hash] options ({}) Options of the filter
371
- # @option options [Array<>] :valid_values Valid values for this filter
372
- # @option options :default The default value of the filter (if any)
373
- # @option options :skip_if Value of the filter that will cause it to be skipped
374
- # @option options [Regexp] :matcher (nil) A Regexp used to check whether a specific option is handled by this filter. If not provided the filter will be used for options matching the filter name.
375
- #
376
- # If a Symbol is passed for the name the block should accept | current_expression, option_value |, while if a Regexp
377
- # is passed for the name the block should accept | current_expression, option_name, option_value |. In either case
378
- # the block should return the modified expression
379
-
380
- def_delegators :@filter_set, :node_filter, :expression_filter, :filter
381
-
382
- def locator_filter(*types, **options, &block)
383
- types.each { |type| options[type] = true }
384
- @locator_filter = Filters::LocatorFilter.new(block, options) if block
385
- @locator_filter
386
- end
387
-
388
- def filter_set(name, filters_to_use = nil)
389
- @filter_set.import(name, filters_to_use)
390
- end
391
-
392
- def_delegator :@filter_set, :describe
393
-
394
- def describe_expression_filters(&block)
395
- if block_given?
396
- describe(:expression_filters, &block)
397
- else
398
- describe(:expression_filters) do |**options|
399
- describe_all_expression_filters(options)
400
- end
401
- end
402
- end
403
-
404
- def describe_node_filters(&block)
405
- describe(:node_filters, &block)
64
+ warn "Locator #{locator.class}:#{locator.inspect} for selector #{name.inspect} must #{locator_description}. This will raise an error in a future version of Capybara." unless locator_valid?(locator)
406
65
  end
407
66
 
408
- ##
409
- #
410
- # Set the default visibility mode that shouble be used if no visibile option is passed when using the selector.
411
- # If not specified will default to the behavior indicated by Capybara.ignore_hidden_elements
412
- #
413
- # @param [Symbol] default_visibility Only find elements with the specified visibility:
414
- # * :all - finds visible and invisible elements.
415
- # * :hidden - only finds invisible elements.
416
- # * :visible - only finds visible elements.
417
- def visible(default_visibility = nil, &block)
418
- @default_visibility = block || default_visibility
67
+ def add_error(error_msg)
68
+ errors << error_msg
419
69
  end
420
70
 
421
- def default_visibility(fallback = Capybara.ignore_hidden_elements, options = {})
422
- vis = if @default_visibility&.respond_to?(:call)
423
- @default_visibility.call(options)
424
- else
425
- @default_visibility
426
- end
427
- vis.nil? ? fallback : vis
71
+ def expression_for(name, locator, config: @config, format: current_format, **options)
72
+ Selector.new(name, config: config, format: format).call(locator, **options)
428
73
  end
429
74
 
430
- def add_error(error_msg)
431
- errors << error_msg
75
+ # @api private
76
+ def with_filter_errors(errors)
77
+ old_errors = @errors
78
+ @errors = errors
79
+ yield
80
+ ensure
81
+ @errors = old_errors
432
82
  end
433
83
 
434
84
  # @api private
@@ -439,39 +89,12 @@ module Capybara
439
89
  when :xpath
440
90
  Capybara::Selector::XPathBuilder
441
91
  else
442
- raise NotImplementedError, "No builder exists for selector of type #{format}"
92
+ raise NotImplementedError, "No builder exists for selector of type #{default_format}"
443
93
  end.new(expr)
444
94
  end
445
95
 
446
- # @api private
447
- def with_filter_errors(errors)
448
- Thread.current["capybara_#{object_id}_errors"] = errors
449
- yield
450
- ensure
451
- Thread.current["capybara_#{object_id}_errors"] = nil
452
- end
453
-
454
- # @api private
455
- def raw_locator?
456
- !!@raw_locator
457
- end
458
-
459
96
  private
460
97
 
461
- def locator_types
462
- return nil unless @locator_type
463
-
464
- Array(@locator_type)
465
- end
466
-
467
- def locator_valid?(locator)
468
- return true unless locator && locator_types
469
-
470
- locator_types&.any? do |type_or_method|
471
- type_or_method.is_a?(Symbol) ? locator.respond_to?(type_or_method) : type_or_method === locator # rubocop:disable Style/CaseEquality
472
- end
473
- end
474
-
475
98
  def locator_description
476
99
  locator_types.group_by { |lt| lt.is_a? Symbol }.map do |symbol, types_or_methods|
477
100
  if symbol
@@ -482,16 +105,12 @@ module Capybara
482
105
  end.join(' or ')
483
106
  end
484
107
 
485
- def errors
486
- Thread.current["capybara_#{object_id}_errors"] || []
487
- end
488
-
489
- def enable_aria_label
490
- @config[:enable_aria_label]
491
- end
108
+ def locator_valid?(locator)
109
+ return true unless locator && locator_types
492
110
 
493
- def test_id
494
- @config[:test_id]
111
+ locator_types&.any? do |type_or_method|
112
+ type_or_method.is_a?(Symbol) ? locator.respond_to?(type_or_method) : type_or_method === locator # rubocop:disable Style/CaseEquality
113
+ end
495
114
  end
496
115
 
497
116
  def locate_field(xpath, locator, **_options)
@@ -510,22 +129,6 @@ module Capybara
510
129
  locate_xpath + XPath.descendant(:label)[XPath.string.n.is(locator)].descendant(xpath)
511
130
  end
512
131
 
513
- def describe_all_expression_filters(**opts)
514
- expression_filters.map do |ef_name, ef|
515
- if ef.matcher?
516
- handled_custom_keys(ef, opts.keys).map { |key| " with #{ef_name}[#{key} => #{opts[key]}]" }.join
517
- elsif opts.key?(ef_name)
518
- " with #{ef_name} #{opts[ef_name]}"
519
- end
520
- end.join
521
- end
522
-
523
- def handled_custom_keys(filter, keys)
524
- keys.select do |key|
525
- filter.handles_option?(key) && !::Capybara::Queries::SelectorQuery::VALID_KEYS.include?(key)
526
- end
527
- end
528
-
529
132
  def find_by_attr(attribute, value)
530
133
  finder_name = "find_by_#{attribute}_attr"
531
134
  if respond_to?(finder_name, true)
@@ -538,20 +141,5 @@ module Capybara
538
141
  def find_by_class_attr(classes)
539
142
  Array(classes).map { |klass| XPath.attr(:class).contains_word(klass) }.reduce(:&)
540
143
  end
541
-
542
- def parameter_names(block)
543
- block.parameters.select { |(type, _name)| %i[key keyreq].include? type }.map { |(_type, name)| name }
544
- end
545
-
546
- def expression(type, allowed_filters, &block)
547
- if block
548
- @format, @expression = type, block
549
- allowed_filters = parameter_names(block) if allowed_filters.empty?
550
- allowed_filters.flatten.each { |ef| expression_filters[ef] = Filters::IdentityExpressionFilter.new(ef) }
551
- end
552
- format == type ? @expression : nil
553
- end
554
144
  end
555
145
  end
556
-
557
- # rubocop:enable Style/AsciiComments