capybara 3.0.0 → 3.36.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 +777 -10
- data/License.txt +1 -1
- data/README.md +75 -58
- 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 +34 -9
- data/lib/capybara/dsl.rb +14 -6
- data/lib/capybara/helpers.rb +52 -7
- data/lib/capybara/minitest/spec.rb +173 -84
- data/lib/capybara/minitest.rb +250 -144
- data/lib/capybara/node/actions.rb +248 -124
- data/lib/capybara/node/base.rb +34 -20
- data/lib/capybara/node/document.rb +14 -2
- data/lib/capybara/node/document_matchers.rb +13 -15
- data/lib/capybara/node/element.rb +339 -113
- data/lib/capybara/node/finders.rb +95 -82
- data/lib/capybara/node/matchers.rb +338 -157
- data/lib/capybara/node/simple.rb +54 -15
- 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 +600 -103
- 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 +47 -28
- data/lib/capybara/rack_test/driver.rb +12 -3
- data/lib/capybara/rack_test/errors.rb +6 -0
- data/lib/capybara/rack_test/form.rb +53 -50
- data/lib/capybara/rack_test/node.rb +114 -37
- data/lib/capybara/rails.rb +1 -1
- 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 +86 -52
- data/lib/capybara/rspec/features.rb +8 -10
- data/lib/capybara/rspec/matcher_proxies.rb +39 -18
- 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 +138 -316
- 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 +89 -12
- 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 +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 +279 -0
- data/lib/capybara/selector/filter.rb +1 -0
- data/lib/capybara/selector/filter_set.rb +72 -27
- 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 +214 -0
- data/lib/capybara/selector/selector.rb +85 -200
- data/lib/capybara/selector/xpath_extensions.rb +17 -0
- data/lib/capybara/selector.rb +226 -537
- 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 +296 -238
- 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 +436 -166
- 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 +69 -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 +38 -6
- data/lib/capybara/session/matchers.rb +26 -19
- data/lib/capybara/session.rb +258 -190
- 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 +122 -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 -42
- 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} +62 -38
- data/lib/capybara/spec/session/{assert_title.rb → assert_title_spec.rb} +12 -12
- data/lib/capybara/spec/session/attach_file_spec.rb +120 -72
- data/lib/capybara/spec/session/body_spec.rb +11 -13
- data/lib/capybara/spec/session/check_spec.rb +111 -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 +78 -55
- data/lib/capybara/spec/session/current_scope_spec.rb +7 -7
- 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 +104 -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 +32 -14
- data/lib/capybara/spec/session/find_spec.rb +236 -141
- data/lib/capybara/spec/session/first_spec.rb +44 -43
- 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 +44 -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 +18 -18
- 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 +92 -12
- data/lib/capybara/spec/session/has_css_spec.rb +271 -137
- data/lib/capybara/spec/session/has_current_path_spec.rb +48 -33
- data/lib/capybara/spec/session/has_field_spec.rb +137 -58
- data/lib/capybara/spec/session/has_link_spec.rb +30 -6
- data/lib/capybara/spec/session/has_none_selectors_spec.rb +26 -24
- data/lib/capybara/spec/session/has_select_spec.rb +75 -47
- data/lib/capybara/spec/session/has_selector_spec.rb +102 -69
- data/lib/capybara/spec/session/has_sibling_spec.rb +50 -0
- data/lib/capybara/spec/session/has_table_spec.rb +170 -4
- data/lib/capybara/spec/session/has_text_spec.rb +108 -52
- 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 +35 -0
- data/lib/capybara/spec/session/node_spec.rb +871 -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 +52 -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 +117 -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 +40 -21
- data/lib/capybara/spec/session/unselect_spec.rb +37 -37
- data/lib/capybara/spec/session/visit_spec.rb +59 -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 +10 -7
- data/lib/capybara/spec/session/window/within_window_spec.rb +17 -16
- data/lib/capybara/spec/session/within_spec.rb +69 -44
- data/lib/capybara/spec/spec_helper.rb +41 -53
- data/lib/capybara/spec/test_app.rb +79 -40
- data/lib/capybara/spec/views/animated.erb +49 -0
- data/lib/capybara/spec/views/form.erb +134 -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 +43 -10
- 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_other.erb +6 -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 +31 -25
- data/lib/capybara.rb +120 -100
- data/spec/basic_node_spec.rb +59 -34
- data/spec/capybara_spec.rb +54 -52
- data/spec/css_builder_spec.rb +101 -0
- data/spec/css_splitter_spec.rb +38 -0
- data/spec/dsl_spec.rb +80 -53
- 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 +4 -4
- data/spec/fixtures/selenium_driver_rspec_success.rb +4 -4
- data/spec/minitest_spec.rb +38 -5
- data/spec/minitest_spec_spec.rb +88 -62
- data/spec/per_session_config_spec.rb +5 -5
- data/spec/rack_test_spec.rb +169 -115
- data/spec/regexp_dissassembler_spec.rb +250 -0
- data/spec/result_spec.rb +86 -33
- data/spec/rspec/features_spec.rb +26 -23
- data/spec/rspec/scenarios_spec.rb +8 -4
- data/spec/rspec/shared_spec_matchers.rb +393 -363
- data/spec/rspec/views_spec.rb +4 -3
- data/spec/rspec_matchers_spec.rb +10 -10
- data/spec/rspec_spec.rb +109 -85
- data/spec/sauce_spec_chrome.rb +43 -0
- data/spec/selector_spec.rb +391 -61
- data/spec/selenium_spec_chrome.rb +180 -41
- data/spec/selenium_spec_chrome_remote.rb +101 -0
- data/spec/selenium_spec_edge.rb +28 -8
- data/spec/selenium_spec_firefox.rb +211 -0
- data/spec/selenium_spec_firefox_remote.rb +80 -0
- data/spec/selenium_spec_ie.rb +127 -11
- data/spec/selenium_spec_safari.rb +156 -0
- data/spec/server_spec.rb +177 -78
- data/spec/session_spec.rb +52 -16
- data/spec/shared_selenium_node.rb +79 -0
- data/spec/shared_selenium_session.rb +455 -123
- data/spec/spec_helper.rb +91 -2
- data/spec/xpath_builder_spec.rb +93 -0
- metadata +343 -42
- 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
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
require 'capybara/rack_test/errors'
|
|
4
|
+
|
|
3
5
|
class Capybara::RackTest::Node < Capybara::Driver::Node
|
|
4
6
|
BLOCK_ELEMENTS = %w[p h1 h2 h3 h4 h5 h6 ol ul pre address blockquote dl div fieldset form hr noscript table].freeze
|
|
5
7
|
|
|
@@ -7,16 +9,16 @@ class Capybara::RackTest::Node < Capybara::Driver::Node
|
|
|
7
9
|
native.text
|
|
8
10
|
.gsub(/[\u200b\u200e\u200f]/, '')
|
|
9
11
|
.gsub(/[\ \n\f\t\v\u2028\u2029]+/, ' ')
|
|
10
|
-
.gsub(/\A[[:space:]&&[^\u00a0]]+/,
|
|
11
|
-
.gsub(/[[:space:]&&[^\u00a0]]+\z/,
|
|
12
|
+
.gsub(/\A[[:space:]&&[^\u00a0]]+/, '')
|
|
13
|
+
.gsub(/[[:space:]&&[^\u00a0]]+\z/, '')
|
|
12
14
|
.tr("\u00a0", ' ')
|
|
13
15
|
end
|
|
14
16
|
|
|
15
17
|
def visible_text
|
|
16
|
-
displayed_text.
|
|
18
|
+
displayed_text.squeeze(' ')
|
|
17
19
|
.gsub(/[\ \n]*\n[\ \n]*/, "\n")
|
|
18
|
-
.gsub(/\A[[:space:]&&[^\u00a0]]+/,
|
|
19
|
-
.gsub(/[[:space:]&&[^\u00a0]]+\z/,
|
|
20
|
+
.gsub(/\A[[:space:]&&[^\u00a0]]+/, '')
|
|
21
|
+
.gsub(/[[:space:]&&[^\u00a0]]+\z/, '')
|
|
20
22
|
.tr("\u00a0", ' ')
|
|
21
23
|
end
|
|
22
24
|
|
|
@@ -24,6 +26,10 @@ class Capybara::RackTest::Node < Capybara::Driver::Node
|
|
|
24
26
|
string_node[name]
|
|
25
27
|
end
|
|
26
28
|
|
|
29
|
+
def style(_styles)
|
|
30
|
+
raise NotImplementedError, 'The rack_test driver does not process CSS'
|
|
31
|
+
end
|
|
32
|
+
|
|
27
33
|
def value
|
|
28
34
|
string_node.value
|
|
29
35
|
end
|
|
@@ -39,6 +45,7 @@ class Capybara::RackTest::Node < Capybara::Driver::Node
|
|
|
39
45
|
|
|
40
46
|
if radio? then set_radio(value)
|
|
41
47
|
elsif checkbox? then set_checkbox(value)
|
|
48
|
+
elsif range? then set_range(value)
|
|
42
49
|
elsif input_field? then set_input(value)
|
|
43
50
|
elsif textarea? then native['_capybara_raw_value'] = value.to_s
|
|
44
51
|
end
|
|
@@ -46,17 +53,20 @@ class Capybara::RackTest::Node < Capybara::Driver::Node
|
|
|
46
53
|
|
|
47
54
|
def select_option
|
|
48
55
|
return if disabled?
|
|
56
|
+
|
|
49
57
|
deselect_options unless select_node.multiple?
|
|
50
|
-
native[
|
|
58
|
+
native['selected'] = 'selected'
|
|
51
59
|
end
|
|
52
60
|
|
|
53
61
|
def unselect_option
|
|
54
|
-
raise Capybara::UnselectNotAllowed,
|
|
62
|
+
raise Capybara::UnselectNotAllowed, 'Cannot unselect option from single select box.' unless select_node.multiple?
|
|
63
|
+
|
|
55
64
|
native.remove_attribute('selected')
|
|
56
65
|
end
|
|
57
66
|
|
|
58
|
-
def click(keys = [],
|
|
59
|
-
|
|
67
|
+
def click(keys = [], **options)
|
|
68
|
+
options.delete(:offset)
|
|
69
|
+
raise ArgumentError, 'The RackTest driver does not support click options' unless keys.empty? && options.empty?
|
|
60
70
|
|
|
61
71
|
if link?
|
|
62
72
|
follow_link
|
|
@@ -67,6 +77,8 @@ class Capybara::RackTest::Node < Capybara::Driver::Node
|
|
|
67
77
|
set(!checked?)
|
|
68
78
|
elsif tag_name == 'label'
|
|
69
79
|
click_label
|
|
80
|
+
elsif (details = native.xpath('.//ancestor-or-self::details').last)
|
|
81
|
+
toggle_details(details)
|
|
70
82
|
end
|
|
71
83
|
end
|
|
72
84
|
|
|
@@ -90,26 +102,48 @@ class Capybara::RackTest::Node < Capybara::Driver::Node
|
|
|
90
102
|
return true if string_node.disabled?
|
|
91
103
|
|
|
92
104
|
if %w[option optgroup].include? tag_name
|
|
93
|
-
find_xpath(
|
|
105
|
+
find_xpath(OPTION_OWNER_XPATH)[0].disabled?
|
|
94
106
|
else
|
|
95
|
-
!find_xpath(
|
|
107
|
+
!find_xpath(DISABLED_BY_FIELDSET_XPATH).empty?
|
|
96
108
|
end
|
|
97
109
|
end
|
|
98
110
|
|
|
111
|
+
def readonly?
|
|
112
|
+
# readonly attribute not valid on these input types
|
|
113
|
+
return false if input_field? && %w[hidden range color checkbox radio file submit image reset button].include?(type)
|
|
114
|
+
|
|
115
|
+
super
|
|
116
|
+
end
|
|
117
|
+
|
|
99
118
|
def path
|
|
100
119
|
native.path
|
|
101
120
|
end
|
|
102
121
|
|
|
103
|
-
def find_xpath(locator)
|
|
104
|
-
native.xpath(locator).map { |
|
|
122
|
+
def find_xpath(locator, **_hints)
|
|
123
|
+
native.xpath(locator).map { |el| self.class.new(driver, el) }
|
|
105
124
|
end
|
|
106
125
|
|
|
107
|
-
def find_css(locator)
|
|
108
|
-
native.css(locator, Capybara::RackTest::CSSHandlers.new).map { |
|
|
126
|
+
def find_css(locator, **_hints)
|
|
127
|
+
native.css(locator, Capybara::RackTest::CSSHandlers.new).map { |el| self.class.new(driver, el) }
|
|
109
128
|
end
|
|
110
129
|
|
|
111
|
-
|
|
112
|
-
|
|
130
|
+
public_instance_methods(false).each do |meth_name|
|
|
131
|
+
alias_method "unchecked_#{meth_name}", meth_name
|
|
132
|
+
private "unchecked_#{meth_name}" # rubocop:disable Style/AccessModifierDeclarations
|
|
133
|
+
|
|
134
|
+
if RUBY_VERSION >= '2.7'
|
|
135
|
+
class_eval <<~METHOD, __FILE__, __LINE__ + 1
|
|
136
|
+
def #{meth_name}(...)
|
|
137
|
+
stale_check
|
|
138
|
+
method(:"unchecked_#{meth_name}").call(...)
|
|
139
|
+
end
|
|
140
|
+
METHOD
|
|
141
|
+
else
|
|
142
|
+
define_method meth_name do |*args|
|
|
143
|
+
stale_check
|
|
144
|
+
send("unchecked_#{meth_name}", *args)
|
|
145
|
+
end
|
|
146
|
+
end
|
|
113
147
|
end
|
|
114
148
|
|
|
115
149
|
protected
|
|
@@ -128,15 +162,19 @@ protected
|
|
|
128
162
|
end.join || ''
|
|
129
163
|
text = "\n#{text}\n" if BLOCK_ELEMENTS.include?(tag_name)
|
|
130
164
|
text
|
|
131
|
-
else
|
|
165
|
+
else # rubocop:disable Lint/DuplicateBranch
|
|
132
166
|
''
|
|
133
167
|
end
|
|
134
168
|
end
|
|
135
169
|
|
|
136
170
|
private
|
|
137
171
|
|
|
172
|
+
def stale_check
|
|
173
|
+
raise Capybara::RackTest::Errors::StaleElementReferenceError unless native.document == driver.dom
|
|
174
|
+
end
|
|
175
|
+
|
|
138
176
|
def deselect_options
|
|
139
|
-
select_node.find_xpath(
|
|
177
|
+
select_node.find_xpath('.//option[@selected]').each { |node| node.native.remove_attribute('selected') }
|
|
140
178
|
end
|
|
141
179
|
|
|
142
180
|
def string_node
|
|
@@ -161,8 +199,8 @@ private
|
|
|
161
199
|
end
|
|
162
200
|
|
|
163
201
|
def set_radio(_value) # rubocop:disable Naming/AccessorMethodName
|
|
164
|
-
other_radios_xpath = XPath.generate { |
|
|
165
|
-
driver.dom.xpath(other_radios_xpath).each { |node| node.remove_attribute(
|
|
202
|
+
other_radios_xpath = XPath.generate { |xp| xp.anywhere(:input)[xp.attr(:name) == self[:name]] }.to_s
|
|
203
|
+
driver.dom.xpath(other_radios_xpath).each { |node| node.remove_attribute('checked') }
|
|
166
204
|
native['checked'] = 'checked'
|
|
167
205
|
end
|
|
168
206
|
|
|
@@ -174,6 +212,14 @@ private
|
|
|
174
212
|
end
|
|
175
213
|
end
|
|
176
214
|
|
|
215
|
+
def set_range(value) # rubocop:disable Naming/AccessorMethodName
|
|
216
|
+
min, max, step = (native['min'] || 0).to_f, (native['max'] || 100).to_f, (native['step'] || 1).to_f
|
|
217
|
+
value = value.to_f
|
|
218
|
+
value = value.clamp(min, max)
|
|
219
|
+
value = (((value - min) / step).round * step) + min
|
|
220
|
+
native['value'] = value.clamp(min, max)
|
|
221
|
+
end
|
|
222
|
+
|
|
177
223
|
def set_input(value) # rubocop:disable Naming/AccessorMethodName
|
|
178
224
|
if text_or_password? && attribute_is_not_blank?(:maxlength)
|
|
179
225
|
# Browser behavior for maxlength="0" is inconsistent, so we stick with
|
|
@@ -181,11 +227,11 @@ private
|
|
|
181
227
|
value = value.to_s[0...self[:maxlength].to_i]
|
|
182
228
|
end
|
|
183
229
|
if value.is_a?(Array) # Assert multiple attribute is present
|
|
184
|
-
value.each do |
|
|
230
|
+
value.each do |val|
|
|
185
231
|
new_native = native.clone
|
|
186
232
|
new_native.remove_attribute('value')
|
|
187
233
|
native.add_next_sibling(new_native)
|
|
188
|
-
new_native['value'] =
|
|
234
|
+
new_native['value'] = val.to_s
|
|
189
235
|
end
|
|
190
236
|
native.remove
|
|
191
237
|
else
|
|
@@ -198,7 +244,7 @@ private
|
|
|
198
244
|
end
|
|
199
245
|
|
|
200
246
|
def follow_link
|
|
201
|
-
method = self[
|
|
247
|
+
method = self['data-method'] if driver.options[:respect_data_method]
|
|
202
248
|
method ||= :get
|
|
203
249
|
driver.follow(method, self[:href].to_s)
|
|
204
250
|
end
|
|
@@ -207,11 +253,20 @@ private
|
|
|
207
253
|
labelled_control = if native[:for]
|
|
208
254
|
find_xpath("//input[@id='#{native[:for]}']")
|
|
209
255
|
else
|
|
210
|
-
find_xpath(
|
|
256
|
+
find_xpath('.//input')
|
|
211
257
|
end.first
|
|
212
258
|
|
|
213
|
-
|
|
214
|
-
|
|
259
|
+
labelled_control.set(!labelled_control.checked?) if checkbox_or_radio?(labelled_control)
|
|
260
|
+
end
|
|
261
|
+
|
|
262
|
+
def toggle_details(details = nil)
|
|
263
|
+
details ||= native.xpath('.//ancestor-or-self::details').last
|
|
264
|
+
return unless details
|
|
265
|
+
|
|
266
|
+
if details.has_attribute?('open')
|
|
267
|
+
details.remove_attribute('open')
|
|
268
|
+
else
|
|
269
|
+
details.set_attribute('open', 'open')
|
|
215
270
|
end
|
|
216
271
|
end
|
|
217
272
|
|
|
@@ -220,32 +275,54 @@ private
|
|
|
220
275
|
end
|
|
221
276
|
|
|
222
277
|
def submits?
|
|
223
|
-
(tag_name == 'input'
|
|
278
|
+
(tag_name == 'input' && %w[submit image].include?(type)) || (tag_name == 'button' && [nil, 'submit'].include?(type))
|
|
224
279
|
end
|
|
225
280
|
|
|
226
281
|
def checkable?
|
|
227
|
-
tag_name == 'input'
|
|
282
|
+
tag_name == 'input' && %w[checkbox radio].include?(type)
|
|
228
283
|
end
|
|
229
284
|
|
|
230
285
|
protected
|
|
231
286
|
|
|
232
|
-
def
|
|
233
|
-
|
|
287
|
+
def checkbox_or_radio?(field = self)
|
|
288
|
+
field&.checkbox? || field&.radio?
|
|
234
289
|
end
|
|
235
290
|
|
|
236
|
-
def
|
|
237
|
-
|
|
291
|
+
def checkbox?
|
|
292
|
+
input_field? && type == 'checkbox'
|
|
238
293
|
end
|
|
239
294
|
|
|
240
295
|
def radio?
|
|
241
296
|
input_field? && type == 'radio'
|
|
242
297
|
end
|
|
243
298
|
|
|
244
|
-
def textarea?
|
|
245
|
-
tag_name == "textarea"
|
|
246
|
-
end
|
|
247
|
-
|
|
248
299
|
def text_or_password?
|
|
249
300
|
input_field? && (type == 'text' || type == 'password')
|
|
250
301
|
end
|
|
302
|
+
|
|
303
|
+
def input_field?
|
|
304
|
+
tag_name == 'input'
|
|
305
|
+
end
|
|
306
|
+
|
|
307
|
+
def textarea?
|
|
308
|
+
tag_name == 'textarea'
|
|
309
|
+
end
|
|
310
|
+
|
|
311
|
+
def range?
|
|
312
|
+
input_field? && type == 'range'
|
|
313
|
+
end
|
|
314
|
+
|
|
315
|
+
OPTION_OWNER_XPATH = XPath.parent(:optgroup, :select, :datalist).to_s.freeze
|
|
316
|
+
DISABLED_BY_FIELDSET_XPATH = XPath.generate do |x|
|
|
317
|
+
x.parent(:fieldset)[
|
|
318
|
+
XPath.attr(:disabled)
|
|
319
|
+
] + x.ancestor[
|
|
320
|
+
~x.self(:legend) |
|
|
321
|
+
x.preceding_sibling(:legend)
|
|
322
|
+
][
|
|
323
|
+
x.parent(:fieldset)[
|
|
324
|
+
x.attr(:disabled)
|
|
325
|
+
]
|
|
326
|
+
]
|
|
327
|
+
end.to_s.freeze
|
|
251
328
|
end
|
data/lib/capybara/rails.rb
CHANGED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Capybara
|
|
4
|
+
# @api private
|
|
5
|
+
class RegistrationContainer
|
|
6
|
+
def names
|
|
7
|
+
@registered.keys
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def [](name)
|
|
11
|
+
@registered[name]
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def []=(name, value)
|
|
15
|
+
Capybara::Helpers.warn 'DEPRECATED: Directly setting drivers/servers is deprecated, please use Capybara.register_driver/register_server instead'
|
|
16
|
+
@registered[name] = value
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def method_missing(method_name, *args, **options, &block)
|
|
20
|
+
if @registered.respond_to?(method_name)
|
|
21
|
+
Capybara::Helpers.warn "DEPRECATED: Calling '#{method_name}' on the drivers/servers container is deprecated without replacement"
|
|
22
|
+
# RUBY 2.6 will send an empty hash rather than nothing with **options so fix that
|
|
23
|
+
return @registered.public_send(method_name, *args, &block) if options.empty?
|
|
24
|
+
|
|
25
|
+
return @registered.public_send(method_name, *args, **options, &block)
|
|
26
|
+
end
|
|
27
|
+
super
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def respond_to_missing?(method_name, include_all)
|
|
31
|
+
@registered.respond_to?(method_name) || super
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
private
|
|
35
|
+
|
|
36
|
+
def initialize
|
|
37
|
+
@registered = {}
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def register(name, block)
|
|
41
|
+
@registered[name] = block
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
end
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
Capybara.register_driver :rack_test do |app|
|
|
4
|
+
Capybara::RackTest::Driver.new(app)
|
|
5
|
+
end
|
|
6
|
+
|
|
7
|
+
Capybara.register_driver :selenium do |app|
|
|
8
|
+
Capybara::Selenium::Driver.new(app)
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
Capybara.register_driver :selenium_headless do |app|
|
|
12
|
+
version = Capybara::Selenium::Driver.load_selenium
|
|
13
|
+
options_key = Capybara::Selenium::Driver::CAPS_VERSION.satisfied_by?(version) ? :capabilities : :options
|
|
14
|
+
browser_options = ::Selenium::WebDriver::Firefox::Options.new.tap do |opts|
|
|
15
|
+
opts.add_argument '-headless'
|
|
16
|
+
end
|
|
17
|
+
Capybara::Selenium::Driver.new(app, **{ :browser => :firefox, options_key => browser_options })
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
Capybara.register_driver :selenium_chrome do |app|
|
|
21
|
+
version = Capybara::Selenium::Driver.load_selenium
|
|
22
|
+
options_key = Capybara::Selenium::Driver::CAPS_VERSION.satisfied_by?(version) ? :capabilities : :options
|
|
23
|
+
browser_options = ::Selenium::WebDriver::Chrome::Options.new.tap do |opts|
|
|
24
|
+
# Workaround https://bugs.chromium.org/p/chromedriver/issues/detail?id=2650&q=load&sort=-id&colspec=ID%20Status%20Pri%20Owner%20Summary
|
|
25
|
+
opts.add_argument('--disable-site-isolation-trials')
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
Capybara::Selenium::Driver.new(app, **{ :browser => :chrome, options_key => browser_options })
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
Capybara.register_driver :selenium_chrome_headless do |app|
|
|
32
|
+
version = Capybara::Selenium::Driver.load_selenium
|
|
33
|
+
options_key = Capybara::Selenium::Driver::CAPS_VERSION.satisfied_by?(version) ? :capabilities : :options
|
|
34
|
+
browser_options = ::Selenium::WebDriver::Chrome::Options.new.tap do |opts|
|
|
35
|
+
opts.add_argument('--headless')
|
|
36
|
+
opts.add_argument('--disable-gpu') if Gem.win_platform?
|
|
37
|
+
# Workaround https://bugs.chromium.org/p/chromedriver/issues/detail?id=2650&q=load&sort=-id&colspec=ID%20Status%20Pri%20Owner%20Summary
|
|
38
|
+
opts.add_argument('--disable-site-isolation-trials')
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
Capybara::Selenium::Driver.new(app, **{ :browser => :chrome, options_key => browser_options })
|
|
42
|
+
end
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Puma
|
|
4
|
+
module MiniSSL
|
|
5
|
+
class Socket
|
|
6
|
+
def read_nonblock(size, *_)
|
|
7
|
+
wait_states = %i[wait_readable wait_writable]
|
|
8
|
+
|
|
9
|
+
loop do
|
|
10
|
+
output = engine_read_all
|
|
11
|
+
return output if output
|
|
12
|
+
|
|
13
|
+
data = @socket.read_nonblock(size, exception: false)
|
|
14
|
+
raise IO::EAGAINWaitReadable if wait_states.include? data
|
|
15
|
+
return nil if data.nil?
|
|
16
|
+
|
|
17
|
+
@engine.inject(data)
|
|
18
|
+
output = engine_read_all
|
|
19
|
+
|
|
20
|
+
return output if output
|
|
21
|
+
|
|
22
|
+
while (neg_data = @engine.extract)
|
|
23
|
+
@socket.write neg_data
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
Capybara.register_server :default do |app, port, _host|
|
|
4
|
+
Capybara.run_default_server(app, port)
|
|
5
|
+
end
|
|
6
|
+
|
|
7
|
+
Capybara.register_server :webrick do |app, port, host, **options|
|
|
8
|
+
require 'rack/handler/webrick'
|
|
9
|
+
options = { Host: host, Port: port, AccessLog: [], Logger: WEBrick::Log.new(nil, 0) }.merge(options)
|
|
10
|
+
Rack::Handler::WEBrick.run(app, **options)
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
Capybara.register_server :puma do |app, port, host, **options|
|
|
14
|
+
begin
|
|
15
|
+
require 'rack/handler/puma'
|
|
16
|
+
rescue LoadError
|
|
17
|
+
raise LoadError, 'Capybara is unable to load `puma` for its server, please add `puma` to your project or specify a different server via something like `Capybara.server = :webrick`.'
|
|
18
|
+
else
|
|
19
|
+
unless Rack::Handler::Puma.respond_to?(:config)
|
|
20
|
+
raise LoadError, 'Capybara requires `puma` version 3.8.0 or higher, please upgrade `puma` or register and specify your own server block'
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
# If we just run the Puma Rack handler it installs signal handlers which prevent us from being able to interrupt tests.
|
|
25
|
+
# Therefore construct and run the Server instance ourselves.
|
|
26
|
+
# Rack::Handler::Puma.run(app, { Host: host, Port: port, Threads: "0:4", workers: 0, daemon: false }.merge(options))
|
|
27
|
+
default_options = { Host: host, Port: port, Threads: '0:4', workers: 0, daemon: false }
|
|
28
|
+
options = default_options.merge(options)
|
|
29
|
+
|
|
30
|
+
conf = Rack::Handler::Puma.config(app, options)
|
|
31
|
+
conf.clamp
|
|
32
|
+
events = conf.options[:Silent] ? ::Puma::Events.strings : ::Puma::Events.stdio
|
|
33
|
+
|
|
34
|
+
puma_ver = Gem::Version.new(Puma::Const::PUMA_VERSION)
|
|
35
|
+
require_relative 'patches/puma_ssl' if Gem::Requirement.new('>=4.0.0', '< 4.1.0').satisfied_by?(puma_ver)
|
|
36
|
+
|
|
37
|
+
events.log 'Capybara starting Puma...'
|
|
38
|
+
events.log "* Version #{Puma::Const::PUMA_VERSION} , codename: #{Puma::Const::CODE_NAME}"
|
|
39
|
+
events.log "* Min threads: #{conf.options[:min_threads]}, max threads: #{conf.options[:max_threads]}"
|
|
40
|
+
|
|
41
|
+
Puma::Server.new(conf.app, events, conf.options).tap do |s|
|
|
42
|
+
s.binder.parse conf.options[:binds], s.events
|
|
43
|
+
s.min_threads, s.max_threads = conf.options[:min_threads], conf.options[:max_threads]
|
|
44
|
+
end.run.join
|
|
45
|
+
end
|
data/lib/capybara/result.rb
CHANGED
|
@@ -7,7 +7,7 @@ module Capybara
|
|
|
7
7
|
# A {Capybara::Result} represents a collection of {Capybara::Node::Element} on the page. It is possible to interact with this
|
|
8
8
|
# collection similar to an Array because it implements Enumerable and offers the following Array methods through delegation:
|
|
9
9
|
#
|
|
10
|
-
# * []
|
|
10
|
+
# * \[\]
|
|
11
11
|
# * each()
|
|
12
12
|
# * at()
|
|
13
13
|
# * size()
|
|
@@ -16,6 +16,8 @@ module Capybara
|
|
|
16
16
|
# * first()
|
|
17
17
|
# * last()
|
|
18
18
|
# * empty?()
|
|
19
|
+
# * values_at()
|
|
20
|
+
# * sample()
|
|
19
21
|
#
|
|
20
22
|
# @see Capybara::Node::Element
|
|
21
23
|
#
|
|
@@ -26,35 +28,52 @@ module Capybara
|
|
|
26
28
|
def initialize(elements, query)
|
|
27
29
|
@elements = elements
|
|
28
30
|
@result_cache = []
|
|
29
|
-
@
|
|
31
|
+
@filter_errors = []
|
|
32
|
+
@results_enum = lazy_select_elements { |node| query.matches_filters?(node, @filter_errors) }
|
|
30
33
|
@query = query
|
|
34
|
+
@allow_reload = false
|
|
31
35
|
end
|
|
32
36
|
|
|
33
37
|
def_delegators :full_results, :size, :length, :last, :values_at, :inspect, :sample
|
|
34
38
|
|
|
35
|
-
alias
|
|
39
|
+
alias index find_index
|
|
36
40
|
|
|
37
41
|
def each(&block)
|
|
38
|
-
return enum_for(:each) unless
|
|
42
|
+
return enum_for(:each) unless block
|
|
39
43
|
|
|
40
44
|
@result_cache.each(&block)
|
|
41
45
|
loop do
|
|
42
46
|
next_result = @results_enum.next
|
|
43
|
-
|
|
47
|
+
add_to_cache(next_result)
|
|
44
48
|
yield next_result
|
|
45
49
|
end
|
|
46
50
|
self
|
|
47
51
|
end
|
|
48
52
|
|
|
49
53
|
def [](*args)
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
+
idx, length = args
|
|
55
|
+
max_idx = case idx
|
|
56
|
+
when Integer
|
|
57
|
+
if idx.negative?
|
|
58
|
+
nil
|
|
59
|
+
else
|
|
60
|
+
length.nil? ? idx : idx + length - 1
|
|
61
|
+
end
|
|
62
|
+
when Range
|
|
63
|
+
# idx.max is broken with beginless ranges
|
|
64
|
+
# idx.end && idx.max # endless range will have end == nil
|
|
65
|
+
max = idx.end
|
|
66
|
+
max = nil if max&.negative?
|
|
67
|
+
max -= 1 if max && idx.exclude_end?
|
|
68
|
+
max
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
if max_idx.nil?
|
|
54
72
|
full_results[*args]
|
|
73
|
+
else
|
|
74
|
+
load_up_to(max_idx + 1)
|
|
75
|
+
@result_cache[*args]
|
|
55
76
|
end
|
|
56
|
-
rescue StopIteration
|
|
57
|
-
return nil
|
|
58
77
|
end
|
|
59
78
|
alias :at :[]
|
|
60
79
|
|
|
@@ -63,46 +82,28 @@ module Capybara
|
|
|
63
82
|
end
|
|
64
83
|
|
|
65
84
|
def compare_count
|
|
85
|
+
return 0 unless @query
|
|
86
|
+
|
|
87
|
+
count, min, max, between = @query.options.values_at(:count, :minimum, :maximum, :between)
|
|
88
|
+
|
|
66
89
|
# Only check filters for as many elements as necessary to determine result
|
|
67
|
-
if
|
|
68
|
-
|
|
69
|
-
loop do
|
|
70
|
-
break if @result_cache.size > count_opt
|
|
71
|
-
@result_cache << @results_enum.next
|
|
72
|
-
end
|
|
73
|
-
return @result_cache.size <=> count_opt
|
|
90
|
+
if count && (count = Integer(count))
|
|
91
|
+
return load_up_to(count + 1) <=> count
|
|
74
92
|
end
|
|
75
93
|
|
|
76
|
-
if
|
|
77
|
-
min_opt = Integer(@query.options[:minimum])
|
|
78
|
-
begin
|
|
79
|
-
@result_cache << @results_enum.next while @result_cache.size < min_opt
|
|
80
|
-
rescue StopIteration
|
|
81
|
-
return -1
|
|
82
|
-
end
|
|
83
|
-
end
|
|
94
|
+
return -1 if min && (min = Integer(min)) && (load_up_to(min) < min)
|
|
84
95
|
|
|
85
|
-
if
|
|
86
|
-
max_opt = Integer(@query.options[:maximum])
|
|
87
|
-
begin
|
|
88
|
-
@result_cache << @results_enum.next while @result_cache.size <= max_opt
|
|
89
|
-
return 1
|
|
90
|
-
rescue StopIteration
|
|
91
|
-
end
|
|
92
|
-
end
|
|
96
|
+
return 1 if max && (max = Integer(max)) && (load_up_to(max + 1) > max)
|
|
93
97
|
|
|
94
|
-
if
|
|
95
|
-
min, max =
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
return 0 if @query.options[:between].include?(@result_cache.size)
|
|
101
|
-
return -1 if @result_cache.size < min
|
|
102
|
-
return 1
|
|
98
|
+
if between
|
|
99
|
+
min, max = (between.begin && between.min) || 1, between.end
|
|
100
|
+
max -= 1 if max && between.exclude_end?
|
|
101
|
+
|
|
102
|
+
size = load_up_to(max ? max + 1 : min)
|
|
103
|
+
return size <=> min unless between.include?(size)
|
|
103
104
|
end
|
|
104
105
|
|
|
105
|
-
|
|
106
|
+
0
|
|
106
107
|
end
|
|
107
108
|
|
|
108
109
|
def matches_count?
|
|
@@ -112,13 +113,15 @@ module Capybara
|
|
|
112
113
|
def failure_message
|
|
113
114
|
message = @query.failure_message
|
|
114
115
|
if count.zero?
|
|
115
|
-
message <<
|
|
116
|
+
message << ' but there were no matches'
|
|
116
117
|
else
|
|
117
|
-
message << ", found #{count} #{Capybara::Helpers.declension(
|
|
118
|
+
message << ", found #{count} #{Capybara::Helpers.declension('match', 'matches', count)}: " \
|
|
119
|
+
<< full_results.map(&:text).map(&:inspect).join(', ')
|
|
118
120
|
end
|
|
119
121
|
unless rest.empty?
|
|
120
|
-
elements = rest.map { |el| el.text rescue
|
|
121
|
-
message <<
|
|
122
|
+
elements = rest.map { |el| el.text rescue '<<ERROR>>' }.map(&:inspect).join(', ') # rubocop:disable Style/RescueModifier
|
|
123
|
+
message << '. Also found ' << elements << ', which matched the selector but not all filters. '
|
|
124
|
+
message << @filter_errors.join('. ') if (rest.size == 1) && count.zero?
|
|
122
125
|
end
|
|
123
126
|
message
|
|
124
127
|
end
|
|
@@ -127,8 +130,34 @@ module Capybara
|
|
|
127
130
|
failure_message.sub(/(to find)/, 'not \1')
|
|
128
131
|
end
|
|
129
132
|
|
|
133
|
+
def unfiltered_size
|
|
134
|
+
@elements.length
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
##
|
|
138
|
+
# @api private
|
|
139
|
+
#
|
|
140
|
+
def allow_reload!
|
|
141
|
+
@allow_reload = true
|
|
142
|
+
self
|
|
143
|
+
end
|
|
144
|
+
|
|
130
145
|
private
|
|
131
146
|
|
|
147
|
+
def add_to_cache(elem)
|
|
148
|
+
elem.allow_reload!(@result_cache.size) if @allow_reload
|
|
149
|
+
@result_cache << elem
|
|
150
|
+
end
|
|
151
|
+
|
|
152
|
+
def load_up_to(num)
|
|
153
|
+
loop do
|
|
154
|
+
break if @result_cache.size >= num
|
|
155
|
+
|
|
156
|
+
add_to_cache(@results_enum.next)
|
|
157
|
+
end
|
|
158
|
+
@result_cache.size
|
|
159
|
+
end
|
|
160
|
+
|
|
132
161
|
def full_results
|
|
133
162
|
loop { @result_cache << @results_enum.next }
|
|
134
163
|
@result_cache
|
|
@@ -138,13 +167,18 @@ module Capybara
|
|
|
138
167
|
@rest ||= @elements - full_results
|
|
139
168
|
end
|
|
140
169
|
|
|
141
|
-
|
|
142
|
-
# JRuby has an issue with lazy enumerators which
|
|
170
|
+
if RUBY_PLATFORM == 'java'
|
|
171
|
+
# JRuby < 9.2.8.0 has an issue with lazy enumerators which
|
|
143
172
|
# causes a concurrency issue with network requests here
|
|
144
173
|
# https://github.com/jruby/jruby/issues/4212
|
|
145
|
-
|
|
174
|
+
# while JRuby >= 9.2.8.0 leaks threads when using lazy enumerators
|
|
175
|
+
# https://github.com/teamcapybara/capybara/issues/2349
|
|
176
|
+
# so disable the use and JRuby users will need to pay a performance penalty
|
|
177
|
+
def lazy_select_elements(&block)
|
|
146
178
|
@elements.select(&block).to_enum # non-lazy evaluation
|
|
147
|
-
|
|
179
|
+
end
|
|
180
|
+
else
|
|
181
|
+
def lazy_select_elements(&block)
|
|
148
182
|
@elements.lazy.select(&block)
|
|
149
183
|
end
|
|
150
184
|
end
|