capybara 3.12.0 → 3.13.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 +13 -0
- data/README.md +13 -3
- data/lib/capybara.rb +8 -4
- data/lib/capybara/config.rb +3 -1
- data/lib/capybara/driver/base.rb +2 -2
- data/lib/capybara/driver/node.rb +11 -2
- data/lib/capybara/minitest.rb +3 -3
- data/lib/capybara/minitest/spec.rb +10 -3
- data/lib/capybara/node/actions.rb +4 -0
- data/lib/capybara/node/base.rb +13 -5
- data/lib/capybara/node/document.rb +12 -0
- data/lib/capybara/node/element.rb +37 -0
- data/lib/capybara/node/finders.rb +1 -0
- data/lib/capybara/node/matchers.rb +19 -4
- data/lib/capybara/node/simple.rb +7 -2
- data/lib/capybara/queries/selector_query.rb +93 -9
- data/lib/capybara/rspec/matchers.rb +11 -3
- data/lib/capybara/rspec/matchers/become_closed.rb +1 -1
- data/lib/capybara/rspec/matchers/match_style.rb +38 -0
- data/lib/capybara/selector.rb +30 -30
- data/lib/capybara/selector/selector.rb +47 -3
- data/lib/capybara/selenium/driver.rb +12 -11
- data/lib/capybara/selenium/driver_specializations/chrome_driver.rb +3 -3
- data/lib/capybara/selenium/driver_specializations/firefox_driver.rb +2 -2
- data/lib/capybara/selenium/driver_specializations/internet_explorer_driver.rb +13 -0
- data/lib/capybara/selenium/extensions/find.rb +81 -0
- data/lib/capybara/selenium/extensions/scroll.rb +78 -0
- data/lib/capybara/selenium/node.rb +52 -28
- data/lib/capybara/session.rb +13 -1
- data/lib/capybara/spec/public/test.js +1 -0
- data/lib/capybara/spec/session/assert_style_spec.rb +4 -4
- data/lib/capybara/spec/session/attach_file_spec.rb +6 -6
- data/lib/capybara/spec/session/click_button_spec.rb +1 -1
- data/lib/capybara/spec/session/evaluate_script_spec.rb +1 -0
- data/lib/capybara/spec/session/execute_script_spec.rb +1 -0
- data/lib/capybara/spec/session/fill_in_spec.rb +7 -1
- data/lib/capybara/spec/session/find_field_spec.rb +1 -1
- data/lib/capybara/spec/session/find_spec.rb +11 -0
- data/lib/capybara/spec/session/frame/switch_to_frame_spec.rb +0 -1
- data/lib/capybara/spec/session/has_css_spec.rb +32 -0
- data/lib/capybara/spec/session/has_select_spec.rb +2 -2
- data/lib/capybara/spec/session/has_selector_spec.rb +7 -0
- data/lib/capybara/spec/session/has_text_spec.rb +1 -1
- data/lib/capybara/spec/session/matches_style_spec.rb +35 -0
- data/lib/capybara/spec/session/scroll_spec.rb +117 -0
- data/lib/capybara/spec/session/select_spec.rb +5 -0
- data/lib/capybara/spec/spec_helper.rb +1 -0
- data/lib/capybara/spec/views/obscured.erb +1 -1
- data/lib/capybara/spec/views/scroll.erb +20 -0
- data/lib/capybara/spec/views/with_html.erb +1 -1
- data/lib/capybara/version.rb +1 -1
- data/lib/capybara/window.rb +1 -1
- data/spec/basic_node_spec.rb +11 -0
- data/spec/dsl_spec.rb +1 -1
- data/spec/minitest_spec.rb +2 -2
- data/spec/minitest_spec_spec.rb +1 -1
- data/spec/rack_test_spec.rb +1 -0
- data/spec/result_spec.rb +2 -2
- data/spec/selector_spec.rb +11 -1
- data/spec/selenium_spec_firefox.rb +1 -1
- data/spec/selenium_spec_ie.rb +70 -9
- data/spec/session_spec.rb +9 -0
- data/spec/shared_selenium_session.rb +4 -3
- data/spec/spec_helper.rb +2 -0
- metadata +38 -6
- data/lib/capybara/rspec/matchers/have_style.rb +0 -23
- data/lib/capybara/spec/session/has_style_spec.rb +0 -25
@@ -4,7 +4,7 @@ module Capybara
|
|
4
4
|
module Queries
|
5
5
|
class SelectorQuery < Queries::BaseQuery
|
6
6
|
attr_reader :expression, :selector, :locator, :options
|
7
|
-
VALID_KEYS = COUNT_KEYS + %i[text id class visible exact exact_text normalize_ws match wait filter_set]
|
7
|
+
VALID_KEYS = COUNT_KEYS + %i[text id class style visible exact exact_text normalize_ws match wait filter_set]
|
8
8
|
VALID_MATCH = %i[first smart prefer_exact one].freeze
|
9
9
|
|
10
10
|
def initialize(*args,
|
@@ -14,6 +14,7 @@ module Capybara
|
|
14
14
|
**options,
|
15
15
|
&filter_block)
|
16
16
|
@resolved_node = nil
|
17
|
+
@resolved_count = 0
|
17
18
|
@options = options.dup
|
18
19
|
super(@options)
|
19
20
|
self.session_options = session_options
|
@@ -50,9 +51,24 @@ module Capybara
|
|
50
51
|
end
|
51
52
|
desc << " with id #{options[:id]}" if options[:id]
|
52
53
|
desc << " with classes [#{Array(options[:class]).join(',')}]" if options[:class]
|
54
|
+
desc << case options[:style]
|
55
|
+
when String
|
56
|
+
" with style attribute #{options[:style].inspect}"
|
57
|
+
when Regexp
|
58
|
+
" with style attribute matching #{options[:style].inspect}"
|
59
|
+
when Hash
|
60
|
+
" with styles #{options[:style].inspect}"
|
61
|
+
else ''
|
62
|
+
end
|
53
63
|
desc << selector.description(node_filters: show_for[:node], **options)
|
54
64
|
desc << ' that also matches the custom filter block' if @filter_block && show_for[:node]
|
55
65
|
desc << " within #{@resolved_node.inspect}" if describe_within?
|
66
|
+
if locator.is_a?(String) && locator.start_with?('#', './/', '//')
|
67
|
+
unless selector.raw_locator?
|
68
|
+
desc << "\nNote: It appears you may be passing a CSS selector or XPath expression rather than a locator. " \
|
69
|
+
"Please see the documentation for acceptable locator values.\n\n"
|
70
|
+
end
|
71
|
+
end
|
56
72
|
desc
|
57
73
|
end
|
58
74
|
|
@@ -91,7 +107,9 @@ module Capybara
|
|
91
107
|
exact = exact? if exact.nil?
|
92
108
|
expr = apply_expression_filters(@expression)
|
93
109
|
expr = exact ? expr.to_xpath(:exact) : expr.to_s if expr.respond_to?(:to_xpath)
|
94
|
-
filtered_expression(expr)
|
110
|
+
expr = filtered_expression(expr)
|
111
|
+
expr = "(#{expr})[#{xpath_text_conditions}]" if try_text_match_in_expression?
|
112
|
+
expr
|
95
113
|
end
|
96
114
|
|
97
115
|
def css
|
@@ -102,6 +120,7 @@ module Capybara
|
|
102
120
|
def resolve_for(node, exact = nil)
|
103
121
|
applied_filters.clear
|
104
122
|
@resolved_node = node
|
123
|
+
@resolved_count += 1
|
105
124
|
node.synchronize do
|
106
125
|
children = find_nodes_by_selector_format(node, exact).map(&method(:to_element))
|
107
126
|
Capybara::Result.new(children, self)
|
@@ -123,6 +142,26 @@ module Capybara
|
|
123
142
|
|
124
143
|
private
|
125
144
|
|
145
|
+
def text_fragments
|
146
|
+
text = (options[:text] || options[:exact_text])
|
147
|
+
text.is_a?(String) ? text.split : []
|
148
|
+
end
|
149
|
+
|
150
|
+
def xpath_text_conditions
|
151
|
+
(options[:text] || options[:exact_text]).split.map { |txt| XPath.contains(txt) }.reduce(&:&)
|
152
|
+
end
|
153
|
+
|
154
|
+
def try_text_match_in_expression?
|
155
|
+
first_try? &&
|
156
|
+
(options[:text] || options[:exact_text]).is_a?(String) &&
|
157
|
+
@resolved_node&.respond_to?(:session) &&
|
158
|
+
@resolved_node.session.driver.wait?
|
159
|
+
end
|
160
|
+
|
161
|
+
def first_try?
|
162
|
+
@resolved_count == 1
|
163
|
+
end
|
164
|
+
|
126
165
|
def show_for_stage(only_applied)
|
127
166
|
lambda do |stage = :any|
|
128
167
|
!only_applied || (stage == :any ? applied_filters.any? : applied_filters.include?(stage))
|
@@ -141,10 +180,25 @@ module Capybara
|
|
141
180
|
end
|
142
181
|
|
143
182
|
def find_nodes_by_selector_format(node, exact)
|
183
|
+
hints = {}
|
184
|
+
hints[:uses_visibility] = true unless visible == :all
|
185
|
+
hints[:texts] = text_fragments unless selector.format == :xpath
|
186
|
+
hints[:styles] = options[:style] if use_default_style_filter?
|
187
|
+
|
144
188
|
if selector.format == :css
|
145
|
-
node.find_css
|
189
|
+
if node.method(:find_css).arity != 1
|
190
|
+
node.find_css(css, **hints)
|
191
|
+
else
|
192
|
+
node.find_css(css)
|
193
|
+
end
|
194
|
+
elsif selector.format == :xpath
|
195
|
+
if node.method(:find_xpath).arity != 1
|
196
|
+
node.find_xpath(xpath(exact), **hints)
|
197
|
+
else
|
198
|
+
node.find_xpath(xpath(exact))
|
199
|
+
end
|
146
200
|
else
|
147
|
-
|
201
|
+
raise ArgumentError, "Unknown format: #{selector.format}"
|
148
202
|
end
|
149
203
|
end
|
150
204
|
|
@@ -237,6 +291,7 @@ module Capybara
|
|
237
291
|
conditions = {}
|
238
292
|
conditions[:id] = options[:id] if use_default_id_filter?
|
239
293
|
conditions[:class] = options[:class] if use_default_class_filter?
|
294
|
+
conditions[:style] = options[:style] if use_default_style_filter? && !options[:style].is_a?(Hash)
|
240
295
|
builder(expr).add_attribute_conditions(conditions)
|
241
296
|
end
|
242
297
|
|
@@ -248,6 +303,10 @@ module Capybara
|
|
248
303
|
options.key?(:class) && !custom_keys.include?(:class)
|
249
304
|
end
|
250
305
|
|
306
|
+
def use_default_style_filter?
|
307
|
+
options.key?(:style) && !custom_keys.include?(:style)
|
308
|
+
end
|
309
|
+
|
251
310
|
def apply_expression_filters(expression)
|
252
311
|
unapplied_options = options.keys - valid_keys
|
253
312
|
expression_filters.inject(expression) do |expr, (name, ef)|
|
@@ -298,11 +357,12 @@ module Capybara
|
|
298
357
|
def matches_system_filters?(node)
|
299
358
|
applied_filters << :system
|
300
359
|
|
301
|
-
|
360
|
+
matches_visible_filter?(node) &&
|
361
|
+
matches_id_filter?(node) &&
|
302
362
|
matches_class_filter?(node) &&
|
363
|
+
matches_style_filter?(node) &&
|
303
364
|
matches_text_filter?(node) &&
|
304
|
-
matches_exact_text_filter?(node)
|
305
|
-
matches_visible_filter?(node)
|
365
|
+
matches_exact_text_filter?(node)
|
306
366
|
end
|
307
367
|
|
308
368
|
def matches_id_filter?(node)
|
@@ -317,6 +377,28 @@ module Capybara
|
|
317
377
|
node[:class] =~ options[:class]
|
318
378
|
end
|
319
379
|
|
380
|
+
def matches_style_filter?(node)
|
381
|
+
case options[:style]
|
382
|
+
when String, nil
|
383
|
+
true
|
384
|
+
when Regexp
|
385
|
+
node[:style] =~ options[:style]
|
386
|
+
when Hash
|
387
|
+
matches_style?(node, options[:style])
|
388
|
+
end
|
389
|
+
end
|
390
|
+
|
391
|
+
def matches_style?(node, styles)
|
392
|
+
@actual_styles = node.initial_cache[:style] || node.style(*styles.keys)
|
393
|
+
styles.all? do |style, value|
|
394
|
+
if value.is_a? Regexp
|
395
|
+
@actual_styles[style.to_s] =~ value
|
396
|
+
else
|
397
|
+
@actual_styles[style.to_s] == value
|
398
|
+
end
|
399
|
+
end
|
400
|
+
end
|
401
|
+
|
320
402
|
def matches_text_filter?(node)
|
321
403
|
value = options[:text]
|
322
404
|
return true unless value
|
@@ -334,8 +416,10 @@ module Capybara
|
|
334
416
|
|
335
417
|
def matches_visible_filter?(node)
|
336
418
|
case visible
|
337
|
-
when :visible then
|
338
|
-
|
419
|
+
when :visible then
|
420
|
+
node.initial_cache[:visible] || (node.initial_cache[:visible].nil? && node.visible?)
|
421
|
+
when :hidden then
|
422
|
+
(node.initial_cache[:visible] == false) || (node.initial_cache[:visbile].nil? && !node.visible?)
|
339
423
|
else true
|
340
424
|
end
|
341
425
|
end
|
@@ -3,7 +3,7 @@
|
|
3
3
|
require 'capybara/rspec/matchers/have_selector'
|
4
4
|
require 'capybara/rspec/matchers/match_selector'
|
5
5
|
require 'capybara/rspec/matchers/have_current_path'
|
6
|
-
require 'capybara/rspec/matchers/
|
6
|
+
require 'capybara/rspec/matchers/match_style'
|
7
7
|
require 'capybara/rspec/matchers/have_text'
|
8
8
|
require 'capybara/rspec/matchers/have_title'
|
9
9
|
require 'capybara/rspec/matchers/become_closed'
|
@@ -124,9 +124,17 @@ module Capybara
|
|
124
124
|
end
|
125
125
|
|
126
126
|
# RSpec matcher for element style
|
127
|
-
# See {Capybara::Node::Matchers#
|
127
|
+
# See {Capybara::Node::Matchers#matches_style?}
|
128
|
+
def match_style(styles, **options)
|
129
|
+
Matchers::MatchStyle.new(styles, options)
|
130
|
+
end
|
131
|
+
|
132
|
+
##
|
133
|
+
# @deprecated
|
134
|
+
#
|
128
135
|
def have_style(styles, **options)
|
129
|
-
|
136
|
+
warn 'DEPRECATED: have_style is deprecated, please use match_style'
|
137
|
+
match_style(styles, **options)
|
130
138
|
end
|
131
139
|
|
132
140
|
%w[selector css xpath text title current_path link button
|
@@ -0,0 +1,38 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'capybara/rspec/matchers/base'
|
4
|
+
|
5
|
+
module Capybara
|
6
|
+
module RSpecMatchers
|
7
|
+
module Matchers
|
8
|
+
class MatchStyle < WrappedElementMatcher
|
9
|
+
def element_matches?(el)
|
10
|
+
el.assert_matches_style(*@args)
|
11
|
+
end
|
12
|
+
|
13
|
+
def does_not_match?(_actual)
|
14
|
+
raise ArgumentError, 'The match_style matcher does not support use with not_to/should_not'
|
15
|
+
end
|
16
|
+
|
17
|
+
def description
|
18
|
+
'match style'
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
module Capybara
|
26
|
+
module RSpecMatchers
|
27
|
+
module Matchers
|
28
|
+
##
|
29
|
+
# @deprecated
|
30
|
+
class HaveStyle < MatchStyle
|
31
|
+
def initialize(*args, &filter_block)
|
32
|
+
warn 'HaveStyle matcher is deprecated, please use the MatchStyle matcher instead'
|
33
|
+
super
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
data/lib/capybara/selector.rb
CHANGED
@@ -25,24 +25,24 @@ end
|
|
25
25
|
|
26
26
|
# rubocop:disable Metrics/BlockLength
|
27
27
|
|
28
|
-
Capybara.add_selector(:xpath) do
|
28
|
+
Capybara.add_selector(:xpath, locator_type: [:to_xpath, String], raw_locator: true) do
|
29
29
|
xpath { |xpath| xpath }
|
30
30
|
end
|
31
31
|
|
32
|
-
Capybara.add_selector(:css) do
|
32
|
+
Capybara.add_selector(:css, locator_type: [String, Symbol], raw_locator: true) do
|
33
33
|
css { |css| css }
|
34
34
|
end
|
35
35
|
|
36
|
-
Capybara.add_selector(:id) do
|
36
|
+
Capybara.add_selector(:id, locator_type: [String, Symbol, Regexp]) do
|
37
37
|
xpath { |id| builder(XPath.descendant).add_attribute_conditions(id: id) }
|
38
38
|
locator_filter { |node, id| id.is_a?(Regexp) ? node[:id] =~ id : true }
|
39
39
|
end
|
40
40
|
|
41
|
-
Capybara.add_selector(:field) do
|
42
|
-
visible { |options| :hidden if options[:type] == 'hidden' }
|
41
|
+
Capybara.add_selector(:field, locator_type: [String, Symbol]) do
|
42
|
+
visible { |options| :hidden if options[:type].to_s == 'hidden' }
|
43
43
|
xpath do |locator, **options|
|
44
44
|
invalid_types = %w[submit image]
|
45
|
-
invalid_types << 'hidden' unless options[:type] == 'hidden'
|
45
|
+
invalid_types << 'hidden' unless options[:type].to_s == 'hidden'
|
46
46
|
xpath = XPath.descendant(:input, :textarea, :select)[!XPath.attr(:type).one_of(*invalid_types)]
|
47
47
|
locate_field(xpath, locator, options)
|
48
48
|
end
|
@@ -78,7 +78,7 @@ Capybara.add_selector(:field) do
|
|
78
78
|
end
|
79
79
|
end
|
80
80
|
|
81
|
-
Capybara.add_selector(:fieldset) do
|
81
|
+
Capybara.add_selector(:fieldset, locator_type: [String, Symbol]) do
|
82
82
|
xpath do |locator, legend: nil, **|
|
83
83
|
locator_matchers = (XPath.attr(:id) == locator.to_s) | XPath.child(:legend)[XPath.string.n.is(locator.to_s)]
|
84
84
|
locator_matchers |= XPath.attr(test_id) == locator.to_s if test_id
|
@@ -90,7 +90,7 @@ Capybara.add_selector(:fieldset) do
|
|
90
90
|
node_filter(:disabled, :boolean) { |node, value| !(value ^ node.disabled?) }
|
91
91
|
end
|
92
92
|
|
93
|
-
Capybara.add_selector(:link) do
|
93
|
+
Capybara.add_selector(:link, locator_type: [String, Symbol]) do
|
94
94
|
xpath do |locator, href: true, alt: nil, title: nil, **|
|
95
95
|
xpath = builder(XPath.descendant(:a)).add_attribute_conditions(href: href)
|
96
96
|
|
@@ -132,7 +132,7 @@ Capybara.add_selector(:link) do
|
|
132
132
|
end
|
133
133
|
end
|
134
134
|
|
135
|
-
Capybara.add_selector(:button) do
|
135
|
+
Capybara.add_selector(:button, locator_type: [String, Symbol]) do
|
136
136
|
xpath(:value, :title, :type) do |locator, **options|
|
137
137
|
input_btn_xpath = XPath.descendant(:input)[XPath.attr(:type).one_of('submit', 'reset', 'image', 'button')]
|
138
138
|
btn_xpath = XPath.descendant(:button)
|
@@ -166,7 +166,7 @@ Capybara.add_selector(:button) do
|
|
166
166
|
end
|
167
167
|
end
|
168
168
|
|
169
|
-
Capybara.add_selector(:link_or_button) do
|
169
|
+
Capybara.add_selector(:link_or_button, locator_type: [String, Symbol]) do
|
170
170
|
label 'link or button'
|
171
171
|
xpath do |locator, **options|
|
172
172
|
self.class.all.values_at(:link, :button).map do |selector|
|
@@ -181,9 +181,8 @@ Capybara.add_selector(:link_or_button) do
|
|
181
181
|
end
|
182
182
|
end
|
183
183
|
|
184
|
-
Capybara.add_selector(:fillable_field) do
|
184
|
+
Capybara.add_selector(:fillable_field, locator_type: [String, Symbol]) do
|
185
185
|
label 'field'
|
186
|
-
|
187
186
|
xpath do |locator, allow_self: nil, **options|
|
188
187
|
xpath = XPath.axis(allow_self ? :"descendant-or-self" : :descendant, :input, :textarea)[
|
189
188
|
!XPath.attr(:type).one_of('submit', 'image', 'radio', 'checkbox', 'hidden', 'file')
|
@@ -215,9 +214,8 @@ Capybara.add_selector(:fillable_field) do
|
|
215
214
|
end
|
216
215
|
end
|
217
216
|
|
218
|
-
Capybara.add_selector(:radio_button) do
|
217
|
+
Capybara.add_selector(:radio_button, locator_type: [String, Symbol]) do
|
219
218
|
label 'radio button'
|
220
|
-
|
221
219
|
xpath do |locator, allow_self: nil, **options|
|
222
220
|
xpath = XPath.axis(allow_self ? :"descendant-or-self" : :descendant, :input)[
|
223
221
|
XPath.attr(:type) == 'radio'
|
@@ -240,7 +238,7 @@ Capybara.add_selector(:radio_button) do
|
|
240
238
|
end
|
241
239
|
end
|
242
240
|
|
243
|
-
Capybara.add_selector(:checkbox) do
|
241
|
+
Capybara.add_selector(:checkbox, locator_type: [String, Symbol]) do
|
244
242
|
xpath do |locator, allow_self: nil, **options|
|
245
243
|
xpath = XPath.axis(allow_self ? :"descendant-or-self" : :descendant, :input)[
|
246
244
|
XPath.attr(:type) == 'checkbox'
|
@@ -263,7 +261,7 @@ Capybara.add_selector(:checkbox) do
|
|
263
261
|
end
|
264
262
|
end
|
265
263
|
|
266
|
-
Capybara.add_selector(:select) do
|
264
|
+
Capybara.add_selector(:select, locator_type: [String, Symbol]) do
|
267
265
|
label 'select box'
|
268
266
|
|
269
267
|
xpath do |locator, **options|
|
@@ -320,7 +318,7 @@ Capybara.add_selector(:select) do
|
|
320
318
|
end
|
321
319
|
end
|
322
320
|
|
323
|
-
Capybara.add_selector(:datalist_input) do
|
321
|
+
Capybara.add_selector(:datalist_input, locator_type: [String, Symbol]) do
|
324
322
|
label 'input box with datalist completion'
|
325
323
|
|
326
324
|
xpath do |locator, **options|
|
@@ -355,7 +353,7 @@ Capybara.add_selector(:datalist_input) do
|
|
355
353
|
end
|
356
354
|
end
|
357
355
|
|
358
|
-
Capybara.add_selector(:option) do
|
356
|
+
Capybara.add_selector(:option, locator_type: [String, Symbol]) do
|
359
357
|
xpath do |locator|
|
360
358
|
xpath = XPath.descendant(:option)
|
361
359
|
xpath = xpath[XPath.string.n.is(locator.to_s)] unless locator.nil?
|
@@ -373,7 +371,7 @@ Capybara.add_selector(:option) do
|
|
373
371
|
end
|
374
372
|
end
|
375
373
|
|
376
|
-
Capybara.add_selector(:datalist_option) do
|
374
|
+
Capybara.add_selector(:datalist_option, locator_type: [String, Symbol]) do
|
377
375
|
label 'datalist option'
|
378
376
|
visible(:all)
|
379
377
|
|
@@ -390,7 +388,7 @@ Capybara.add_selector(:datalist_option) do
|
|
390
388
|
end
|
391
389
|
end
|
392
390
|
|
393
|
-
Capybara.add_selector(:file_field) do
|
391
|
+
Capybara.add_selector(:file_field, locator_type: [String, Symbol]) do
|
394
392
|
label 'file field'
|
395
393
|
xpath do |locator, allow_self: nil, **options|
|
396
394
|
xpath = XPath.axis(allow_self ? :"descendant-or-self" : :descendant, :input)[
|
@@ -404,7 +402,7 @@ Capybara.add_selector(:file_field) do
|
|
404
402
|
describe_expression_filters
|
405
403
|
end
|
406
404
|
|
407
|
-
Capybara.add_selector(:label) do
|
405
|
+
Capybara.add_selector(:label, locator_type: [String, Symbol]) do
|
408
406
|
label 'label'
|
409
407
|
xpath(:for) do |locator, options|
|
410
408
|
xpath = XPath.descendant(:label)
|
@@ -413,12 +411,14 @@ Capybara.add_selector(:label) do
|
|
413
411
|
locator_matchers |= XPath.attr(test_id) == locator if test_id
|
414
412
|
xpath = xpath[locator_matchers]
|
415
413
|
end
|
416
|
-
if options.key?(:for)
|
417
|
-
|
418
|
-
|
419
|
-
|
420
|
-
|
421
|
-
|
414
|
+
if options.key?(:for)
|
415
|
+
if (for_option = options[:for].is_a?(Capybara::Node::Element) ? options[:for][:id] : options[:for])
|
416
|
+
with_attr = XPath.attr(:for) == for_option.to_s
|
417
|
+
labelable_elements = %i[button input keygen meter output progress select textarea]
|
418
|
+
wrapped = !XPath.attr(:for) &
|
419
|
+
XPath.descendant(*labelable_elements)[XPath.attr(:id) == for_option.to_s]
|
420
|
+
xpath = xpath[with_attr | wrapped]
|
421
|
+
end
|
422
422
|
end
|
423
423
|
xpath
|
424
424
|
end
|
@@ -442,7 +442,7 @@ Capybara.add_selector(:label) do
|
|
442
442
|
end
|
443
443
|
end
|
444
444
|
|
445
|
-
Capybara.add_selector(:table) do
|
445
|
+
Capybara.add_selector(:table, locator_type: [String, Symbol]) do
|
446
446
|
xpath do |locator, caption: nil, **|
|
447
447
|
xpath = XPath.descendant(:table)
|
448
448
|
unless locator.nil?
|
@@ -459,7 +459,7 @@ Capybara.add_selector(:table) do
|
|
459
459
|
end
|
460
460
|
end
|
461
461
|
|
462
|
-
Capybara.add_selector(:frame) do
|
462
|
+
Capybara.add_selector(:frame, locator_type: [String, Symbol]) do
|
463
463
|
xpath do |locator, name: nil, **|
|
464
464
|
xpath = XPath.descendant(:iframe).union(XPath.descendant(:frame))
|
465
465
|
unless locator.nil?
|
@@ -475,7 +475,7 @@ Capybara.add_selector(:frame) do
|
|
475
475
|
end
|
476
476
|
end
|
477
477
|
|
478
|
-
Capybara.add_selector(:element) do
|
478
|
+
Capybara.add_selector(:element, locator_type: [String, Symbol]) do
|
479
479
|
xpath do |locator, **|
|
480
480
|
XPath.descendant.where(locator ? XPath.local_name == locator.to_s : nil)
|
481
481
|
end
|