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