capybara 3.18.0 → 3.19.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.
- checksums.yaml +4 -4
- data/History.md +16 -0
- data/README.md +14 -44
- data/lib/capybara/node/actions.rb +2 -2
- data/lib/capybara/node/element.rb +3 -5
- data/lib/capybara/queries/selector_query.rb +30 -11
- data/lib/capybara/rack_test/node.rb +1 -1
- data/lib/capybara/result.rb +2 -0
- data/lib/capybara/rspec/matcher_proxies.rb +2 -0
- data/lib/capybara/rspec/matchers/base.rb +2 -2
- data/lib/capybara/rspec/matchers/count_sugar.rb +36 -0
- data/lib/capybara/rspec/matchers/have_selector.rb +3 -0
- data/lib/capybara/rspec/matchers/have_text.rb +3 -0
- data/lib/capybara/selector.rb +196 -599
- data/lib/capybara/selector/css.rb +2 -0
- data/lib/capybara/selector/definition.rb +276 -0
- data/lib/capybara/selector/definition/button.rb +46 -0
- data/lib/capybara/selector/definition/checkbox.rb +23 -0
- data/lib/capybara/selector/definition/css.rb +5 -0
- data/lib/capybara/selector/definition/datalist_input.rb +35 -0
- data/lib/capybara/selector/definition/datalist_option.rb +25 -0
- data/lib/capybara/selector/definition/element.rb +27 -0
- data/lib/capybara/selector/definition/field.rb +40 -0
- data/lib/capybara/selector/definition/fieldset.rb +14 -0
- data/lib/capybara/selector/definition/file_field.rb +13 -0
- data/lib/capybara/selector/definition/fillable_field.rb +33 -0
- data/lib/capybara/selector/definition/frame.rb +17 -0
- data/lib/capybara/selector/definition/id.rb +6 -0
- data/lib/capybara/selector/definition/label.rb +43 -0
- data/lib/capybara/selector/definition/link.rb +45 -0
- data/lib/capybara/selector/definition/link_or_button.rb +16 -0
- data/lib/capybara/selector/definition/option.rb +27 -0
- data/lib/capybara/selector/definition/radio_button.rb +24 -0
- data/lib/capybara/selector/definition/select.rb +62 -0
- data/lib/capybara/selector/definition/table.rb +106 -0
- data/lib/capybara/selector/definition/table_row.rb +21 -0
- data/lib/capybara/selector/definition/xpath.rb +5 -0
- data/lib/capybara/selector/filters/base.rb +4 -0
- data/lib/capybara/selector/filters/locator_filter.rb +12 -2
- data/lib/capybara/selector/selector.rb +40 -452
- data/lib/capybara/selenium/driver.rb +4 -10
- data/lib/capybara/selenium/driver_specializations/chrome_driver.rb +3 -9
- data/lib/capybara/selenium/driver_specializations/internet_explorer_driver.rb +8 -0
- data/lib/capybara/selenium/extensions/find.rb +1 -1
- data/lib/capybara/selenium/logger_suppressor.rb +5 -0
- data/lib/capybara/selenium/node.rb +19 -13
- data/lib/capybara/selenium/nodes/chrome_node.rb +30 -0
- data/lib/capybara/selenium/nodes/firefox_node.rb +14 -12
- data/lib/capybara/selenium/nodes/ie_node.rb +11 -0
- data/lib/capybara/selenium/nodes/safari_node.rb +7 -12
- data/lib/capybara/server/checker.rb +7 -3
- data/lib/capybara/session.rb +2 -2
- data/lib/capybara/spec/session/all_spec.rb +1 -1
- data/lib/capybara/spec/session/find_spec.rb +1 -1
- data/lib/capybara/spec/session/first_spec.rb +1 -1
- data/lib/capybara/spec/session/has_css_spec.rb +7 -0
- data/lib/capybara/spec/session/has_text_spec.rb +6 -0
- data/lib/capybara/spec/session/save_screenshot_spec.rb +11 -0
- data/lib/capybara/spec/session/select_spec.rb +0 -5
- data/lib/capybara/spec/test_app.rb +8 -3
- data/lib/capybara/version.rb +1 -1
- data/lib/capybara/window.rb +1 -1
- data/spec/minitest_spec_spec.rb +1 -0
- data/spec/selector_spec.rb +12 -6
- data/spec/selenium_spec_firefox.rb +0 -3
- data/spec/selenium_spec_firefox_remote.rb +0 -3
- data/spec/selenium_spec_ie.rb +3 -1
- data/spec/server_spec.rb +1 -1
- data/spec/shared_selenium_session.rb +1 -1
- data/spec/spec_helper.rb +9 -2
- 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
|
@@ -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
|
-
|
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
|
-
@
|
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] =
|
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
|
-
|
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
|
238
|
-
|
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
|
242
|
-
@
|
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
|
-
@
|
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
|
422
|
-
|
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
|
-
|
431
|
-
|
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 #{
|
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
|
486
|
-
|
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
|
-
|
494
|
-
|
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
|