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
|
@@ -2,21 +2,23 @@
|
|
|
2
2
|
|
|
3
3
|
module Capybara
|
|
4
4
|
module Queries
|
|
5
|
-
class SiblingQuery <
|
|
5
|
+
class SiblingQuery < SelectorQuery
|
|
6
6
|
# @api private
|
|
7
7
|
def resolve_for(node, exact = nil)
|
|
8
8
|
@sibling_node = node
|
|
9
9
|
node.synchronize do
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
10
|
+
scope = node.respond_to?(:session) ? node.session.current_scope : node.find(:xpath, '/*')
|
|
11
|
+
match_results = super(scope, exact)
|
|
12
|
+
siblings = node.find_xpath((XPath.preceding_sibling + XPath.following_sibling).to_s)
|
|
13
|
+
.map(&method(:to_element))
|
|
14
|
+
.select { |el| match_results.include?(el) }
|
|
15
|
+
Capybara::Result.new(ordered_results(siblings), self)
|
|
14
16
|
end
|
|
15
17
|
end
|
|
16
18
|
|
|
17
|
-
def description
|
|
19
|
+
def description(applied = false) # rubocop:disable Style/OptionalBooleanParameter
|
|
18
20
|
desc = super
|
|
19
|
-
sibling_query = @sibling_node
|
|
21
|
+
sibling_query = @sibling_node&.instance_variable_get(:@query)
|
|
20
22
|
desc += " that is a sibling of #{sibling_query.description}" if sibling_query
|
|
21
23
|
desc
|
|
22
24
|
end
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Capybara
|
|
4
|
+
# @api private
|
|
5
|
+
module Queries
|
|
6
|
+
class StyleQuery < BaseQuery
|
|
7
|
+
def initialize(expected_styles, session_options:, **options)
|
|
8
|
+
@expected_styles = stringify_keys(expected_styles)
|
|
9
|
+
@options = options
|
|
10
|
+
@actual_styles = {}
|
|
11
|
+
super(@options)
|
|
12
|
+
self.session_options = session_options
|
|
13
|
+
|
|
14
|
+
assert_valid_keys
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def resolves_for?(node)
|
|
18
|
+
@node = node
|
|
19
|
+
@actual_styles = node.style(*@expected_styles.keys)
|
|
20
|
+
@expected_styles.all? do |style, value|
|
|
21
|
+
if value.is_a? Regexp
|
|
22
|
+
value.match? @actual_styles[style]
|
|
23
|
+
else
|
|
24
|
+
@actual_styles[style] == value
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def failure_message
|
|
30
|
+
+"Expected node to have styles #{@expected_styles.inspect}. " \
|
|
31
|
+
"Actual styles were #{@actual_styles.inspect}"
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
private
|
|
35
|
+
|
|
36
|
+
def stringify_keys(hsh)
|
|
37
|
+
hsh.transform_keys(&:to_s)
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def valid_keys
|
|
41
|
+
%i[wait]
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
end
|
|
@@ -5,17 +5,20 @@ module Capybara
|
|
|
5
5
|
module Queries
|
|
6
6
|
class TextQuery < BaseQuery
|
|
7
7
|
def initialize(type = nil, expected_text, session_options:, **options) # rubocop:disable Style/OptionalArguments
|
|
8
|
-
@type =
|
|
9
|
-
|
|
10
|
-
else
|
|
11
|
-
type
|
|
12
|
-
end
|
|
8
|
+
@type = type.nil? ? default_type : type
|
|
9
|
+
raise ArgumentError, "#{@type} is not a valid type for a text query" unless valid_types.include?(@type)
|
|
13
10
|
|
|
14
|
-
@expected_text = expected_text.is_a?(Regexp) ? expected_text : expected_text.to_s
|
|
15
11
|
@options = options
|
|
16
12
|
super(@options)
|
|
17
13
|
self.session_options = session_options
|
|
18
14
|
|
|
15
|
+
if expected_text.nil? && !exact?
|
|
16
|
+
warn 'Checking for expected text of nil is confusing and/or pointless since it will always match. '\
|
|
17
|
+
"Please specify a string or regexp instead. #{Capybara::Helpers.filter_backtrace(caller)}"
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
@expected_text = expected_text.is_a?(Regexp) ? expected_text : expected_text.to_s
|
|
21
|
+
|
|
19
22
|
@search_regexp = Capybara::Helpers.to_regexp(@expected_text, exact: exact?)
|
|
20
23
|
|
|
21
24
|
assert_valid_keys
|
|
@@ -23,7 +26,7 @@ module Capybara
|
|
|
23
26
|
|
|
24
27
|
def resolve_for(node)
|
|
25
28
|
@node = node
|
|
26
|
-
@actual_text = text
|
|
29
|
+
@actual_text = text
|
|
27
30
|
@count = @actual_text.scan(@search_regexp).size
|
|
28
31
|
end
|
|
29
32
|
|
|
@@ -39,7 +42,7 @@ module Capybara
|
|
|
39
42
|
if @expected_text.is_a?(Regexp)
|
|
40
43
|
"text matching #{@expected_text.inspect}"
|
|
41
44
|
else
|
|
42
|
-
"#{
|
|
45
|
+
"#{'exact ' if exact?}text #{@expected_text.inspect}"
|
|
43
46
|
end
|
|
44
47
|
end
|
|
45
48
|
|
|
@@ -50,15 +53,15 @@ module Capybara
|
|
|
50
53
|
end
|
|
51
54
|
|
|
52
55
|
def build_message(report_on_invisible)
|
|
53
|
-
message =
|
|
56
|
+
message = +''
|
|
54
57
|
unless (COUNT_KEYS & @options.keys).empty?
|
|
55
58
|
message << " but found #{@count} #{Capybara::Helpers.declension('time', 'times', @count)}"
|
|
56
59
|
end
|
|
57
60
|
message << " in #{@actual_text.inspect}"
|
|
58
61
|
|
|
59
62
|
details_message = []
|
|
60
|
-
details_message << case_insensitive_message if @node
|
|
61
|
-
details_message << invisible_message if @node
|
|
63
|
+
details_message << case_insensitive_message if @node && check_case_insensitive?
|
|
64
|
+
details_message << invisible_message if @node && check_visible_text? && report_on_invisible
|
|
62
65
|
details_message.compact!
|
|
63
66
|
|
|
64
67
|
message << ". (However, #{details_message.join(' and ')}.)" unless details_message.empty?
|
|
@@ -68,30 +71,45 @@ module Capybara
|
|
|
68
71
|
def case_insensitive_message
|
|
69
72
|
insensitive_regexp = Capybara::Helpers.to_regexp(@expected_text, options: Regexp::IGNORECASE)
|
|
70
73
|
insensitive_count = @actual_text.scan(insensitive_regexp).size
|
|
71
|
-
if insensitive_count
|
|
72
|
-
|
|
73
|
-
|
|
74
|
+
return if insensitive_count == @count
|
|
75
|
+
|
|
76
|
+
"it was found #{occurrences insensitive_count} using a case insensitive search"
|
|
74
77
|
end
|
|
75
78
|
|
|
76
79
|
def invisible_message
|
|
77
|
-
invisible_text = text(
|
|
80
|
+
invisible_text = text(query_type: :all)
|
|
78
81
|
invisible_count = invisible_text.scan(@search_regexp).size
|
|
79
|
-
if invisible_count
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
rescue
|
|
82
|
+
return if invisible_count == @count
|
|
83
|
+
|
|
84
|
+
"it was found #{occurrences invisible_count} including non-visible text"
|
|
85
|
+
rescue StandardError
|
|
86
|
+
# An error getting the non-visible text (if element goes out of scope) should not affect the response
|
|
87
|
+
nil
|
|
83
88
|
end
|
|
84
89
|
|
|
85
90
|
def valid_keys
|
|
86
|
-
COUNT_KEYS + %i[wait exact]
|
|
91
|
+
COUNT_KEYS + %i[wait exact normalize_ws]
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
def valid_types
|
|
95
|
+
%i[all visible]
|
|
87
96
|
end
|
|
88
97
|
|
|
89
98
|
def check_visible_text?
|
|
90
99
|
@type == :visible
|
|
91
100
|
end
|
|
92
101
|
|
|
93
|
-
def
|
|
94
|
-
|
|
102
|
+
def check_case_insensitive?
|
|
103
|
+
!@expected_text.is_a?(Regexp)
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
def text(node: @node, query_type: @type)
|
|
107
|
+
normalize_ws = options.fetch(:normalize_ws, session_options.default_normalize_ws)
|
|
108
|
+
node.text(query_type, normalize_ws: normalize_ws)
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
def default_type
|
|
112
|
+
Capybara.ignore_hidden_elements || Capybara.visible_text_only ? :visible : :all
|
|
95
113
|
end
|
|
96
114
|
end
|
|
97
115
|
end
|
|
@@ -8,12 +8,12 @@ module Capybara
|
|
|
8
8
|
@expected_title = expected_title.is_a?(Regexp) ? expected_title : expected_title.to_s
|
|
9
9
|
@options = options
|
|
10
10
|
super(@options)
|
|
11
|
-
@search_regexp =
|
|
11
|
+
@search_regexp = Helpers.to_regexp(@expected_title, all_whitespace: true, exact: options.fetch(:exact, false))
|
|
12
12
|
assert_valid_keys
|
|
13
13
|
end
|
|
14
14
|
|
|
15
15
|
def resolves_for?(node)
|
|
16
|
-
(@actual_title = node.title).match(@search_regexp)
|
|
16
|
+
(@actual_title = node.title).match?(@search_regexp)
|
|
17
17
|
end
|
|
18
18
|
|
|
19
19
|
def failure_message
|
|
@@ -8,6 +8,7 @@ class Capybara::RackTest::Browser
|
|
|
8
8
|
|
|
9
9
|
def initialize(driver)
|
|
10
10
|
@driver = driver
|
|
11
|
+
@current_fragment = nil
|
|
11
12
|
end
|
|
12
13
|
|
|
13
14
|
def app
|
|
@@ -30,58 +31,70 @@ class Capybara::RackTest::Browser
|
|
|
30
31
|
|
|
31
32
|
def submit(method, path, attributes)
|
|
32
33
|
path = request_path if path.nil? || path.empty?
|
|
33
|
-
|
|
34
|
+
uri = build_uri(path)
|
|
35
|
+
uri.query = '' if method.to_s.casecmp('get').zero?
|
|
36
|
+
process_and_follow_redirects(method, uri.to_s, attributes, 'HTTP_REFERER' => current_url)
|
|
34
37
|
end
|
|
35
38
|
|
|
36
39
|
def follow(method, path, **attributes)
|
|
37
|
-
return if
|
|
40
|
+
return if fragment_or_script?(path)
|
|
41
|
+
|
|
38
42
|
process_and_follow_redirects(method, path, attributes, 'HTTP_REFERER' => current_url)
|
|
39
43
|
end
|
|
40
44
|
|
|
41
45
|
def process_and_follow_redirects(method, path, attributes = {}, env = {})
|
|
46
|
+
@current_fragment = build_uri(path).fragment
|
|
42
47
|
process(method, path, attributes, env)
|
|
43
48
|
|
|
44
49
|
return unless driver.follow_redirects?
|
|
45
50
|
|
|
46
51
|
driver.redirect_limit.times do
|
|
47
|
-
|
|
52
|
+
if last_response.redirect?
|
|
53
|
+
if [307, 308].include? last_response.status
|
|
54
|
+
process(last_request.request_method, last_response['Location'], last_request.params, env)
|
|
55
|
+
else
|
|
56
|
+
process(:get, last_response['Location'], {}, env)
|
|
57
|
+
end
|
|
58
|
+
end
|
|
48
59
|
end
|
|
49
|
-
raise Capybara::InfiniteRedirectError, "redirected more than #{driver.redirect_limit} times, check for infinite redirects." if last_response.redirect?
|
|
50
|
-
end
|
|
51
60
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
method.downcase! unless method.is_a? Symbol
|
|
55
|
-
if path.empty?
|
|
56
|
-
new_uri.path = request_path
|
|
57
|
-
else
|
|
58
|
-
new_uri.path = request_path if path.start_with?("?")
|
|
59
|
-
new_uri.path = "/" if new_uri.path.empty?
|
|
60
|
-
new_uri.path = request_path.sub(%r{/[^/]*$}, '/') + new_uri.path unless new_uri.path.start_with?('/')
|
|
61
|
+
if last_response.redirect? # rubocop:disable Style/GuardClause
|
|
62
|
+
raise Capybara::InfiniteRedirectError, "redirected more than #{driver.redirect_limit} times, check for infinite redirects."
|
|
61
63
|
end
|
|
62
|
-
|
|
63
|
-
new_uri.host ||= @current_host
|
|
64
|
-
new_uri.port ||= @current_port unless new_uri.default_port == @current_port
|
|
65
|
-
|
|
66
|
-
@current_scheme = new_uri.scheme
|
|
67
|
-
@current_host = new_uri.host
|
|
68
|
-
@current_port = new_uri.port
|
|
64
|
+
end
|
|
69
65
|
|
|
66
|
+
def process(method, path, attributes = {}, env = {})
|
|
67
|
+
method = method.downcase
|
|
68
|
+
new_uri = build_uri(path)
|
|
69
|
+
@current_scheme, @current_host, @current_port = new_uri.select(:scheme, :host, :port)
|
|
70
|
+
@current_fragment = new_uri.fragment || @current_fragment
|
|
70
71
|
reset_cache!
|
|
71
72
|
send(method, new_uri.to_s, attributes, env.merge(options[:headers] || {}))
|
|
72
73
|
end
|
|
73
74
|
|
|
75
|
+
def build_uri(path)
|
|
76
|
+
URI.parse(path).tap do |uri|
|
|
77
|
+
uri.path = request_path if path.empty? || path.start_with?('?')
|
|
78
|
+
uri.path = '/' if uri.path.empty?
|
|
79
|
+
uri.path = request_path.sub(%r{/[^/]*$}, '/') + uri.path unless uri.path.start_with?('/')
|
|
80
|
+
|
|
81
|
+
uri.scheme ||= @current_scheme
|
|
82
|
+
uri.host ||= @current_host
|
|
83
|
+
uri.port ||= @current_port unless uri.default_port == @current_port
|
|
84
|
+
end
|
|
85
|
+
end
|
|
86
|
+
|
|
74
87
|
def current_url
|
|
75
|
-
last_request.url
|
|
88
|
+
uri = build_uri(last_request.url)
|
|
89
|
+
uri.fragment = @current_fragment if @current_fragment
|
|
90
|
+
uri.to_s
|
|
76
91
|
rescue Rack::Test::Error
|
|
77
|
-
|
|
92
|
+
''
|
|
78
93
|
end
|
|
79
94
|
|
|
80
95
|
def reset_host!
|
|
81
96
|
uri = URI.parse(driver.session_options.app_host || driver.session_options.default_host)
|
|
82
|
-
@current_scheme = uri.scheme
|
|
83
|
-
@current_host = uri.host
|
|
84
|
-
@current_port = uri.port
|
|
97
|
+
@current_scheme, @current_host, @current_port = uri.select(:scheme, :host, :port)
|
|
85
98
|
end
|
|
86
99
|
|
|
87
100
|
def reset_cache!
|
|
@@ -103,7 +116,7 @@ class Capybara::RackTest::Browser
|
|
|
103
116
|
def html
|
|
104
117
|
last_response.body
|
|
105
118
|
rescue Rack::Test::Error
|
|
106
|
-
|
|
119
|
+
''
|
|
107
120
|
end
|
|
108
121
|
|
|
109
122
|
def title
|
|
@@ -120,6 +133,12 @@ protected
|
|
|
120
133
|
def request_path
|
|
121
134
|
last_request.path
|
|
122
135
|
rescue Rack::Test::Error
|
|
123
|
-
|
|
136
|
+
'/'
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
private
|
|
140
|
+
|
|
141
|
+
def fragment_or_script?(path)
|
|
142
|
+
path.gsub(/^#{Regexp.escape(request_path)}/, '').start_with?('#') || path.downcase.start_with?('javascript:')
|
|
124
143
|
end
|
|
125
144
|
end
|
|
@@ -15,8 +15,9 @@ class Capybara::RackTest::Driver < Capybara::Driver::Base
|
|
|
15
15
|
attr_reader :app, :options
|
|
16
16
|
|
|
17
17
|
def initialize(app, **options)
|
|
18
|
-
raise ArgumentError,
|
|
19
|
-
|
|
18
|
+
raise ArgumentError, 'rack-test requires a rack application, but none was given' unless app
|
|
19
|
+
|
|
20
|
+
super()
|
|
20
21
|
@app = app
|
|
21
22
|
@options = DEFAULT_OPTIONS.merge(options)
|
|
22
23
|
end
|
|
@@ -42,7 +43,7 @@ class Capybara::RackTest::Driver < Capybara::Driver::Base
|
|
|
42
43
|
end
|
|
43
44
|
|
|
44
45
|
def visit(path, **attributes)
|
|
45
|
-
browser.visit(path, attributes)
|
|
46
|
+
browser.visit(path, **attributes)
|
|
46
47
|
end
|
|
47
48
|
|
|
48
49
|
def refresh
|
|
@@ -75,6 +76,10 @@ class Capybara::RackTest::Driver < Capybara::Driver::Base
|
|
|
75
76
|
|
|
76
77
|
def find_css(selector)
|
|
77
78
|
browser.find(:css, selector)
|
|
79
|
+
rescue Nokogiri::CSS::SyntaxError
|
|
80
|
+
raise unless selector.include?(' i]')
|
|
81
|
+
|
|
82
|
+
raise ArgumentError, "This driver doesn't support case insensitive attribute matching when using CSS base selectors"
|
|
78
83
|
end
|
|
79
84
|
|
|
80
85
|
def html
|
|
@@ -98,4 +103,8 @@ class Capybara::RackTest::Driver < Capybara::Driver::Base
|
|
|
98
103
|
def put(*args, &block); browser.put(*args, &block); end
|
|
99
104
|
def delete(*args, &block); browser.delete(*args, &block); end
|
|
100
105
|
def header(key, value); browser.header(key, value); end
|
|
106
|
+
|
|
107
|
+
def invalid_element_errors
|
|
108
|
+
[Capybara::RackTest::Errors::StaleElementReferenceError]
|
|
109
|
+
end
|
|
101
110
|
end
|
|
@@ -6,51 +6,45 @@ class Capybara::RackTest::Form < Capybara::RackTest::Node
|
|
|
6
6
|
# That check should be based solely on the form element's 'enctype' attribute value,
|
|
7
7
|
# which should probably be provided to Rack::Test in its non-GET request methods.
|
|
8
8
|
class NilUploadedFile < Rack::Test::UploadedFile
|
|
9
|
-
def initialize
|
|
10
|
-
@empty_file = Tempfile.new(
|
|
9
|
+
def initialize # rubocop:disable Lint/MissingSuper
|
|
10
|
+
@empty_file = Tempfile.new('nil_uploaded_file')
|
|
11
11
|
@empty_file.close
|
|
12
12
|
end
|
|
13
13
|
|
|
14
|
-
def original_filename;
|
|
15
|
-
def content_type;
|
|
14
|
+
def original_filename; ''; end
|
|
15
|
+
def content_type; 'application/octet-stream'; end
|
|
16
16
|
def path; @empty_file.path; end
|
|
17
17
|
def size; 0; end
|
|
18
|
-
def read;
|
|
18
|
+
def read; ''; end
|
|
19
19
|
end
|
|
20
20
|
|
|
21
21
|
def params(button)
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
xpath
|
|
27
|
-
xpath += x.anywhere(*form_element_types).where(x.attr(:form) == native[:id]) if native[:id]
|
|
28
|
-
xpath.where(!x.attr(:disabled))
|
|
22
|
+
form_element_types = %i[input select textarea button]
|
|
23
|
+
form_elements_xpath = XPath.generate do |xp|
|
|
24
|
+
xpath = xp.descendant(*form_element_types).where(!xp.attr(:form))
|
|
25
|
+
xpath += xp.anywhere(*form_element_types).where(xp.attr(:form) == native[:id]) if native[:id]
|
|
26
|
+
xpath.where(!xp.attr(:disabled))
|
|
29
27
|
end.to_s
|
|
30
28
|
|
|
31
|
-
native.xpath(form_elements_xpath).
|
|
29
|
+
form_elements = native.xpath(form_elements_xpath).reject { |el| submitter?(el) && (el != button.native) }
|
|
30
|
+
|
|
31
|
+
form_elements.each_with_object(make_params) do |field, params|
|
|
32
32
|
case field.name
|
|
33
|
-
when 'input'
|
|
34
|
-
|
|
35
|
-
when '
|
|
36
|
-
add_select_param(field, params)
|
|
37
|
-
when 'textarea'
|
|
38
|
-
add_textarea_param(field, params)
|
|
33
|
+
when 'input', 'button' then add_input_param(field, params)
|
|
34
|
+
when 'select' then add_select_param(field, params)
|
|
35
|
+
when 'textarea' then add_textarea_param(field, params)
|
|
39
36
|
end
|
|
40
|
-
end
|
|
41
|
-
merge_param!(params, button[:name], button[:value] || "") if button[:name]
|
|
42
|
-
|
|
43
|
-
params.to_params_hash
|
|
37
|
+
end.to_params_hash
|
|
44
38
|
end
|
|
45
39
|
|
|
46
40
|
def submit(button)
|
|
47
|
-
action =
|
|
48
|
-
method =
|
|
41
|
+
action = button&.[]('formaction') || native['action']
|
|
42
|
+
method = button&.[]('formmethod') || request_method
|
|
49
43
|
driver.submit(method, action.to_s, params(button))
|
|
50
44
|
end
|
|
51
45
|
|
|
52
46
|
def multipart?
|
|
53
|
-
self[:enctype] ==
|
|
47
|
+
self[:enctype] == 'multipart/form-data'
|
|
54
48
|
end
|
|
55
49
|
|
|
56
50
|
private
|
|
@@ -62,10 +56,11 @@ private
|
|
|
62
56
|
end
|
|
63
57
|
|
|
64
58
|
def request_method
|
|
65
|
-
self[:method]
|
|
59
|
+
/post/i.match?(self[:method] || '') ? :post : :get
|
|
66
60
|
end
|
|
67
61
|
|
|
68
62
|
def merge_param!(params, key, value)
|
|
63
|
+
key = key.to_s
|
|
69
64
|
if Rack::Utils.respond_to?(:default_query_parser)
|
|
70
65
|
Rack::Utils.default_query_parser.normalize_params(params, key, value, Rack::Utils.param_depth_limit)
|
|
71
66
|
else
|
|
@@ -82,43 +77,51 @@ private
|
|
|
82
77
|
end
|
|
83
78
|
|
|
84
79
|
def add_input_param(field, params)
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
80
|
+
name, value = field['name'].to_s, field['value'].to_s
|
|
81
|
+
return if name.empty?
|
|
82
|
+
|
|
83
|
+
value = case field['type']
|
|
84
|
+
when 'radio', 'checkbox'
|
|
85
|
+
return unless field['checked']
|
|
86
|
+
|
|
87
|
+
Capybara::RackTest::Node.new(driver, field).value.to_s
|
|
88
|
+
when 'file'
|
|
94
89
|
if multipart?
|
|
95
|
-
|
|
96
|
-
NilUploadedFile.new
|
|
97
|
-
else
|
|
98
|
-
mime_info = MiniMime.lookup_by_filename(value)
|
|
99
|
-
Rack::Test::UploadedFile.new(value, (mime_info && mime_info.content_type).to_s)
|
|
100
|
-
end
|
|
101
|
-
merge_param!(params, field['name'].to_s, file)
|
|
90
|
+
file_to_upload(value)
|
|
102
91
|
else
|
|
103
|
-
|
|
92
|
+
File.basename(value)
|
|
104
93
|
end
|
|
105
94
|
else
|
|
106
|
-
|
|
95
|
+
value
|
|
96
|
+
end
|
|
97
|
+
merge_param!(params, name, value)
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
def file_to_upload(filename)
|
|
101
|
+
if filename.empty?
|
|
102
|
+
NilUploadedFile.new
|
|
103
|
+
else
|
|
104
|
+
mime_info = MiniMime.lookup_by_filename(filename)
|
|
105
|
+
Rack::Test::UploadedFile.new(filename, mime_info&.content_type&.to_s)
|
|
107
106
|
end
|
|
108
107
|
end
|
|
109
108
|
|
|
110
109
|
def add_select_param(field, params)
|
|
111
|
-
if field
|
|
112
|
-
field.xpath(
|
|
113
|
-
merge_param!(params, field['name']
|
|
110
|
+
if field.has_attribute?('multiple')
|
|
111
|
+
field.xpath('.//option[@selected]').each do |option|
|
|
112
|
+
merge_param!(params, field['name'], (option['value'] || option.text).to_s)
|
|
114
113
|
end
|
|
115
114
|
else
|
|
116
115
|
option = field.xpath('.//option[@selected]').first || field.xpath('.//option').first
|
|
117
|
-
merge_param!(params, field['name']
|
|
116
|
+
merge_param!(params, field['name'], (option['value'] || option.text).to_s) if option
|
|
118
117
|
end
|
|
119
118
|
end
|
|
120
119
|
|
|
121
120
|
def add_textarea_param(field, params)
|
|
122
|
-
merge_param!(params, field['name']
|
|
121
|
+
merge_param!(params, field['name'], field['_capybara_raw_value'].to_s.gsub(/\n/, "\r\n"))
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
def submitter?(el)
|
|
125
|
+
(%w[submit image].include? el['type']) || (el.name == 'button')
|
|
123
126
|
end
|
|
124
127
|
end
|