capybara 2.7.0 → 3.35.3
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 +5 -5
- data/.yardopts +1 -0
- data/History.md +1147 -11
- data/License.txt +1 -1
- data/README.md +252 -131
- data/lib/capybara/config.rb +92 -0
- data/lib/capybara/cucumber.rb +3 -3
- data/lib/capybara/driver/base.rb +52 -21
- data/lib/capybara/driver/node.rb +48 -14
- data/lib/capybara/dsl.rb +16 -9
- data/lib/capybara/helpers.rb +72 -81
- data/lib/capybara/minitest/spec.rb +267 -0
- data/lib/capybara/minitest.rb +385 -0
- data/lib/capybara/node/actions.rb +337 -89
- data/lib/capybara/node/base.rb +50 -32
- data/lib/capybara/node/document.rb +19 -3
- data/lib/capybara/node/document_matchers.rb +22 -24
- data/lib/capybara/node/element.rb +388 -125
- data/lib/capybara/node/finders.rb +231 -121
- data/lib/capybara/node/matchers.rb +503 -217
- data/lib/capybara/node/simple.rb +64 -27
- data/lib/capybara/queries/ancestor_query.rb +27 -0
- data/lib/capybara/queries/base_query.rb +87 -11
- data/lib/capybara/queries/current_path_query.rb +24 -24
- data/lib/capybara/queries/match_query.rb +15 -10
- data/lib/capybara/queries/selector_query.rb +675 -81
- data/lib/capybara/queries/sibling_query.rb +26 -0
- data/lib/capybara/queries/style_query.rb +45 -0
- data/lib/capybara/queries/text_query.rb +88 -20
- data/lib/capybara/queries/title_query.rb +9 -11
- data/lib/capybara/rack_test/browser.rb +63 -39
- data/lib/capybara/rack_test/css_handlers.rb +6 -4
- data/lib/capybara/rack_test/driver.rb +26 -16
- data/lib/capybara/rack_test/errors.rb +6 -0
- data/lib/capybara/rack_test/form.rb +73 -58
- data/lib/capybara/rack_test/node.rb +187 -67
- data/lib/capybara/rails.rb +4 -8
- data/lib/capybara/registration_container.rb +44 -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 +45 -0
- data/lib/capybara/result.rb +142 -14
- data/lib/capybara/rspec/features.rb +17 -42
- data/lib/capybara/rspec/matcher_proxies.rb +82 -0
- data/lib/capybara/rspec/matchers/base.rb +111 -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 +77 -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 +143 -244
- data/lib/capybara/rspec.rb +10 -12
- 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 +102 -0
- data/lib/capybara/selector/definition/button.rb +63 -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 +54 -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 +278 -0
- data/lib/capybara/selector/filter.rb +3 -46
- data/lib/capybara/selector/filter_set.rb +124 -0
- data/lib/capybara/selector/filters/base.rb +77 -0
- data/lib/capybara/selector/filters/expression_filter.rb +22 -0
- data/lib/capybara/selector/filters/locator_filter.rb +29 -0
- data/lib/capybara/selector/filters/node_filter.rb +31 -0
- data/lib/capybara/selector/regexp_disassembler.rb +214 -0
- data/lib/capybara/selector/selector.rb +155 -0
- data/lib/capybara/selector/xpath_extensions.rb +17 -0
- data/lib/capybara/selector.rb +232 -369
- 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 +380 -142
- data/lib/capybara/selenium/driver_specializations/chrome_driver.rb +117 -0
- data/lib/capybara/selenium/driver_specializations/edge_driver.rb +124 -0
- data/lib/capybara/selenium/driver_specializations/firefox_driver.rb +89 -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 +228 -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/logger_suppressor.rb +40 -0
- data/lib/capybara/selenium/node.rb +528 -97
- data/lib/capybara/selenium/nodes/chrome_node.rb +137 -0
- data/lib/capybara/selenium/nodes/edge_node.rb +104 -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/action_pauser.rb +26 -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 +63 -0
- data/lib/capybara/server/checker.rb +44 -0
- data/lib/capybara/server/middleware.rb +71 -0
- data/lib/capybara/server.rb +74 -71
- data/lib/capybara/session/config.rb +126 -0
- data/lib/capybara/session/matchers.rb +44 -27
- data/lib/capybara/session.rb +500 -297
- data/lib/capybara/spec/fixtures/no_extension +1 -0
- 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 +168 -14
- data/lib/capybara/spec/session/accept_alert_spec.rb +37 -14
- data/lib/capybara/spec/session/accept_confirm_spec.rb +7 -6
- data/lib/capybara/spec/session/accept_prompt_spec.rb +38 -10
- data/lib/capybara/spec/session/all_spec.rb +179 -59
- data/lib/capybara/spec/session/ancestor_spec.rb +88 -0
- data/lib/capybara/spec/session/assert_all_of_selectors_spec.rb +140 -0
- data/lib/capybara/spec/session/assert_current_path_spec.rb +75 -0
- 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_spec.rb +258 -0
- data/lib/capybara/spec/session/assert_title_spec.rb +93 -0
- data/lib/capybara/spec/session/attach_file_spec.rb +154 -48
- data/lib/capybara/spec/session/body_spec.rb +12 -13
- data/lib/capybara/spec/session/check_spec.rb +168 -41
- data/lib/capybara/spec/session/choose_spec.rb +75 -23
- data/lib/capybara/spec/session/click_button_spec.rb +243 -175
- data/lib/capybara/spec/session/click_link_or_button_spec.rb +57 -32
- data/lib/capybara/spec/session/click_link_spec.rb +100 -53
- data/lib/capybara/spec/session/current_scope_spec.rb +11 -10
- data/lib/capybara/spec/session/current_url_spec.rb +61 -35
- data/lib/capybara/spec/session/dismiss_confirm_spec.rb +7 -7
- data/lib/capybara/spec/session/dismiss_prompt_spec.rb +5 -4
- data/lib/capybara/spec/session/element/{assert_match_selector.rb → assert_match_selector_spec.rb} +13 -6
- data/lib/capybara/spec/session/element/match_css_spec.rb +21 -7
- data/lib/capybara/spec/session/element/match_xpath_spec.rb +9 -7
- data/lib/capybara/spec/session/element/matches_selector_spec.rb +91 -34
- data/lib/capybara/spec/session/evaluate_async_script_spec.rb +23 -0
- data/lib/capybara/spec/session/evaluate_script_spec.rb +45 -3
- data/lib/capybara/spec/session/execute_script_spec.rb +24 -4
- data/lib/capybara/spec/session/fill_in_spec.rb +166 -64
- data/lib/capybara/spec/session/find_button_spec.rb +37 -18
- data/lib/capybara/spec/session/find_by_id_spec.rb +10 -9
- data/lib/capybara/spec/session/find_field_spec.rb +57 -34
- data/lib/capybara/spec/session/find_link_spec.rb +47 -10
- data/lib/capybara/spec/session/find_spec.rb +290 -144
- data/lib/capybara/spec/session/first_spec.rb +91 -48
- 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 +116 -0
- data/lib/capybara/spec/session/frame/within_frame_spec.rb +112 -0
- data/lib/capybara/spec/session/go_back_spec.rb +3 -2
- data/lib/capybara/spec/session/go_forward_spec.rb +3 -2
- data/lib/capybara/spec/session/has_all_selectors_spec.rb +69 -0
- data/lib/capybara/spec/session/has_ancestor_spec.rb +46 -0
- data/lib/capybara/spec/session/has_any_selectors_spec.rb +25 -0
- data/lib/capybara/spec/session/has_button_spec.rb +76 -19
- data/lib/capybara/spec/session/has_css_spec.rb +277 -131
- data/lib/capybara/spec/session/has_current_path_spec.rb +98 -26
- data/lib/capybara/spec/session/has_field_spec.rb +177 -107
- data/lib/capybara/spec/session/has_link_spec.rb +13 -12
- data/lib/capybara/spec/session/has_none_selectors_spec.rb +78 -0
- data/lib/capybara/spec/session/has_select_spec.rb +191 -95
- data/lib/capybara/spec/session/has_selector_spec.rb +128 -64
- data/lib/capybara/spec/session/has_sibling_spec.rb +50 -0
- data/lib/capybara/spec/session/has_table_spec.rb +172 -5
- data/lib/capybara/spec/session/has_text_spec.rb +126 -60
- data/lib/capybara/spec/session/has_title_spec.rb +35 -12
- data/lib/capybara/spec/session/has_xpath_spec.rb +74 -53
- data/lib/capybara/spec/session/{headers.rb → headers_spec.rb} +3 -2
- data/lib/capybara/spec/session/html_spec.rb +14 -6
- data/lib/capybara/spec/session/matches_style_spec.rb +35 -0
- data/lib/capybara/spec/session/node_spec.rb +1028 -131
- data/lib/capybara/spec/session/node_wrapper_spec.rb +39 -0
- data/lib/capybara/spec/session/refresh_spec.rb +34 -0
- data/lib/capybara/spec/session/reset_session_spec.rb +75 -34
- data/lib/capybara/spec/session/{response_code.rb → response_code_spec.rb} +2 -1
- data/lib/capybara/spec/session/save_and_open_page_spec.rb +3 -2
- data/lib/capybara/spec/session/save_and_open_screenshot_spec.rb +11 -15
- data/lib/capybara/spec/session/save_page_spec.rb +42 -55
- data/lib/capybara/spec/session/save_screenshot_spec.rb +16 -14
- data/lib/capybara/spec/session/screenshot_spec.rb +2 -2
- data/lib/capybara/spec/session/scroll_spec.rb +117 -0
- data/lib/capybara/spec/session/select_spec.rb +112 -85
- data/lib/capybara/spec/session/selectors_spec.rb +71 -8
- data/lib/capybara/spec/session/sibling_spec.rb +52 -0
- data/lib/capybara/spec/session/text_spec.rb +38 -23
- data/lib/capybara/spec/session/title_spec.rb +17 -5
- data/lib/capybara/spec/session/uncheck_spec.rb +71 -12
- data/lib/capybara/spec/session/unselect_spec.rb +44 -43
- data/lib/capybara/spec/session/visit_spec.rb +99 -32
- data/lib/capybara/spec/session/window/become_closed_spec.rb +33 -29
- data/lib/capybara/spec/session/window/current_window_spec.rb +5 -3
- data/lib/capybara/spec/session/window/open_new_window_spec.rb +5 -3
- data/lib/capybara/spec/session/window/switch_to_window_spec.rb +39 -30
- data/lib/capybara/spec/session/window/window_opened_by_spec.rb +17 -10
- data/lib/capybara/spec/session/window/window_spec.rb +121 -73
- data/lib/capybara/spec/session/window/windows_spec.rb +12 -10
- data/lib/capybara/spec/session/window/within_window_spec.rb +52 -82
- data/lib/capybara/spec/session/within_spec.rb +76 -43
- data/lib/capybara/spec/spec_helper.rb +67 -33
- data/lib/capybara/spec/test_app.rb +85 -36
- data/lib/capybara/spec/views/animated.erb +49 -0
- data/lib/capybara/spec/views/buttons.erb +1 -1
- data/lib/capybara/spec/views/fieldsets.erb +1 -1
- data/lib/capybara/spec/views/form.erb +227 -20
- data/lib/capybara/spec/views/frame_child.erb +10 -2
- data/lib/capybara/spec/views/frame_one.erb +2 -1
- data/lib/capybara/spec/views/frame_parent.erb +2 -2
- data/lib/capybara/spec/views/frame_two.erb +1 -1
- data/lib/capybara/spec/views/header_links.erb +1 -1
- data/lib/capybara/spec/views/host_links.erb +1 -1
- data/lib/capybara/spec/views/initial_alert.erb +10 -0
- data/lib/capybara/spec/views/obscured.erb +47 -0
- data/lib/capybara/spec/views/offset.erb +32 -0
- data/lib/capybara/spec/views/path.erb +1 -1
- 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/postback.erb +1 -1
- data/lib/capybara/spec/views/react.erb +45 -0
- data/lib/capybara/spec/views/scroll.erb +20 -0
- data/lib/capybara/spec/views/spatial.erb +31 -0
- data/lib/capybara/spec/views/tables.erb +69 -2
- data/lib/capybara/spec/views/with_animation.erb +82 -0
- data/lib/capybara/spec/views/with_base_tag.erb +1 -1
- data/lib/capybara/spec/views/with_count.erb +1 -1
- data/lib/capybara/spec/views/with_dragula.erb +24 -0
- data/lib/capybara/spec/views/with_fixed_header_footer.erb +17 -0
- data/lib/capybara/spec/views/with_hover.erb +7 -1
- data/lib/capybara/spec/views/with_hover1.erb +10 -0
- data/lib/capybara/spec/views/with_html.erb +100 -10
- data/lib/capybara/spec/views/with_html5_svg.erb +20 -0
- data/lib/capybara/spec/views/with_html_entities.erb +1 -1
- data/lib/capybara/spec/views/with_jquery_animation.erb +24 -0
- data/lib/capybara/spec/views/with_js.erb +49 -3
- data/lib/capybara/spec/views/with_jstree.erb +26 -0
- data/lib/capybara/spec/views/with_namespace.erb +20 -0
- data/lib/capybara/spec/views/with_scope.erb +1 -1
- data/lib/capybara/spec/views/with_scope_other.erb +6 -0
- data/lib/capybara/spec/views/with_simple_html.erb +1 -1
- data/lib/capybara/spec/views/with_sortable_js.erb +21 -0
- data/lib/capybara/spec/views/with_title.erb +1 -1
- data/lib/capybara/spec/views/with_unload_alert.erb +3 -1
- data/lib/capybara/spec/views/with_windows.erb +7 -1
- data/lib/capybara/spec/views/within_frames.erb +6 -3
- data/lib/capybara/version.rb +2 -1
- data/lib/capybara/window.rb +39 -21
- data/lib/capybara.rb +208 -186
- data/spec/basic_node_spec.rb +52 -39
- data/spec/capybara_spec.rb +72 -50
- data/spec/css_builder_spec.rb +101 -0
- data/spec/css_splitter_spec.rb +38 -0
- data/spec/dsl_spec.rb +81 -61
- data/spec/filter_set_spec.rb +46 -0
- data/spec/fixtures/capybara.csv +1 -0
- data/spec/fixtures/certificate.pem +25 -0
- data/spec/fixtures/key.pem +27 -0
- data/spec/fixtures/selenium_driver_rspec_failure.rb +7 -3
- data/spec/fixtures/selenium_driver_rspec_success.rb +7 -3
- data/spec/minitest_spec.rb +164 -0
- data/spec/minitest_spec_spec.rb +162 -0
- data/spec/per_session_config_spec.rb +68 -0
- data/spec/rack_test_spec.rb +189 -96
- data/spec/regexp_dissassembler_spec.rb +250 -0
- data/spec/result_spec.rb +143 -13
- data/spec/rspec/features_spec.rb +38 -32
- data/spec/rspec/scenarios_spec.rb +9 -7
- data/spec/rspec/shared_spec_matchers.rb +959 -0
- data/spec/rspec/views_spec.rb +9 -3
- data/spec/rspec_matchers_spec.rb +62 -0
- data/spec/rspec_spec.rb +127 -30
- data/spec/sauce_spec_chrome.rb +43 -0
- data/spec/selector_spec.rb +458 -37
- data/spec/selenium_spec_chrome.rb +196 -9
- data/spec/selenium_spec_chrome_remote.rb +100 -0
- data/spec/selenium_spec_edge.rb +47 -0
- data/spec/selenium_spec_firefox.rb +210 -0
- data/spec/selenium_spec_firefox_remote.rb +80 -0
- data/spec/selenium_spec_ie.rb +150 -0
- data/spec/selenium_spec_safari.rb +148 -0
- data/spec/server_spec.rb +200 -101
- data/spec/session_spec.rb +91 -0
- data/spec/shared_selenium_node.rb +83 -0
- data/spec/shared_selenium_session.rb +558 -0
- data/spec/spec_helper.rb +94 -2
- data/spec/xpath_builder_spec.rb +93 -0
- metadata +420 -60
- data/lib/capybara/query.rb +0 -7
- data/lib/capybara/spec/session/assert_current_path.rb +0 -60
- data/lib/capybara/spec/session/assert_selector.rb +0 -148
- data/lib/capybara/spec/session/assert_text.rb +0 -196
- data/lib/capybara/spec/session/assert_title.rb +0 -70
- data/lib/capybara/spec/session/source_spec.rb +0 -0
- data/lib/capybara/spec/session/within_frame_spec.rb +0 -53
- data/spec/rspec/matchers_spec.rb +0 -827
- data/spec/selenium_spec.rb +0 -151
|
@@ -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) & 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| xp.where(cell) }
|
|
18
|
+
xpath[initial_td[tds]]
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
@@ -0,0 +1,278 @@
|
|
|
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 shouble be used if no visibile 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
|
+
block.parameters.select { |(type, _name)| key_types.include? type }.map { |(_type, name)| name }
|
|
264
|
+
end
|
|
265
|
+
|
|
266
|
+
def expression(type, allowed_filters, &block)
|
|
267
|
+
if block
|
|
268
|
+
@expressions[type] = block
|
|
269
|
+
allowed_filters = parameter_names(block) if allowed_filters.empty?
|
|
270
|
+
allowed_filters.flatten.each do |ef|
|
|
271
|
+
expression_filters[ef] = Capybara::Selector::Filters::IdentityExpressionFilter.new(ef)
|
|
272
|
+
end
|
|
273
|
+
end
|
|
274
|
+
@expressions[type]
|
|
275
|
+
end
|
|
276
|
+
end
|
|
277
|
+
end
|
|
278
|
+
end
|
|
@@ -1,48 +1,5 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
|
-
module Capybara
|
|
3
|
-
class Selector
|
|
4
|
-
class Filter
|
|
5
|
-
def initialize(name, block, options={})
|
|
6
|
-
@name = name
|
|
7
|
-
@block = block
|
|
8
|
-
@options = options
|
|
9
|
-
@options[:valid_values] = [true,false] if options[:boolean]
|
|
10
|
-
end
|
|
11
2
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
def default
|
|
17
|
-
@options[:default]
|
|
18
|
-
end
|
|
19
|
-
|
|
20
|
-
def matches?(node, value)
|
|
21
|
-
return true if skip?(value)
|
|
22
|
-
|
|
23
|
-
if !valid_value?(value)
|
|
24
|
-
msg = "Invalid value #{value.inspect} passed to filter #{@name} - "
|
|
25
|
-
if default?
|
|
26
|
-
warn msg + "defaulting to #{default}"
|
|
27
|
-
value = default
|
|
28
|
-
else
|
|
29
|
-
warn msg + "skipping"
|
|
30
|
-
return true
|
|
31
|
-
end
|
|
32
|
-
end
|
|
33
|
-
|
|
34
|
-
@block.call(node, value)
|
|
35
|
-
end
|
|
36
|
-
|
|
37
|
-
def skip?(value)
|
|
38
|
-
@options.has_key?(:skip_if) && value == @options[:skip_if]
|
|
39
|
-
end
|
|
40
|
-
|
|
41
|
-
private
|
|
42
|
-
|
|
43
|
-
def valid_value?(value)
|
|
44
|
-
!@options.has_key?(:valid_values) || Array(@options[:valid_values]).include?(value)
|
|
45
|
-
end
|
|
46
|
-
end
|
|
47
|
-
end
|
|
48
|
-
end
|
|
3
|
+
require 'capybara/selector/filters/node_filter'
|
|
4
|
+
require 'capybara/selector/filters/expression_filter'
|
|
5
|
+
require 'capybara/selector/filters/locator_filter'
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'capybara/selector/filter'
|
|
4
|
+
|
|
5
|
+
module Capybara
|
|
6
|
+
class Selector
|
|
7
|
+
class FilterSet
|
|
8
|
+
attr_reader :node_filters, :expression_filters
|
|
9
|
+
|
|
10
|
+
def initialize(name, &block)
|
|
11
|
+
@name = name
|
|
12
|
+
@node_filters = {}
|
|
13
|
+
@expression_filters = {}
|
|
14
|
+
@descriptions = Hash.new { |hsh, key| hsh[key] = [] }
|
|
15
|
+
instance_eval(&block) if block
|
|
16
|
+
end
|
|
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
|
|
22
|
+
end
|
|
23
|
+
alias_method :filter, :node_filter
|
|
24
|
+
|
|
25
|
+
def expression_filter(name, *types, **options, &block)
|
|
26
|
+
add_filter(name, Filters::ExpressionFilter, *types, **options, &block)
|
|
27
|
+
end
|
|
28
|
+
|
|
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
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def description(node_filters: true, expression_filters: true, **options)
|
|
43
|
+
opts = options_with_defaults(options)
|
|
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
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def descriptions
|
|
52
|
+
Capybara::Helpers.warn 'DEPRECATED: FilterSet#descriptions is deprecated without replacement'
|
|
53
|
+
[undeclared_descriptions, node_filter_descriptions, expression_filter_descriptions].flatten
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def import(name, filters = nil)
|
|
57
|
+
filter_selector = filters.nil? ? ->(*) { true } : ->(filter_name, _) { filters.include? filter_name }
|
|
58
|
+
|
|
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
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
class << self
|
|
70
|
+
def all
|
|
71
|
+
@filter_sets ||= {} # rubocop:disable Naming/MemoizedInstanceVariableName
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
def [](name)
|
|
75
|
+
all.fetch(name.to_sym) { |set_name| raise ArgumentError, "Unknown filter set (:#{set_name})" }
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
def add(name, &block)
|
|
79
|
+
all[name.to_sym] = FilterSet.new(name.to_sym, &block)
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
def remove(name)
|
|
83
|
+
all.delete(name.to_sym)
|
|
84
|
+
end
|
|
85
|
+
end
|
|
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
|
+
|
|
101
|
+
private
|
|
102
|
+
|
|
103
|
+
def options_with_defaults(options)
|
|
104
|
+
options = options.dup
|
|
105
|
+
[expression_filters, node_filters].each do |filters|
|
|
106
|
+
filters.select { |_n, filter| filter.default? }.each do |name, filter|
|
|
107
|
+
options[name] = filter.default unless options.key?(name)
|
|
108
|
+
end
|
|
109
|
+
end
|
|
110
|
+
options
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
def add_filter(name, filter_class, *types, matcher: nil, **options, &block)
|
|
114
|
+
types.each { |type| options[type] = true }
|
|
115
|
+
if matcher && options[:default]
|
|
116
|
+
raise 'ArgumentError', ':default option is not supported for filters with a :matcher option'
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
filter = filter_class.new(name, matcher, block, **options)
|
|
120
|
+
(filter_class <= Filters::ExpressionFilter ? @expression_filters : @node_filters)[name] = filter
|
|
121
|
+
end
|
|
122
|
+
end
|
|
123
|
+
end
|
|
124
|
+
end
|