capybara 3.3.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 -0
- data/History.md +803 -13
- data/License.txt +1 -1
- data/README.md +257 -84
- data/lib/capybara/config.rb +25 -9
- data/lib/capybara/cucumber.rb +1 -1
- data/lib/capybara/driver/base.rb +17 -3
- data/lib/capybara/driver/node.rb +31 -6
- data/lib/capybara/dsl.rb +9 -7
- data/lib/capybara/helpers.rb +31 -7
- data/lib/capybara/minitest/spec.rb +180 -88
- data/lib/capybara/minitest.rb +262 -149
- data/lib/capybara/node/actions.rb +202 -116
- data/lib/capybara/node/base.rb +34 -19
- data/lib/capybara/node/document.rb +14 -2
- data/lib/capybara/node/document_matchers.rb +10 -12
- data/lib/capybara/node/element.rb +269 -115
- data/lib/capybara/node/finders.rb +99 -77
- data/lib/capybara/node/matchers.rb +327 -151
- data/lib/capybara/node/simple.rb +48 -13
- data/lib/capybara/node/whitespace_normalizer.rb +81 -0
- data/lib/capybara/queries/active_element_query.rb +18 -0
- data/lib/capybara/queries/ancestor_query.rb +8 -9
- data/lib/capybara/queries/base_query.rb +23 -16
- data/lib/capybara/queries/current_path_query.rb +16 -6
- data/lib/capybara/queries/match_query.rb +1 -0
- data/lib/capybara/queries/selector_query.rb +587 -130
- data/lib/capybara/queries/sibling_query.rb +8 -6
- data/lib/capybara/queries/style_query.rb +6 -2
- data/lib/capybara/queries/text_query.rb +28 -14
- data/lib/capybara/queries/title_query.rb +2 -2
- data/lib/capybara/rack_test/browser.rb +92 -25
- 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 +68 -41
- data/lib/capybara/rack_test/node.rb +106 -39
- 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 +75 -52
- data/lib/capybara/rspec/features.rb +7 -7
- 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 +141 -339
- data/lib/capybara/rspec.rb +2 -0
- 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 +27 -25
- 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 +73 -25
- data/lib/capybara/selector/filters/base.rb +24 -5
- data/lib/capybara/selector/filters/expression_filter.rb +3 -3
- data/lib/capybara/selector/filters/locator_filter.rb +29 -0
- data/lib/capybara/selector/filters/node_filter.rb +16 -2
- data/lib/capybara/selector/regexp_disassembler.rb +211 -0
- data/lib/capybara/selector/selector.rb +85 -348
- data/lib/capybara/selector/xpath_extensions.rb +17 -0
- data/lib/capybara/selector.rb +474 -447
- 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 +255 -143
- data/lib/capybara/selenium/driver_specializations/chrome_driver.rb +93 -11
- 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 +436 -134
- 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 +56 -19
- data/lib/capybara/server/checker.rb +9 -3
- data/lib/capybara/server/middleware.rb +28 -12
- data/lib/capybara/server.rb +33 -10
- data/lib/capybara/session/config.rb +34 -10
- data/lib/capybara/session/matchers.rb +23 -16
- data/lib/capybara/session.rb +230 -170
- 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 +121 -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 +127 -40
- 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_spec.rb +21 -18
- data/lib/capybara/spec/session/assert_selector_spec.rb +52 -58
- data/lib/capybara/spec/session/assert_style_spec.rb +7 -7
- data/lib/capybara/spec/session/assert_text_spec.rb +74 -50
- data/lib/capybara/spec/session/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 +6 -6
- data/lib/capybara/spec/session/check_spec.rb +102 -47
- data/lib/capybara/spec/session/choose_spec.rb +58 -32
- data/lib/capybara/spec/session/click_button_spec.rb +219 -163
- data/lib/capybara/spec/session/click_link_or_button_spec.rb +49 -23
- data/lib/capybara/spec/session/click_link_spec.rb +77 -54
- data/lib/capybara/spec/session/current_scope_spec.rb +8 -8
- data/lib/capybara/spec/session/current_url_spec.rb +38 -29
- 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_spec.rb +8 -8
- data/lib/capybara/spec/session/element/match_css_spec.rb +16 -10
- data/lib/capybara/spec/session/element/match_xpath_spec.rb +6 -6
- data/lib/capybara/spec/session/element/matches_selector_spec.rb +68 -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 +101 -46
- data/lib/capybara/spec/session/find_button_spec.rb +23 -23
- data/lib/capybara/spec/session/find_by_id_spec.rb +7 -7
- data/lib/capybara/spec/session/find_field_spec.rb +32 -30
- data/lib/capybara/spec/session/find_link_spec.rb +31 -21
- data/lib/capybara/spec/session/find_spec.rb +244 -141
- data/lib/capybara/spec/session/first_spec.rb +43 -43
- data/lib/capybara/spec/session/frame/frame_title_spec.rb +5 -5
- data/lib/capybara/spec/session/frame/frame_url_spec.rb +5 -5
- data/lib/capybara/spec/session/frame/switch_to_frame_spec.rb +30 -18
- data/lib/capybara/spec/session/frame/within_frame_spec.rb +45 -18
- 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 +94 -13
- data/lib/capybara/spec/session/has_css_spec.rb +272 -132
- 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 +44 -4
- data/lib/capybara/spec/session/has_none_selectors_spec.rb +31 -31
- data/lib/capybara/spec/session/has_select_spec.rb +84 -50
- data/lib/capybara/spec/session/has_selector_spec.rb +111 -71
- 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_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 +894 -142
- data/lib/capybara/spec/session/node_wrapper_spec.rb +10 -7
- data/lib/capybara/spec/session/refresh_spec.rb +9 -7
- data/lib/capybara/spec/session/reset_session_spec.rb +63 -35
- data/lib/capybara/spec/session/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 +2 -2
- data/lib/capybara/spec/session/save_page_spec.rb +37 -37
- data/lib/capybara/spec/session/save_screenshot_spec.rb +10 -10
- 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 +85 -85
- data/lib/capybara/spec/session/selectors_spec.rb +49 -18
- data/lib/capybara/spec/session/sibling_spec.rb +9 -9
- data/lib/capybara/spec/session/text_spec.rb +25 -24
- data/lib/capybara/spec/session/title_spec.rb +7 -6
- data/lib/capybara/spec/session/uncheck_spec.rb +33 -21
- data/lib/capybara/spec/session/unselect_spec.rb +37 -37
- data/lib/capybara/spec/session/visit_spec.rb +68 -49
- data/lib/capybara/spec/session/window/become_closed_spec.rb +20 -17
- data/lib/capybara/spec/session/window/current_window_spec.rb +1 -1
- data/lib/capybara/spec/session/window/switch_to_window_spec.rb +20 -16
- data/lib/capybara/spec/session/window/window_opened_by_spec.rb +6 -2
- data/lib/capybara/spec/session/window/window_spec.rb +62 -63
- data/lib/capybara/spec/session/window/windows_spec.rb +5 -1
- data/lib/capybara/spec/session/window/within_window_spec.rb +14 -14
- data/lib/capybara/spec/session/within_spec.rb +79 -42
- data/lib/capybara/spec/spec_helper.rb +41 -53
- data/lib/capybara/spec/test_app.rb +132 -43
- data/lib/capybara/spec/views/animated.erb +49 -0
- data/lib/capybara/spec/views/form.erb +139 -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 +67 -0
- data/lib/capybara/spec/views/with_animation.erb +39 -4
- data/lib/capybara/spec/views/with_base_tag.erb +2 -2
- data/lib/capybara/spec/views/with_dragula.erb +24 -0
- data/lib/capybara/spec/views/with_fixed_header_footer.erb +2 -1
- data/lib/capybara/spec/views/with_hover.erb +3 -2
- data/lib/capybara/spec/views/with_hover1.erb +10 -0
- data/lib/capybara/spec/views/with_html.erb +37 -9
- 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 +26 -5
- data/lib/capybara/spec/views/with_jstree.erb +26 -0
- data/lib/capybara/spec/views/with_namespace.erb +1 -0
- data/lib/capybara/spec/views/with_scope.erb +2 -2
- data/lib/capybara/spec/views/with_scope_other.erb +6 -0
- data/lib/capybara/spec/views/with_shadow.erb +31 -0
- data/lib/capybara/spec/views/with_slow_unload.erb +2 -1
- data/lib/capybara/spec/views/with_sortable_js.erb +21 -0
- data/lib/capybara/spec/views/with_unload_alert.erb +1 -0
- data/lib/capybara/spec/views/with_windows.erb +1 -1
- data/lib/capybara/spec/views/within_frames.erb +1 -1
- data/lib/capybara/version.rb +1 -1
- data/lib/capybara/window.rb +19 -25
- data/lib/capybara.rb +126 -111
- data/spec/basic_node_spec.rb +59 -34
- data/spec/capybara_spec.rb +56 -44
- data/spec/counter_spec.rb +35 -0
- data/spec/css_builder_spec.rb +101 -0
- data/spec/css_splitter_spec.rb +8 -8
- data/spec/dsl_spec.rb +79 -52
- data/spec/filter_set_spec.rb +9 -9
- 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 +45 -7
- data/spec/minitest_spec_spec.rb +87 -64
- data/spec/per_session_config_spec.rb +6 -6
- data/spec/rack_test_spec.rb +172 -116
- data/spec/regexp_dissassembler_spec.rb +250 -0
- data/spec/result_spec.rb +80 -72
- data/spec/rspec/features_spec.rb +21 -16
- data/spec/rspec/scenarios_spec.rb +10 -6
- data/spec/rspec/shared_spec_matchers.rb +407 -365
- data/spec/rspec/views_spec.rb +3 -3
- data/spec/rspec_matchers_spec.rb +35 -10
- data/spec/rspec_spec.rb +63 -41
- data/spec/sauce_spec_chrome.rb +43 -0
- data/spec/selector_spec.rb +334 -89
- data/spec/selenium_spec_chrome.rb +176 -62
- data/spec/selenium_spec_chrome_remote.rb +54 -14
- 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 +171 -97
- data/spec/session_spec.rb +34 -18
- data/spec/shared_selenium_node.rb +79 -0
- data/spec/shared_selenium_session.rb +344 -80
- data/spec/spec_helper.rb +124 -2
- data/spec/whitespace_normalizer_spec.rb +54 -0
- data/spec/xpath_builder_spec.rb +93 -0
- metadata +326 -28
- data/lib/capybara/rspec/compound.rb +0 -94
- data/lib/capybara/selenium/driver_specializations/marionette_driver.rb +0 -31
- data/lib/capybara/selenium/nodes/marionette_node.rb +0 -31
- data/lib/capybara/spec/session/has_style_spec.rb +0 -25
- 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 -167
|
@@ -1,23 +1,19 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
require 'capybara/rack_test/errors'
|
|
4
|
+
require 'capybara/node/whitespace_normalizer'
|
|
5
|
+
|
|
3
6
|
class Capybara::RackTest::Node < Capybara::Driver::Node
|
|
7
|
+
include Capybara::Node::WhitespaceNormalizer
|
|
8
|
+
|
|
4
9
|
BLOCK_ELEMENTS = %w[p h1 h2 h3 h4 h5 h6 ol ul pre address blockquote dl div fieldset form hr noscript table].freeze
|
|
5
10
|
|
|
6
11
|
def all_text
|
|
7
|
-
native.text
|
|
8
|
-
.gsub(/[\u200b\u200e\u200f]/, '')
|
|
9
|
-
.gsub(/[\ \n\f\t\v\u2028\u2029]+/, ' ')
|
|
10
|
-
.gsub(/\A[[:space:]&&[^\u00a0]]+/, "")
|
|
11
|
-
.gsub(/[[:space:]&&[^\u00a0]]+\z/, "")
|
|
12
|
-
.tr("\u00a0", ' ')
|
|
12
|
+
normalize_spacing(native.text)
|
|
13
13
|
end
|
|
14
14
|
|
|
15
15
|
def visible_text
|
|
16
|
-
displayed_text
|
|
17
|
-
.gsub(/[\ \n]*\n[\ \n]*/, "\n")
|
|
18
|
-
.gsub(/\A[[:space:]&&[^\u00a0]]+/, "")
|
|
19
|
-
.gsub(/[[:space:]&&[^\u00a0]]+\z/, "")
|
|
20
|
-
.tr("\u00a0", ' ')
|
|
16
|
+
normalize_visible_spacing(displayed_text)
|
|
21
17
|
end
|
|
22
18
|
|
|
23
19
|
def [](name)
|
|
@@ -25,7 +21,7 @@ class Capybara::RackTest::Node < Capybara::Driver::Node
|
|
|
25
21
|
end
|
|
26
22
|
|
|
27
23
|
def style(_styles)
|
|
28
|
-
raise NotImplementedError,
|
|
24
|
+
raise NotImplementedError, 'The rack_test driver does not process CSS'
|
|
29
25
|
end
|
|
30
26
|
|
|
31
27
|
def value
|
|
@@ -43,6 +39,7 @@ class Capybara::RackTest::Node < Capybara::Driver::Node
|
|
|
43
39
|
|
|
44
40
|
if radio? then set_radio(value)
|
|
45
41
|
elsif checkbox? then set_checkbox(value)
|
|
42
|
+
elsif range? then set_range(value)
|
|
46
43
|
elsif input_field? then set_input(value)
|
|
47
44
|
elsif textarea? then native['_capybara_raw_value'] = value.to_s
|
|
48
45
|
end
|
|
@@ -50,17 +47,20 @@ class Capybara::RackTest::Node < Capybara::Driver::Node
|
|
|
50
47
|
|
|
51
48
|
def select_option
|
|
52
49
|
return if disabled?
|
|
50
|
+
|
|
53
51
|
deselect_options unless select_node.multiple?
|
|
54
|
-
native[
|
|
52
|
+
native['selected'] = 'selected'
|
|
55
53
|
end
|
|
56
54
|
|
|
57
55
|
def unselect_option
|
|
58
|
-
raise Capybara::UnselectNotAllowed,
|
|
56
|
+
raise Capybara::UnselectNotAllowed, 'Cannot unselect option from single select box.' unless select_node.multiple?
|
|
57
|
+
|
|
59
58
|
native.remove_attribute('selected')
|
|
60
59
|
end
|
|
61
60
|
|
|
62
|
-
def click(keys = [], **
|
|
63
|
-
|
|
61
|
+
def click(keys = [], **options)
|
|
62
|
+
options.delete(:offset)
|
|
63
|
+
raise ArgumentError, 'The RackTest driver does not support click options' unless keys.empty? && options.empty?
|
|
64
64
|
|
|
65
65
|
if link?
|
|
66
66
|
follow_link
|
|
@@ -71,6 +71,8 @@ class Capybara::RackTest::Node < Capybara::Driver::Node
|
|
|
71
71
|
set(!checked?)
|
|
72
72
|
elsif tag_name == 'label'
|
|
73
73
|
click_label
|
|
74
|
+
elsif (details = native.xpath('.//ancestor-or-self::details').last)
|
|
75
|
+
toggle_details(details)
|
|
74
76
|
end
|
|
75
77
|
end
|
|
76
78
|
|
|
@@ -94,26 +96,41 @@ class Capybara::RackTest::Node < Capybara::Driver::Node
|
|
|
94
96
|
return true if string_node.disabled?
|
|
95
97
|
|
|
96
98
|
if %w[option optgroup].include? tag_name
|
|
97
|
-
find_xpath(
|
|
99
|
+
find_xpath(OPTION_OWNER_XPATH)[0].disabled?
|
|
98
100
|
else
|
|
99
|
-
!find_xpath(
|
|
101
|
+
!find_xpath(DISABLED_BY_FIELDSET_XPATH).empty?
|
|
100
102
|
end
|
|
101
103
|
end
|
|
102
104
|
|
|
105
|
+
def readonly?
|
|
106
|
+
# readonly attribute not valid on these input types
|
|
107
|
+
return false if input_field? && %w[hidden range color checkbox radio file submit image reset button].include?(type)
|
|
108
|
+
|
|
109
|
+
super
|
|
110
|
+
end
|
|
111
|
+
|
|
103
112
|
def path
|
|
104
113
|
native.path
|
|
105
114
|
end
|
|
106
115
|
|
|
107
|
-
def find_xpath(locator)
|
|
108
|
-
native.xpath(locator).map { |
|
|
116
|
+
def find_xpath(locator, **_hints)
|
|
117
|
+
native.xpath(locator).map { |el| self.class.new(driver, el) }
|
|
109
118
|
end
|
|
110
119
|
|
|
111
|
-
def find_css(locator)
|
|
112
|
-
native.css(locator, Capybara::RackTest::CSSHandlers.new).map { |
|
|
120
|
+
def find_css(locator, **_hints)
|
|
121
|
+
native.css(locator, Capybara::RackTest::CSSHandlers.new).map { |el| self.class.new(driver, el) }
|
|
113
122
|
end
|
|
114
123
|
|
|
115
|
-
|
|
116
|
-
|
|
124
|
+
public_instance_methods(false).each do |meth_name|
|
|
125
|
+
alias_method "unchecked_#{meth_name}", meth_name
|
|
126
|
+
private "unchecked_#{meth_name}" # rubocop:disable Style/AccessModifierDeclarations
|
|
127
|
+
|
|
128
|
+
class_eval <<~METHOD, __FILE__, __LINE__ + 1
|
|
129
|
+
def #{meth_name}(...)
|
|
130
|
+
stale_check
|
|
131
|
+
method(:"unchecked_#{meth_name}").call(...)
|
|
132
|
+
end
|
|
133
|
+
METHOD
|
|
117
134
|
end
|
|
118
135
|
|
|
119
136
|
protected
|
|
@@ -123,24 +140,30 @@ protected
|
|
|
123
140
|
if !string_node.visible?(check_ancestor)
|
|
124
141
|
''
|
|
125
142
|
elsif native.text?
|
|
126
|
-
native
|
|
127
|
-
|
|
128
|
-
|
|
143
|
+
native
|
|
144
|
+
.text
|
|
145
|
+
.delete(REMOVED_CHARACTERS)
|
|
146
|
+
.tr(SQUEEZED_SPACES, ' ')
|
|
147
|
+
.squeeze(' ')
|
|
129
148
|
elsif native.element?
|
|
130
149
|
text = native.children.map do |child|
|
|
131
150
|
Capybara::RackTest::Node.new(driver, child).displayed_text(check_ancestor: false)
|
|
132
151
|
end.join || ''
|
|
133
152
|
text = "\n#{text}\n" if BLOCK_ELEMENTS.include?(tag_name)
|
|
134
153
|
text
|
|
135
|
-
else
|
|
154
|
+
else # rubocop:disable Lint/DuplicateBranch
|
|
136
155
|
''
|
|
137
156
|
end
|
|
138
157
|
end
|
|
139
158
|
|
|
140
159
|
private
|
|
141
160
|
|
|
161
|
+
def stale_check
|
|
162
|
+
raise Capybara::RackTest::Errors::StaleElementReferenceError unless native.document == driver.dom
|
|
163
|
+
end
|
|
164
|
+
|
|
142
165
|
def deselect_options
|
|
143
|
-
select_node.find_xpath(
|
|
166
|
+
select_node.find_xpath('.//option[@selected]').each { |node| node.native.remove_attribute('selected') }
|
|
144
167
|
end
|
|
145
168
|
|
|
146
169
|
def string_node
|
|
@@ -165,8 +188,8 @@ private
|
|
|
165
188
|
end
|
|
166
189
|
|
|
167
190
|
def set_radio(_value) # rubocop:disable Naming/AccessorMethodName
|
|
168
|
-
other_radios_xpath = XPath.generate { |
|
|
169
|
-
driver.dom.xpath(other_radios_xpath).each { |node| node.remove_attribute(
|
|
191
|
+
other_radios_xpath = XPath.generate { |xp| xp.anywhere(:input)[xp.attr(:name) == self[:name]] }.to_s
|
|
192
|
+
driver.dom.xpath(other_radios_xpath).each { |node| node.remove_attribute('checked') }
|
|
170
193
|
native['checked'] = 'checked'
|
|
171
194
|
end
|
|
172
195
|
|
|
@@ -178,6 +201,14 @@ private
|
|
|
178
201
|
end
|
|
179
202
|
end
|
|
180
203
|
|
|
204
|
+
def set_range(value) # rubocop:disable Naming/AccessorMethodName
|
|
205
|
+
min, max, step = (native['min'] || 0).to_f, (native['max'] || 100).to_f, (native['step'] || 1).to_f
|
|
206
|
+
value = value.to_f
|
|
207
|
+
value = value.clamp(min, max)
|
|
208
|
+
value = (((value - min) / step).round * step) + min
|
|
209
|
+
native['value'] = value.clamp(min, max)
|
|
210
|
+
end
|
|
211
|
+
|
|
181
212
|
def set_input(value) # rubocop:disable Naming/AccessorMethodName
|
|
182
213
|
if text_or_password? && attribute_is_not_blank?(:maxlength)
|
|
183
214
|
# Browser behavior for maxlength="0" is inconsistent, so we stick with
|
|
@@ -185,15 +216,22 @@ private
|
|
|
185
216
|
value = value.to_s[0...self[:maxlength].to_i]
|
|
186
217
|
end
|
|
187
218
|
if value.is_a?(Array) # Assert multiple attribute is present
|
|
188
|
-
value.each do |
|
|
219
|
+
value.each do |val|
|
|
189
220
|
new_native = native.clone
|
|
190
221
|
new_native.remove_attribute('value')
|
|
191
222
|
native.add_next_sibling(new_native)
|
|
192
|
-
new_native['value'] =
|
|
223
|
+
new_native['value'] = val.to_s
|
|
193
224
|
end
|
|
194
225
|
native.remove
|
|
195
226
|
else
|
|
196
|
-
|
|
227
|
+
value.to_s.tap do |set_value|
|
|
228
|
+
if set_value.end_with?("\n") && form&.css('input, textarea')&.count == 1
|
|
229
|
+
native['value'] = set_value.to_s.chop
|
|
230
|
+
Capybara::RackTest::Form.new(driver, form).submit(self)
|
|
231
|
+
else
|
|
232
|
+
native['value'] = set_value
|
|
233
|
+
end
|
|
234
|
+
end
|
|
197
235
|
end
|
|
198
236
|
end
|
|
199
237
|
|
|
@@ -202,7 +240,7 @@ private
|
|
|
202
240
|
end
|
|
203
241
|
|
|
204
242
|
def follow_link
|
|
205
|
-
method = self[
|
|
243
|
+
method = self['data-method'] || self['data-turbo-method'] if driver.options[:respect_data_method]
|
|
206
244
|
method ||= :get
|
|
207
245
|
driver.follow(method, self[:href].to_s)
|
|
208
246
|
end
|
|
@@ -211,18 +249,29 @@ private
|
|
|
211
249
|
labelled_control = if native[:for]
|
|
212
250
|
find_xpath("//input[@id='#{native[:for]}']")
|
|
213
251
|
else
|
|
214
|
-
find_xpath(
|
|
252
|
+
find_xpath('.//input')
|
|
215
253
|
end.first
|
|
216
254
|
|
|
217
255
|
labelled_control.set(!labelled_control.checked?) if checkbox_or_radio?(labelled_control)
|
|
218
256
|
end
|
|
219
257
|
|
|
258
|
+
def toggle_details(details = nil)
|
|
259
|
+
details ||= native.xpath('.//ancestor-or-self::details').last
|
|
260
|
+
return unless details
|
|
261
|
+
|
|
262
|
+
if details.has_attribute?('open')
|
|
263
|
+
details.remove_attribute('open')
|
|
264
|
+
else
|
|
265
|
+
details.set_attribute('open', 'open')
|
|
266
|
+
end
|
|
267
|
+
end
|
|
268
|
+
|
|
220
269
|
def link?
|
|
221
270
|
tag_name == 'a' && !self[:href].nil?
|
|
222
271
|
end
|
|
223
272
|
|
|
224
273
|
def submits?
|
|
225
|
-
(tag_name == 'input' && %w[submit image].include?(type)) || (tag_name == 'button' && [nil,
|
|
274
|
+
(tag_name == 'input' && %w[submit image].include?(type)) || (tag_name == 'button' && [nil, 'submit'].include?(type))
|
|
226
275
|
end
|
|
227
276
|
|
|
228
277
|
def checkable?
|
|
@@ -252,6 +301,24 @@ protected
|
|
|
252
301
|
end
|
|
253
302
|
|
|
254
303
|
def textarea?
|
|
255
|
-
tag_name ==
|
|
256
|
-
end
|
|
304
|
+
tag_name == 'textarea'
|
|
305
|
+
end
|
|
306
|
+
|
|
307
|
+
def range?
|
|
308
|
+
input_field? && type == 'range'
|
|
309
|
+
end
|
|
310
|
+
|
|
311
|
+
OPTION_OWNER_XPATH = XPath.parent(:optgroup, :select, :datalist).to_s.freeze
|
|
312
|
+
DISABLED_BY_FIELDSET_XPATH = XPath.generate do |x|
|
|
313
|
+
x.parent(:fieldset)[
|
|
314
|
+
XPath.attr(:disabled)
|
|
315
|
+
] + x.ancestor[
|
|
316
|
+
~x.self(:legend) |
|
|
317
|
+
x.preceding_sibling(:legend)
|
|
318
|
+
][
|
|
319
|
+
x.parent(:fieldset)[
|
|
320
|
+
x.attr(:disabled)
|
|
321
|
+
]
|
|
322
|
+
]
|
|
323
|
+
end.to_s.freeze
|
|
257
324
|
end
|
data/lib/capybara/rails.rb
CHANGED
|
@@ -0,0 +1,41 @@
|
|
|
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, ...)
|
|
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
|
+
return @registered.public_send(method_name, ...)
|
|
23
|
+
end
|
|
24
|
+
super
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def respond_to_missing?(method_name, include_all)
|
|
28
|
+
@registered.respond_to?(method_name) || super
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
private
|
|
32
|
+
|
|
33
|
+
def initialize
|
|
34
|
+
@registered = {}
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def register(name, block)
|
|
38
|
+
@registered[name] = block
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
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=new')
|
|
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,66 @@
|
|
|
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
|
+
base_class = begin
|
|
9
|
+
require 'rack/handler/webrick'
|
|
10
|
+
Rack
|
|
11
|
+
rescue LoadError
|
|
12
|
+
# Rack 3 separated out the webrick handle - no way test currently in Capybaras automated
|
|
13
|
+
# tests due to Sinatra not yet supporting Rack 3 - experimental
|
|
14
|
+
require 'rackup/handler/webrick'
|
|
15
|
+
Rackup
|
|
16
|
+
end
|
|
17
|
+
options = { Host: host, Port: port, AccessLog: [], Logger: WEBrick::Log.new(nil, 0) }.merge(options)
|
|
18
|
+
base_class::Handler::WEBrick.run(app, **options)
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
Capybara.register_server :puma do |app, port, host, **options| # rubocop:disable Metrics/BlockLength
|
|
22
|
+
begin
|
|
23
|
+
require 'rackup'
|
|
24
|
+
rescue LoadError # rubocop:disable Lint/SuppressedException
|
|
25
|
+
end
|
|
26
|
+
begin
|
|
27
|
+
require 'rack/handler/puma'
|
|
28
|
+
rescue LoadError
|
|
29
|
+
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`.'
|
|
30
|
+
end
|
|
31
|
+
puma_rack_handler = defined?(Rackup::Handler::Puma) ? Rackup::Handler::Puma : Rack::Handler::Puma
|
|
32
|
+
|
|
33
|
+
unless puma_rack_handler.respond_to?(:config)
|
|
34
|
+
raise LoadError, 'Capybara requires `puma` version 3.8.0 or higher, please upgrade `puma` or register and specify your own server block'
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
# If we just run the Puma Rack handler it installs signal handlers which prevent us from being able to interrupt tests.
|
|
38
|
+
# Therefore construct and run the Server instance ourselves.
|
|
39
|
+
# puma_rack_handler.run(app, { Host: host, Port: port, Threads: "0:4", workers: 0, daemon: false }.merge(options))
|
|
40
|
+
default_options = { Host: host, Port: port, Threads: '0:4', workers: 0, daemon: false }
|
|
41
|
+
options = default_options.merge(options)
|
|
42
|
+
|
|
43
|
+
conf = puma_rack_handler.config(app, options)
|
|
44
|
+
conf.clamp
|
|
45
|
+
|
|
46
|
+
puma_ver = Gem::Version.new(Puma::Const::PUMA_VERSION)
|
|
47
|
+
require_relative 'patches/puma_ssl' if Gem::Requirement.new('>=4.0.0', '< 4.1.0').satisfied_by?(puma_ver)
|
|
48
|
+
|
|
49
|
+
logger = (defined?(Puma::LogWriter) ? Puma::LogWriter : Puma::Events).then do |cls|
|
|
50
|
+
conf.options[:Silent] ? cls.strings : cls.stdio
|
|
51
|
+
end
|
|
52
|
+
conf.options[:log_writer] = logger
|
|
53
|
+
|
|
54
|
+
logger.log 'Capybara starting Puma...'
|
|
55
|
+
logger.log "* Version #{Puma::Const::PUMA_VERSION}, codename: #{Puma::Const::CODE_NAME}"
|
|
56
|
+
logger.log "* Min threads: #{conf.options[:min_threads]}, max threads: #{conf.options[:max_threads]}"
|
|
57
|
+
|
|
58
|
+
Puma::Server.new(
|
|
59
|
+
conf.app,
|
|
60
|
+
defined?(Puma::LogWriter) ? nil : logger,
|
|
61
|
+
conf.options
|
|
62
|
+
).tap do |s|
|
|
63
|
+
s.binder.parse conf.options[:binds], (s.log_writer rescue s.events) # rubocop:disable Style/RescueModifier
|
|
64
|
+
s.min_threads, s.max_threads = conf.options[:min_threads], conf.options[:max_threads] if s.respond_to? :min_threads=
|
|
65
|
+
end.run.join
|
|
66
|
+
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,21 +28,23 @@ 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
|
-
def_delegators :full_results, :size, :length, :last, :values_at, :inspect, :sample
|
|
37
|
+
def_delegators :full_results, :size, :length, :last, :values_at, :inspect, :sample, :to_ary
|
|
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
|
|
@@ -50,22 +54,24 @@ module Capybara
|
|
|
50
54
|
idx, length = args
|
|
51
55
|
max_idx = case idx
|
|
52
56
|
when Integer
|
|
53
|
-
if
|
|
54
|
-
length.nil? ? idx : idx + length - 1
|
|
55
|
-
else
|
|
57
|
+
if idx.negative?
|
|
56
58
|
nil
|
|
59
|
+
else
|
|
60
|
+
length.nil? ? idx : idx + length - 1
|
|
57
61
|
end
|
|
58
62
|
when Range
|
|
59
|
-
idx.max
|
|
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
|
|
60
69
|
end
|
|
61
70
|
|
|
62
71
|
if max_idx.nil?
|
|
63
72
|
full_results[*args]
|
|
64
73
|
else
|
|
65
|
-
|
|
66
|
-
break if @result_cache.size > max_idx
|
|
67
|
-
@result_cache << @results_enum.next
|
|
68
|
-
end
|
|
74
|
+
load_up_to(max_idx + 1)
|
|
69
75
|
@result_cache[*args]
|
|
70
76
|
end
|
|
71
77
|
end
|
|
@@ -76,41 +82,25 @@ module Capybara
|
|
|
76
82
|
end
|
|
77
83
|
|
|
78
84
|
def compare_count
|
|
85
|
+
return 0 unless @query
|
|
86
|
+
|
|
87
|
+
count, min, max, between = @query.options.values_at(:count, :minimum, :maximum, :between)
|
|
88
|
+
|
|
79
89
|
# Only check filters for as many elements as necessary to determine result
|
|
80
|
-
if
|
|
81
|
-
|
|
82
|
-
loop do
|
|
83
|
-
break if @result_cache.size > count_opt
|
|
84
|
-
@result_cache << @results_enum.next
|
|
85
|
-
end
|
|
86
|
-
return @result_cache.size <=> count_opt
|
|
90
|
+
if count && (count = Integer(count))
|
|
91
|
+
return load_up_to(count + 1) <=> count
|
|
87
92
|
end
|
|
88
93
|
|
|
89
|
-
if
|
|
90
|
-
min_opt = Integer(@query.options[:minimum])
|
|
91
|
-
begin
|
|
92
|
-
@result_cache << @results_enum.next while @result_cache.size < min_opt
|
|
93
|
-
rescue StopIteration
|
|
94
|
-
return -1
|
|
95
|
-
end
|
|
96
|
-
end
|
|
94
|
+
return -1 if min && (min = Integer(min)) && (load_up_to(min) < min)
|
|
97
95
|
|
|
98
|
-
if
|
|
99
|
-
max_opt = Integer(@query.options[:maximum])
|
|
100
|
-
loop do
|
|
101
|
-
return 1 if @result_cache.size > max_opt
|
|
102
|
-
@result_cache << @results_enum.next
|
|
103
|
-
end
|
|
104
|
-
end
|
|
96
|
+
return 1 if max && (max = Integer(max)) && (load_up_to(max + 1) > max)
|
|
105
97
|
|
|
106
|
-
if
|
|
107
|
-
min, max =
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
return 0 if @query.options[:between].include? @result_cache.size
|
|
113
|
-
return @result_cache.size <=> min
|
|
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)
|
|
114
104
|
end
|
|
115
105
|
|
|
116
106
|
0
|
|
@@ -123,13 +113,15 @@ module Capybara
|
|
|
123
113
|
def failure_message
|
|
124
114
|
message = @query.failure_message
|
|
125
115
|
if count.zero?
|
|
126
|
-
message <<
|
|
116
|
+
message << ' but there were no matches'
|
|
127
117
|
else
|
|
128
|
-
message << ", found #{count} #{Capybara::Helpers.declension('match', 'matches', count)}: "
|
|
118
|
+
message << ", found #{count} #{Capybara::Helpers.declension('match', 'matches', count)}: " \
|
|
119
|
+
<< full_results.map { |r| r.text.inspect }.join(', ')
|
|
129
120
|
end
|
|
130
121
|
unless rest.empty?
|
|
131
|
-
elements = rest.map { |el| el.text rescue
|
|
132
|
-
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?
|
|
133
125
|
end
|
|
134
126
|
message
|
|
135
127
|
end
|
|
@@ -138,8 +130,34 @@ module Capybara
|
|
|
138
130
|
failure_message.sub(/(to find)/, 'not \1')
|
|
139
131
|
end
|
|
140
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
|
+
|
|
141
145
|
private
|
|
142
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
|
+
|
|
143
161
|
def full_results
|
|
144
162
|
loop { @result_cache << @results_enum.next }
|
|
145
163
|
@result_cache
|
|
@@ -149,13 +167,18 @@ module Capybara
|
|
|
149
167
|
@rest ||= @elements - full_results
|
|
150
168
|
end
|
|
151
169
|
|
|
152
|
-
|
|
153
|
-
# 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
|
|
154
172
|
# causes a concurrency issue with network requests here
|
|
155
173
|
# https://github.com/jruby/jruby/issues/4212
|
|
156
|
-
|
|
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)
|
|
157
178
|
@elements.select(&block).to_enum # non-lazy evaluation
|
|
158
|
-
|
|
179
|
+
end
|
|
180
|
+
else
|
|
181
|
+
def lazy_select_elements(&block)
|
|
159
182
|
@elements.lazy.select(&block)
|
|
160
183
|
end
|
|
161
184
|
end
|