capybara 3.0.0 → 3.40.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.yardopts +1 -1
- data/History.md +891 -12
- data/License.txt +1 -1
- data/README.md +257 -84
- data/lib/capybara/config.rb +29 -10
- data/lib/capybara/cucumber.rb +1 -1
- data/lib/capybara/driver/base.rb +22 -4
- data/lib/capybara/driver/node.rb +38 -9
- data/lib/capybara/dsl.rb +9 -7
- data/lib/capybara/helpers.rb +57 -8
- data/lib/capybara/minitest/spec.rb +185 -84
- data/lib/capybara/minitest.rb +264 -145
- data/lib/capybara/node/actions.rb +248 -124
- data/lib/capybara/node/base.rb +35 -20
- data/lib/capybara/node/document.rb +14 -2
- data/lib/capybara/node/document_matchers.rb +13 -15
- data/lib/capybara/node/element.rb +350 -113
- data/lib/capybara/node/finders.rb +104 -82
- data/lib/capybara/node/matchers.rb +363 -157
- data/lib/capybara/node/simple.rb +54 -15
- data/lib/capybara/node/whitespace_normalizer.rb +81 -0
- data/lib/capybara/queries/active_element_query.rb +18 -0
- data/lib/capybara/queries/ancestor_query.rb +9 -10
- data/lib/capybara/queries/base_query.rb +25 -18
- data/lib/capybara/queries/current_path_query.rb +16 -6
- data/lib/capybara/queries/match_query.rb +11 -0
- data/lib/capybara/queries/selector_query.rb +617 -104
- data/lib/capybara/queries/sibling_query.rb +9 -7
- data/lib/capybara/queries/style_query.rb +45 -0
- data/lib/capybara/queries/text_query.rb +40 -22
- data/lib/capybara/queries/title_query.rb +2 -2
- data/lib/capybara/rack_test/browser.rb +106 -31
- data/lib/capybara/rack_test/driver.rb +16 -7
- data/lib/capybara/rack_test/errors.rb +6 -0
- data/lib/capybara/rack_test/form.rb +74 -49
- data/lib/capybara/rack_test/node.rb +120 -47
- data/lib/capybara/rails.rb +1 -1
- data/lib/capybara/registration_container.rb +41 -0
- data/lib/capybara/registrations/drivers.rb +42 -0
- data/lib/capybara/registrations/patches/puma_ssl.rb +29 -0
- data/lib/capybara/registrations/servers.rb +66 -0
- data/lib/capybara/result.rb +87 -53
- data/lib/capybara/rspec/features.rb +8 -10
- data/lib/capybara/rspec/matcher_proxies.rb +39 -18
- data/lib/capybara/rspec/matchers/base.rb +113 -0
- data/lib/capybara/rspec/matchers/become_closed.rb +33 -0
- data/lib/capybara/rspec/matchers/compound.rb +88 -0
- data/lib/capybara/rspec/matchers/count_sugar.rb +37 -0
- data/lib/capybara/rspec/matchers/have_ancestor.rb +28 -0
- data/lib/capybara/rspec/matchers/have_current_path.rb +29 -0
- data/lib/capybara/rspec/matchers/have_selector.rb +69 -0
- data/lib/capybara/rspec/matchers/have_sibling.rb +27 -0
- data/lib/capybara/rspec/matchers/have_text.rb +33 -0
- data/lib/capybara/rspec/matchers/have_title.rb +29 -0
- data/lib/capybara/rspec/matchers/match_selector.rb +27 -0
- data/lib/capybara/rspec/matchers/match_style.rb +43 -0
- data/lib/capybara/rspec/matchers/spatial_sugar.rb +39 -0
- data/lib/capybara/rspec/matchers.rb +142 -315
- data/lib/capybara/rspec.rb +3 -2
- data/lib/capybara/selector/builders/css_builder.rb +84 -0
- data/lib/capybara/selector/builders/xpath_builder.rb +71 -0
- data/lib/capybara/selector/css.rb +85 -8
- data/lib/capybara/selector/definition/button.rb +68 -0
- data/lib/capybara/selector/definition/checkbox.rb +26 -0
- data/lib/capybara/selector/definition/css.rb +10 -0
- data/lib/capybara/selector/definition/datalist_input.rb +35 -0
- data/lib/capybara/selector/definition/datalist_option.rb +25 -0
- data/lib/capybara/selector/definition/element.rb +28 -0
- data/lib/capybara/selector/definition/field.rb +40 -0
- data/lib/capybara/selector/definition/fieldset.rb +14 -0
- data/lib/capybara/selector/definition/file_field.rb +13 -0
- data/lib/capybara/selector/definition/fillable_field.rb +33 -0
- data/lib/capybara/selector/definition/frame.rb +17 -0
- data/lib/capybara/selector/definition/id.rb +6 -0
- data/lib/capybara/selector/definition/label.rb +62 -0
- data/lib/capybara/selector/definition/link.rb +55 -0
- data/lib/capybara/selector/definition/link_or_button.rb +16 -0
- data/lib/capybara/selector/definition/option.rb +27 -0
- data/lib/capybara/selector/definition/radio_button.rb +27 -0
- data/lib/capybara/selector/definition/select.rb +81 -0
- data/lib/capybara/selector/definition/table.rb +109 -0
- data/lib/capybara/selector/definition/table_row.rb +21 -0
- data/lib/capybara/selector/definition/xpath.rb +5 -0
- data/lib/capybara/selector/definition.rb +280 -0
- data/lib/capybara/selector/filter.rb +1 -0
- data/lib/capybara/selector/filter_set.rb +72 -28
- data/lib/capybara/selector/filters/base.rb +45 -2
- data/lib/capybara/selector/filters/expression_filter.rb +5 -6
- data/lib/capybara/selector/filters/locator_filter.rb +29 -0
- data/lib/capybara/selector/filters/node_filter.rb +18 -4
- data/lib/capybara/selector/regexp_disassembler.rb +211 -0
- data/lib/capybara/selector/selector.rb +89 -200
- data/lib/capybara/selector/xpath_extensions.rb +17 -0
- data/lib/capybara/selector.rb +474 -534
- data/lib/capybara/selenium/atoms/getAttribute.min.js +1 -0
- data/lib/capybara/selenium/atoms/isDisplayed.min.js +1 -0
- data/lib/capybara/selenium/atoms/src/getAttribute.js +161 -0
- data/lib/capybara/selenium/atoms/src/isDisplayed.js +454 -0
- data/lib/capybara/selenium/driver.rb +270 -245
- data/lib/capybara/selenium/driver_specializations/chrome_driver.rb +117 -0
- data/lib/capybara/selenium/driver_specializations/edge_driver.rb +128 -0
- data/lib/capybara/selenium/driver_specializations/firefox_driver.rb +84 -0
- data/lib/capybara/selenium/driver_specializations/internet_explorer_driver.rb +26 -0
- data/lib/capybara/selenium/driver_specializations/safari_driver.rb +24 -0
- data/lib/capybara/selenium/extensions/file_input_click_emulation.rb +34 -0
- data/lib/capybara/selenium/extensions/find.rb +110 -0
- data/lib/capybara/selenium/extensions/html5_drag.rb +229 -0
- data/lib/capybara/selenium/extensions/modifier_keys_stack.rb +28 -0
- data/lib/capybara/selenium/extensions/scroll.rb +76 -0
- data/lib/capybara/selenium/node.rb +460 -170
- data/lib/capybara/selenium/nodes/chrome_node.rb +125 -0
- data/lib/capybara/selenium/nodes/edge_node.rb +110 -0
- data/lib/capybara/selenium/nodes/firefox_node.rb +136 -0
- data/lib/capybara/selenium/nodes/ie_node.rb +22 -0
- data/lib/capybara/selenium/nodes/safari_node.rb +118 -0
- data/lib/capybara/selenium/patches/atoms.rb +18 -0
- data/lib/capybara/selenium/patches/is_displayed.rb +16 -0
- data/lib/capybara/selenium/patches/logs.rb +45 -0
- data/lib/capybara/selenium/patches/pause_duration_fix.rb +9 -0
- data/lib/capybara/selenium/patches/persistent_client.rb +20 -0
- data/lib/capybara/server/animation_disabler.rb +80 -0
- data/lib/capybara/server/checker.rb +44 -0
- data/lib/capybara/server/middleware.rb +71 -0
- data/lib/capybara/server.rb +58 -67
- data/lib/capybara/session/config.rb +40 -6
- data/lib/capybara/session/matchers.rb +26 -19
- data/lib/capybara/session.rb +252 -194
- data/lib/capybara/spec/public/jquery.js +5 -5
- data/lib/capybara/spec/public/offset.js +6 -0
- data/lib/capybara/spec/public/test.js +126 -8
- data/lib/capybara/spec/session/accept_alert_spec.rb +11 -11
- data/lib/capybara/spec/session/accept_confirm_spec.rb +3 -3
- data/lib/capybara/spec/session/accept_prompt_spec.rb +9 -10
- data/lib/capybara/spec/session/active_element_spec.rb +31 -0
- data/lib/capybara/spec/session/all_spec.rb +135 -44
- data/lib/capybara/spec/session/ancestor_spec.rb +24 -19
- data/lib/capybara/spec/session/assert_all_of_selectors_spec.rb +67 -38
- data/lib/capybara/spec/session/{assert_current_path.rb → assert_current_path_spec.rb} +20 -18
- data/lib/capybara/spec/session/assert_selector_spec.rb +143 -0
- data/lib/capybara/spec/session/assert_style_spec.rb +26 -0
- data/lib/capybara/spec/session/{assert_text.rb → assert_text_spec.rb} +76 -52
- data/lib/capybara/spec/session/{assert_title.rb → assert_title_spec.rb} +12 -12
- data/lib/capybara/spec/session/attach_file_spec.rb +126 -72
- data/lib/capybara/spec/session/body_spec.rb +11 -13
- data/lib/capybara/spec/session/check_spec.rb +112 -51
- data/lib/capybara/spec/session/choose_spec.rb +62 -30
- data/lib/capybara/spec/session/click_button_spec.rb +227 -161
- data/lib/capybara/spec/session/click_link_or_button_spec.rb +49 -30
- data/lib/capybara/spec/session/click_link_spec.rb +89 -55
- data/lib/capybara/spec/session/current_scope_spec.rb +8 -8
- data/lib/capybara/spec/session/current_url_spec.rb +44 -37
- data/lib/capybara/spec/session/dismiss_confirm_spec.rb +3 -3
- data/lib/capybara/spec/session/dismiss_prompt_spec.rb +2 -2
- data/lib/capybara/spec/session/element/{assert_match_selector.rb → assert_match_selector_spec.rb} +11 -9
- data/lib/capybara/spec/session/element/match_css_spec.rb +18 -10
- data/lib/capybara/spec/session/element/match_xpath_spec.rb +8 -6
- data/lib/capybara/spec/session/element/matches_selector_spec.rb +70 -56
- data/lib/capybara/spec/session/evaluate_async_script_spec.rb +7 -7
- data/lib/capybara/spec/session/evaluate_script_spec.rb +28 -8
- data/lib/capybara/spec/session/execute_script_spec.rb +8 -7
- data/lib/capybara/spec/session/fill_in_spec.rb +110 -44
- data/lib/capybara/spec/session/find_button_spec.rb +23 -23
- data/lib/capybara/spec/session/find_by_id_spec.rb +8 -8
- data/lib/capybara/spec/session/find_field_spec.rb +33 -31
- data/lib/capybara/spec/session/find_link_spec.rb +42 -14
- data/lib/capybara/spec/session/find_spec.rb +251 -142
- data/lib/capybara/spec/session/first_spec.rb +45 -44
- data/lib/capybara/spec/session/frame/frame_title_spec.rb +6 -6
- data/lib/capybara/spec/session/frame/frame_url_spec.rb +6 -6
- data/lib/capybara/spec/session/frame/switch_to_frame_spec.rb +32 -20
- data/lib/capybara/spec/session/frame/within_frame_spec.rb +46 -19
- data/lib/capybara/spec/session/go_back_spec.rb +1 -1
- data/lib/capybara/spec/session/go_forward_spec.rb +1 -1
- data/lib/capybara/spec/session/has_all_selectors_spec.rb +23 -23
- data/lib/capybara/spec/session/has_ancestor_spec.rb +46 -0
- data/lib/capybara/spec/session/has_any_selectors_spec.rb +29 -0
- data/lib/capybara/spec/session/has_button_spec.rb +98 -12
- data/lib/capybara/spec/session/has_css_spec.rb +271 -137
- data/lib/capybara/spec/session/has_current_path_spec.rb +50 -35
- data/lib/capybara/spec/session/has_element_spec.rb +47 -0
- data/lib/capybara/spec/session/has_field_spec.rb +137 -58
- data/lib/capybara/spec/session/has_link_spec.rb +46 -6
- data/lib/capybara/spec/session/has_none_selectors_spec.rb +33 -31
- data/lib/capybara/spec/session/has_select_spec.rb +84 -50
- data/lib/capybara/spec/session/has_selector_spec.rb +117 -69
- data/lib/capybara/spec/session/has_sibling_spec.rb +50 -0
- data/lib/capybara/spec/session/has_table_spec.rb +181 -4
- data/lib/capybara/spec/session/has_text_spec.rb +101 -53
- data/lib/capybara/spec/session/has_title_spec.rb +19 -14
- data/lib/capybara/spec/session/has_xpath_spec.rb +56 -38
- data/lib/capybara/spec/session/{headers.rb → headers_spec.rb} +1 -1
- data/lib/capybara/spec/session/html_spec.rb +13 -6
- data/lib/capybara/spec/session/matches_style_spec.rb +37 -0
- data/lib/capybara/spec/session/node_spec.rb +958 -122
- data/lib/capybara/spec/session/node_wrapper_spec.rb +15 -12
- data/lib/capybara/spec/session/refresh_spec.rb +9 -7
- data/lib/capybara/spec/session/reset_session_spec.rb +65 -37
- data/lib/capybara/spec/session/{response_code.rb → response_code_spec.rb} +1 -1
- data/lib/capybara/spec/session/save_and_open_page_spec.rb +2 -2
- data/lib/capybara/spec/session/save_and_open_screenshot_spec.rb +5 -4
- data/lib/capybara/spec/session/save_page_spec.rb +41 -38
- data/lib/capybara/spec/session/save_screenshot_spec.rb +13 -11
- data/lib/capybara/spec/session/screenshot_spec.rb +2 -2
- data/lib/capybara/spec/session/scroll_spec.rb +119 -0
- data/lib/capybara/spec/session/select_spec.rb +102 -76
- data/lib/capybara/spec/session/selectors_spec.rb +51 -18
- data/lib/capybara/spec/session/sibling_spec.rb +9 -9
- data/lib/capybara/spec/session/text_spec.rb +26 -24
- data/lib/capybara/spec/session/title_spec.rb +8 -6
- data/lib/capybara/spec/session/uncheck_spec.rb +41 -22
- data/lib/capybara/spec/session/unselect_spec.rb +37 -37
- data/lib/capybara/spec/session/visit_spec.rb +79 -53
- data/lib/capybara/spec/session/window/become_closed_spec.rb +22 -19
- data/lib/capybara/spec/session/window/current_window_spec.rb +4 -3
- data/lib/capybara/spec/session/window/open_new_window_spec.rb +4 -3
- data/lib/capybara/spec/session/window/switch_to_window_spec.rb +25 -21
- data/lib/capybara/spec/session/window/window_opened_by_spec.rb +10 -5
- data/lib/capybara/spec/session/window/window_spec.rb +88 -54
- data/lib/capybara/spec/session/window/windows_spec.rb +11 -8
- data/lib/capybara/spec/session/window/within_window_spec.rb +17 -16
- data/lib/capybara/spec/session/within_spec.rb +82 -44
- data/lib/capybara/spec/spec_helper.rb +46 -52
- data/lib/capybara/spec/test_app.rb +148 -41
- data/lib/capybara/spec/views/animated.erb +49 -0
- data/lib/capybara/spec/views/form.erb +156 -42
- data/lib/capybara/spec/views/frame_child.erb +4 -3
- data/lib/capybara/spec/views/frame_one.erb +2 -1
- data/lib/capybara/spec/views/frame_parent.erb +1 -1
- data/lib/capybara/spec/views/frame_two.erb +1 -1
- data/lib/capybara/spec/views/initial_alert.erb +2 -1
- data/lib/capybara/spec/views/layout.erb +10 -0
- data/lib/capybara/spec/views/obscured.erb +47 -0
- data/lib/capybara/spec/views/offset.erb +33 -0
- data/lib/capybara/spec/views/path.erb +2 -2
- data/lib/capybara/spec/views/popup_one.erb +1 -1
- data/lib/capybara/spec/views/popup_two.erb +1 -1
- data/lib/capybara/spec/views/react.erb +45 -0
- data/lib/capybara/spec/views/scroll.erb +21 -0
- data/lib/capybara/spec/views/spatial.erb +31 -0
- data/lib/capybara/spec/views/tables.erb +68 -1
- data/lib/capybara/spec/views/with_animation.erb +81 -0
- data/lib/capybara/spec/views/with_base_tag.erb +2 -2
- data/lib/capybara/spec/views/with_dragula.erb +24 -0
- data/lib/capybara/spec/views/with_fixed_header_footer.erb +2 -1
- data/lib/capybara/spec/views/with_hover.erb +3 -2
- data/lib/capybara/spec/views/with_hover1.erb +10 -0
- data/lib/capybara/spec/views/with_html.erb +46 -11
- data/lib/capybara/spec/views/with_html5_svg.erb +20 -0
- data/lib/capybara/spec/views/with_jquery_animation.erb +24 -0
- data/lib/capybara/spec/views/with_js.erb +30 -5
- data/lib/capybara/spec/views/with_jstree.erb +26 -0
- data/lib/capybara/spec/views/with_namespace.erb +21 -0
- data/lib/capybara/spec/views/with_scope.erb +2 -2
- data/lib/capybara/spec/views/with_scope_other.erb +6 -0
- data/lib/capybara/spec/views/with_shadow.erb +31 -0
- data/lib/capybara/spec/views/with_slow_unload.erb +2 -1
- data/lib/capybara/spec/views/with_sortable_js.erb +21 -0
- data/lib/capybara/spec/views/with_unload_alert.erb +1 -0
- data/lib/capybara/spec/views/with_windows.erb +1 -1
- data/lib/capybara/spec/views/within_frames.erb +1 -1
- data/lib/capybara/version.rb +1 -1
- data/lib/capybara/window.rb +32 -26
- data/lib/capybara.rb +128 -104
- data/spec/basic_node_spec.rb +59 -34
- data/spec/capybara_spec.rb +65 -51
- data/spec/counter_spec.rb +35 -0
- data/spec/css_builder_spec.rb +101 -0
- data/spec/css_splitter_spec.rb +38 -0
- data/spec/dsl_spec.rb +84 -55
- data/spec/filter_set_spec.rb +24 -7
- data/spec/fixtures/certificate.pem +25 -0
- data/spec/fixtures/key.pem +27 -0
- data/spec/fixtures/selenium_driver_rspec_failure.rb +5 -5
- data/spec/fixtures/selenium_driver_rspec_success.rb +5 -5
- data/spec/minitest_spec.rb +49 -5
- data/spec/minitest_spec_spec.rb +92 -62
- data/spec/per_session_config_spec.rb +6 -6
- data/spec/rack_test_spec.rb +183 -115
- data/spec/regexp_dissassembler_spec.rb +250 -0
- data/spec/result_spec.rb +99 -39
- data/spec/rspec/features_spec.rb +28 -25
- data/spec/rspec/scenarios_spec.rb +10 -6
- data/spec/rspec/shared_spec_matchers.rb +418 -364
- data/spec/rspec/views_spec.rb +4 -3
- data/spec/rspec_matchers_spec.rb +35 -10
- data/spec/rspec_spec.rb +109 -85
- data/spec/sauce_spec_chrome.rb +43 -0
- data/spec/selector_spec.rb +392 -62
- data/spec/selenium_spec_chrome.rb +183 -41
- data/spec/selenium_spec_chrome_remote.rb +96 -0
- data/spec/selenium_spec_edge.rb +41 -8
- data/spec/selenium_spec_firefox.rb +228 -0
- data/spec/selenium_spec_firefox_remote.rb +94 -0
- data/spec/selenium_spec_ie.rb +129 -11
- data/spec/selenium_spec_safari.rb +162 -0
- data/spec/server_spec.rb +192 -81
- data/spec/session_spec.rb +52 -16
- data/spec/shared_selenium_node.rb +79 -0
- data/spec/shared_selenium_session.rb +460 -123
- data/spec/spec_helper.rb +124 -2
- data/spec/whitespace_normalizer_spec.rb +54 -0
- data/spec/xpath_builder_spec.rb +93 -0
- metadata +344 -45
- data/.yard/templates_custom/default/class/html/selectors.erb +0 -38
- data/.yard/templates_custom/default/class/html/setup.rb +0 -17
- data/.yard/yard_extensions.rb +0 -78
- data/lib/capybara/rspec/compound.rb +0 -90
- data/lib/capybara/spec/session/assert_selector.rb +0 -149
- data/lib/capybara/spec/session/source_spec.rb +0 -0
- data/lib/capybara/spec/views/with_title.erb +0 -5
- data/spec/selenium_spec_marionette.rb +0 -143
data/lib/capybara/session.rb
CHANGED
@@ -6,7 +6,7 @@ require 'addressable/uri'
|
|
6
6
|
module Capybara
|
7
7
|
##
|
8
8
|
#
|
9
|
-
# The Session class represents a single user's interaction with the system. The Session can use
|
9
|
+
# The {Session} class represents a single user's interaction with the system. The {Session} can use
|
10
10
|
# any of the underlying drivers. A session can be initialized manually like this:
|
11
11
|
#
|
12
12
|
# session = Capybara::Session.new(:culerity, MyRackApp)
|
@@ -17,39 +17,41 @@ module Capybara
|
|
17
17
|
# session = Capybara::Session.new(:culerity)
|
18
18
|
# session.visit('http://www.google.com')
|
19
19
|
#
|
20
|
-
# When Capybara.threadsafe
|
20
|
+
# When {Capybara.configure threadsafe} is `true` the sessions options will be initially set to the
|
21
21
|
# current values of the global options and a configuration block can be passed to the session initializer.
|
22
|
-
# For available options see {Capybara::SessionConfig::OPTIONS}
|
22
|
+
# For available options see {Capybara::SessionConfig::OPTIONS}:
|
23
23
|
#
|
24
24
|
# session = Capybara::Session.new(:driver, MyRackApp) do |config|
|
25
25
|
# config.app_host = "http://my_host.dev"
|
26
26
|
# end
|
27
27
|
#
|
28
|
-
# Session provides a number of methods for controlling the navigation of the page, such as
|
29
|
-
#
|
28
|
+
# The {Session} provides a number of methods for controlling the navigation of the page, such as {#visit},
|
29
|
+
# {#current_path}, and so on. It also delegates a number of methods to a {Capybara::Document}, representing
|
30
30
|
# the current HTML document. This allows interaction:
|
31
31
|
#
|
32
32
|
# session.fill_in('q', with: 'Capybara')
|
33
33
|
# session.click_button('Search')
|
34
34
|
# expect(session).to have_content('Capybara')
|
35
35
|
#
|
36
|
-
# When using capybara/dsl
|
36
|
+
# When using `capybara/dsl`, the {Session} is initialized automatically for you.
|
37
37
|
#
|
38
38
|
class Session
|
39
39
|
include Capybara::SessionMatchers
|
40
40
|
|
41
41
|
NODE_METHODS = %i[
|
42
|
-
all first attach_file text check choose
|
42
|
+
all first attach_file text check choose scroll_to scroll_by
|
43
|
+
click double_click right_click
|
43
44
|
click_link_or_button click_button click_link
|
44
45
|
fill_in find find_all find_button find_by_id find_field find_link
|
45
46
|
has_content? has_text? has_css? has_no_content? has_no_text?
|
46
|
-
has_no_css? has_no_xpath?
|
47
|
+
has_no_css? has_no_xpath? has_xpath? select uncheck
|
48
|
+
has_element? has_no_element?
|
47
49
|
has_link? has_no_link? has_button? has_no_button? has_field?
|
48
50
|
has_no_field? has_checked_field? has_unchecked_field?
|
49
51
|
has_no_table? has_table? unselect has_select? has_no_select?
|
50
52
|
has_selector? has_no_selector? click_on has_no_checked_field?
|
51
53
|
has_no_unchecked_field? query assert_selector assert_no_selector
|
52
|
-
assert_all_of_selectors assert_none_of_selectors
|
54
|
+
assert_all_of_selectors assert_none_of_selectors assert_any_of_selectors
|
53
55
|
refute_selector assert_text assert_no_text
|
54
56
|
].freeze
|
55
57
|
# @api private
|
@@ -58,7 +60,7 @@ module Capybara
|
|
58
60
|
].freeze
|
59
61
|
SESSION_METHODS = %i[
|
60
62
|
body html source current_url current_host current_path
|
61
|
-
execute_script evaluate_script visit refresh go_back go_forward
|
63
|
+
execute_script evaluate_script evaluate_async_script visit refresh go_back go_forward send_keys
|
62
64
|
within within_element within_fieldset within_table within_frame switch_to_frame
|
63
65
|
current_window windows open_new_window switch_to_window within_window window_opened_by
|
64
66
|
save_page save_and_open_page save_screenshot
|
@@ -75,26 +77,30 @@ module Capybara
|
|
75
77
|
attr_accessor :synchronized
|
76
78
|
|
77
79
|
def initialize(mode, app = nil)
|
78
|
-
|
79
|
-
|
80
|
+
if app && !app.respond_to?(:call)
|
81
|
+
raise TypeError, 'The second parameter to Session::new should be a rack app if passed.'
|
82
|
+
end
|
83
|
+
|
84
|
+
@@instance_created = true # rubocop:disable Style/ClassVars
|
80
85
|
@mode = mode
|
81
86
|
@app = app
|
82
87
|
if block_given?
|
83
|
-
raise
|
88
|
+
raise 'A configuration block is only accepted when Capybara.threadsafe == true' unless Capybara.threadsafe
|
89
|
+
|
84
90
|
yield config
|
85
91
|
end
|
86
|
-
@server = if config.run_server
|
87
|
-
|
88
|
-
|
89
|
-
|
92
|
+
@server = if config.run_server && @app && driver.needs_server?
|
93
|
+
server_options = { port: config.server_port, host: config.server_host, reportable_errors: config.server_errors }
|
94
|
+
server_options[:extra_middleware] = [Capybara::Server::AnimationDisabler] if config.disable_animation
|
95
|
+
Capybara::Server.new(@app, **server_options).boot
|
90
96
|
end
|
91
97
|
@touched = false
|
92
98
|
end
|
93
99
|
|
94
100
|
def driver
|
95
101
|
@driver ||= begin
|
96
|
-
unless Capybara.drivers
|
97
|
-
other_drivers = Capybara.drivers.
|
102
|
+
unless Capybara.drivers[mode]
|
103
|
+
other_drivers = Capybara.drivers.names.map(&:inspect)
|
98
104
|
raise Capybara::DriverNotFoundError, "no driver called #{mode.inspect} was found, available drivers: #{other_drivers.join(', ')}"
|
99
105
|
end
|
100
106
|
driver = Capybara.drivers[mode].call(app)
|
@@ -105,28 +111,30 @@ module Capybara
|
|
105
111
|
|
106
112
|
##
|
107
113
|
#
|
108
|
-
# Reset the session (i.e. remove cookies and navigate to blank page)
|
114
|
+
# Reset the session (i.e. remove cookies and navigate to blank page).
|
109
115
|
#
|
110
116
|
# This method does not:
|
111
117
|
#
|
112
|
-
#
|
113
|
-
#
|
114
|
-
#
|
118
|
+
# * accept modal dialogs if they are present (Selenium driver now does, others may not)
|
119
|
+
# * clear browser cache/HTML 5 local storage/IndexedDB/Web SQL database/etc.
|
120
|
+
# * modify state of the driver/underlying browser in any other way
|
115
121
|
#
|
116
122
|
# as doing so will result in performance downsides and it's not needed to do everything from the list above for most apps.
|
117
123
|
#
|
118
124
|
# If you want to do anything from the list above on a general basis you can:
|
119
125
|
#
|
120
|
-
#
|
121
|
-
#
|
122
|
-
#
|
126
|
+
# * write RSpec/Cucumber/etc. after hook
|
127
|
+
# * monkeypatch this method
|
128
|
+
# * use Ruby's `prepend` method
|
123
129
|
#
|
124
130
|
def reset!
|
125
131
|
if @touched
|
126
132
|
driver.reset!
|
127
133
|
@touched = false
|
134
|
+
switch_to_frame(:top) rescue nil # rubocop:disable Style/RescueModifier
|
135
|
+
@scopes = [nil]
|
128
136
|
end
|
129
|
-
@server
|
137
|
+
@server&.wait_for_pending_requests
|
130
138
|
raise_server_error!
|
131
139
|
end
|
132
140
|
alias_method :cleanup!, :reset!
|
@@ -134,29 +142,39 @@ module Capybara
|
|
134
142
|
|
135
143
|
##
|
136
144
|
#
|
137
|
-
#
|
145
|
+
# Disconnect from the current driver. A new driver will be instantiated on the next interaction.
|
146
|
+
#
|
147
|
+
def quit
|
148
|
+
@driver.quit if @driver.respond_to? :quit
|
149
|
+
@document = @driver = nil
|
150
|
+
@touched = false
|
151
|
+
@server&.reset_error!
|
152
|
+
end
|
153
|
+
|
154
|
+
##
|
155
|
+
#
|
156
|
+
# Raise errors encountered in the server.
|
138
157
|
#
|
139
158
|
def raise_server_error!
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
rescue CapybaraError
|
147
|
-
# needed to get the cause set correctly in JRuby -- otherwise we could just do raise @server.error
|
148
|
-
raise @server.error, @server.error.message, @server.error.backtrace
|
149
|
-
ensure
|
150
|
-
@server.reset_error!
|
159
|
+
return unless @server&.error
|
160
|
+
|
161
|
+
# Force an explanation for the error being raised as the exception cause
|
162
|
+
begin
|
163
|
+
if config.raise_server_errors
|
164
|
+
raise CapybaraError, 'Your application server raised an error - It has been raised in your test code because Capybara.raise_server_errors == true'
|
151
165
|
end
|
166
|
+
rescue CapybaraError => capy_error # rubocop:disable Naming/RescuedExceptionsVariableName
|
167
|
+
raise @server.error, cause: capy_error
|
168
|
+
ensure
|
169
|
+
@server.reset_error!
|
152
170
|
end
|
153
171
|
end
|
154
172
|
|
155
173
|
##
|
156
174
|
#
|
157
|
-
# Returns a hash of response headers. Not supported by all drivers (e.g. Selenium)
|
175
|
+
# Returns a hash of response headers. Not supported by all drivers (e.g. Selenium).
|
158
176
|
#
|
159
|
-
# @return [Hash
|
177
|
+
# @return [Hash<String, String>] A hash of response headers.
|
160
178
|
#
|
161
179
|
def response_headers
|
162
180
|
driver.response_headers
|
@@ -164,7 +182,7 @@ module Capybara
|
|
164
182
|
|
165
183
|
##
|
166
184
|
#
|
167
|
-
# Returns the current HTTP status code as an
|
185
|
+
# Returns the current HTTP status code as an integer. Not supported by all drivers (e.g. Selenium).
|
168
186
|
#
|
169
187
|
# @return [Integer] Current HTTP status code
|
170
188
|
#
|
@@ -177,7 +195,7 @@ module Capybara
|
|
177
195
|
# @return [String] A snapshot of the DOM of the current document, as it looks right now (potentially modified by JavaScript).
|
178
196
|
#
|
179
197
|
def html
|
180
|
-
driver.html
|
198
|
+
driver.html || ''
|
181
199
|
end
|
182
200
|
alias_method :body, :html
|
183
201
|
alias_method :source, :html
|
@@ -190,14 +208,11 @@ module Capybara
|
|
190
208
|
# Addressable parsing is more lenient than URI
|
191
209
|
uri = ::Addressable::URI.parse(current_url)
|
192
210
|
|
193
|
-
# If current_url ends up being nil, won't be able to call .path on a NilClass.
|
194
|
-
return nil if uri.nil?
|
195
|
-
|
196
211
|
# Addressable doesn't support opaque URIs - we want nil here
|
197
|
-
return nil if uri
|
212
|
+
return nil if uri&.scheme == 'about'
|
198
213
|
|
199
|
-
path = uri
|
200
|
-
path
|
214
|
+
path = uri&.path
|
215
|
+
path unless path&.empty?
|
201
216
|
end
|
202
217
|
|
203
218
|
##
|
@@ -227,13 +242,13 @@ module Capybara
|
|
227
242
|
#
|
228
243
|
# For drivers which can run against an external application, such as the selenium driver
|
229
244
|
# giving an absolute URL will navigate to that page. This allows testing applications
|
230
|
-
# running on remote servers. For these drivers, setting {Capybara.app_host} will make the
|
245
|
+
# running on remote servers. For these drivers, setting {Capybara.configure app_host} will make the
|
231
246
|
# remote server the default. For example:
|
232
247
|
#
|
233
248
|
# Capybara.app_host = 'http://google.com'
|
234
249
|
# session.visit('/') # visits the google homepage
|
235
250
|
#
|
236
|
-
# If {Capybara.always_include_port} is set to true and this session is running against
|
251
|
+
# If {Capybara.configure always_include_port} is set to `true` and this session is running against
|
237
252
|
# a rack application, then the port that the rack application is running on will automatically
|
238
253
|
# be inserted into the URL. Supposing the app is running on port `4567`, doing something like:
|
239
254
|
#
|
@@ -248,27 +263,19 @@ module Capybara
|
|
248
263
|
@touched = true
|
249
264
|
|
250
265
|
visit_uri = ::Addressable::URI.parse(visit_uri.to_s)
|
266
|
+
base_uri = ::Addressable::URI.parse(config.app_host || server_url)
|
251
267
|
|
252
|
-
|
253
|
-
::Addressable::URI.parse(config.app_host || "http://#{@server.host}:#{@server.port}")
|
254
|
-
else
|
255
|
-
config.app_host && ::Addressable::URI.parse(config.app_host)
|
256
|
-
end
|
257
|
-
|
258
|
-
if uri_base && [nil, 'http', 'https'].include?(visit_uri.scheme)
|
268
|
+
if base_uri && [nil, 'http', 'https'].include?(visit_uri.scheme)
|
259
269
|
if visit_uri.relative?
|
260
|
-
|
261
|
-
|
262
|
-
visit_uri_parts = visit_uri.to_hash.delete_if { |_k, v| v.nil? }
|
270
|
+
visit_uri_parts = visit_uri.to_hash.compact
|
263
271
|
|
264
272
|
# Useful to people deploying to a subdirectory
|
265
273
|
# and/or single page apps where only the url fragment changes
|
266
|
-
visit_uri_parts[:path] =
|
274
|
+
visit_uri_parts[:path] = base_uri.path + visit_uri.path
|
267
275
|
|
268
|
-
visit_uri =
|
269
|
-
elsif @server && config.always_include_port
|
270
|
-
visit_uri.port ||= @server.port
|
276
|
+
visit_uri = base_uri.merge(visit_uri_parts)
|
271
277
|
end
|
278
|
+
adjust_server_port(visit_uri)
|
272
279
|
end
|
273
280
|
|
274
281
|
driver.visit(visit_uri.to_s)
|
@@ -276,7 +283,7 @@ module Capybara
|
|
276
283
|
|
277
284
|
##
|
278
285
|
#
|
279
|
-
# Refresh the page
|
286
|
+
# Refresh the page.
|
280
287
|
#
|
281
288
|
def refresh
|
282
289
|
raise_server_error!
|
@@ -300,9 +307,27 @@ module Capybara
|
|
300
307
|
end
|
301
308
|
|
302
309
|
##
|
310
|
+
# @!method send_keys
|
311
|
+
# @see Capybara::Node::Element#send_keys
|
312
|
+
#
|
313
|
+
def send_keys(...)
|
314
|
+
driver.send_keys(...)
|
315
|
+
end
|
316
|
+
|
317
|
+
##
|
318
|
+
#
|
319
|
+
# Returns the element with focus.
|
303
320
|
#
|
304
|
-
#
|
305
|
-
#
|
321
|
+
# Not supported by Rack Test
|
322
|
+
#
|
323
|
+
def active_element
|
324
|
+
Capybara::Queries::ActiveElementQuery.new.resolve_for(self)[0].tap(&:allow_reload!)
|
325
|
+
end
|
326
|
+
|
327
|
+
##
|
328
|
+
#
|
329
|
+
# Executes the given block within the context of a node. {#within} takes the
|
330
|
+
# same options as {Capybara::Node::Finders#find #find}, as well as a block. For the duration of the
|
306
331
|
# block, any command to Capybara will be handled as though it were scoped
|
307
332
|
# to the given element.
|
308
333
|
#
|
@@ -310,18 +335,18 @@ module Capybara
|
|
310
335
|
# fill_in('Street', with: '12 Main Street')
|
311
336
|
# end
|
312
337
|
#
|
313
|
-
# Just as with
|
314
|
-
#
|
338
|
+
# Just as with `#find`, if multiple elements match the selector given to
|
339
|
+
# {#within}, an error will be raised, and just as with `#find`, this
|
315
340
|
# behaviour can be controlled through the `:match` and `:exact` options.
|
316
341
|
#
|
317
342
|
# It is possible to omit the first parameter, in that case, the selector is
|
318
|
-
# assumed to be of the type set in Capybara.default_selector.
|
343
|
+
# assumed to be of the type set in {Capybara.configure default_selector}.
|
319
344
|
#
|
320
345
|
# within('div#delivery-address') do
|
321
346
|
# fill_in('Street', with: '12 Main Street')
|
322
347
|
# end
|
323
348
|
#
|
324
|
-
# Note that a lot of uses of
|
349
|
+
# Note that a lot of uses of {#within} can be replaced more succinctly with
|
325
350
|
# chaining:
|
326
351
|
#
|
327
352
|
# find('div#delivery-address').fill_in('Street', with: '12 Main Street')
|
@@ -334,11 +359,11 @@ module Capybara
|
|
334
359
|
#
|
335
360
|
# @raise [Capybara::ElementNotFound] If the scope can't be found before time expires
|
336
361
|
#
|
337
|
-
def within(*args)
|
338
|
-
new_scope = args.first.respond_to?(:to_capybara_node) ? args.first.to_capybara_node : find(*args)
|
362
|
+
def within(*args, **kw_args)
|
363
|
+
new_scope = args.first.respond_to?(:to_capybara_node) ? args.first.to_capybara_node : find(*args, **kw_args)
|
339
364
|
begin
|
340
365
|
scopes.push(new_scope)
|
341
|
-
yield
|
366
|
+
yield new_scope if block_given?
|
342
367
|
ensure
|
343
368
|
scopes.pop
|
344
369
|
end
|
@@ -351,8 +376,8 @@ module Capybara
|
|
351
376
|
#
|
352
377
|
# @param [String] locator Id or legend of the fieldset
|
353
378
|
#
|
354
|
-
def within_fieldset(locator)
|
355
|
-
within(:fieldset, locator)
|
379
|
+
def within_fieldset(locator, &block)
|
380
|
+
within(:fieldset, locator, &block)
|
356
381
|
end
|
357
382
|
|
358
383
|
##
|
@@ -361,24 +386,24 @@ module Capybara
|
|
361
386
|
#
|
362
387
|
# @param [String] locator Id or caption of the table
|
363
388
|
#
|
364
|
-
def within_table(locator)
|
365
|
-
within(:table, locator)
|
389
|
+
def within_table(locator, &block)
|
390
|
+
within(:table, locator, &block)
|
366
391
|
end
|
367
392
|
|
368
393
|
##
|
369
394
|
#
|
370
|
-
# Switch to the given frame
|
395
|
+
# Switch to the given frame.
|
371
396
|
#
|
372
397
|
# If you use this method you are responsible for making sure you switch back to the parent frame when done in the frame changed to.
|
373
|
-
#
|
398
|
+
# {#within_frame} is preferred over this method and should be used when possible.
|
374
399
|
# May not be supported by all drivers.
|
375
400
|
#
|
376
401
|
# @overload switch_to_frame(element)
|
377
|
-
# @param [Capybara::Node::Element]
|
378
|
-
# @overload switch_to_frame(
|
379
|
-
#
|
380
|
-
#
|
381
|
-
#
|
402
|
+
# @param [Capybara::Node::Element] element iframe/frame element to switch to
|
403
|
+
# @overload switch_to_frame(location)
|
404
|
+
# @param [Symbol] location relative location of the frame to switch to
|
405
|
+
# * :parent - the parent frame
|
406
|
+
# * :top - the top level document
|
382
407
|
#
|
383
408
|
def switch_to_frame(frame)
|
384
409
|
case frame
|
@@ -387,21 +412,24 @@ module Capybara
|
|
387
412
|
scopes.push(:frame)
|
388
413
|
when :parent
|
389
414
|
if scopes.last != :frame
|
390
|
-
raise Capybara::ScopeError, "`switch_to_frame(:parent)` cannot be called from inside a descendant frame's "\
|
391
|
-
|
415
|
+
raise Capybara::ScopeError, "`switch_to_frame(:parent)` cannot be called from inside a descendant frame's " \
|
416
|
+
'`within` block.'
|
392
417
|
end
|
393
418
|
scopes.pop
|
394
419
|
driver.switch_to_frame(:parent)
|
395
420
|
when :top
|
396
421
|
idx = scopes.index(:frame)
|
422
|
+
top_level_scopes = [:frame, nil]
|
397
423
|
if idx
|
398
|
-
if scopes.slice(idx
|
399
|
-
raise Capybara::ScopeError, "`switch_to_frame(:top)` cannot be called from inside a descendant frame's "\
|
400
|
-
|
424
|
+
if scopes.slice(idx..).any? { |scope| !top_level_scopes.include?(scope) }
|
425
|
+
raise Capybara::ScopeError, "`switch_to_frame(:top)` cannot be called from inside a descendant frame's " \
|
426
|
+
'`within` block.'
|
401
427
|
end
|
402
|
-
scopes.slice!(idx
|
428
|
+
scopes.slice!(idx..)
|
403
429
|
driver.switch_to_frame(:top)
|
404
430
|
end
|
431
|
+
else
|
432
|
+
raise ArgumentError, 'You must provide a frame element, :parent, or :top when calling switch_to_frame'
|
405
433
|
end
|
406
434
|
end
|
407
435
|
|
@@ -412,15 +440,15 @@ module Capybara
|
|
412
440
|
#
|
413
441
|
# @overload within_frame(element)
|
414
442
|
# @param [Capybara::Node::Element] frame element
|
415
|
-
# @overload within_frame([kind = :frame], locator, options
|
416
|
-
# @param [Symbol] kind Optional selector type (:
|
443
|
+
# @overload within_frame([kind = :frame], locator, **options)
|
444
|
+
# @param [Symbol] kind Optional selector type (:frame, :css, :xpath, etc.) - Defaults to :frame
|
417
445
|
# @param [String] locator The locator for the given selector kind. For :frame this is the name/id of a frame/iframe element
|
418
446
|
# @overload within_frame(index)
|
419
447
|
# @param [Integer] index index of a frame (0 based)
|
420
|
-
def within_frame(*args)
|
421
|
-
switch_to_frame(_find_frame(*args))
|
448
|
+
def within_frame(*args, **kw_args)
|
449
|
+
switch_to_frame(_find_frame(*args, **kw_args))
|
422
450
|
begin
|
423
|
-
yield
|
451
|
+
yield if block_given?
|
424
452
|
ensure
|
425
453
|
switch_to_frame(:parent)
|
426
454
|
end
|
@@ -447,22 +475,28 @@ module Capybara
|
|
447
475
|
end
|
448
476
|
|
449
477
|
##
|
450
|
-
# Open new window.
|
451
|
-
#
|
478
|
+
# Open a new window.
|
479
|
+
# The current window doesn't change as the result of this call.
|
452
480
|
# It should be switched to explicitly.
|
453
481
|
#
|
454
482
|
# @return [Capybara::Window] window that has been opened
|
455
483
|
#
|
456
|
-
def open_new_window
|
484
|
+
def open_new_window(kind = :tab)
|
457
485
|
window_opened_by do
|
458
|
-
driver.open_new_window
|
486
|
+
if driver.method(:open_new_window).arity.zero?
|
487
|
+
driver.open_new_window
|
488
|
+
else
|
489
|
+
driver.open_new_window(kind)
|
490
|
+
end
|
459
491
|
end
|
460
492
|
end
|
461
493
|
|
462
494
|
##
|
495
|
+
# Switch to the given window.
|
496
|
+
#
|
463
497
|
# @overload switch_to_window(&block)
|
464
498
|
# Switches to the first window for which given block returns a value other than false or nil.
|
465
|
-
# If window that matches block can't be found, the window will be switched back and
|
499
|
+
# If window that matches block can't be found, the window will be switched back and {Capybara::WindowError} will be raised.
|
466
500
|
# @example
|
467
501
|
# window = switch_to_window { title == 'Page title' }
|
468
502
|
# @raise [Capybara::WindowError] if no window matches given block
|
@@ -471,20 +505,20 @@ module Capybara
|
|
471
505
|
# @raise [Capybara::Driver::Base#no_such_window_error] if nonexistent (e.g. closed) window was passed
|
472
506
|
#
|
473
507
|
# @return [Capybara::Window] window that has been switched to
|
474
|
-
# @raise [Capybara::ScopeError] if this method is invoked inside
|
475
|
-
#
|
508
|
+
# @raise [Capybara::ScopeError] if this method is invoked inside {#within} or
|
509
|
+
# {#within_frame} methods
|
476
510
|
# @raise [ArgumentError] if both or neither arguments were provided
|
477
511
|
#
|
478
512
|
def switch_to_window(window = nil, **options, &window_locator)
|
479
|
-
|
480
|
-
raise ArgumentError,
|
481
|
-
|
513
|
+
raise ArgumentError, '`switch_to_window` can take either a block or a window, not both' if window && window_locator
|
514
|
+
raise ArgumentError, '`switch_to_window`: either window or block should be provided' if !window && !window_locator
|
515
|
+
|
482
516
|
unless scopes.last.nil?
|
483
|
-
raise Capybara::ScopeError,
|
484
|
-
|
517
|
+
raise Capybara::ScopeError, '`switch_to_window` is not supposed to be invoked from ' \
|
518
|
+
'`within` or `within_frame` blocks.'
|
485
519
|
end
|
486
520
|
|
487
|
-
_switch_to_window(window, options, &window_locator)
|
521
|
+
_switch_to_window(window, **options, &window_locator)
|
488
522
|
end
|
489
523
|
|
490
524
|
##
|
@@ -492,20 +526,20 @@ module Capybara
|
|
492
526
|
#
|
493
527
|
# 1. Switches to the given window (it can be located by window instance/lambda/string).
|
494
528
|
# 2. Executes the given block (within window located at previous step).
|
495
|
-
# 3. Switches back (this step will be invoked even if exception
|
529
|
+
# 3. Switches back (this step will be invoked even if an exception occurs at the second step).
|
496
530
|
#
|
497
531
|
# @overload within_window(window) { do_something }
|
498
|
-
# @param window [Capybara::Window] instance of
|
532
|
+
# @param window [Capybara::Window] instance of {Capybara::Window} class
|
499
533
|
# that will be switched to
|
500
534
|
# @raise [driver#no_such_window_error] if nonexistent (e.g. closed) window was passed
|
501
535
|
# @overload within_window(proc_or_lambda) { do_something }
|
502
|
-
# @param lambda [Proc]
|
536
|
+
# @param lambda [Proc] First window for which lambda
|
503
537
|
# returns a value other than false or nil will be switched to.
|
504
538
|
# @example
|
505
539
|
# within_window(->{ page.title == 'Page title' }) { click_button 'Submit' }
|
506
540
|
# @raise [Capybara::WindowError] if no window matching lambda was found
|
507
541
|
#
|
508
|
-
# @raise [Capybara::ScopeError] if this method is invoked inside
|
542
|
+
# @raise [Capybara::ScopeError] if this method is invoked inside {#within_frame} method
|
509
543
|
# @return value returned by the block
|
510
544
|
#
|
511
545
|
def within_window(window_or_proc)
|
@@ -518,11 +552,11 @@ module Capybara
|
|
518
552
|
when Proc
|
519
553
|
_switch_to_window { window_or_proc.call }
|
520
554
|
else
|
521
|
-
raise ArgumentError
|
555
|
+
raise ArgumentError, '`#within_window` requires a `Capybara::Window` instance or a lambda'
|
522
556
|
end
|
523
557
|
|
524
558
|
begin
|
525
|
-
yield
|
559
|
+
yield if block_given?
|
526
560
|
ensure
|
527
561
|
_switch_to_window(original) unless original == window_or_proc
|
528
562
|
end
|
@@ -535,11 +569,11 @@ module Capybara
|
|
535
569
|
# Get the window that has been opened by the passed block.
|
536
570
|
# It will wait for it to be opened (in the same way as other Capybara methods wait).
|
537
571
|
# It's better to use this method than `windows.last`
|
538
|
-
# {https://dvcs.w3.org/hg/webdriver/raw-file/default/webdriver-spec.html#h_note_10 as order of windows isn't defined in some drivers}
|
572
|
+
# {https://dvcs.w3.org/hg/webdriver/raw-file/default/webdriver-spec.html#h_note_10 as order of windows isn't defined in some drivers}.
|
539
573
|
#
|
540
574
|
# @overload window_opened_by(**options, &block)
|
541
575
|
# @param options [Hash]
|
542
|
-
# @option options [Numeric] :wait
|
576
|
+
# @option options [Numeric] :wait maximum wait time. Defaults to {Capybara.configure default_max_wait_time}
|
543
577
|
# @return [Capybara::Window] the window that has been opened within a block
|
544
578
|
# @raise [Capybara::WindowError] if block passed to window hasn't opened window
|
545
579
|
# or opened more than one window
|
@@ -548,11 +582,10 @@ module Capybara
|
|
548
582
|
old_handles = driver.window_handles
|
549
583
|
yield
|
550
584
|
|
551
|
-
|
552
|
-
document.synchronize(wait_time, errors: [Capybara::WindowError]) do
|
585
|
+
synchronize_windows(options) do
|
553
586
|
opened_handles = (driver.window_handles - old_handles)
|
554
587
|
if opened_handles.size != 1
|
555
|
-
raise Capybara::WindowError,
|
588
|
+
raise Capybara::WindowError, 'block passed to #window_opened_by ' \
|
556
589
|
"opened #{opened_handles.size} windows instead of 1"
|
557
590
|
end
|
558
591
|
Window.new(self, opened_handles.first)
|
@@ -562,29 +595,30 @@ module Capybara
|
|
562
595
|
##
|
563
596
|
#
|
564
597
|
# Execute the given script, not returning a result. This is useful for scripts that return
|
565
|
-
# complex objects, such as jQuery statements.
|
566
|
-
#
|
598
|
+
# complex objects, such as jQuery statements. {#execute_script} should be used over
|
599
|
+
# {#evaluate_script} whenever possible.
|
567
600
|
#
|
568
601
|
# @param [String] script A string of JavaScript to execute
|
569
|
-
# @param args Optional arguments that will be passed to the script.
|
602
|
+
# @param args Optional arguments that will be passed to the script. Driver support for this is optional and types of objects supported may differ between drivers
|
570
603
|
#
|
571
604
|
def execute_script(script, *args)
|
572
605
|
@touched = true
|
573
|
-
driver.execute_script(script, *args
|
606
|
+
driver.execute_script(script, *driver_args(args))
|
574
607
|
end
|
575
608
|
|
576
609
|
##
|
577
610
|
#
|
578
611
|
# Evaluate the given JavaScript and return the result. Be careful when using this with
|
579
|
-
# scripts that return complex objects, such as jQuery statements.
|
612
|
+
# scripts that return complex objects, such as jQuery statements. {#execute_script} might
|
580
613
|
# be a better alternative.
|
581
614
|
#
|
582
615
|
# @param [String] script A string of JavaScript to evaluate
|
616
|
+
# @param args Optional arguments that will be passed to the script
|
583
617
|
# @return [Object] The result of the evaluated JavaScript (may be driver specific)
|
584
618
|
#
|
585
619
|
def evaluate_script(script, *args)
|
586
620
|
@touched = true
|
587
|
-
result = driver.evaluate_script(script, *args
|
621
|
+
result = driver.evaluate_script(script.strip, *driver_args(args))
|
588
622
|
element_script_result(result)
|
589
623
|
end
|
590
624
|
|
@@ -593,11 +627,12 @@ module Capybara
|
|
593
627
|
# Evaluate the given JavaScript and obtain the result from a callback function which will be passed as the last argument to the script.
|
594
628
|
#
|
595
629
|
# @param [String] script A string of JavaScript to evaluate
|
630
|
+
# @param args Optional arguments that will be passed to the script
|
596
631
|
# @return [Object] The result of the evaluated JavaScript (may be driver specific)
|
597
632
|
#
|
598
633
|
def evaluate_async_script(script, *args)
|
599
634
|
@touched = true
|
600
|
-
result = driver.evaluate_async_script(script, *args
|
635
|
+
result = driver.evaluate_async_script(script, *driver_args(args))
|
601
636
|
element_script_result(result)
|
602
637
|
end
|
603
638
|
|
@@ -606,17 +641,17 @@ module Capybara
|
|
606
641
|
# Execute the block, accepting a alert.
|
607
642
|
#
|
608
643
|
# @!macro modal_params
|
609
|
-
# Expects a block whose actions will trigger the display modal to appear
|
644
|
+
# Expects a block whose actions will trigger the display modal to appear.
|
610
645
|
# @example
|
611
646
|
# $0 do
|
612
647
|
# click_link('link that triggers appearance of system modal')
|
613
648
|
# end
|
614
|
-
# @overload $0(text, options
|
615
|
-
# @param text [String, Regexp] Text or regex to match against the text in the modal.
|
616
|
-
# @option options [Numeric] :wait
|
649
|
+
# @overload $0(text, **options, &blk)
|
650
|
+
# @param text [String, Regexp] Text or regex to match against the text in the modal. If not provided any modal is matched.
|
651
|
+
# @option options [Numeric] :wait Maximum time to wait for the modal to appear after executing the block. Defaults to {Capybara.configure default_max_wait_time}.
|
617
652
|
# @yield Block whose actions will trigger the system modal
|
618
|
-
# @overload $0(options
|
619
|
-
# @option options [Numeric] :wait
|
653
|
+
# @overload $0(**options, &blk)
|
654
|
+
# @option options [Numeric] :wait Maximum time to wait for the modal to appear after executing the block. Defaults to {Capybara.configure default_max_wait_time}.
|
620
655
|
# @yield Block whose actions will trigger the system modal
|
621
656
|
# @return [String] the message shown in the modal
|
622
657
|
# @raise [Capybara::ModalNotFound] if modal dialog hasn't been found
|
@@ -668,19 +703,19 @@ module Capybara
|
|
668
703
|
|
669
704
|
##
|
670
705
|
#
|
671
|
-
# Save a snapshot of the page. If
|
672
|
-
#
|
706
|
+
# Save a snapshot of the page. If {Capybara.configure asset_host} is set it will inject `base` tag
|
707
|
+
# pointing to {Capybara.configure asset_host}.
|
673
708
|
#
|
674
|
-
# If invoked without arguments it will save file to
|
675
|
-
#
|
676
|
-
#
|
709
|
+
# If invoked without arguments it will save file to {Capybara.configure save_path}
|
710
|
+
# and file will be given randomly generated filename. If invoked with a relative path
|
711
|
+
# the path will be relative to {Capybara.configure save_path}.
|
677
712
|
#
|
678
713
|
# @param [String] path the path to where it should be saved
|
679
714
|
# @return [String] the path to which the file was saved
|
680
715
|
#
|
681
716
|
def save_page(path = nil)
|
682
|
-
prepare_path(path, 'html').tap do |
|
683
|
-
File.write(
|
717
|
+
prepare_path(path, 'html').tap do |p_path|
|
718
|
+
File.write(p_path, Capybara::Helpers.inject_asset_host(body, host: config.asset_host), mode: 'wb')
|
684
719
|
end
|
685
720
|
end
|
686
721
|
|
@@ -688,46 +723,44 @@ module Capybara
|
|
688
723
|
#
|
689
724
|
# Save a snapshot of the page and open it in a browser for inspection.
|
690
725
|
#
|
691
|
-
# If invoked without arguments it will save file to
|
692
|
-
#
|
693
|
-
#
|
726
|
+
# If invoked without arguments it will save file to {Capybara.configure save_path}
|
727
|
+
# and file will be given randomly generated filename. If invoked with a relative path
|
728
|
+
# the path will be relative to {Capybara.configure save_path}.
|
694
729
|
#
|
695
730
|
# @param [String] path the path to where it should be saved
|
696
731
|
#
|
697
732
|
def save_and_open_page(path = nil)
|
698
|
-
save_page(path).tap { |
|
733
|
+
save_page(path).tap { |s_path| open_file(s_path) }
|
699
734
|
end
|
700
735
|
|
701
736
|
##
|
702
737
|
#
|
703
738
|
# Save a screenshot of page.
|
704
739
|
#
|
705
|
-
# If invoked without arguments it will save file to
|
706
|
-
#
|
707
|
-
#
|
740
|
+
# If invoked without arguments it will save file to {Capybara.configure save_path}
|
741
|
+
# and file will be given randomly generated filename. If invoked with a relative path
|
742
|
+
# the path will be relative to {Capybara.configure save_path}.
|
708
743
|
#
|
709
744
|
# @param [String] path the path to where it should be saved
|
710
745
|
# @param [Hash] options a customizable set of options
|
711
746
|
# @return [String] the path to which the file was saved
|
712
747
|
def save_screenshot(path = nil, **options)
|
713
|
-
prepare_path(path, 'png').tap { |
|
748
|
+
prepare_path(path, 'png').tap { |p_path| driver.save_screenshot(p_path, **options) }
|
714
749
|
end
|
715
750
|
|
716
751
|
##
|
717
752
|
#
|
718
753
|
# Save a screenshot of the page and open it for inspection.
|
719
754
|
#
|
720
|
-
# If invoked without arguments it will save file to
|
721
|
-
#
|
722
|
-
#
|
755
|
+
# If invoked without arguments it will save file to {Capybara.configure save_path}
|
756
|
+
# and file will be given randomly generated filename. If invoked with a relative path
|
757
|
+
# the path will be relative to {Capybara.configure save_path}.
|
723
758
|
#
|
724
759
|
# @param [String] path the path to where it should be saved
|
725
760
|
# @param [Hash] options a customizable set of options
|
726
761
|
#
|
727
762
|
def save_and_open_screenshot(path = nil, **options)
|
728
|
-
|
729
|
-
save_screenshot(path, options).tap { |p| open_file(p) }
|
730
|
-
# rubocop:enable Lint/Debugger
|
763
|
+
save_screenshot(path, **options).tap { |s_path| open_file(s_path) }
|
731
764
|
end
|
732
765
|
|
733
766
|
def document
|
@@ -735,16 +768,20 @@ module Capybara
|
|
735
768
|
end
|
736
769
|
|
737
770
|
NODE_METHODS.each do |method|
|
738
|
-
|
739
|
-
|
740
|
-
|
741
|
-
|
771
|
+
class_eval <<~METHOD, __FILE__, __LINE__ + 1
|
772
|
+
def #{method}(...)
|
773
|
+
@touched = true
|
774
|
+
current_scope.#{method}(...)
|
775
|
+
end
|
776
|
+
METHOD
|
742
777
|
end
|
743
778
|
|
744
779
|
DOCUMENT_METHODS.each do |method|
|
745
|
-
|
746
|
-
|
747
|
-
|
780
|
+
class_eval <<~METHOD, __FILE__, __LINE__ + 1
|
781
|
+
def #{method}(...)
|
782
|
+
document.#{method}(...)
|
783
|
+
end
|
784
|
+
METHOD
|
748
785
|
end
|
749
786
|
|
750
787
|
def inspect
|
@@ -758,9 +795,9 @@ module Capybara
|
|
758
795
|
|
759
796
|
##
|
760
797
|
#
|
761
|
-
# Yield a block using a specific wait time
|
798
|
+
# Yield a block using a specific maximum wait time.
|
762
799
|
#
|
763
|
-
def using_wait_time(seconds)
|
800
|
+
def using_wait_time(seconds, &block)
|
764
801
|
if Capybara.threadsafe
|
765
802
|
begin
|
766
803
|
previous_wait_time = config.default_max_wait_time
|
@@ -770,17 +807,18 @@ module Capybara
|
|
770
807
|
config.default_max_wait_time = previous_wait_time
|
771
808
|
end
|
772
809
|
else
|
773
|
-
Capybara.using_wait_time(seconds)
|
810
|
+
Capybara.using_wait_time(seconds, &block)
|
774
811
|
end
|
775
812
|
end
|
776
813
|
|
777
814
|
##
|
778
815
|
#
|
779
|
-
#
|
780
|
-
#
|
816
|
+
# Accepts a block to set the configuration options if {Capybara.configure threadsafe} is `true`. Note that some options only have an effect
|
817
|
+
# if set at initialization time, so look at the configuration block that can be passed to the initializer too.
|
781
818
|
#
|
782
819
|
def configure
|
783
|
-
raise
|
820
|
+
raise 'Session configuration is only supported when Capybara.threadsafe == true' unless Capybara.threadsafe
|
821
|
+
|
784
822
|
yield config
|
785
823
|
end
|
786
824
|
|
@@ -796,16 +834,24 @@ module Capybara
|
|
796
834
|
end
|
797
835
|
end
|
798
836
|
|
837
|
+
def server_url
|
838
|
+
@server&.base_url
|
839
|
+
end
|
840
|
+
|
799
841
|
private
|
800
842
|
|
801
|
-
@@instance_created = false
|
843
|
+
@@instance_created = false # rubocop:disable Style/ClassVars
|
844
|
+
|
845
|
+
def driver_args(args)
|
846
|
+
args.map { |arg| arg.is_a?(Capybara::Node::Element) ? arg.base : arg }
|
847
|
+
end
|
802
848
|
|
803
849
|
def accept_modal(type, text_or_options, options, &blk)
|
804
|
-
driver.accept_modal(type, modal_options(text_or_options, options), &blk)
|
850
|
+
driver.accept_modal(type, **modal_options(text_or_options, **options), &blk)
|
805
851
|
end
|
806
852
|
|
807
853
|
def dismiss_modal(type, text_or_options, options, &blk)
|
808
|
-
driver.dismiss_modal(type, modal_options(text_or_options, options), &blk)
|
854
|
+
driver.dismiss_modal(type, **modal_options(text_or_options, **options), &blk)
|
809
855
|
end
|
810
856
|
|
811
857
|
def modal_options(text = nil, **options)
|
@@ -815,18 +861,20 @@ module Capybara
|
|
815
861
|
end
|
816
862
|
|
817
863
|
def open_file(path)
|
818
|
-
require
|
864
|
+
require 'launchy'
|
819
865
|
Launchy.open(path)
|
820
866
|
rescue LoadError
|
821
867
|
warn "File saved to #{path}.\nPlease install the launchy gem to open the file automatically."
|
822
868
|
end
|
823
869
|
|
824
870
|
def prepare_path(path, extension)
|
825
|
-
File.expand_path(path || default_fn(extension), config.save_path).tap
|
871
|
+
File.expand_path(path || default_fn(extension), config.save_path).tap do |p_path|
|
872
|
+
FileUtils.mkdir_p(File.dirname(p_path))
|
873
|
+
end
|
826
874
|
end
|
827
875
|
|
828
876
|
def default_fn(extension)
|
829
|
-
timestamp = Time.new.strftime(
|
877
|
+
timestamp = Time.new.strftime('%Y%m%d%H%M%S')
|
830
878
|
"capybara-#{timestamp}#{rand(10**10)}.#{extension}"
|
831
879
|
end
|
832
880
|
|
@@ -837,9 +885,9 @@ module Capybara
|
|
837
885
|
def element_script_result(arg)
|
838
886
|
case arg
|
839
887
|
when Array
|
840
|
-
arg.map { |
|
888
|
+
arg.map { |subarg| element_script_result(subarg) }
|
841
889
|
when Hash
|
842
|
-
arg.
|
890
|
+
arg.transform_values! { |value| element_script_result(value) }
|
843
891
|
when Capybara::Driver::Node
|
844
892
|
Capybara::Node::Element.new(self, arg, nil, nil)
|
845
893
|
else
|
@@ -847,14 +895,18 @@ module Capybara
|
|
847
895
|
end
|
848
896
|
end
|
849
897
|
|
850
|
-
def
|
898
|
+
def adjust_server_port(uri)
|
899
|
+
uri.port ||= @server.port if @server && config.always_include_port
|
900
|
+
end
|
901
|
+
|
902
|
+
def _find_frame(*args, **kw_args)
|
851
903
|
case args[0]
|
852
904
|
when Capybara::Node::Element
|
853
905
|
args[0]
|
854
|
-
when String,
|
855
|
-
find(:frame, *args)
|
906
|
+
when String, nil
|
907
|
+
find(:frame, *args, **kw_args)
|
856
908
|
when Symbol
|
857
|
-
find(*args)
|
909
|
+
find(*args, **kw_args)
|
858
910
|
when Integer
|
859
911
|
idx = args[0]
|
860
912
|
all(:frame, minimum: idx + 1)[idx]
|
@@ -863,31 +915,37 @@ module Capybara
|
|
863
915
|
end
|
864
916
|
end
|
865
917
|
|
866
|
-
def _switch_to_window(window = nil, **options)
|
867
|
-
raise Capybara::ScopeError,
|
868
|
-
raise Capybara::ScopeError,
|
918
|
+
def _switch_to_window(window = nil, **options, &window_locator)
|
919
|
+
raise Capybara::ScopeError, 'Window cannot be switched inside a `within_frame` block' if scopes.include?(:frame)
|
920
|
+
raise Capybara::ScopeError, 'Window cannot be switched inside a `within` block' unless scopes.last.nil?
|
869
921
|
|
870
922
|
if window
|
871
923
|
driver.switch_to_window(window.handle)
|
872
924
|
window
|
873
925
|
else
|
874
|
-
|
875
|
-
document.synchronize(wait_time, errors: [Capybara::WindowError]) do
|
926
|
+
synchronize_windows(options) do
|
876
927
|
original_window_handle = driver.current_window_handle
|
877
928
|
begin
|
878
|
-
|
879
|
-
|
880
|
-
return Window.new(self, handle) if yield
|
881
|
-
end
|
882
|
-
rescue => e
|
929
|
+
_switch_to_window_by_locator(&window_locator)
|
930
|
+
rescue StandardError
|
883
931
|
driver.switch_to_window(original_window_handle)
|
884
|
-
raise
|
885
|
-
else
|
886
|
-
driver.switch_to_window(original_window_handle)
|
887
|
-
raise Capybara::WindowError, "Could not find a window matching block/lambda"
|
932
|
+
raise
|
888
933
|
end
|
889
934
|
end
|
890
935
|
end
|
891
936
|
end
|
937
|
+
|
938
|
+
def _switch_to_window_by_locator
|
939
|
+
driver.window_handles.each do |handle|
|
940
|
+
driver.switch_to_window handle
|
941
|
+
return Window.new(self, handle) if yield
|
942
|
+
end
|
943
|
+
raise Capybara::WindowError, 'Could not find a window matching block/lambda'
|
944
|
+
end
|
945
|
+
|
946
|
+
def synchronize_windows(options, &block)
|
947
|
+
wait_time = Capybara::Queries::BaseQuery.wait(options, config.default_max_wait_time)
|
948
|
+
document.synchronize(wait_time, errors: [Capybara::WindowError], &block)
|
949
|
+
end
|
892
950
|
end
|
893
951
|
end
|