capybara 2.18.0 → 3.0.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 +55 -1
- data/README.md +18 -17
- data/lib/capybara/config.rb +11 -58
- data/lib/capybara/cucumber.rb +2 -3
- data/lib/capybara/driver/base.rb +15 -16
- data/lib/capybara/driver/node.rb +5 -4
- data/lib/capybara/dsl.rb +1 -0
- data/lib/capybara/helpers.rb +19 -29
- data/lib/capybara/minitest/spec.rb +15 -14
- data/lib/capybara/minitest.rb +139 -138
- data/lib/capybara/node/actions.rb +60 -81
- data/lib/capybara/node/base.rb +11 -18
- data/lib/capybara/node/document.rb +2 -2
- data/lib/capybara/node/document_matchers.rb +8 -8
- data/lib/capybara/node/element.rb +30 -40
- data/lib/capybara/node/finders.rb +62 -70
- data/lib/capybara/node/matchers.rb +50 -71
- data/lib/capybara/node/simple.rb +11 -17
- data/lib/capybara/queries/ancestor_query.rb +11 -7
- data/lib/capybara/queries/base_query.rb +22 -18
- data/lib/capybara/queries/current_path_query.rb +8 -24
- data/lib/capybara/queries/match_query.rb +3 -7
- data/lib/capybara/queries/selector_query.rb +92 -95
- data/lib/capybara/queries/sibling_query.rb +4 -4
- data/lib/capybara/queries/text_query.rb +35 -35
- data/lib/capybara/queries/title_query.rb +8 -11
- data/lib/capybara/rack_test/browser.rb +15 -18
- data/lib/capybara/rack_test/css_handlers.rb +6 -4
- data/lib/capybara/rack_test/driver.rb +6 -10
- data/lib/capybara/rack_test/form.rb +50 -40
- data/lib/capybara/rack_test/node.rb +93 -63
- data/lib/capybara/rails.rb +2 -6
- data/lib/capybara/result.rb +22 -22
- data/lib/capybara/rspec/compound.rb +5 -10
- data/lib/capybara/rspec/features.rb +17 -48
- data/lib/capybara/rspec/matcher_proxies.rb +31 -15
- data/lib/capybara/rspec/matchers.rb +70 -61
- data/lib/capybara/rspec.rb +5 -10
- data/lib/capybara/selector/css.rb +6 -11
- data/lib/capybara/selector/filter.rb +1 -17
- data/lib/capybara/selector/filter_set.rb +18 -15
- data/lib/capybara/selector/filters/base.rb +7 -6
- data/lib/capybara/selector/filters/expression_filter.rb +6 -23
- data/lib/capybara/selector/filters/node_filter.rb +2 -12
- data/lib/capybara/selector/selector.rb +28 -34
- data/lib/capybara/selector.rb +129 -117
- data/lib/capybara/selenium/driver.rb +131 -125
- data/lib/capybara/selenium/node.rb +197 -115
- data/lib/capybara/server.rb +3 -2
- data/lib/capybara/session/config.rb +47 -67
- data/lib/capybara/session/matchers.rb +8 -7
- data/lib/capybara/session.rb +138 -224
- data/lib/capybara/spec/public/test.js +25 -4
- data/lib/capybara/spec/session/accept_alert_spec.rb +1 -0
- data/lib/capybara/spec/session/accept_confirm_spec.rb +3 -2
- data/lib/capybara/spec/session/accept_prompt_spec.rb +1 -0
- data/lib/capybara/spec/session/all_spec.rb +31 -18
- data/lib/capybara/spec/session/ancestor_spec.rb +6 -8
- data/lib/capybara/spec/session/assert_all_of_selectors_spec.rb +6 -5
- data/lib/capybara/spec/session/assert_current_path.rb +12 -11
- data/lib/capybara/spec/session/assert_selector.rb +1 -0
- data/lib/capybara/spec/session/assert_text.rb +23 -23
- data/lib/capybara/spec/session/assert_title.rb +13 -3
- data/lib/capybara/spec/session/attach_file_spec.rb +51 -30
- data/lib/capybara/spec/session/body_spec.rb +1 -0
- data/lib/capybara/spec/session/check_spec.rb +7 -6
- data/lib/capybara/spec/session/choose_spec.rb +5 -4
- data/lib/capybara/spec/session/click_button_spec.rb +24 -32
- data/lib/capybara/spec/session/click_link_or_button_spec.rb +8 -7
- data/lib/capybara/spec/session/click_link_spec.rb +8 -7
- data/lib/capybara/spec/session/current_scope_spec.rb +4 -3
- data/lib/capybara/spec/session/current_url_spec.rb +17 -6
- data/lib/capybara/spec/session/dismiss_confirm_spec.rb +1 -1
- data/lib/capybara/spec/session/dismiss_prompt_spec.rb +1 -0
- data/lib/capybara/spec/session/element/assert_match_selector.rb +1 -1
- data/lib/capybara/spec/session/element/match_xpath_spec.rb +1 -1
- data/lib/capybara/spec/session/element/matches_selector_spec.rb +5 -5
- data/lib/capybara/spec/session/evaluate_async_script_spec.rb +3 -2
- data/lib/capybara/spec/session/evaluate_script_spec.rb +4 -3
- data/lib/capybara/spec/session/execute_script_spec.rb +4 -3
- data/lib/capybara/spec/session/fill_in_spec.rb +30 -5
- data/lib/capybara/spec/session/find_button_spec.rb +4 -3
- data/lib/capybara/spec/session/find_by_id_spec.rb +2 -1
- data/lib/capybara/spec/session/find_field_spec.rb +9 -15
- data/lib/capybara/spec/session/find_link_spec.rb +6 -5
- data/lib/capybara/spec/session/find_spec.rb +37 -31
- data/lib/capybara/spec/session/first_spec.rb +60 -33
- data/lib/capybara/spec/session/frame/frame_title_spec.rb +23 -0
- data/lib/capybara/spec/session/frame/frame_url_spec.rb +23 -0
- data/lib/capybara/spec/session/frame/switch_to_frame_spec.rb +2 -1
- data/lib/capybara/spec/session/frame/within_frame_spec.rb +9 -16
- data/lib/capybara/spec/session/go_back_spec.rb +1 -0
- data/lib/capybara/spec/session/go_forward_spec.rb +1 -0
- data/lib/capybara/spec/session/has_all_selectors_spec.rb +15 -15
- data/lib/capybara/spec/session/has_button_spec.rb +2 -1
- data/lib/capybara/spec/session/has_css_spec.rb +3 -2
- data/lib/capybara/spec/session/has_current_path_spec.rb +12 -28
- data/lib/capybara/spec/session/has_field_spec.rb +4 -3
- data/lib/capybara/spec/session/has_link_spec.rb +1 -0
- data/lib/capybara/spec/session/has_none_selectors_spec.rb +17 -17
- data/lib/capybara/spec/session/has_select_spec.rb +30 -29
- data/lib/capybara/spec/session/has_selector_spec.rb +5 -4
- data/lib/capybara/spec/session/has_table_spec.rb +2 -1
- data/lib/capybara/spec/session/has_text_spec.rb +9 -13
- data/lib/capybara/spec/session/has_title_spec.rb +1 -0
- data/lib/capybara/spec/session/has_xpath_spec.rb +1 -0
- data/lib/capybara/spec/session/headers.rb +2 -1
- data/lib/capybara/spec/session/html_spec.rb +1 -0
- data/lib/capybara/spec/session/node_spec.rb +91 -56
- data/lib/capybara/spec/session/node_wrapper_spec.rb +36 -0
- data/lib/capybara/spec/session/refresh_spec.rb +6 -2
- data/lib/capybara/spec/session/reset_session_spec.rb +19 -0
- data/lib/capybara/spec/session/response_code.rb +1 -0
- data/lib/capybara/spec/session/save_and_open_page_spec.rb +1 -0
- data/lib/capybara/spec/session/save_and_open_screenshot_spec.rb +6 -11
- data/lib/capybara/spec/session/save_page_spec.rb +1 -17
- data/lib/capybara/spec/session/save_screenshot_spec.rb +3 -3
- data/lib/capybara/spec/session/select_spec.rb +20 -20
- data/lib/capybara/spec/session/selectors_spec.rb +2 -2
- data/lib/capybara/spec/session/sibling_spec.rb +1 -1
- data/lib/capybara/spec/session/text_spec.rb +17 -3
- data/lib/capybara/spec/session/title_spec.rb +11 -1
- data/lib/capybara/spec/session/uncheck_spec.rb +4 -3
- data/lib/capybara/spec/session/unselect_spec.rb +6 -5
- data/lib/capybara/spec/session/visit_spec.rb +9 -3
- data/lib/capybara/spec/session/window/become_closed_spec.rb +2 -1
- data/lib/capybara/spec/session/window/current_window_spec.rb +1 -0
- data/lib/capybara/spec/session/window/open_new_window_spec.rb +1 -0
- data/lib/capybara/spec/session/window/switch_to_window_spec.rb +2 -1
- data/lib/capybara/spec/session/window/window_opened_by_spec.rb +2 -1
- data/lib/capybara/spec/session/window/window_spec.rb +12 -12
- data/lib/capybara/spec/session/window/windows_spec.rb +2 -3
- data/lib/capybara/spec/session/window/within_window_spec.rb +15 -71
- data/lib/capybara/spec/session/within_spec.rb +1 -0
- data/lib/capybara/spec/spec_helper.rb +34 -18
- data/lib/capybara/spec/test_app.rb +17 -9
- data/lib/capybara/spec/views/form.erb +7 -0
- data/lib/capybara/spec/views/with_html.erb +23 -1
- data/lib/capybara/spec/views/within_frames.erb +4 -1
- data/lib/capybara/version.rb +2 -1
- data/lib/capybara/window.rb +6 -10
- data/lib/capybara.rb +28 -25
- data/spec/basic_node_spec.rb +1 -0
- data/spec/capybara_spec.rb +11 -50
- data/spec/dsl_spec.rb +5 -13
- data/spec/filter_set_spec.rb +5 -4
- data/spec/fixtures/selenium_driver_rspec_failure.rb +2 -1
- data/spec/fixtures/selenium_driver_rspec_success.rb +3 -2
- data/spec/minitest_spec.rb +4 -3
- data/spec/minitest_spec_spec.rb +3 -2
- data/spec/per_session_config_spec.rb +9 -8
- data/spec/rack_test_spec.rb +21 -20
- data/spec/result_spec.rb +17 -16
- data/spec/rspec/features_spec.rb +17 -14
- data/spec/rspec/scenarios_spec.rb +5 -7
- data/spec/rspec/shared_spec_matchers.rb +96 -99
- data/spec/rspec/views_spec.rb +2 -1
- data/spec/rspec_matchers_spec.rb +18 -2
- data/spec/rspec_spec.rb +11 -15
- data/spec/selector_spec.rb +5 -6
- data/spec/selenium_spec_chrome.rb +9 -4
- data/spec/selenium_spec_edge.rb +27 -0
- data/spec/selenium_spec_ie.rb +31 -0
- data/spec/selenium_spec_marionette.rb +28 -12
- data/spec/server_spec.rb +33 -33
- data/spec/session_spec.rb +2 -1
- data/spec/shared_selenium_session.rb +36 -22
- data/spec/spec_helper.rb +3 -6
- metadata +68 -85
- data/lib/capybara/query.rb +0 -7
- data/spec/selenium_spec_firefox.rb +0 -68
@@ -1,9 +1,10 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
+
|
2
3
|
require 'capybara/selector/filter_set'
|
3
4
|
require 'capybara/selector/css'
|
4
5
|
require 'xpath'
|
5
6
|
|
6
|
-
#Patch XPath to allow a nil condition in where
|
7
|
+
# Patch XPath to allow a nil condition in where
|
7
8
|
module XPath
|
8
9
|
class Renderer
|
9
10
|
undef :where if method_defined?(:where)
|
@@ -12,7 +13,7 @@ module XPath
|
|
12
13
|
if !condition.empty?
|
13
14
|
"#{on}[#{condition}]"
|
14
15
|
else
|
15
|
-
|
16
|
+
on.to_s
|
16
17
|
end
|
17
18
|
end
|
18
19
|
end
|
@@ -20,12 +21,11 @@ end
|
|
20
21
|
|
21
22
|
module Capybara
|
22
23
|
class Selector
|
23
|
-
|
24
24
|
attr_reader :name, :format
|
25
25
|
|
26
26
|
class << self
|
27
27
|
def all
|
28
|
-
@selectors ||= {}
|
28
|
+
@selectors ||= {} # rubocop:disable Naming/MemoizedInstanceVariableName
|
29
29
|
end
|
30
30
|
|
31
31
|
def add(name, &block)
|
@@ -43,7 +43,7 @@ module Capybara
|
|
43
43
|
|
44
44
|
def initialize(name, &block)
|
45
45
|
@name = name
|
46
|
-
@filter_set = FilterSet.add(name){}
|
46
|
+
@filter_set = FilterSet.add(name) {}
|
47
47
|
@match = nil
|
48
48
|
@label = nil
|
49
49
|
@failure_message = nil
|
@@ -134,7 +134,7 @@ module Capybara
|
|
134
134
|
# @overload label()
|
135
135
|
# @return [String] The currently set label
|
136
136
|
#
|
137
|
-
def label(label=nil)
|
137
|
+
def label(label = nil)
|
138
138
|
@label = label if label
|
139
139
|
@label
|
140
140
|
end
|
@@ -146,13 +146,12 @@ module Capybara
|
|
146
146
|
# @param [Hash] options The options of the query used to generate the description
|
147
147
|
# @return [String] Description of the selector when used with the options passed
|
148
148
|
#
|
149
|
-
def description(options
|
149
|
+
def description(**options)
|
150
150
|
@filter_set.description(options)
|
151
151
|
end
|
152
152
|
|
153
|
-
def call(locator, options
|
153
|
+
def call(locator, **options)
|
154
154
|
if format
|
155
|
-
# @expression.call(locator, options.select {|k,v| @expression_filters.include?(k)})
|
156
155
|
@expression.call(locator, options)
|
157
156
|
else
|
158
157
|
warn "Selector has no format"
|
@@ -185,15 +184,11 @@ module Capybara
|
|
185
184
|
# @option options :skip_if Value of the filter that will cause it to be skipped
|
186
185
|
#
|
187
186
|
def filter(name, *types_and_options, &block)
|
188
|
-
|
189
|
-
types_and_options.each { |k| options[k] = true }
|
190
|
-
custom_filters[name] = Filters::NodeFilter.new(name, block, options)
|
187
|
+
add_filter(name, Filters::NodeFilter, *types_and_options, &block)
|
191
188
|
end
|
192
189
|
|
193
190
|
def expression_filter(name, *types_and_options, &block)
|
194
|
-
|
195
|
-
types_and_options.each { |k| options[k] = true }
|
196
|
-
custom_filters[name] = Filters::ExpressionFilter.new(name, block, options)
|
191
|
+
add_filter(name, Filters::ExpressionFilter, *types_and_options, &block)
|
197
192
|
end
|
198
193
|
|
199
194
|
def filter_set(name, filters_to_use = nil)
|
@@ -205,7 +200,7 @@ module Capybara
|
|
205
200
|
f_set.descriptions.each { |desc| @filter_set.describe(&desc) }
|
206
201
|
end
|
207
202
|
|
208
|
-
def describe
|
203
|
+
def describe(&block)
|
209
204
|
@filter_set.describe(&block)
|
210
205
|
end
|
211
206
|
|
@@ -230,17 +225,22 @@ module Capybara
|
|
230
225
|
end
|
231
226
|
end
|
232
227
|
|
233
|
-
|
228
|
+
private
|
229
|
+
|
230
|
+
def add_filter(name, filter_class, *types, **options, &block)
|
231
|
+
types.each { |k| options[k] = true }
|
232
|
+
custom_filters[name] = filter_class.new(name, block, options)
|
233
|
+
end
|
234
234
|
|
235
|
-
def locate_field(xpath, locator,
|
236
|
-
locate_xpath = xpath #
|
235
|
+
def locate_field(xpath, locator, enable_aria_label: false, **_options)
|
236
|
+
locate_xpath = xpath # Need to save original xpath for the label wrap
|
237
237
|
if locator
|
238
238
|
locator = locator.to_s
|
239
|
-
attr_matchers =
|
240
|
-
XPath.attr(:name)
|
241
|
-
XPath.attr(:placeholder)
|
242
|
-
XPath.attr(:id)
|
243
|
-
attr_matchers
|
239
|
+
attr_matchers = [XPath.attr(:id) == locator,
|
240
|
+
XPath.attr(:name) == locator,
|
241
|
+
XPath.attr(:placeholder) == locator,
|
242
|
+
XPath.attr(:id) == XPath.anywhere(:label)[XPath.string.n.is(locator)].attr(:for)].reduce(:|)
|
243
|
+
attr_matchers |= XPath.attr(:'aria-label').is(locator) if enable_aria_label
|
244
244
|
|
245
245
|
locate_xpath = locate_xpath[attr_matchers]
|
246
246
|
locate_xpath = locate_xpath.union(XPath.descendant(:label)[XPath.string.n.is(locator)].descendant(xpath))
|
@@ -250,8 +250,8 @@ module Capybara
|
|
250
250
|
locate_xpath
|
251
251
|
end
|
252
252
|
|
253
|
-
def describe_all_expression_filters(opts
|
254
|
-
expression_filters.map { |ef| " with #{ef} #{opts[ef]}" if opts.
|
253
|
+
def describe_all_expression_filters(**opts)
|
254
|
+
expression_filters.map { |ef| " with #{ef} #{opts[ef]}" if opts.key?(ef) }.join
|
255
255
|
end
|
256
256
|
|
257
257
|
def find_by_attr(attribute, value)
|
@@ -259,18 +259,12 @@ module Capybara
|
|
259
259
|
if respond_to?(finder_name, true)
|
260
260
|
send(finder_name, value)
|
261
261
|
else
|
262
|
-
value ? XPath.attr(attribute)
|
262
|
+
value ? XPath.attr(attribute) == value : nil
|
263
263
|
end
|
264
264
|
end
|
265
265
|
|
266
266
|
def find_by_class_attr(classes)
|
267
|
-
|
268
|
-
Array(classes).map do |klass|
|
269
|
-
"contains(concat(' ',normalize-space(@class),' '),' #{klass} ')"
|
270
|
-
end.join(" and ").to_sym
|
271
|
-
else
|
272
|
-
nil
|
273
|
-
end
|
267
|
+
Array(classes).map { |klass| XPath.attr(:class).contains_word(klass) }.reduce(:&)
|
274
268
|
end
|
275
269
|
end
|
276
270
|
end
|
data/lib/capybara/selector.rb
CHANGED
@@ -1,27 +1,31 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
+
|
2
3
|
require 'capybara/selector/selector'
|
3
4
|
Capybara::Selector::FilterSet.add(:_field) do
|
4
|
-
filter(:checked, :boolean) { |node, value|
|
5
|
+
filter(:checked, :boolean) { |node, value| !(value ^ node.checked?) }
|
5
6
|
filter(:unchecked, :boolean) { |node, value| (value ^ node.checked?) }
|
6
|
-
filter(:disabled, :boolean, default: false, skip_if: :all) { |node, value|
|
7
|
+
filter(:disabled, :boolean, default: false, skip_if: :all) { |node, value| !(value ^ node.disabled?) }
|
7
8
|
filter(:multiple, :boolean) { |node, value| !(value ^ node.multiple?) }
|
8
9
|
|
9
|
-
expression_filter(:name) { |xpath, val| xpath[XPath.attr(:name)
|
10
|
-
expression_filter(:placeholder) { |xpath, val| xpath[XPath.attr(:placeholder)
|
10
|
+
expression_filter(:name) { |xpath, val| xpath[XPath.attr(:name) == val] }
|
11
|
+
expression_filter(:placeholder) { |xpath, val| xpath[XPath.attr(:placeholder) == val] }
|
11
12
|
|
12
|
-
describe do |
|
13
|
-
desc, states =
|
14
|
-
states << 'checked' if
|
15
|
-
states << 'not checked' if
|
16
|
-
states << 'disabled' if
|
17
|
-
states << 'not disabled' if
|
13
|
+
describe do |checked: nil, unchecked: nil, disabled: nil, multiple: nil, **_options|
|
14
|
+
desc, states = "".dup, []
|
15
|
+
states << 'checked' if checked || (unchecked == false)
|
16
|
+
states << 'not checked' if unchecked || (checked == false)
|
17
|
+
states << 'disabled' if disabled == true
|
18
|
+
states << 'not disabled' if disabled == false
|
18
19
|
desc << " that is #{states.join(' and ')}" unless states.empty?
|
19
|
-
desc << " with the multiple attribute" if
|
20
|
-
desc << " without the multiple attribute" if
|
20
|
+
desc << " with the multiple attribute" if multiple == true
|
21
|
+
desc << " without the multiple attribute" if multiple == false
|
21
22
|
desc
|
22
23
|
end
|
23
24
|
end
|
24
25
|
|
26
|
+
# rubocop:disable Metrics/BlockLength
|
27
|
+
# rubocop:disable Metrics/ParameterLists
|
28
|
+
|
25
29
|
##
|
26
30
|
#
|
27
31
|
# Select elements by XPath expression
|
@@ -69,31 +73,31 @@ end
|
|
69
73
|
# @filter [Boolean] :disabled Match disabled field?
|
70
74
|
# @filter [Boolean] :multiple Match fields that accept multiple values
|
71
75
|
Capybara.add_selector(:field) do
|
72
|
-
xpath do |locator, options|
|
73
|
-
xpath = XPath.descendant(:input, :textarea, :select)[
|
76
|
+
xpath do |locator, **options|
|
77
|
+
xpath = XPath.descendant(:input, :textarea, :select)[!XPath.attr(:type).one_of('submit', 'image', 'hidden')]
|
74
78
|
locate_field(xpath, locator, options)
|
75
79
|
end
|
76
80
|
|
77
81
|
expression_filter(:type) do |expr, type|
|
78
82
|
type = type.to_s
|
79
|
-
if [
|
80
|
-
expr.
|
83
|
+
if %w[textarea select].include?(type)
|
84
|
+
expr.self(type.to_sym)
|
81
85
|
else
|
82
|
-
expr[XPath.attr(:type)
|
86
|
+
expr[XPath.attr(:type) == type]
|
83
87
|
end
|
84
88
|
end
|
85
89
|
|
86
90
|
filter_set(:_field) # checked/unchecked/disabled/multiple/name/placeholder
|
87
91
|
|
88
|
-
filter(:readonly, :boolean) { |node, value|
|
92
|
+
filter(:readonly, :boolean) { |node, value| !(value ^ node.readonly?) }
|
89
93
|
filter(:with) do |node, with|
|
90
94
|
with.is_a?(Regexp) ? node.value =~ with : node.value == with.to_s
|
91
95
|
end
|
92
|
-
describe do |options|
|
93
|
-
desc =
|
94
|
-
(expression_filters.keys - [:type]).each { |ef| desc << " with #{ef} #{options[ef]}" if options.
|
95
|
-
desc << " of type #{
|
96
|
-
desc << " with value #{options[:with].to_s.inspect}" if options.
|
96
|
+
describe do |type: nil, **options|
|
97
|
+
desc = "".dup
|
98
|
+
(expression_filters.keys - [:type]).each { |ef| desc << " with #{ef} #{options[ef]}" if options.key?(ef) }
|
99
|
+
desc << " of type #{type.inspect}" if type
|
100
|
+
desc << " with value #{options[:with].to_s.inspect}" if options.key?(:with)
|
97
101
|
desc
|
98
102
|
end
|
99
103
|
end
|
@@ -109,10 +113,10 @@ end
|
|
109
113
|
# @filter [String, Array<String>] :class Matches the class(es) provided
|
110
114
|
#
|
111
115
|
Capybara.add_selector(:fieldset) do
|
112
|
-
xpath(:legend) do |locator,
|
116
|
+
xpath(:legend) do |locator, legend: nil, **_options|
|
113
117
|
xpath = XPath.descendant(:fieldset)
|
114
|
-
xpath = xpath[XPath.attr(:id)
|
115
|
-
xpath = xpath[XPath.child(:legend)[XPath.string.n.is(
|
118
|
+
xpath = xpath[(XPath.attr(:id) == locator.to_s) | XPath.child(:legend)[XPath.string.n.is(locator.to_s)]] unless locator.nil?
|
119
|
+
xpath = xpath[XPath.child(:legend)[XPath.string.n.is(legend)]] if legend
|
116
120
|
xpath
|
117
121
|
end
|
118
122
|
end
|
@@ -130,24 +134,24 @@ end
|
|
130
134
|
# @filter [String, Regexp,nil] :href Matches the normalized href of the link, if nil will find <a> elements with no href attribute
|
131
135
|
#
|
132
136
|
Capybara.add_selector(:link) do
|
133
|
-
xpath(:title, :alt) do |locator,
|
137
|
+
xpath(:title, :alt) do |locator, href: true, enable_aria_label: false, alt: nil, title: nil, **_options|
|
134
138
|
xpath = XPath.descendant(:a)
|
135
|
-
xpath = if
|
136
|
-
xpath[
|
139
|
+
xpath = if href.nil?
|
140
|
+
xpath[!XPath.attr(:href)]
|
137
141
|
else
|
138
142
|
xpath[XPath.attr(:href)]
|
139
143
|
end
|
140
144
|
unless locator.nil?
|
141
145
|
locator = locator.to_s
|
142
|
-
matchers = XPath.attr(:id)
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
matchers
|
146
|
+
matchers = [XPath.attr(:id) == locator,
|
147
|
+
XPath.string.n.is(locator),
|
148
|
+
XPath.attr(:title).is(locator),
|
149
|
+
XPath.descendant(:img)[XPath.attr(:alt).is(locator)]].reduce(:|)
|
150
|
+
matchers |= XPath.attr(:'aria-label').is(locator) if enable_aria_label
|
147
151
|
xpath = xpath[matchers]
|
148
152
|
end
|
149
|
-
xpath =
|
150
|
-
xpath = xpath[XPath.descendant(:img)[XPath.attr(:alt)
|
153
|
+
xpath = xpath[find_by_attr(:title, title)]
|
154
|
+
xpath = xpath[XPath.descendant(:img)[XPath.attr(:alt) == alt]] if alt
|
151
155
|
xpath
|
152
156
|
end
|
153
157
|
|
@@ -158,12 +162,12 @@ Capybara.add_selector(:link) do
|
|
158
162
|
when Regexp
|
159
163
|
node[:href].match href
|
160
164
|
else
|
161
|
-
node.first(:xpath, XPath.
|
165
|
+
node.first(:xpath, XPath.self[XPath.attr(:href) == href.to_s], minimum: 0)
|
162
166
|
end
|
163
167
|
end
|
164
168
|
|
165
|
-
describe do
|
166
|
-
desc =
|
169
|
+
describe do |**options|
|
170
|
+
desc = "".dup
|
167
171
|
desc << " with href #{options[:href].inspect}" if options[:href]
|
168
172
|
desc << " with no href attribute" if options.fetch(:href, true).nil?
|
169
173
|
end
|
@@ -181,22 +185,22 @@ end
|
|
181
185
|
# @filter [String] :value Matches the value of an input button
|
182
186
|
#
|
183
187
|
Capybara.add_selector(:button) do
|
184
|
-
xpath(:value, :title) do |locator, options
|
188
|
+
xpath(:value, :title) do |locator, **options|
|
185
189
|
input_btn_xpath = XPath.descendant(:input)[XPath.attr(:type).one_of('submit', 'reset', 'image', 'button')]
|
186
190
|
btn_xpath = XPath.descendant(:button)
|
187
|
-
image_btn_xpath = XPath.descendant(:input)[XPath.attr(:type)
|
191
|
+
image_btn_xpath = XPath.descendant(:input)[XPath.attr(:type) == 'image']
|
188
192
|
|
189
193
|
unless locator.nil?
|
190
194
|
locator = locator.to_s
|
191
|
-
locator_matches = XPath.attr(:id).equals(locator)
|
192
|
-
locator_matches
|
195
|
+
locator_matches = XPath.attr(:id).equals(locator) | XPath.attr(:value).is(locator) | XPath.attr(:title).is(locator)
|
196
|
+
locator_matches |= XPath.attr(:'aria-label').is(locator) if options[:enable_aria_label]
|
193
197
|
|
194
198
|
input_btn_xpath = input_btn_xpath[locator_matches]
|
195
199
|
|
196
|
-
btn_xpath = btn_xpath[locator_matches
|
200
|
+
btn_xpath = btn_xpath[locator_matches | XPath.string.n.is(locator) | XPath.descendant(:img)[XPath.attr(:alt).is(locator)]]
|
197
201
|
|
198
202
|
alt_matches = XPath.attr(:alt).is(locator)
|
199
|
-
alt_matches
|
203
|
+
alt_matches |= XPath.attr(:'aria-label').is(locator) if options[:enable_aria_label]
|
200
204
|
image_btn_xpath = image_btn_xpath[alt_matches]
|
201
205
|
end
|
202
206
|
|
@@ -207,11 +211,11 @@ Capybara.add_selector(:button) do
|
|
207
211
|
res_xpath
|
208
212
|
end
|
209
213
|
|
210
|
-
filter(:disabled, :boolean, default: false, skip_if: :all) { |node, value|
|
214
|
+
filter(:disabled, :boolean, default: false, skip_if: :all) { |node, value| !(value ^ node.disabled?) }
|
211
215
|
|
212
|
-
describe do |options|
|
213
|
-
desc =
|
214
|
-
desc << " that is disabled" if
|
216
|
+
describe do |disabled: nil, **options|
|
217
|
+
desc = "".dup
|
218
|
+
desc << " that is disabled" if disabled == true
|
215
219
|
desc << describe_all_expression_filters(options)
|
216
220
|
desc
|
217
221
|
end
|
@@ -223,13 +227,13 @@ end
|
|
223
227
|
#
|
224
228
|
Capybara.add_selector(:link_or_button) do
|
225
229
|
label "link or button"
|
226
|
-
xpath do |locator, options|
|
227
|
-
self.class.all.values_at(:link, :button).map {|selector| selector.xpath.call(locator, options)}.reduce(:union)
|
230
|
+
xpath do |locator, **options|
|
231
|
+
self.class.all.values_at(:link, :button).map { |selector| selector.xpath.call(locator, options) }.reduce(:union)
|
228
232
|
end
|
229
233
|
|
230
|
-
filter(:disabled, :boolean, default: false, skip_if: :all) { |node, value| node.tag_name == "a" or
|
234
|
+
filter(:disabled, :boolean, default: false, skip_if: :all) { |node, value| node.tag_name == "a" or !(value ^ node.disabled?) }
|
231
235
|
|
232
|
-
describe { |
|
236
|
+
describe { |disabled: nil, **_options| " that is disabled" if disabled == true }
|
233
237
|
end
|
234
238
|
|
235
239
|
##
|
@@ -248,30 +252,31 @@ end
|
|
248
252
|
#
|
249
253
|
Capybara.add_selector(:fillable_field) do
|
250
254
|
label "field"
|
251
|
-
|
252
|
-
|
255
|
+
|
256
|
+
xpath do |locator, **options|
|
257
|
+
xpath = XPath.descendant(:input, :textarea)[!XPath.attr(:type).one_of('submit', 'image', 'radio', 'checkbox', 'hidden', 'file')]
|
253
258
|
locate_field(xpath, locator, options)
|
254
259
|
end
|
255
260
|
|
256
261
|
expression_filter(:type) do |expr, type|
|
257
262
|
type = type.to_s
|
258
263
|
if ['textarea'].include?(type)
|
259
|
-
expr.
|
264
|
+
expr.self(type.to_sym)
|
260
265
|
else
|
261
|
-
expr[XPath.attr(:type)
|
266
|
+
expr[XPath.attr(:type) == type]
|
262
267
|
end
|
263
268
|
end
|
264
269
|
|
265
|
-
filter_set(:_field, [
|
270
|
+
filter_set(:_field, %i[disabled multiple name placeholder])
|
266
271
|
|
267
272
|
filter(:with) do |node, with|
|
268
273
|
with.is_a?(Regexp) ? node.value =~ with : node.value == with.to_s
|
269
274
|
end
|
270
275
|
|
271
276
|
describe do |options|
|
272
|
-
desc =
|
277
|
+
desc = "".dup
|
273
278
|
desc << describe_all_expression_filters(options)
|
274
|
-
desc << " with value #{options[:with].to_s.inspect}" if options.
|
279
|
+
desc << " with value #{options[:with].to_s.inspect}" if options.key?(:with)
|
275
280
|
desc
|
276
281
|
end
|
277
282
|
end
|
@@ -291,18 +296,19 @@ end
|
|
291
296
|
#
|
292
297
|
Capybara.add_selector(:radio_button) do
|
293
298
|
label "radio button"
|
294
|
-
|
295
|
-
|
299
|
+
|
300
|
+
xpath do |locator, **options|
|
301
|
+
xpath = XPath.descendant(:input)[XPath.attr(:type) == 'radio']
|
296
302
|
locate_field(xpath, locator, options)
|
297
303
|
end
|
298
304
|
|
299
|
-
filter_set(:_field, [
|
305
|
+
filter_set(:_field, %i[checked unchecked disabled name])
|
300
306
|
|
301
|
-
filter(:option)
|
307
|
+
filter(:option) { |node, value| node.value == value.to_s }
|
302
308
|
|
303
|
-
describe do |options|
|
304
|
-
desc =
|
305
|
-
desc << " with value #{
|
309
|
+
describe do |option: nil, **options|
|
310
|
+
desc = "".dup
|
311
|
+
desc << " with value #{option.inspect}" if option
|
306
312
|
desc << describe_all_expression_filters(options)
|
307
313
|
desc
|
308
314
|
end
|
@@ -322,18 +328,18 @@ end
|
|
322
328
|
# @filter [String] :option Match the value
|
323
329
|
#
|
324
330
|
Capybara.add_selector(:checkbox) do
|
325
|
-
xpath do |locator, options|
|
326
|
-
xpath = XPath.descendant(:input)[XPath.attr(:type)
|
331
|
+
xpath do |locator, **options|
|
332
|
+
xpath = XPath.descendant(:input)[XPath.attr(:type) == 'checkbox']
|
327
333
|
locate_field(xpath, locator, options)
|
328
334
|
end
|
329
335
|
|
330
|
-
filter_set(:_field, [
|
336
|
+
filter_set(:_field, %i[checked unchecked disabled name])
|
331
337
|
|
332
|
-
filter(:option)
|
338
|
+
filter(:option) { |node, value| node.value == value.to_s }
|
333
339
|
|
334
|
-
describe do |options|
|
335
|
-
desc =
|
336
|
-
desc << " with value #{
|
340
|
+
describe do |option: nil, **options|
|
341
|
+
desc = "".dup
|
342
|
+
desc << " with value #{option.inspect}" if option
|
337
343
|
desc << describe_all_expression_filters(options)
|
338
344
|
desc
|
339
345
|
end
|
@@ -357,47 +363,46 @@ end
|
|
357
363
|
#
|
358
364
|
Capybara.add_selector(:select) do
|
359
365
|
label "select box"
|
360
|
-
|
366
|
+
|
367
|
+
xpath do |locator, **options|
|
361
368
|
xpath = XPath.descendant(:select)
|
362
369
|
locate_field(xpath, locator, options)
|
363
370
|
end
|
364
371
|
|
365
|
-
filter_set(:_field, [
|
372
|
+
filter_set(:_field, %i[disabled multiple name placeholder])
|
366
373
|
|
367
374
|
filter(:options) do |node, options|
|
368
|
-
if node.visible?
|
369
|
-
|
375
|
+
actual = if node.visible?
|
376
|
+
node.all(:xpath, './/option', wait: false).map(&:text)
|
370
377
|
else
|
371
|
-
|
378
|
+
node.all(:xpath, './/option', visible: false, wait: false).map { |option| option.text(:all) }
|
372
379
|
end
|
373
380
|
options.sort == actual.sort
|
374
381
|
end
|
375
382
|
|
376
383
|
filter(:with_options) do |node, options|
|
377
384
|
finder_settings = { minimum: 0 }
|
378
|
-
|
379
|
-
finder_settings[:visible] = false
|
380
|
-
end
|
385
|
+
finder_settings[:visible] = false unless node.visible?
|
381
386
|
options.all? { |option| node.first(:option, option, finder_settings) }
|
382
387
|
end
|
383
388
|
|
384
389
|
filter(:selected) do |node, selected|
|
385
|
-
actual = node.all(:xpath, './/option', visible: false).select(&:selected?).map { |option| option.text(:all) }
|
390
|
+
actual = node.all(:xpath, './/option', visible: false, wait: false).select(&:selected?).map { |option| option.text(:all) }
|
386
391
|
Array(selected).sort == actual.sort
|
387
392
|
end
|
388
393
|
|
389
394
|
filter(:with_selected) do |node, selected|
|
390
|
-
actual = node.all(:xpath, './/option', visible: false).select(&:selected?).map { |option| option.text(:all) }
|
395
|
+
actual = node.all(:xpath, './/option', visible: false, wait: false).select(&:selected?).map { |option| option.text(:all) }
|
391
396
|
(Array(selected) - actual).empty?
|
392
397
|
end
|
393
398
|
|
394
|
-
describe do |options|
|
395
|
-
desc =
|
396
|
-
desc << " with options #{options
|
397
|
-
desc << " with at least options #{
|
398
|
-
desc << " with #{
|
399
|
-
desc << " with at least #{
|
400
|
-
desc << describe_all_expression_filters(
|
399
|
+
describe do |options: nil, with_options: nil, selected: nil, with_selected: nil, **opts|
|
400
|
+
desc = "".dup
|
401
|
+
desc << " with options #{options.inspect}" if options
|
402
|
+
desc << " with at least options #{with_options.inspect}" if with_options
|
403
|
+
desc << " with #{selected.inspect} selected" if selected
|
404
|
+
desc << " with at least #{with_selected.inspect} selected" if with_selected
|
405
|
+
desc << describe_all_expression_filters(opts)
|
401
406
|
desc
|
402
407
|
end
|
403
408
|
end
|
@@ -417,13 +422,13 @@ Capybara.add_selector(:option) do
|
|
417
422
|
xpath
|
418
423
|
end
|
419
424
|
|
420
|
-
filter(:disabled, :boolean) { |node, value|
|
421
|
-
filter(:selected, :boolean) { |node, value|
|
425
|
+
filter(:disabled, :boolean) { |node, value| !(value ^ node.disabled?) }
|
426
|
+
filter(:selected, :boolean) { |node, value| !(value ^ node.selected?) }
|
422
427
|
|
423
|
-
describe do
|
424
|
-
desc =
|
425
|
-
desc << " that is#{' not' unless options[:disabled]} disabled" if options.
|
426
|
-
desc << " that is#{' not' unless options[:selected]} selected" if options.
|
428
|
+
describe do |**options|
|
429
|
+
desc = "".dup
|
430
|
+
desc << " that is#{' not' unless options[:disabled]} disabled" if options.key?(:disabled)
|
431
|
+
desc << " that is#{' not' unless options[:selected]} selected" if options.key?(:selected)
|
427
432
|
desc
|
428
433
|
end
|
429
434
|
end
|
@@ -442,14 +447,14 @@ end
|
|
442
447
|
Capybara.add_selector(:file_field) do
|
443
448
|
label "file field"
|
444
449
|
xpath do |locator, options|
|
445
|
-
xpath = XPath.descendant(:input)[XPath.attr(:type)
|
450
|
+
xpath = XPath.descendant(:input)[XPath.attr(:type) == 'file']
|
446
451
|
locate_field(xpath, locator, options)
|
447
452
|
end
|
448
453
|
|
449
|
-
filter_set(:_field, [
|
454
|
+
filter_set(:_field, %i[disabled multiple name])
|
450
455
|
|
451
|
-
describe do
|
452
|
-
desc =
|
456
|
+
describe do |**options|
|
457
|
+
desc = "".dup
|
453
458
|
desc << describe_all_expression_filters(options)
|
454
459
|
desc
|
455
460
|
end
|
@@ -466,9 +471,13 @@ Capybara.add_selector(:label) do
|
|
466
471
|
label "label"
|
467
472
|
xpath(:for) do |locator, options|
|
468
473
|
xpath = XPath.descendant(:label)
|
469
|
-
xpath = xpath[XPath.string.n.is(locator.to_s)
|
470
|
-
if options.
|
471
|
-
|
474
|
+
xpath = xpath[XPath.string.n.is(locator.to_s) | (XPath.attr(:id) == locator.to_s)] unless locator.nil?
|
475
|
+
if options.key?(:for) && !options[:for].is_a?(Capybara::Node::Element)
|
476
|
+
with_attr = XPath.attr(:for) == options[:for].to_s
|
477
|
+
labelable_elements = %i[button input keygen meter output progress select textarea]
|
478
|
+
wrapped = !XPath.attr(:for) &
|
479
|
+
XPath.descendant(*labelable_elements)[XPath.attr(:id) == options[:for].to_s]
|
480
|
+
xpath = xpath[with_attr | wrapped]
|
472
481
|
end
|
473
482
|
xpath
|
474
483
|
end
|
@@ -481,13 +490,13 @@ Capybara.add_selector(:label) do
|
|
481
490
|
field_or_value.find_xpath('./ancestor::label[1]').include? node.base
|
482
491
|
end
|
483
492
|
else
|
484
|
-
#Non element values were handled through the expression filter
|
493
|
+
# Non element values were handled through the expression filter
|
485
494
|
true
|
486
495
|
end
|
487
496
|
end
|
488
497
|
|
489
|
-
describe do
|
490
|
-
desc =
|
498
|
+
describe do |**options|
|
499
|
+
desc = "".dup
|
491
500
|
desc << " for #{options[:for]}" if options[:for]
|
492
501
|
desc
|
493
502
|
end
|
@@ -505,14 +514,14 @@ end
|
|
505
514
|
Capybara.add_selector(:table) do
|
506
515
|
xpath(:caption) do |locator, options|
|
507
516
|
xpath = XPath.descendant(:table)
|
508
|
-
xpath = xpath[XPath.attr(:id)
|
509
|
-
xpath = xpath[XPath.descendant(:caption)
|
517
|
+
xpath = xpath[(XPath.attr(:id) == locator.to_s) | XPath.descendant(:caption).is(locator.to_s)] unless locator.nil?
|
518
|
+
xpath = xpath[XPath.descendant(:caption) == options[:caption]] if options[:caption]
|
510
519
|
xpath
|
511
520
|
end
|
512
521
|
|
513
|
-
describe do |
|
514
|
-
desc =
|
515
|
-
desc << " with caption #{
|
522
|
+
describe do |caption: nil, **_options|
|
523
|
+
desc = "".dup
|
524
|
+
desc << " with caption #{caption}" if caption
|
516
525
|
desc
|
517
526
|
end
|
518
527
|
end
|
@@ -527,16 +536,19 @@ end
|
|
527
536
|
# @filter [String, Array<String>] :class Matches the class(es) provided
|
528
537
|
#
|
529
538
|
Capybara.add_selector(:frame) do
|
530
|
-
xpath(:name) do |locator, options|
|
539
|
+
xpath(:name) do |locator, **options|
|
531
540
|
xpath = XPath.descendant(:iframe).union(XPath.descendant(:frame))
|
532
|
-
xpath = xpath[XPath.attr(:id)
|
541
|
+
xpath = xpath[(XPath.attr(:id) == locator.to_s) | (XPath.attr(:name) == locator.to_s)] unless locator.nil?
|
533
542
|
xpath = expression_filters.keys.inject(xpath) { |memo, ef| memo[find_by_attr(ef, options[ef])] }
|
534
543
|
xpath
|
535
544
|
end
|
536
545
|
|
537
|
-
describe do |
|
538
|
-
desc =
|
539
|
-
desc << " with name #{
|
546
|
+
describe do |name: nil, **_options|
|
547
|
+
desc = "".dup
|
548
|
+
desc << " with name #{name}" if name
|
540
549
|
desc
|
541
550
|
end
|
542
551
|
end
|
552
|
+
|
553
|
+
# rubocop:enable Metrics/BlockLength
|
554
|
+
# rubocop:enable Metrics/ParameterLists
|