capybara 3.0.0 → 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 -1
- data/History.md +891 -12
- data/License.txt +1 -1
- data/README.md +257 -84
- data/lib/capybara/config.rb +29 -10
- data/lib/capybara/cucumber.rb +1 -1
- data/lib/capybara/driver/base.rb +22 -4
- data/lib/capybara/driver/node.rb +38 -9
- data/lib/capybara/dsl.rb +9 -7
- data/lib/capybara/helpers.rb +57 -8
- data/lib/capybara/minitest/spec.rb +185 -84
- data/lib/capybara/minitest.rb +264 -145
- data/lib/capybara/node/actions.rb +248 -124
- data/lib/capybara/node/base.rb +35 -20
- data/lib/capybara/node/document.rb +14 -2
- data/lib/capybara/node/document_matchers.rb +13 -15
- data/lib/capybara/node/element.rb +350 -113
- data/lib/capybara/node/finders.rb +104 -82
- data/lib/capybara/node/matchers.rb +363 -157
- data/lib/capybara/node/simple.rb +54 -15
- 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 +9 -10
- data/lib/capybara/queries/base_query.rb +25 -18
- data/lib/capybara/queries/current_path_query.rb +16 -6
- data/lib/capybara/queries/match_query.rb +11 -0
- data/lib/capybara/queries/selector_query.rb +617 -104
- data/lib/capybara/queries/sibling_query.rb +9 -7
- data/lib/capybara/queries/style_query.rb +45 -0
- data/lib/capybara/queries/text_query.rb +40 -22
- data/lib/capybara/queries/title_query.rb +2 -2
- data/lib/capybara/rack_test/browser.rb +106 -31
- data/lib/capybara/rack_test/driver.rb +16 -7
- data/lib/capybara/rack_test/errors.rb +6 -0
- data/lib/capybara/rack_test/form.rb +74 -49
- data/lib/capybara/rack_test/node.rb +120 -47
- data/lib/capybara/rails.rb +1 -1
- 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 +87 -53
- data/lib/capybara/rspec/features.rb +8 -10
- data/lib/capybara/rspec/matcher_proxies.rb +39 -18
- data/lib/capybara/rspec/matchers/base.rb +113 -0
- data/lib/capybara/rspec/matchers/become_closed.rb +33 -0
- data/lib/capybara/rspec/matchers/compound.rb +88 -0
- 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 +29 -0
- data/lib/capybara/rspec/matchers/have_selector.rb +69 -0
- data/lib/capybara/rspec/matchers/have_sibling.rb +27 -0
- data/lib/capybara/rspec/matchers/have_text.rb +33 -0
- data/lib/capybara/rspec/matchers/have_title.rb +29 -0
- data/lib/capybara/rspec/matchers/match_selector.rb +27 -0
- data/lib/capybara/rspec/matchers/match_style.rb +43 -0
- data/lib/capybara/rspec/matchers/spatial_sugar.rb +39 -0
- data/lib/capybara/rspec/matchers.rb +142 -315
- data/lib/capybara/rspec.rb +3 -2
- data/lib/capybara/selector/builders/css_builder.rb +84 -0
- data/lib/capybara/selector/builders/xpath_builder.rb +71 -0
- data/lib/capybara/selector/css.rb +85 -8
- 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.rb +1 -0
- data/lib/capybara/selector/filter_set.rb +72 -28
- data/lib/capybara/selector/filters/base.rb +45 -2
- data/lib/capybara/selector/filters/expression_filter.rb +5 -6
- data/lib/capybara/selector/filters/locator_filter.rb +29 -0
- data/lib/capybara/selector/filters/node_filter.rb +18 -4
- data/lib/capybara/selector/regexp_disassembler.rb +211 -0
- data/lib/capybara/selector/selector.rb +89 -200
- data/lib/capybara/selector/xpath_extensions.rb +17 -0
- data/lib/capybara/selector.rb +474 -534
- 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 +270 -245
- data/lib/capybara/selenium/driver_specializations/chrome_driver.rb +117 -0
- data/lib/capybara/selenium/driver_specializations/edge_driver.rb +128 -0
- data/lib/capybara/selenium/driver_specializations/firefox_driver.rb +84 -0
- data/lib/capybara/selenium/driver_specializations/internet_explorer_driver.rb +26 -0
- 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 +110 -0
- data/lib/capybara/selenium/extensions/html5_drag.rb +229 -0
- data/lib/capybara/selenium/extensions/modifier_keys_stack.rb +28 -0
- data/lib/capybara/selenium/extensions/scroll.rb +76 -0
- data/lib/capybara/selenium/node.rb +460 -170
- data/lib/capybara/selenium/nodes/chrome_node.rb +125 -0
- data/lib/capybara/selenium/nodes/edge_node.rb +110 -0
- data/lib/capybara/selenium/nodes/firefox_node.rb +136 -0
- 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 +9 -0
- data/lib/capybara/selenium/patches/persistent_client.rb +20 -0
- data/lib/capybara/server/animation_disabler.rb +80 -0
- data/lib/capybara/server/checker.rb +44 -0
- data/lib/capybara/server/middleware.rb +71 -0
- data/lib/capybara/server.rb +58 -67
- data/lib/capybara/session/config.rb +40 -6
- data/lib/capybara/session/matchers.rb +26 -19
- data/lib/capybara/session.rb +252 -194
- data/lib/capybara/spec/public/jquery.js +5 -5
- data/lib/capybara/spec/public/offset.js +6 -0
- data/lib/capybara/spec/public/test.js +126 -8
- data/lib/capybara/spec/session/accept_alert_spec.rb +11 -11
- data/lib/capybara/spec/session/accept_confirm_spec.rb +3 -3
- data/lib/capybara/spec/session/accept_prompt_spec.rb +9 -10
- data/lib/capybara/spec/session/active_element_spec.rb +31 -0
- data/lib/capybara/spec/session/all_spec.rb +135 -44
- data/lib/capybara/spec/session/ancestor_spec.rb +24 -19
- data/lib/capybara/spec/session/assert_all_of_selectors_spec.rb +67 -38
- data/lib/capybara/spec/session/{assert_current_path.rb → assert_current_path_spec.rb} +20 -18
- data/lib/capybara/spec/session/assert_selector_spec.rb +143 -0
- data/lib/capybara/spec/session/assert_style_spec.rb +26 -0
- data/lib/capybara/spec/session/{assert_text.rb → assert_text_spec.rb} +76 -52
- data/lib/capybara/spec/session/{assert_title.rb → assert_title_spec.rb} +12 -12
- data/lib/capybara/spec/session/attach_file_spec.rb +126 -72
- data/lib/capybara/spec/session/body_spec.rb +11 -13
- data/lib/capybara/spec/session/check_spec.rb +112 -51
- data/lib/capybara/spec/session/choose_spec.rb +62 -30
- data/lib/capybara/spec/session/click_button_spec.rb +227 -161
- data/lib/capybara/spec/session/click_link_or_button_spec.rb +49 -30
- data/lib/capybara/spec/session/click_link_spec.rb +89 -55
- data/lib/capybara/spec/session/current_scope_spec.rb +8 -8
- data/lib/capybara/spec/session/current_url_spec.rb +44 -37
- data/lib/capybara/spec/session/dismiss_confirm_spec.rb +3 -3
- data/lib/capybara/spec/session/dismiss_prompt_spec.rb +2 -2
- data/lib/capybara/spec/session/element/{assert_match_selector.rb → assert_match_selector_spec.rb} +11 -9
- data/lib/capybara/spec/session/element/match_css_spec.rb +18 -10
- data/lib/capybara/spec/session/element/match_xpath_spec.rb +8 -6
- data/lib/capybara/spec/session/element/matches_selector_spec.rb +70 -56
- data/lib/capybara/spec/session/evaluate_async_script_spec.rb +7 -7
- data/lib/capybara/spec/session/evaluate_script_spec.rb +28 -8
- data/lib/capybara/spec/session/execute_script_spec.rb +8 -7
- data/lib/capybara/spec/session/fill_in_spec.rb +110 -44
- data/lib/capybara/spec/session/find_button_spec.rb +23 -23
- data/lib/capybara/spec/session/find_by_id_spec.rb +8 -8
- data/lib/capybara/spec/session/find_field_spec.rb +33 -31
- data/lib/capybara/spec/session/find_link_spec.rb +42 -14
- data/lib/capybara/spec/session/find_spec.rb +251 -142
- data/lib/capybara/spec/session/first_spec.rb +45 -44
- data/lib/capybara/spec/session/frame/frame_title_spec.rb +6 -6
- data/lib/capybara/spec/session/frame/frame_url_spec.rb +6 -6
- data/lib/capybara/spec/session/frame/switch_to_frame_spec.rb +32 -20
- data/lib/capybara/spec/session/frame/within_frame_spec.rb +46 -19
- data/lib/capybara/spec/session/go_back_spec.rb +1 -1
- data/lib/capybara/spec/session/go_forward_spec.rb +1 -1
- data/lib/capybara/spec/session/has_all_selectors_spec.rb +23 -23
- data/lib/capybara/spec/session/has_ancestor_spec.rb +46 -0
- data/lib/capybara/spec/session/has_any_selectors_spec.rb +29 -0
- data/lib/capybara/spec/session/has_button_spec.rb +98 -12
- data/lib/capybara/spec/session/has_css_spec.rb +271 -137
- data/lib/capybara/spec/session/has_current_path_spec.rb +50 -35
- data/lib/capybara/spec/session/has_element_spec.rb +47 -0
- data/lib/capybara/spec/session/has_field_spec.rb +137 -58
- data/lib/capybara/spec/session/has_link_spec.rb +46 -6
- data/lib/capybara/spec/session/has_none_selectors_spec.rb +33 -31
- data/lib/capybara/spec/session/has_select_spec.rb +84 -50
- data/lib/capybara/spec/session/has_selector_spec.rb +117 -69
- data/lib/capybara/spec/session/has_sibling_spec.rb +50 -0
- data/lib/capybara/spec/session/has_table_spec.rb +181 -4
- data/lib/capybara/spec/session/has_text_spec.rb +101 -53
- data/lib/capybara/spec/session/has_title_spec.rb +19 -14
- data/lib/capybara/spec/session/has_xpath_spec.rb +56 -38
- data/lib/capybara/spec/session/{headers.rb → headers_spec.rb} +1 -1
- data/lib/capybara/spec/session/html_spec.rb +13 -6
- data/lib/capybara/spec/session/matches_style_spec.rb +37 -0
- data/lib/capybara/spec/session/node_spec.rb +958 -122
- data/lib/capybara/spec/session/node_wrapper_spec.rb +15 -12
- data/lib/capybara/spec/session/refresh_spec.rb +9 -7
- data/lib/capybara/spec/session/reset_session_spec.rb +65 -37
- data/lib/capybara/spec/session/{response_code.rb → response_code_spec.rb} +1 -1
- data/lib/capybara/spec/session/save_and_open_page_spec.rb +2 -2
- data/lib/capybara/spec/session/save_and_open_screenshot_spec.rb +5 -4
- data/lib/capybara/spec/session/save_page_spec.rb +41 -38
- data/lib/capybara/spec/session/save_screenshot_spec.rb +13 -11
- data/lib/capybara/spec/session/screenshot_spec.rb +2 -2
- data/lib/capybara/spec/session/scroll_spec.rb +119 -0
- data/lib/capybara/spec/session/select_spec.rb +102 -76
- data/lib/capybara/spec/session/selectors_spec.rb +51 -18
- data/lib/capybara/spec/session/sibling_spec.rb +9 -9
- data/lib/capybara/spec/session/text_spec.rb +26 -24
- data/lib/capybara/spec/session/title_spec.rb +8 -6
- data/lib/capybara/spec/session/uncheck_spec.rb +41 -22
- data/lib/capybara/spec/session/unselect_spec.rb +37 -37
- data/lib/capybara/spec/session/visit_spec.rb +79 -53
- data/lib/capybara/spec/session/window/become_closed_spec.rb +22 -19
- data/lib/capybara/spec/session/window/current_window_spec.rb +4 -3
- data/lib/capybara/spec/session/window/open_new_window_spec.rb +4 -3
- data/lib/capybara/spec/session/window/switch_to_window_spec.rb +25 -21
- data/lib/capybara/spec/session/window/window_opened_by_spec.rb +10 -5
- data/lib/capybara/spec/session/window/window_spec.rb +88 -54
- data/lib/capybara/spec/session/window/windows_spec.rb +11 -8
- data/lib/capybara/spec/session/window/within_window_spec.rb +17 -16
- data/lib/capybara/spec/session/within_spec.rb +82 -44
- data/lib/capybara/spec/spec_helper.rb +46 -52
- data/lib/capybara/spec/test_app.rb +148 -41
- data/lib/capybara/spec/views/animated.erb +49 -0
- data/lib/capybara/spec/views/form.erb +156 -42
- data/lib/capybara/spec/views/frame_child.erb +4 -3
- 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 +47 -0
- 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 +21 -0
- data/lib/capybara/spec/views/spatial.erb +31 -0
- data/lib/capybara/spec/views/tables.erb +68 -1
- data/lib/capybara/spec/views/with_animation.erb +81 -0
- 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 +46 -11
- data/lib/capybara/spec/views/with_html5_svg.erb +20 -0
- data/lib/capybara/spec/views/with_jquery_animation.erb +24 -0
- data/lib/capybara/spec/views/with_js.erb +30 -5
- data/lib/capybara/spec/views/with_jstree.erb +26 -0
- data/lib/capybara/spec/views/with_namespace.erb +21 -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 +32 -26
- data/lib/capybara.rb +128 -104
- data/spec/basic_node_spec.rb +59 -34
- data/spec/capybara_spec.rb +65 -51
- data/spec/counter_spec.rb +35 -0
- data/spec/css_builder_spec.rb +101 -0
- data/spec/css_splitter_spec.rb +38 -0
- data/spec/dsl_spec.rb +84 -55
- data/spec/filter_set_spec.rb +24 -7
- data/spec/fixtures/certificate.pem +25 -0
- data/spec/fixtures/key.pem +27 -0
- data/spec/fixtures/selenium_driver_rspec_failure.rb +5 -5
- data/spec/fixtures/selenium_driver_rspec_success.rb +5 -5
- data/spec/minitest_spec.rb +49 -5
- data/spec/minitest_spec_spec.rb +92 -62
- data/spec/per_session_config_spec.rb +6 -6
- data/spec/rack_test_spec.rb +183 -115
- data/spec/regexp_dissassembler_spec.rb +250 -0
- data/spec/result_spec.rb +99 -39
- data/spec/rspec/features_spec.rb +28 -25
- data/spec/rspec/scenarios_spec.rb +10 -6
- data/spec/rspec/shared_spec_matchers.rb +418 -364
- data/spec/rspec/views_spec.rb +4 -3
- data/spec/rspec_matchers_spec.rb +35 -10
- data/spec/rspec_spec.rb +109 -85
- data/spec/sauce_spec_chrome.rb +43 -0
- data/spec/selector_spec.rb +392 -62
- data/spec/selenium_spec_chrome.rb +183 -41
- data/spec/selenium_spec_chrome_remote.rb +96 -0
- data/spec/selenium_spec_edge.rb +41 -8
- data/spec/selenium_spec_firefox.rb +228 -0
- data/spec/selenium_spec_firefox_remote.rb +94 -0
- data/spec/selenium_spec_ie.rb +129 -11
- data/spec/selenium_spec_safari.rb +162 -0
- data/spec/server_spec.rb +192 -81
- data/spec/session_spec.rb +52 -16
- data/spec/shared_selenium_node.rb +79 -0
- data/spec/shared_selenium_session.rb +460 -123
- data/spec/spec_helper.rb +124 -2
- data/spec/whitespace_normalizer_spec.rb +54 -0
- data/spec/xpath_builder_spec.rb +93 -0
- metadata +344 -45
- data/.yard/templates_custom/default/class/html/selectors.erb +0 -38
- data/.yard/templates_custom/default/class/html/setup.rb +0 -17
- data/.yard/yard_extensions.rb +0 -78
- data/lib/capybara/rspec/compound.rb +0 -90
- data/lib/capybara/spec/session/assert_selector.rb +0 -149
- data/lib/capybara/spec/session/source_spec.rb +0 -0
- data/lib/capybara/spec/views/with_title.erb +0 -5
- data/spec/selenium_spec_marionette.rb +0 -143
@@ -0,0 +1,27 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
Capybara.add_selector(:radio_button, locator_type: [String, Symbol]) do
|
4
|
+
label 'radio button'
|
5
|
+
xpath do |locator, allow_self: nil, **options|
|
6
|
+
xpath = XPath.axis(allow_self ? :'descendant-or-self' : :descendant, :input)[
|
7
|
+
XPath.attr(:type) == 'radio'
|
8
|
+
]
|
9
|
+
locate_field(xpath, locator, **options)
|
10
|
+
end
|
11
|
+
|
12
|
+
filter_set(:_field, %i[checked unchecked disabled name])
|
13
|
+
|
14
|
+
node_filter(%i[option with]) do |node, value|
|
15
|
+
val = node.value
|
16
|
+
(value.is_a?(Regexp) ? value.match?(val) : val == value.to_s).tap do |res|
|
17
|
+
add_error("Expected value to be #{value.inspect} but it was #{val.inspect}") unless res
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
describe_node_filters do |option: nil, with: nil, **|
|
22
|
+
desc = +''
|
23
|
+
desc << " with value #{option.inspect}" if option
|
24
|
+
desc << " with value #{with.inspect}" if with
|
25
|
+
desc
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,81 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
Capybara.add_selector(:select, locator_type: [String, Symbol]) do
|
4
|
+
label 'select box'
|
5
|
+
|
6
|
+
xpath do |locator, **options|
|
7
|
+
xpath = XPath.descendant(:select)
|
8
|
+
locate_field(xpath, locator, **options)
|
9
|
+
end
|
10
|
+
|
11
|
+
filter_set(:_field, %i[disabled multiple name placeholder])
|
12
|
+
|
13
|
+
node_filter(:options) do |node, options|
|
14
|
+
actual = options_text(node)
|
15
|
+
(options.sort == actual.sort).tap do |res|
|
16
|
+
add_error("Expected options #{options.inspect} found #{actual.inspect}") unless res
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
node_filter(:enabled_options) do |node, options|
|
21
|
+
actual = options_text(node) { |o| !o.disabled? }
|
22
|
+
(options.sort == actual.sort).tap do |res|
|
23
|
+
add_error("Expected enabled options #{options.inspect} found #{actual.inspect}") unless res
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
node_filter(:disabled_options) do |node, options|
|
28
|
+
actual = options_text(node, &:disabled?)
|
29
|
+
(options.sort == actual.sort).tap do |res|
|
30
|
+
add_error("Expected disabled options #{options.inspect} found #{actual.inspect}") unless res
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
expression_filter(:with_options) do |expr, options|
|
35
|
+
options.inject(expr) do |xpath, option|
|
36
|
+
xpath.where(expression_for(:option, option))
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
node_filter(:selected) do |node, selected|
|
41
|
+
actual = options_text(node, visible: false, &:selected?)
|
42
|
+
(Array(selected).sort == actual.sort).tap do |res|
|
43
|
+
add_error("Expected #{selected.inspect} to be selected found #{actual.inspect}") unless res
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
node_filter(:with_selected) do |node, selected|
|
48
|
+
actual = options_text(node, visible: false, &:selected?)
|
49
|
+
(Array(selected) - actual).empty?.tap do |res|
|
50
|
+
add_error("Expected at least #{selected.inspect} to be selected found #{actual.inspect}") unless res
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
describe_expression_filters do |with_options: nil, **|
|
55
|
+
desc = +''
|
56
|
+
desc << " with at least options #{with_options.inspect}" if with_options
|
57
|
+
desc
|
58
|
+
end
|
59
|
+
|
60
|
+
describe_node_filters do |
|
61
|
+
options: nil, disabled_options: nil, enabled_options: nil,
|
62
|
+
selected: nil, with_selected: nil,
|
63
|
+
disabled: nil, **|
|
64
|
+
desc = +''
|
65
|
+
desc << " with options #{options.inspect}" if options
|
66
|
+
desc << " with disabled options #{disabled_options.inspect}}" if disabled_options
|
67
|
+
desc << " with enabled options #{enabled_options.inspect}" if enabled_options
|
68
|
+
desc << " with #{selected.inspect} selected" if selected
|
69
|
+
desc << " with at least #{with_selected.inspect} selected" if with_selected
|
70
|
+
desc << ' which is disabled' if disabled
|
71
|
+
desc
|
72
|
+
end
|
73
|
+
|
74
|
+
def options_text(node, **opts, &filter_block)
|
75
|
+
opts[:wait] = false
|
76
|
+
opts[:visible] = false unless node.visible?
|
77
|
+
node.all(:xpath, './/option', **opts, &filter_block).map do |o|
|
78
|
+
o.text((:all if opts[:visible] == false))
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
@@ -0,0 +1,109 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
Capybara.add_selector(:table, locator_type: [String, Symbol]) do
|
4
|
+
xpath do |locator, caption: nil, **|
|
5
|
+
xpath = XPath.descendant(:table)
|
6
|
+
unless locator.nil?
|
7
|
+
locator_matchers = (XPath.attr(:id) == locator.to_s) | XPath.descendant(:caption).is(locator.to_s)
|
8
|
+
locator_matchers |= XPath.attr(test_id) == locator if test_id
|
9
|
+
xpath = xpath[locator_matchers]
|
10
|
+
end
|
11
|
+
xpath = xpath[XPath.descendant(:caption) == caption] if caption
|
12
|
+
xpath
|
13
|
+
end
|
14
|
+
|
15
|
+
expression_filter(:with_cols, valid_values: [Array]) do |xpath, cols|
|
16
|
+
col_conditions = cols.map do |col|
|
17
|
+
if col.is_a? Hash
|
18
|
+
col.reduce(nil) do |xp, (header, cell_str)|
|
19
|
+
header = XPath.descendant(:th)[XPath.string.n.is(header)]
|
20
|
+
td = XPath.descendant(:tr)[header].descendant(:td)
|
21
|
+
cell_condition = XPath.string.n.is(cell_str)
|
22
|
+
if xp
|
23
|
+
prev_cell = XPath.ancestor(:table)[1].join(xp)
|
24
|
+
cell_condition &= (prev_cell & prev_col_position?(prev_cell))
|
25
|
+
end
|
26
|
+
td[cell_condition]
|
27
|
+
end
|
28
|
+
else
|
29
|
+
cells_xp = col.reduce(nil) do |prev_cell, cell_str|
|
30
|
+
cell_condition = XPath.string.n.is(cell_str)
|
31
|
+
|
32
|
+
if prev_cell
|
33
|
+
prev_cell = XPath.ancestor(:tr)[1].preceding_sibling(:tr).join(prev_cell)
|
34
|
+
cell_condition &= (prev_cell & prev_col_position?(prev_cell))
|
35
|
+
end
|
36
|
+
|
37
|
+
XPath.descendant(:td)[cell_condition]
|
38
|
+
end
|
39
|
+
XPath.descendant(:tr).join(cells_xp)
|
40
|
+
end
|
41
|
+
end.reduce(:&)
|
42
|
+
xpath[col_conditions]
|
43
|
+
end
|
44
|
+
|
45
|
+
expression_filter(:cols, valid_values: [Array]) do |xpath, cols|
|
46
|
+
raise ArgumentError, ':cols must be an Array of Arrays' unless cols.all?(Array)
|
47
|
+
|
48
|
+
rows = cols.transpose
|
49
|
+
col_conditions = rows.map { |row| match_row(row, match_size: true) }.reduce(:&)
|
50
|
+
xpath[match_row_count(rows.size)][col_conditions]
|
51
|
+
end
|
52
|
+
|
53
|
+
expression_filter(:with_rows, valid_values: [Array]) do |xpath, rows|
|
54
|
+
rows_conditions = rows.map { |row| match_row(row) }.reduce(:&)
|
55
|
+
xpath[rows_conditions]
|
56
|
+
end
|
57
|
+
|
58
|
+
expression_filter(:rows, valid_values: [Array]) do |xpath, rows|
|
59
|
+
rows_conditions = rows.map { |row| match_row(row, match_size: true) }.reduce(:&)
|
60
|
+
xpath[match_row_count(rows.size)][rows_conditions]
|
61
|
+
end
|
62
|
+
|
63
|
+
describe_expression_filters do |caption: nil, **|
|
64
|
+
" with caption \"#{caption}\"" if caption
|
65
|
+
end
|
66
|
+
|
67
|
+
def prev_col_position?(cell)
|
68
|
+
XPath.position.equals(cell_position(cell))
|
69
|
+
end
|
70
|
+
|
71
|
+
def cell_position(cell)
|
72
|
+
cell.preceding_sibling(:td).count.plus(1)
|
73
|
+
end
|
74
|
+
|
75
|
+
def match_row(row, match_size: false)
|
76
|
+
xp = XPath.descendant(:tr)[
|
77
|
+
if row.is_a? Hash
|
78
|
+
row_match_cells_to_headers(row)
|
79
|
+
else
|
80
|
+
XPath.descendant(:td)[row_match_ordered_cells(row)]
|
81
|
+
end
|
82
|
+
]
|
83
|
+
xp = xp[XPath.descendant(:td).count.equals(row.size)] if match_size
|
84
|
+
xp
|
85
|
+
end
|
86
|
+
|
87
|
+
def match_row_count(size)
|
88
|
+
XPath.descendant(:tbody).descendant(:tr).count.equals(size) |
|
89
|
+
(XPath.descendant(:tr).count.equals(size) & ~XPath.descendant(:tbody))
|
90
|
+
end
|
91
|
+
|
92
|
+
def row_match_cells_to_headers(row)
|
93
|
+
row.map do |header, cell|
|
94
|
+
header_xp = XPath.ancestor(:table)[1].descendant(:tr)[1].descendant(:th)[XPath.string.n.is(header)]
|
95
|
+
XPath.descendant(:td)[
|
96
|
+
XPath.string.n.is(cell) & header_xp.boolean & XPath.position.equals(header_xp.preceding_sibling.count.plus(1))
|
97
|
+
]
|
98
|
+
end.reduce(:&)
|
99
|
+
end
|
100
|
+
|
101
|
+
def row_match_ordered_cells(row)
|
102
|
+
row_conditions = row.map do |cell|
|
103
|
+
XPath.self(:td)[XPath.string.n.is(cell)]
|
104
|
+
end
|
105
|
+
row_conditions.reverse.reduce do |cond, cell|
|
106
|
+
cell[XPath.following_sibling[cond]]
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
Capybara.add_selector(:table_row, locator_type: [Array, Hash]) do
|
4
|
+
xpath do |locator|
|
5
|
+
xpath = XPath.descendant(:tr)
|
6
|
+
if locator.is_a? Hash
|
7
|
+
locator.reduce(xpath) do |xp, (header, cell)|
|
8
|
+
header_xp = XPath.ancestor(:table)[1].descendant(:tr)[1].descendant(:th)[XPath.string.n.is(header)]
|
9
|
+
cell_xp = XPath.descendant(:td)[
|
10
|
+
XPath.string.n.is(cell) & header_xp.boolean & XPath.position.equals(header_xp.preceding_sibling.count.plus(1))
|
11
|
+
]
|
12
|
+
xp.where(cell_xp)
|
13
|
+
end
|
14
|
+
else
|
15
|
+
initial_td = XPath.descendant(:td)[XPath.string.n.is(locator.shift)]
|
16
|
+
tds = locator.reverse.map { |cell| XPath.following_sibling(:td)[XPath.string.n.is(cell)] }
|
17
|
+
.reduce { |xp, cell| cell.where(xp) }
|
18
|
+
xpath[initial_td[tds]]
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -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
|
@@ -5,43 +5,65 @@ require 'capybara/selector/filter'
|
|
5
5
|
module Capybara
|
6
6
|
class Selector
|
7
7
|
class FilterSet
|
8
|
-
attr_reader :
|
8
|
+
attr_reader :node_filters, :expression_filters
|
9
9
|
|
10
10
|
def initialize(name, &block)
|
11
11
|
@name = name
|
12
|
-
@
|
13
|
-
|
12
|
+
@node_filters = {}
|
13
|
+
@expression_filters = {}
|
14
|
+
@descriptions = Hash.new { |hsh, key| hsh[key] = [] }
|
15
|
+
instance_eval(&block) if block
|
14
16
|
end
|
15
17
|
|
16
|
-
def
|
17
|
-
|
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
|
18
22
|
end
|
23
|
+
alias_method :filter, :node_filter
|
19
24
|
|
20
|
-
def expression_filter(name, *
|
21
|
-
add_filter(name, Filters::ExpressionFilter, *
|
25
|
+
def expression_filter(name, *types, **options, &block)
|
26
|
+
add_filter(name, Filters::ExpressionFilter, *types, **options, &block)
|
22
27
|
end
|
23
28
|
|
24
|
-
def describe(&block)
|
25
|
-
|
29
|
+
def describe(what = nil, &block)
|
30
|
+
case what
|
31
|
+
when nil
|
32
|
+
undeclared_descriptions.push block
|
33
|
+
when :node_filters
|
34
|
+
node_filter_descriptions.push block
|
35
|
+
when :expression_filters
|
36
|
+
expression_filter_descriptions.push block
|
37
|
+
else
|
38
|
+
raise ArgumentError, 'Unknown description type'
|
39
|
+
end
|
26
40
|
end
|
27
41
|
|
28
|
-
def description(**options)
|
42
|
+
def description(node_filters: true, expression_filters: true, **options)
|
29
43
|
opts = options_with_defaults(options)
|
30
|
-
|
31
|
-
|
32
|
-
|
44
|
+
description = +''
|
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
|
48
|
+
description
|
33
49
|
end
|
34
50
|
|
35
|
-
def
|
36
|
-
|
51
|
+
def descriptions
|
52
|
+
Capybara::Helpers.warn 'DEPRECATED: FilterSet#descriptions is deprecated without replacement'
|
53
|
+
[undeclared_descriptions, node_filter_descriptions, expression_filter_descriptions].flatten
|
37
54
|
end
|
38
55
|
|
39
|
-
def
|
40
|
-
filters.
|
41
|
-
end
|
56
|
+
def import(name, filters = nil)
|
57
|
+
filter_selector = filters.nil? ? ->(*) { true } : ->(filter_name, _) { filters.include? filter_name }
|
42
58
|
|
43
|
-
|
44
|
-
|
59
|
+
self.class[name].tap do |f_set|
|
60
|
+
expression_filters.merge!(f_set.expression_filters.select(&filter_selector))
|
61
|
+
node_filters.merge!(f_set.node_filters.select(&filter_selector))
|
62
|
+
f_set.undeclared_descriptions.each { |desc| describe(&desc) }
|
63
|
+
f_set.expression_filter_descriptions.each { |desc| describe(:expression_filters, &desc) }
|
64
|
+
f_set.node_filter_descriptions.each { |desc| describe(:node_filters, &desc) }
|
65
|
+
end
|
66
|
+
self
|
45
67
|
end
|
46
68
|
|
47
69
|
class << self
|
@@ -49,6 +71,10 @@ module Capybara
|
|
49
71
|
@filter_sets ||= {} # rubocop:disable Naming/MemoizedInstanceVariableName
|
50
72
|
end
|
51
73
|
|
74
|
+
def [](name)
|
75
|
+
all.fetch(name.to_sym) { |set_name| raise ArgumentError, "Unknown filter set (:#{set_name})" }
|
76
|
+
end
|
77
|
+
|
52
78
|
def add(name, &block)
|
53
79
|
all[name.to_sym] = FilterSet.new(name.to_sym, &block)
|
54
80
|
end
|
@@ -58,19 +84,37 @@ module Capybara
|
|
58
84
|
end
|
59
85
|
end
|
60
86
|
|
87
|
+
protected
|
88
|
+
|
89
|
+
def undeclared_descriptions
|
90
|
+
@descriptions[:undeclared]
|
91
|
+
end
|
92
|
+
|
93
|
+
def node_filter_descriptions
|
94
|
+
@descriptions[:node_filters]
|
95
|
+
end
|
96
|
+
|
97
|
+
def expression_filter_descriptions
|
98
|
+
@descriptions[:expression_filters]
|
99
|
+
end
|
100
|
+
|
61
101
|
private
|
62
102
|
|
63
103
|
def options_with_defaults(options)
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
options
|
104
|
+
expression_filters
|
105
|
+
.chain(node_filters)
|
106
|
+
.filter_map { |name, filter| [name, filter.default] if filter.default? }
|
107
|
+
.to_h.merge!(options)
|
69
108
|
end
|
70
109
|
|
71
|
-
def add_filter(name, filter_class, *types, **options, &block)
|
72
|
-
types.each { |
|
73
|
-
|
110
|
+
def add_filter(name, filter_class, *types, matcher: nil, **options, &block)
|
111
|
+
types.each { |type| options[type] = true }
|
112
|
+
if matcher && options[:default]
|
113
|
+
raise 'ArgumentError', ':default option is not supported for filters with a :matcher option'
|
114
|
+
end
|
115
|
+
|
116
|
+
filter = filter_class.new(name, matcher, block, **options)
|
117
|
+
(filter_class <= Filters::ExpressionFilter ? @expression_filters : @node_filters)[name] = filter
|
74
118
|
end
|
75
119
|
end
|
76
120
|
end
|