capybara 3.13.2 → 3.40.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/.yardopts +1 -0
- data/History.md +587 -16
- data/README.md +240 -90
- data/lib/capybara/config.rb +24 -11
- data/lib/capybara/cucumber.rb +1 -1
- data/lib/capybara/driver/base.rb +8 -0
- data/lib/capybara/driver/node.rb +20 -4
- data/lib/capybara/dsl.rb +5 -3
- data/lib/capybara/helpers.rb +25 -4
- data/lib/capybara/minitest/spec.rb +174 -90
- data/lib/capybara/minitest.rb +256 -142
- data/lib/capybara/node/actions.rb +123 -77
- data/lib/capybara/node/base.rb +20 -12
- data/lib/capybara/node/document.rb +2 -2
- data/lib/capybara/node/document_matchers.rb +3 -3
- data/lib/capybara/node/element.rb +223 -117
- data/lib/capybara/node/finders.rb +81 -71
- data/lib/capybara/node/matchers.rb +271 -134
- data/lib/capybara/node/simple.rb +18 -5
- data/lib/capybara/node/whitespace_normalizer.rb +81 -0
- data/lib/capybara/queries/active_element_query.rb +18 -0
- data/lib/capybara/queries/ancestor_query.rb +8 -9
- data/lib/capybara/queries/base_query.rb +3 -2
- data/lib/capybara/queries/current_path_query.rb +15 -5
- data/lib/capybara/queries/selector_query.rb +364 -54
- data/lib/capybara/queries/sibling_query.rb +8 -6
- data/lib/capybara/queries/style_query.rb +2 -2
- data/lib/capybara/queries/text_query.rb +13 -1
- data/lib/capybara/queries/title_query.rb +1 -1
- data/lib/capybara/rack_test/browser.rb +76 -11
- data/lib/capybara/rack_test/driver.rb +10 -5
- data/lib/capybara/rack_test/errors.rb +6 -0
- data/lib/capybara/rack_test/form.rb +31 -9
- data/lib/capybara/rack_test/node.rb +74 -23
- data/lib/capybara/registration_container.rb +41 -0
- data/lib/capybara/registrations/drivers.rb +42 -0
- data/lib/capybara/registrations/patches/puma_ssl.rb +29 -0
- data/lib/capybara/registrations/servers.rb +66 -0
- data/lib/capybara/result.rb +44 -20
- data/lib/capybara/rspec/matcher_proxies.rb +13 -11
- data/lib/capybara/rspec/matchers/base.rb +31 -16
- data/lib/capybara/rspec/matchers/compound.rb +1 -1
- data/lib/capybara/rspec/matchers/count_sugar.rb +37 -0
- data/lib/capybara/rspec/matchers/have_ancestor.rb +28 -0
- data/lib/capybara/rspec/matchers/have_current_path.rb +2 -2
- data/lib/capybara/rspec/matchers/have_selector.rb +21 -21
- data/lib/capybara/rspec/matchers/have_sibling.rb +27 -0
- data/lib/capybara/rspec/matchers/have_text.rb +4 -4
- data/lib/capybara/rspec/matchers/have_title.rb +2 -2
- data/lib/capybara/rspec/matchers/match_selector.rb +3 -3
- data/lib/capybara/rspec/matchers/match_style.rb +7 -2
- data/lib/capybara/rspec/matchers/spatial_sugar.rb +39 -0
- data/lib/capybara/rspec/matchers.rb +111 -68
- data/lib/capybara/rspec.rb +2 -0
- data/lib/capybara/selector/builders/css_builder.rb +11 -7
- data/lib/capybara/selector/builders/xpath_builder.rb +5 -3
- data/lib/capybara/selector/css.rb +11 -9
- data/lib/capybara/selector/definition/button.rb +68 -0
- data/lib/capybara/selector/definition/checkbox.rb +26 -0
- data/lib/capybara/selector/definition/css.rb +10 -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 +28 -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 +62 -0
- data/lib/capybara/selector/definition/link.rb +55 -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 +27 -0
- data/lib/capybara/selector/definition/select.rb +81 -0
- data/lib/capybara/selector/definition/table.rb +109 -0
- data/lib/capybara/selector/definition/table_row.rb +21 -0
- data/lib/capybara/selector/definition/xpath.rb +5 -0
- data/lib/capybara/selector/definition.rb +280 -0
- data/lib/capybara/selector/filter_set.rb +19 -18
- data/lib/capybara/selector/filters/base.rb +11 -2
- data/lib/capybara/selector/filters/locator_filter.rb +13 -3
- data/lib/capybara/selector/regexp_disassembler.rb +11 -7
- data/lib/capybara/selector/selector.rb +50 -440
- data/lib/capybara/selector/xpath_extensions.rb +17 -0
- data/lib/capybara/selector.rb +473 -482
- data/lib/capybara/selenium/atoms/getAttribute.min.js +1 -0
- data/lib/capybara/selenium/atoms/isDisplayed.min.js +1 -0
- data/lib/capybara/selenium/atoms/src/getAttribute.js +161 -0
- data/lib/capybara/selenium/atoms/src/isDisplayed.js +454 -0
- data/lib/capybara/selenium/driver.rb +174 -62
- data/lib/capybara/selenium/driver_specializations/chrome_driver.rb +74 -18
- data/lib/capybara/selenium/driver_specializations/edge_driver.rb +128 -0
- data/lib/capybara/selenium/driver_specializations/firefox_driver.rb +37 -3
- data/lib/capybara/selenium/driver_specializations/internet_explorer_driver.rb +14 -1
- data/lib/capybara/selenium/driver_specializations/safari_driver.rb +24 -0
- data/lib/capybara/selenium/extensions/file_input_click_emulation.rb +34 -0
- data/lib/capybara/selenium/extensions/find.rb +68 -45
- data/lib/capybara/selenium/extensions/html5_drag.rb +192 -22
- data/lib/capybara/selenium/extensions/modifier_keys_stack.rb +28 -0
- data/lib/capybara/selenium/extensions/scroll.rb +8 -10
- data/lib/capybara/selenium/node.rb +268 -72
- data/lib/capybara/selenium/nodes/chrome_node.rb +105 -9
- data/lib/capybara/selenium/nodes/edge_node.rb +110 -0
- data/lib/capybara/selenium/nodes/firefox_node.rb +51 -61
- data/lib/capybara/selenium/nodes/ie_node.rb +22 -0
- data/lib/capybara/selenium/nodes/safari_node.rb +118 -0
- data/lib/capybara/selenium/patches/atoms.rb +18 -0
- data/lib/capybara/selenium/patches/is_displayed.rb +16 -0
- data/lib/capybara/selenium/patches/logs.rb +45 -0
- data/lib/capybara/selenium/patches/pause_duration_fix.rb +1 -1
- data/lib/capybara/selenium/patches/persistent_client.rb +20 -0
- data/lib/capybara/server/animation_disabler.rb +43 -21
- data/lib/capybara/server/checker.rb +6 -2
- data/lib/capybara/server/middleware.rb +25 -13
- data/lib/capybara/server.rb +20 -4
- data/lib/capybara/session/config.rb +15 -11
- data/lib/capybara/session/matchers.rb +11 -11
- data/lib/capybara/session.rb +162 -131
- data/lib/capybara/spec/public/offset.js +6 -0
- data/lib/capybara/spec/public/test.js +105 -6
- data/lib/capybara/spec/session/accept_alert_spec.rb +1 -1
- data/lib/capybara/spec/session/active_element_spec.rb +31 -0
- data/lib/capybara/spec/session/all_spec.rb +89 -15
- data/lib/capybara/spec/session/ancestor_spec.rb +5 -0
- data/lib/capybara/spec/session/assert_current_path_spec.rb +5 -2
- data/lib/capybara/spec/session/assert_text_spec.rb +26 -22
- data/lib/capybara/spec/session/attach_file_spec.rb +64 -31
- data/lib/capybara/spec/session/check_spec.rb +26 -4
- data/lib/capybara/spec/session/choose_spec.rb +14 -2
- data/lib/capybara/spec/session/click_button_spec.rb +109 -61
- data/lib/capybara/spec/session/click_link_or_button_spec.rb +9 -0
- data/lib/capybara/spec/session/click_link_spec.rb +23 -1
- data/lib/capybara/spec/session/current_scope_spec.rb +1 -1
- data/lib/capybara/spec/session/current_url_spec.rb +11 -1
- data/lib/capybara/spec/session/element/matches_selector_spec.rb +40 -39
- data/lib/capybara/spec/session/evaluate_script_spec.rb +12 -0
- data/lib/capybara/spec/session/fill_in_spec.rb +46 -5
- data/lib/capybara/spec/session/find_link_spec.rb +10 -0
- data/lib/capybara/spec/session/find_spec.rb +80 -7
- data/lib/capybara/spec/session/first_spec.rb +2 -2
- data/lib/capybara/spec/session/frame/switch_to_frame_spec.rb +14 -1
- data/lib/capybara/spec/session/frame/within_frame_spec.rb +14 -1
- data/lib/capybara/spec/session/has_all_selectors_spec.rb +5 -5
- data/lib/capybara/spec/session/has_ancestor_spec.rb +46 -0
- data/lib/capybara/spec/session/has_any_selectors_spec.rb +6 -2
- data/lib/capybara/spec/session/has_button_spec.rb +81 -0
- data/lib/capybara/spec/session/has_css_spec.rb +45 -8
- data/lib/capybara/spec/session/has_current_path_spec.rb +22 -7
- data/lib/capybara/spec/session/has_element_spec.rb +47 -0
- data/lib/capybara/spec/session/has_field_spec.rb +59 -1
- data/lib/capybara/spec/session/has_link_spec.rb +40 -0
- data/lib/capybara/spec/session/has_none_selectors_spec.rb +7 -7
- data/lib/capybara/spec/session/has_select_spec.rb +42 -8
- data/lib/capybara/spec/session/has_selector_spec.rb +19 -4
- data/lib/capybara/spec/session/has_sibling_spec.rb +50 -0
- data/lib/capybara/spec/session/has_table_spec.rb +177 -0
- data/lib/capybara/spec/session/has_text_spec.rb +31 -3
- data/lib/capybara/spec/session/html_spec.rb +1 -1
- data/lib/capybara/spec/session/matches_style_spec.rb +6 -4
- data/lib/capybara/spec/session/node_spec.rb +697 -23
- data/lib/capybara/spec/session/node_wrapper_spec.rb +1 -1
- data/lib/capybara/spec/session/refresh_spec.rb +2 -1
- data/lib/capybara/spec/session/reset_session_spec.rb +21 -7
- data/lib/capybara/spec/session/save_and_open_screenshot_spec.rb +2 -2
- data/lib/capybara/spec/session/save_page_spec.rb +4 -4
- data/lib/capybara/spec/session/save_screenshot_spec.rb +4 -4
- data/lib/capybara/spec/session/scroll_spec.rb +9 -7
- data/lib/capybara/spec/session/select_spec.rb +5 -10
- data/lib/capybara/spec/session/selectors_spec.rb +24 -3
- data/lib/capybara/spec/session/uncheck_spec.rb +3 -3
- data/lib/capybara/spec/session/unselect_spec.rb +1 -1
- data/lib/capybara/spec/session/visit_spec.rb +20 -0
- data/lib/capybara/spec/session/window/become_closed_spec.rb +20 -17
- data/lib/capybara/spec/session/window/switch_to_window_spec.rb +1 -1
- data/lib/capybara/spec/session/window/window_opened_by_spec.rb +1 -1
- data/lib/capybara/spec/session/window/window_spec.rb +54 -57
- data/lib/capybara/spec/session/window/windows_spec.rb +2 -2
- data/lib/capybara/spec/session/within_spec.rb +36 -0
- data/lib/capybara/spec/spec_helper.rb +30 -19
- data/lib/capybara/spec/test_app.rb +122 -34
- data/lib/capybara/spec/views/animated.erb +49 -0
- data/lib/capybara/spec/views/form.erb +86 -8
- data/lib/capybara/spec/views/frame_child.erb +3 -2
- data/lib/capybara/spec/views/frame_one.erb +2 -1
- data/lib/capybara/spec/views/frame_parent.erb +1 -1
- data/lib/capybara/spec/views/frame_two.erb +1 -1
- data/lib/capybara/spec/views/initial_alert.erb +2 -1
- data/lib/capybara/spec/views/layout.erb +10 -0
- data/lib/capybara/spec/views/obscured.erb +10 -10
- data/lib/capybara/spec/views/offset.erb +33 -0
- data/lib/capybara/spec/views/path.erb +2 -2
- data/lib/capybara/spec/views/popup_one.erb +1 -1
- data/lib/capybara/spec/views/popup_two.erb +1 -1
- data/lib/capybara/spec/views/react.erb +45 -0
- data/lib/capybara/spec/views/scroll.erb +2 -1
- data/lib/capybara/spec/views/spatial.erb +31 -0
- data/lib/capybara/spec/views/tables.erb +67 -0
- data/lib/capybara/spec/views/with_animation.erb +39 -4
- data/lib/capybara/spec/views/with_base_tag.erb +2 -2
- data/lib/capybara/spec/views/with_dragula.erb +24 -0
- data/lib/capybara/spec/views/with_fixed_header_footer.erb +2 -1
- data/lib/capybara/spec/views/with_hover.erb +3 -2
- data/lib/capybara/spec/views/with_hover1.erb +10 -0
- data/lib/capybara/spec/views/with_html.erb +34 -6
- data/lib/capybara/spec/views/with_jquery_animation.erb +24 -0
- data/lib/capybara/spec/views/with_js.erb +7 -4
- data/lib/capybara/spec/views/with_jstree.erb +26 -0
- data/lib/capybara/spec/views/with_namespace.erb +1 -0
- data/lib/capybara/spec/views/with_scope.erb +2 -2
- data/lib/capybara/spec/views/with_scope_other.erb +6 -0
- data/lib/capybara/spec/views/with_shadow.erb +31 -0
- data/lib/capybara/spec/views/with_slow_unload.erb +2 -1
- data/lib/capybara/spec/views/with_sortable_js.erb +21 -0
- data/lib/capybara/spec/views/with_unload_alert.erb +1 -0
- data/lib/capybara/spec/views/with_windows.erb +1 -1
- data/lib/capybara/spec/views/within_frames.erb +1 -1
- data/lib/capybara/version.rb +1 -1
- data/lib/capybara/window.rb +14 -18
- data/lib/capybara.rb +91 -126
- data/spec/basic_node_spec.rb +30 -16
- data/spec/capybara_spec.rb +40 -28
- data/spec/counter_spec.rb +35 -0
- data/spec/css_builder_spec.rb +3 -1
- data/spec/css_splitter_spec.rb +1 -1
- data/spec/dsl_spec.rb +33 -22
- data/spec/filter_set_spec.rb +5 -5
- data/spec/fixtures/selenium_driver_rspec_failure.rb +3 -3
- data/spec/fixtures/selenium_driver_rspec_success.rb +3 -3
- data/spec/minitest_spec.rb +24 -2
- data/spec/minitest_spec_spec.rb +60 -45
- data/spec/per_session_config_spec.rb +1 -1
- data/spec/rack_test_spec.rb +131 -98
- data/spec/regexp_dissassembler_spec.rb +53 -39
- data/spec/result_spec.rb +68 -66
- data/spec/rspec/features_spec.rb +9 -4
- data/spec/rspec/scenarios_spec.rb +6 -2
- data/spec/rspec/shared_spec_matchers.rb +137 -98
- data/spec/rspec_matchers_spec.rb +25 -0
- data/spec/rspec_spec.rb +23 -21
- data/spec/sauce_spec_chrome.rb +43 -0
- data/spec/selector_spec.rb +77 -21
- data/spec/selenium_spec_chrome.rb +141 -39
- data/spec/selenium_spec_chrome_remote.rb +32 -17
- data/spec/selenium_spec_edge.rb +36 -8
- data/spec/selenium_spec_firefox.rb +110 -68
- data/spec/selenium_spec_firefox_remote.rb +22 -15
- data/spec/selenium_spec_ie.rb +29 -22
- data/spec/selenium_spec_safari.rb +162 -0
- data/spec/server_spec.rb +153 -81
- data/spec/session_spec.rb +11 -4
- data/spec/shared_selenium_node.rb +79 -0
- data/spec/shared_selenium_session.rb +179 -74
- data/spec/spec_helper.rb +80 -5
- data/spec/whitespace_normalizer_spec.rb +54 -0
- data/spec/xpath_builder_spec.rb +3 -1
- metadata +218 -30
- data/lib/capybara/spec/session/source_spec.rb +0 -0
- data/lib/capybara/spec/views/with_title.erb +0 -5
|
@@ -0,0 +1,280 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'capybara/selector/filter_set'
|
|
4
|
+
require 'capybara/selector/css'
|
|
5
|
+
require 'capybara/selector/regexp_disassembler'
|
|
6
|
+
require 'capybara/selector/builders/xpath_builder'
|
|
7
|
+
require 'capybara/selector/builders/css_builder'
|
|
8
|
+
|
|
9
|
+
module Capybara
|
|
10
|
+
class Selector
|
|
11
|
+
class Definition
|
|
12
|
+
attr_reader :name, :expressions
|
|
13
|
+
|
|
14
|
+
extend Forwardable
|
|
15
|
+
|
|
16
|
+
def initialize(name, locator_type: nil, raw_locator: false, supports_exact: nil, &block)
|
|
17
|
+
@name = name
|
|
18
|
+
@filter_set = Capybara::Selector::FilterSet.add(name)
|
|
19
|
+
@match = nil
|
|
20
|
+
@label = nil
|
|
21
|
+
@failure_message = nil
|
|
22
|
+
@expressions = {}
|
|
23
|
+
@expression_filters = {}
|
|
24
|
+
@locator_filter = nil
|
|
25
|
+
@default_visibility = nil
|
|
26
|
+
@locator_type = locator_type
|
|
27
|
+
@raw_locator = raw_locator
|
|
28
|
+
@supports_exact = supports_exact
|
|
29
|
+
instance_eval(&block)
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def custom_filters
|
|
33
|
+
warn "Deprecated: Selector#custom_filters is not valid when same named expression and node filter exist - don't use"
|
|
34
|
+
node_filters.merge(expression_filters).freeze
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def node_filters
|
|
38
|
+
@filter_set.node_filters
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def expression_filters
|
|
42
|
+
@filter_set.expression_filters
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
##
|
|
46
|
+
#
|
|
47
|
+
# Define a selector by an xpath expression
|
|
48
|
+
#
|
|
49
|
+
# @overload xpath(*expression_filters, &block)
|
|
50
|
+
# @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
|
|
51
|
+
# @yield [locator, options] The block to use to generate the XPath expression
|
|
52
|
+
# @yieldparam [String] locator The locator string passed to the query
|
|
53
|
+
# @yieldparam [Hash] options The options hash passed to the query
|
|
54
|
+
# @yieldreturn [#to_xpath, #to_s] An object that can produce an xpath expression
|
|
55
|
+
#
|
|
56
|
+
# @overload xpath()
|
|
57
|
+
# @return [#call] The block that will be called to generate the XPath expression
|
|
58
|
+
#
|
|
59
|
+
def xpath(*allowed_filters, &block)
|
|
60
|
+
expression(:xpath, allowed_filters, &block)
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
##
|
|
64
|
+
#
|
|
65
|
+
# Define a selector by a CSS selector
|
|
66
|
+
#
|
|
67
|
+
# @overload css(*expression_filters, &block)
|
|
68
|
+
# @param [Array<Symbol>] expression_filters ([]) Names of filters that can be implemented via this CSS selector
|
|
69
|
+
# @yield [locator, options] The block to use to generate the CSS selector
|
|
70
|
+
# @yieldparam [String] locator The locator string passed to the query
|
|
71
|
+
# @yieldparam [Hash] options The options hash passed to the query
|
|
72
|
+
# @yieldreturn [#to_s] An object that can produce a CSS selector
|
|
73
|
+
#
|
|
74
|
+
# @overload css()
|
|
75
|
+
# @return [#call] The block that will be called to generate the CSS selector
|
|
76
|
+
#
|
|
77
|
+
def css(*allowed_filters, &block)
|
|
78
|
+
expression(:css, allowed_filters, &block)
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
##
|
|
82
|
+
#
|
|
83
|
+
# Automatic selector detection
|
|
84
|
+
#
|
|
85
|
+
# @yield [locator] This block takes the passed in locator string and returns whether or not it matches the selector
|
|
86
|
+
# @yieldparam [String], locator The locator string used to determine if it matches the selector
|
|
87
|
+
# @yieldreturn [Boolean] Whether this selector matches the locator string
|
|
88
|
+
# @return [#call] The block that will be used to detect selector match
|
|
89
|
+
#
|
|
90
|
+
def match(&block)
|
|
91
|
+
@match = block if block
|
|
92
|
+
@match
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
##
|
|
96
|
+
#
|
|
97
|
+
# Set/get a descriptive label for the selector
|
|
98
|
+
#
|
|
99
|
+
# @overload label(label)
|
|
100
|
+
# @param [String] label A descriptive label for this selector - used in error messages
|
|
101
|
+
# @overload label()
|
|
102
|
+
# @return [String] The currently set label
|
|
103
|
+
#
|
|
104
|
+
def label(label = nil)
|
|
105
|
+
@label = label if label
|
|
106
|
+
@label
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
##
|
|
110
|
+
#
|
|
111
|
+
# Description of the selector
|
|
112
|
+
#
|
|
113
|
+
# @!method description(options)
|
|
114
|
+
# @param [Hash] options The options of the query used to generate the description
|
|
115
|
+
# @return [String] Description of the selector when used with the options passed
|
|
116
|
+
def_delegator :@filter_set, :description
|
|
117
|
+
|
|
118
|
+
##
|
|
119
|
+
#
|
|
120
|
+
# Should this selector be used for the passed in locator
|
|
121
|
+
#
|
|
122
|
+
# This is used by the automatic selector selection mechanism when no selector type is passed to a selector query
|
|
123
|
+
#
|
|
124
|
+
# @param [String] locator The locator passed to the query
|
|
125
|
+
# @return [Boolean] Whether or not to use this selector
|
|
126
|
+
#
|
|
127
|
+
def match?(locator)
|
|
128
|
+
@match&.call(locator)
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
##
|
|
132
|
+
#
|
|
133
|
+
# Define a node filter for use with this selector
|
|
134
|
+
#
|
|
135
|
+
# @!method node_filter(name, *types, options={}, &block)
|
|
136
|
+
# @param [Symbol, Regexp] name The filter name
|
|
137
|
+
# @param [Array<Symbol>] types The types of the filter - currently valid types are [:boolean]
|
|
138
|
+
# @param [Hash] options ({}) Options of the filter
|
|
139
|
+
# @option options [Array<>] :valid_values Valid values for this filter
|
|
140
|
+
# @option options :default The default value of the filter (if any)
|
|
141
|
+
# @option options :skip_if Value of the filter that will cause it to be skipped
|
|
142
|
+
# @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.
|
|
143
|
+
#
|
|
144
|
+
# If a Symbol is passed for the name the block should accept | node, option_value |, while if a Regexp
|
|
145
|
+
# is passed for the name the block should accept | node, option_name, option_value |. In either case
|
|
146
|
+
# the block should return `true` if the node passes the filer or `false` if it doesn't
|
|
147
|
+
|
|
148
|
+
##
|
|
149
|
+
#
|
|
150
|
+
# Define an expression filter for use with this selector
|
|
151
|
+
#
|
|
152
|
+
# @!method expression_filter(name, *types, matcher: nil, **options, &block)
|
|
153
|
+
# @param [Symbol, Regexp] name The filter name
|
|
154
|
+
# @param [Regexp] matcher (nil) A Regexp used to check whether a specific option is handled by this filter
|
|
155
|
+
# @param [Array<Symbol>] types The types of the filter - currently valid types are [:boolean]
|
|
156
|
+
# @param [Hash] options ({}) Options of the filter
|
|
157
|
+
# @option options [Array<>] :valid_values Valid values for this filter
|
|
158
|
+
# @option options :default The default value of the filter (if any)
|
|
159
|
+
# @option options :skip_if Value of the filter that will cause it to be skipped
|
|
160
|
+
# @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.
|
|
161
|
+
#
|
|
162
|
+
# If a Symbol is passed for the name the block should accept | current_expression, option_value |, while if a Regexp
|
|
163
|
+
# is passed for the name the block should accept | current_expression, option_name, option_value |. In either case
|
|
164
|
+
# the block should return the modified expression
|
|
165
|
+
|
|
166
|
+
def_delegators :@filter_set, :node_filter, :expression_filter, :filter
|
|
167
|
+
|
|
168
|
+
def locator_filter(*types, **options, &block)
|
|
169
|
+
types.each { |type| options[type] = true }
|
|
170
|
+
@locator_filter = Capybara::Selector::Filters::LocatorFilter.new(block, **options) if block
|
|
171
|
+
@locator_filter
|
|
172
|
+
end
|
|
173
|
+
|
|
174
|
+
def filter_set(name, filters_to_use = nil)
|
|
175
|
+
@filter_set.import(name, filters_to_use)
|
|
176
|
+
end
|
|
177
|
+
|
|
178
|
+
def_delegator :@filter_set, :describe
|
|
179
|
+
|
|
180
|
+
def describe_expression_filters(&block)
|
|
181
|
+
if block
|
|
182
|
+
describe(:expression_filters, &block)
|
|
183
|
+
else
|
|
184
|
+
describe(:expression_filters) do |**options|
|
|
185
|
+
describe_all_expression_filters(**options)
|
|
186
|
+
end
|
|
187
|
+
end
|
|
188
|
+
end
|
|
189
|
+
|
|
190
|
+
def describe_all_expression_filters(**opts)
|
|
191
|
+
expression_filters.map do |ef_name, ef|
|
|
192
|
+
if ef.matcher?
|
|
193
|
+
handled_custom_options(ef, opts).map { |option, value| " with #{ef_name}[#{option} => #{value}]" }.join
|
|
194
|
+
elsif opts.key?(ef_name)
|
|
195
|
+
" with #{ef_name} #{opts[ef_name]}"
|
|
196
|
+
end
|
|
197
|
+
end.join
|
|
198
|
+
end
|
|
199
|
+
|
|
200
|
+
def describe_node_filters(&block)
|
|
201
|
+
describe(:node_filters, &block)
|
|
202
|
+
end
|
|
203
|
+
|
|
204
|
+
##
|
|
205
|
+
#
|
|
206
|
+
# Set the default visibility mode that should be used if no visible option is passed when using the selector.
|
|
207
|
+
# If not specified will default to the behavior indicated by Capybara.ignore_hidden_elements
|
|
208
|
+
#
|
|
209
|
+
# @param [Symbol] default_visibility Only find elements with the specified visibility:
|
|
210
|
+
# * :all - finds visible and invisible elements.
|
|
211
|
+
# * :hidden - only finds invisible elements.
|
|
212
|
+
# * :visible - only finds visible elements.
|
|
213
|
+
def visible(default_visibility = nil, &block)
|
|
214
|
+
@default_visibility = block || default_visibility
|
|
215
|
+
end
|
|
216
|
+
|
|
217
|
+
def default_visibility(fallback = Capybara.ignore_hidden_elements, options = {})
|
|
218
|
+
vis = if @default_visibility.respond_to?(:call)
|
|
219
|
+
@default_visibility.call(options)
|
|
220
|
+
else
|
|
221
|
+
@default_visibility
|
|
222
|
+
end
|
|
223
|
+
vis.nil? ? fallback : vis
|
|
224
|
+
end
|
|
225
|
+
|
|
226
|
+
# @api private
|
|
227
|
+
def raw_locator?
|
|
228
|
+
!!@raw_locator
|
|
229
|
+
end
|
|
230
|
+
|
|
231
|
+
# @api private
|
|
232
|
+
def supports_exact?
|
|
233
|
+
@supports_exact
|
|
234
|
+
end
|
|
235
|
+
|
|
236
|
+
def default_format
|
|
237
|
+
return nil if @expressions.keys.empty?
|
|
238
|
+
|
|
239
|
+
if @expressions.size == 1
|
|
240
|
+
@expressions.keys.first
|
|
241
|
+
else
|
|
242
|
+
:xpath
|
|
243
|
+
end
|
|
244
|
+
end
|
|
245
|
+
|
|
246
|
+
# @api private
|
|
247
|
+
def locator_types
|
|
248
|
+
return nil unless @locator_type
|
|
249
|
+
|
|
250
|
+
Array(@locator_type)
|
|
251
|
+
end
|
|
252
|
+
|
|
253
|
+
private
|
|
254
|
+
|
|
255
|
+
def handled_custom_options(filter, options)
|
|
256
|
+
options.select do |option, _|
|
|
257
|
+
filter.handles_option?(option) && !::Capybara::Queries::SelectorQuery::VALID_KEYS.include?(option)
|
|
258
|
+
end
|
|
259
|
+
end
|
|
260
|
+
|
|
261
|
+
def parameter_names(block)
|
|
262
|
+
key_types = %i[key keyreq]
|
|
263
|
+
# user filter_map when we drop dupport for 2.6
|
|
264
|
+
# block.parameters.select { |(type, _name)| key_types.include? type }.map { |(_, name)| name }
|
|
265
|
+
block.parameters.filter_map { |(type, name)| name if key_types.include? type }
|
|
266
|
+
end
|
|
267
|
+
|
|
268
|
+
def expression(type, allowed_filters, &block)
|
|
269
|
+
if block
|
|
270
|
+
@expressions[type] = block
|
|
271
|
+
allowed_filters = parameter_names(block) if allowed_filters.empty?
|
|
272
|
+
allowed_filters.flatten.each do |ef|
|
|
273
|
+
expression_filters[ef] = Capybara::Selector::Filters::IdentityExpressionFilter.new(ef)
|
|
274
|
+
end
|
|
275
|
+
end
|
|
276
|
+
@expressions[type]
|
|
277
|
+
end
|
|
278
|
+
end
|
|
279
|
+
end
|
|
280
|
+
end
|
|
@@ -12,16 +12,18 @@ module Capybara
|
|
|
12
12
|
@node_filters = {}
|
|
13
13
|
@expression_filters = {}
|
|
14
14
|
@descriptions = Hash.new { |hsh, key| hsh[key] = [] }
|
|
15
|
-
instance_eval(&block)
|
|
15
|
+
instance_eval(&block) if block
|
|
16
16
|
end
|
|
17
17
|
|
|
18
|
-
def node_filter(
|
|
19
|
-
|
|
18
|
+
def node_filter(names, *types, **options, &block)
|
|
19
|
+
Array(names).each do |name|
|
|
20
|
+
add_filter(name, Filters::NodeFilter, *types, **options, &block)
|
|
21
|
+
end
|
|
20
22
|
end
|
|
21
23
|
alias_method :filter, :node_filter
|
|
22
24
|
|
|
23
|
-
def expression_filter(name, *
|
|
24
|
-
add_filter(name, Filters::ExpressionFilter, *
|
|
25
|
+
def expression_filter(name, *types, **options, &block)
|
|
26
|
+
add_filter(name, Filters::ExpressionFilter, *types, **options, &block)
|
|
25
27
|
end
|
|
26
28
|
|
|
27
29
|
def describe(what = nil, &block)
|
|
@@ -40,14 +42,14 @@ module Capybara
|
|
|
40
42
|
def description(node_filters: true, expression_filters: true, **options)
|
|
41
43
|
opts = options_with_defaults(options)
|
|
42
44
|
description = +''
|
|
43
|
-
description << undeclared_descriptions.map { |desc| desc.call(opts).to_s }.join
|
|
44
|
-
description << expression_filter_descriptions.map { |desc| desc.call(opts).to_s }.join if expression_filters
|
|
45
|
-
description << node_filter_descriptions.map { |desc| desc.call(opts).to_s }.join if node_filters
|
|
45
|
+
description << undeclared_descriptions.map { |desc| desc.call(**opts).to_s }.join
|
|
46
|
+
description << expression_filter_descriptions.map { |desc| desc.call(**opts).to_s }.join if expression_filters
|
|
47
|
+
description << node_filter_descriptions.map { |desc| desc.call(**opts).to_s }.join if node_filters
|
|
46
48
|
description
|
|
47
49
|
end
|
|
48
50
|
|
|
49
51
|
def descriptions
|
|
50
|
-
warn 'DEPRECATED: FilterSet#descriptions is deprecated without replacement'
|
|
52
|
+
Capybara::Helpers.warn 'DEPRECATED: FilterSet#descriptions is deprecated without replacement'
|
|
51
53
|
[undeclared_descriptions, node_filter_descriptions, expression_filter_descriptions].flatten
|
|
52
54
|
end
|
|
53
55
|
|
|
@@ -99,20 +101,19 @@ module Capybara
|
|
|
99
101
|
private
|
|
100
102
|
|
|
101
103
|
def options_with_defaults(options)
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
end
|
|
107
|
-
end
|
|
108
|
-
options
|
|
104
|
+
expression_filters
|
|
105
|
+
.chain(node_filters)
|
|
106
|
+
.filter_map { |name, filter| [name, filter.default] if filter.default? }
|
|
107
|
+
.to_h.merge!(options)
|
|
109
108
|
end
|
|
110
109
|
|
|
111
110
|
def add_filter(name, filter_class, *types, matcher: nil, **options, &block)
|
|
112
111
|
types.each { |type| options[type] = true }
|
|
113
|
-
|
|
112
|
+
if matcher && options[:default]
|
|
113
|
+
raise 'ArgumentError', ':default option is not supported for filters with a :matcher option'
|
|
114
|
+
end
|
|
114
115
|
|
|
115
|
-
filter = filter_class.new(name, matcher, block, options)
|
|
116
|
+
filter = filter_class.new(name, matcher, block, **options)
|
|
116
117
|
(filter_class <= Filters::ExpressionFilter ? @expression_filters : @node_filters)[name] = filter
|
|
117
118
|
end
|
|
118
119
|
end
|
|
@@ -24,6 +24,10 @@ module Capybara
|
|
|
24
24
|
@options.key?(:skip_if) && value == @options[:skip_if]
|
|
25
25
|
end
|
|
26
26
|
|
|
27
|
+
def format
|
|
28
|
+
@options[:format]
|
|
29
|
+
end
|
|
30
|
+
|
|
27
31
|
def matcher?
|
|
28
32
|
!@matcher.nil?
|
|
29
33
|
end
|
|
@@ -34,7 +38,7 @@ module Capybara
|
|
|
34
38
|
|
|
35
39
|
def handles_option?(option_name)
|
|
36
40
|
if matcher?
|
|
37
|
-
|
|
41
|
+
@matcher.match? option_name
|
|
38
42
|
else
|
|
39
43
|
@name == option_name
|
|
40
44
|
end
|
|
@@ -44,7 +48,12 @@ module Capybara
|
|
|
44
48
|
|
|
45
49
|
def apply(subject, name, value, skip_value, ctx)
|
|
46
50
|
return skip_value if skip?(value)
|
|
47
|
-
|
|
51
|
+
|
|
52
|
+
unless valid_value?(value)
|
|
53
|
+
raise ArgumentError,
|
|
54
|
+
"Invalid value #{value.inspect} passed to #{self.class.name.split('::').last} #{name}" \
|
|
55
|
+
"#{" : #{name}" if @name.is_a?(Regexp)}"
|
|
56
|
+
end
|
|
48
57
|
|
|
49
58
|
if @block.arity == 2
|
|
50
59
|
filter_context(ctx).instance_exec(subject, value, &@block)
|
|
@@ -7,11 +7,21 @@ module Capybara
|
|
|
7
7
|
module Filters
|
|
8
8
|
class LocatorFilter < NodeFilter
|
|
9
9
|
def initialize(block, **options)
|
|
10
|
-
super(nil, nil, block, options)
|
|
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
|
|
@@ -69,11 +69,8 @@ module Capybara
|
|
|
69
69
|
suffixes = [[]]
|
|
70
70
|
strs.reverse_each do |str|
|
|
71
71
|
if str.is_a? Set
|
|
72
|
-
prefixes = str.
|
|
73
|
-
|
|
74
|
-
result = []
|
|
75
|
-
prefixes.product(suffixes) { |pair| result << pair.flatten(1) }
|
|
76
|
-
suffixes = result
|
|
72
|
+
prefixes = str.flat_map { |s| combine(s) }
|
|
73
|
+
suffixes = prefixes.product(suffixes).map { |pair| pair.flatten(1) }
|
|
77
74
|
else
|
|
78
75
|
suffixes.each { |arr| arr.unshift str }
|
|
79
76
|
end
|
|
@@ -100,6 +97,8 @@ module Capybara
|
|
|
100
97
|
def extract_strings(process_alternatives)
|
|
101
98
|
strings = []
|
|
102
99
|
each do |exp|
|
|
100
|
+
next if exp.ignore?
|
|
101
|
+
|
|
103
102
|
next strings.push(nil) if exp.optional? && !process_alternatives
|
|
104
103
|
|
|
105
104
|
next strings.push(exp.alternative_strings) if exp.alternation? && process_alternatives
|
|
@@ -159,6 +158,11 @@ module Capybara
|
|
|
159
158
|
alts.all?(&:any?) ? Set.new(alts) : nil
|
|
160
159
|
end
|
|
161
160
|
|
|
161
|
+
def ignore?
|
|
162
|
+
[Regexp::Expression::Assertion::NegativeLookahead,
|
|
163
|
+
Regexp::Expression::Assertion::NegativeLookbehind].any? { |klass| @exp.is_a? klass }
|
|
164
|
+
end
|
|
165
|
+
|
|
162
166
|
private
|
|
163
167
|
|
|
164
168
|
def indeterminate?
|
|
@@ -166,11 +170,11 @@ module Capybara
|
|
|
166
170
|
end
|
|
167
171
|
|
|
168
172
|
def min_repeat
|
|
169
|
-
@exp.
|
|
173
|
+
@exp.repetitions.begin
|
|
170
174
|
end
|
|
171
175
|
|
|
172
176
|
def max_repeat
|
|
173
|
-
@exp.
|
|
177
|
+
@exp.repetitions.end
|
|
174
178
|
end
|
|
175
179
|
|
|
176
180
|
def fixed_repeat?
|