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
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,39 @@ 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
43
|
click_link_or_button click_button click_link
|
|
44
44
|
fill_in find find_all find_button find_by_id find_field find_link
|
|
45
45
|
has_content? has_text? has_css? has_no_content? has_no_text?
|
|
46
|
-
has_no_css? has_no_xpath?
|
|
46
|
+
has_no_css? has_no_xpath? has_xpath? select uncheck
|
|
47
47
|
has_link? has_no_link? has_button? has_no_button? has_field?
|
|
48
48
|
has_no_field? has_checked_field? has_unchecked_field?
|
|
49
49
|
has_no_table? has_table? unselect has_select? has_no_select?
|
|
50
50
|
has_selector? has_no_selector? click_on has_no_checked_field?
|
|
51
51
|
has_no_unchecked_field? query assert_selector assert_no_selector
|
|
52
|
-
assert_all_of_selectors assert_none_of_selectors
|
|
52
|
+
assert_all_of_selectors assert_none_of_selectors assert_any_of_selectors
|
|
53
53
|
refute_selector assert_text assert_no_text
|
|
54
54
|
].freeze
|
|
55
55
|
# @api private
|
|
@@ -58,7 +58,7 @@ module Capybara
|
|
|
58
58
|
].freeze
|
|
59
59
|
SESSION_METHODS = %i[
|
|
60
60
|
body html source current_url current_host current_path
|
|
61
|
-
execute_script evaluate_script visit refresh go_back go_forward
|
|
61
|
+
execute_script evaluate_script visit refresh go_back go_forward send_keys
|
|
62
62
|
within within_element within_fieldset within_table within_frame switch_to_frame
|
|
63
63
|
current_window windows open_new_window switch_to_window within_window window_opened_by
|
|
64
64
|
save_page save_and_open_page save_screenshot
|
|
@@ -75,26 +75,30 @@ module Capybara
|
|
|
75
75
|
attr_accessor :synchronized
|
|
76
76
|
|
|
77
77
|
def initialize(mode, app = nil)
|
|
78
|
-
|
|
79
|
-
|
|
78
|
+
if app && !app.respond_to?(:call)
|
|
79
|
+
raise TypeError, 'The second parameter to Session::new should be a rack app if passed.'
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
@@instance_created = true # rubocop:disable Style/ClassVars
|
|
80
83
|
@mode = mode
|
|
81
84
|
@app = app
|
|
82
85
|
if block_given?
|
|
83
|
-
raise
|
|
86
|
+
raise 'A configuration block is only accepted when Capybara.threadsafe == true' unless Capybara.threadsafe
|
|
87
|
+
|
|
84
88
|
yield config
|
|
85
89
|
end
|
|
86
|
-
@server = if config.run_server
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
+
@server = if config.run_server && @app && driver.needs_server?
|
|
91
|
+
server_options = { port: config.server_port, host: config.server_host, reportable_errors: config.server_errors }
|
|
92
|
+
server_options[:extra_middleware] = [Capybara::Server::AnimationDisabler] if config.disable_animation
|
|
93
|
+
Capybara::Server.new(@app, **server_options).boot
|
|
90
94
|
end
|
|
91
95
|
@touched = false
|
|
92
96
|
end
|
|
93
97
|
|
|
94
98
|
def driver
|
|
95
99
|
@driver ||= begin
|
|
96
|
-
unless Capybara.drivers
|
|
97
|
-
other_drivers = Capybara.drivers.
|
|
100
|
+
unless Capybara.drivers[mode]
|
|
101
|
+
other_drivers = Capybara.drivers.names.map(&:inspect)
|
|
98
102
|
raise Capybara::DriverNotFoundError, "no driver called #{mode.inspect} was found, available drivers: #{other_drivers.join(', ')}"
|
|
99
103
|
end
|
|
100
104
|
driver = Capybara.drivers[mode].call(app)
|
|
@@ -105,28 +109,28 @@ module Capybara
|
|
|
105
109
|
|
|
106
110
|
##
|
|
107
111
|
#
|
|
108
|
-
# Reset the session (i.e. remove cookies and navigate to blank page)
|
|
112
|
+
# Reset the session (i.e. remove cookies and navigate to blank page).
|
|
109
113
|
#
|
|
110
114
|
# This method does not:
|
|
111
115
|
#
|
|
112
|
-
#
|
|
113
|
-
#
|
|
114
|
-
#
|
|
116
|
+
# * accept modal dialogs if they are present (Selenium driver now does, others may not)
|
|
117
|
+
# * clear browser cache/HTML 5 local storage/IndexedDB/Web SQL database/etc.
|
|
118
|
+
# * modify state of the driver/underlying browser in any other way
|
|
115
119
|
#
|
|
116
120
|
# as doing so will result in performance downsides and it's not needed to do everything from the list above for most apps.
|
|
117
121
|
#
|
|
118
122
|
# If you want to do anything from the list above on a general basis you can:
|
|
119
123
|
#
|
|
120
|
-
#
|
|
121
|
-
#
|
|
122
|
-
#
|
|
124
|
+
# * write RSpec/Cucumber/etc. after hook
|
|
125
|
+
# * monkeypatch this method
|
|
126
|
+
# * use Ruby's `prepend` method
|
|
123
127
|
#
|
|
124
128
|
def reset!
|
|
125
129
|
if @touched
|
|
126
130
|
driver.reset!
|
|
127
131
|
@touched = false
|
|
128
132
|
end
|
|
129
|
-
@server
|
|
133
|
+
@server&.wait_for_pending_requests
|
|
130
134
|
raise_server_error!
|
|
131
135
|
end
|
|
132
136
|
alias_method :cleanup!, :reset!
|
|
@@ -134,29 +138,40 @@ module Capybara
|
|
|
134
138
|
|
|
135
139
|
##
|
|
136
140
|
#
|
|
137
|
-
#
|
|
141
|
+
# Disconnect from the current driver. A new driver will be instantiated on the next interaction.
|
|
142
|
+
#
|
|
143
|
+
def quit
|
|
144
|
+
@driver.quit if @driver.respond_to? :quit
|
|
145
|
+
@document = @driver = nil
|
|
146
|
+
@touched = false
|
|
147
|
+
@server&.reset_error!
|
|
148
|
+
end
|
|
149
|
+
|
|
150
|
+
##
|
|
151
|
+
#
|
|
152
|
+
# Raise errors encountered in the server.
|
|
138
153
|
#
|
|
139
154
|
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!
|
|
155
|
+
return unless @server&.error
|
|
156
|
+
|
|
157
|
+
# Force an explanation for the error being raised as the exception cause
|
|
158
|
+
begin
|
|
159
|
+
if config.raise_server_errors
|
|
160
|
+
raise CapybaraError, 'Your application server raised an error - It has been raised in your test code because Capybara.raise_server_errors == true'
|
|
151
161
|
end
|
|
162
|
+
rescue CapybaraError
|
|
163
|
+
# needed to get the cause set correctly in JRuby -- otherwise we could just do raise @server.error
|
|
164
|
+
raise @server.error, @server.error.message, @server.error.backtrace
|
|
165
|
+
ensure
|
|
166
|
+
@server.reset_error!
|
|
152
167
|
end
|
|
153
168
|
end
|
|
154
169
|
|
|
155
170
|
##
|
|
156
171
|
#
|
|
157
|
-
# Returns a hash of response headers. Not supported by all drivers (e.g. Selenium)
|
|
172
|
+
# Returns a hash of response headers. Not supported by all drivers (e.g. Selenium).
|
|
158
173
|
#
|
|
159
|
-
# @return [Hash
|
|
174
|
+
# @return [Hash<String, String>] A hash of response headers.
|
|
160
175
|
#
|
|
161
176
|
def response_headers
|
|
162
177
|
driver.response_headers
|
|
@@ -164,7 +179,7 @@ module Capybara
|
|
|
164
179
|
|
|
165
180
|
##
|
|
166
181
|
#
|
|
167
|
-
# Returns the current HTTP status code as an
|
|
182
|
+
# Returns the current HTTP status code as an integer. Not supported by all drivers (e.g. Selenium).
|
|
168
183
|
#
|
|
169
184
|
# @return [Integer] Current HTTP status code
|
|
170
185
|
#
|
|
@@ -177,7 +192,7 @@ module Capybara
|
|
|
177
192
|
# @return [String] A snapshot of the DOM of the current document, as it looks right now (potentially modified by JavaScript).
|
|
178
193
|
#
|
|
179
194
|
def html
|
|
180
|
-
driver.html
|
|
195
|
+
driver.html || ''
|
|
181
196
|
end
|
|
182
197
|
alias_method :body, :html
|
|
183
198
|
alias_method :source, :html
|
|
@@ -190,14 +205,11 @@ module Capybara
|
|
|
190
205
|
# Addressable parsing is more lenient than URI
|
|
191
206
|
uri = ::Addressable::URI.parse(current_url)
|
|
192
207
|
|
|
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
208
|
# Addressable doesn't support opaque URIs - we want nil here
|
|
197
|
-
return nil if uri
|
|
209
|
+
return nil if uri&.scheme == 'about'
|
|
198
210
|
|
|
199
|
-
path = uri
|
|
200
|
-
path
|
|
211
|
+
path = uri&.path
|
|
212
|
+
path unless path&.empty?
|
|
201
213
|
end
|
|
202
214
|
|
|
203
215
|
##
|
|
@@ -227,13 +239,13 @@ module Capybara
|
|
|
227
239
|
#
|
|
228
240
|
# For drivers which can run against an external application, such as the selenium driver
|
|
229
241
|
# 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
|
|
242
|
+
# running on remote servers. For these drivers, setting {Capybara.configure app_host} will make the
|
|
231
243
|
# remote server the default. For example:
|
|
232
244
|
#
|
|
233
245
|
# Capybara.app_host = 'http://google.com'
|
|
234
246
|
# session.visit('/') # visits the google homepage
|
|
235
247
|
#
|
|
236
|
-
# If {Capybara.always_include_port} is set to true and this session is running against
|
|
248
|
+
# If {Capybara.configure always_include_port} is set to `true` and this session is running against
|
|
237
249
|
# a rack application, then the port that the rack application is running on will automatically
|
|
238
250
|
# be inserted into the URL. Supposing the app is running on port `4567`, doing something like:
|
|
239
251
|
#
|
|
@@ -248,27 +260,19 @@ module Capybara
|
|
|
248
260
|
@touched = true
|
|
249
261
|
|
|
250
262
|
visit_uri = ::Addressable::URI.parse(visit_uri.to_s)
|
|
263
|
+
base_uri = ::Addressable::URI.parse(config.app_host || server_url)
|
|
251
264
|
|
|
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)
|
|
265
|
+
if base_uri && [nil, 'http', 'https'].include?(visit_uri.scheme)
|
|
259
266
|
if visit_uri.relative?
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
visit_uri_parts = visit_uri.to_hash.delete_if { |_k, v| v.nil? }
|
|
267
|
+
visit_uri_parts = visit_uri.to_hash.compact
|
|
263
268
|
|
|
264
269
|
# Useful to people deploying to a subdirectory
|
|
265
270
|
# and/or single page apps where only the url fragment changes
|
|
266
|
-
visit_uri_parts[:path] =
|
|
271
|
+
visit_uri_parts[:path] = base_uri.path + visit_uri.path
|
|
267
272
|
|
|
268
|
-
visit_uri =
|
|
269
|
-
elsif @server && config.always_include_port
|
|
270
|
-
visit_uri.port ||= @server.port
|
|
273
|
+
visit_uri = base_uri.merge(visit_uri_parts)
|
|
271
274
|
end
|
|
275
|
+
adjust_server_port(visit_uri)
|
|
272
276
|
end
|
|
273
277
|
|
|
274
278
|
driver.visit(visit_uri.to_s)
|
|
@@ -276,7 +280,7 @@ module Capybara
|
|
|
276
280
|
|
|
277
281
|
##
|
|
278
282
|
#
|
|
279
|
-
# Refresh the page
|
|
283
|
+
# Refresh the page.
|
|
280
284
|
#
|
|
281
285
|
def refresh
|
|
282
286
|
raise_server_error!
|
|
@@ -300,9 +304,27 @@ module Capybara
|
|
|
300
304
|
end
|
|
301
305
|
|
|
302
306
|
##
|
|
307
|
+
# @!method send_keys
|
|
308
|
+
# @see Capybara::Node::Element#send_keys
|
|
303
309
|
#
|
|
304
|
-
|
|
305
|
-
|
|
310
|
+
def send_keys(*args, **kw_args)
|
|
311
|
+
driver.send_keys(*args, **kw_args)
|
|
312
|
+
end
|
|
313
|
+
|
|
314
|
+
##
|
|
315
|
+
#
|
|
316
|
+
# Returns the element with focus.
|
|
317
|
+
#
|
|
318
|
+
# Not supported by Rack Test
|
|
319
|
+
#
|
|
320
|
+
def active_element
|
|
321
|
+
Capybara::Queries::ActiveElementQuery.new.resolve_for(self)[0].tap(&:allow_reload!)
|
|
322
|
+
end
|
|
323
|
+
|
|
324
|
+
##
|
|
325
|
+
#
|
|
326
|
+
# Executes the given block within the context of a node. {#within} takes the
|
|
327
|
+
# same options as {Capybara::Node::Finders#find #find}, as well as a block. For the duration of the
|
|
306
328
|
# block, any command to Capybara will be handled as though it were scoped
|
|
307
329
|
# to the given element.
|
|
308
330
|
#
|
|
@@ -310,18 +332,18 @@ module Capybara
|
|
|
310
332
|
# fill_in('Street', with: '12 Main Street')
|
|
311
333
|
# end
|
|
312
334
|
#
|
|
313
|
-
# Just as with
|
|
314
|
-
#
|
|
335
|
+
# Just as with `#find`, if multiple elements match the selector given to
|
|
336
|
+
# {#within}, an error will be raised, and just as with `#find`, this
|
|
315
337
|
# behaviour can be controlled through the `:match` and `:exact` options.
|
|
316
338
|
#
|
|
317
339
|
# 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.
|
|
340
|
+
# assumed to be of the type set in {Capybara.configure default_selector}.
|
|
319
341
|
#
|
|
320
342
|
# within('div#delivery-address') do
|
|
321
343
|
# fill_in('Street', with: '12 Main Street')
|
|
322
344
|
# end
|
|
323
345
|
#
|
|
324
|
-
# Note that a lot of uses of
|
|
346
|
+
# Note that a lot of uses of {#within} can be replaced more succinctly with
|
|
325
347
|
# chaining:
|
|
326
348
|
#
|
|
327
349
|
# find('div#delivery-address').fill_in('Street', with: '12 Main Street')
|
|
@@ -334,11 +356,11 @@ module Capybara
|
|
|
334
356
|
#
|
|
335
357
|
# @raise [Capybara::ElementNotFound] If the scope can't be found before time expires
|
|
336
358
|
#
|
|
337
|
-
def within(*args)
|
|
338
|
-
new_scope = args.first.respond_to?(:to_capybara_node) ? args.first.to_capybara_node : find(*args)
|
|
359
|
+
def within(*args, **kw_args)
|
|
360
|
+
new_scope = args.first.respond_to?(:to_capybara_node) ? args.first.to_capybara_node : find(*args, **kw_args)
|
|
339
361
|
begin
|
|
340
362
|
scopes.push(new_scope)
|
|
341
|
-
yield
|
|
363
|
+
yield if block_given?
|
|
342
364
|
ensure
|
|
343
365
|
scopes.pop
|
|
344
366
|
end
|
|
@@ -351,8 +373,8 @@ module Capybara
|
|
|
351
373
|
#
|
|
352
374
|
# @param [String] locator Id or legend of the fieldset
|
|
353
375
|
#
|
|
354
|
-
def within_fieldset(locator)
|
|
355
|
-
within(:fieldset, locator)
|
|
376
|
+
def within_fieldset(locator, &block)
|
|
377
|
+
within(:fieldset, locator, &block)
|
|
356
378
|
end
|
|
357
379
|
|
|
358
380
|
##
|
|
@@ -361,24 +383,24 @@ module Capybara
|
|
|
361
383
|
#
|
|
362
384
|
# @param [String] locator Id or caption of the table
|
|
363
385
|
#
|
|
364
|
-
def within_table(locator)
|
|
365
|
-
within(:table, locator)
|
|
386
|
+
def within_table(locator, &block)
|
|
387
|
+
within(:table, locator, &block)
|
|
366
388
|
end
|
|
367
389
|
|
|
368
390
|
##
|
|
369
391
|
#
|
|
370
|
-
# Switch to the given frame
|
|
392
|
+
# Switch to the given frame.
|
|
371
393
|
#
|
|
372
394
|
# 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
|
-
#
|
|
395
|
+
# {#within_frame} is preferred over this method and should be used when possible.
|
|
374
396
|
# May not be supported by all drivers.
|
|
375
397
|
#
|
|
376
398
|
# @overload switch_to_frame(element)
|
|
377
|
-
# @param [Capybara::Node::Element]
|
|
378
|
-
# @overload switch_to_frame(
|
|
379
|
-
#
|
|
380
|
-
#
|
|
381
|
-
#
|
|
399
|
+
# @param [Capybara::Node::Element] element iframe/frame element to switch to
|
|
400
|
+
# @overload switch_to_frame(location)
|
|
401
|
+
# @param [Symbol] location relative location of the frame to switch to
|
|
402
|
+
# * :parent - the parent frame
|
|
403
|
+
# * :top - the top level document
|
|
382
404
|
#
|
|
383
405
|
def switch_to_frame(frame)
|
|
384
406
|
case frame
|
|
@@ -388,20 +410,23 @@ module Capybara
|
|
|
388
410
|
when :parent
|
|
389
411
|
if scopes.last != :frame
|
|
390
412
|
raise Capybara::ScopeError, "`switch_to_frame(:parent)` cannot be called from inside a descendant frame's "\
|
|
391
|
-
|
|
413
|
+
'`within` block.'
|
|
392
414
|
end
|
|
393
415
|
scopes.pop
|
|
394
416
|
driver.switch_to_frame(:parent)
|
|
395
417
|
when :top
|
|
396
418
|
idx = scopes.index(:frame)
|
|
419
|
+
top_level_scopes = [:frame, nil]
|
|
397
420
|
if idx
|
|
398
|
-
if scopes.slice(idx
|
|
421
|
+
if scopes.slice(idx..).any? { |scope| !top_level_scopes.include?(scope) }
|
|
399
422
|
raise Capybara::ScopeError, "`switch_to_frame(:top)` cannot be called from inside a descendant frame's "\
|
|
400
|
-
|
|
423
|
+
'`within` block.'
|
|
401
424
|
end
|
|
402
|
-
scopes.slice!(idx
|
|
425
|
+
scopes.slice!(idx..)
|
|
403
426
|
driver.switch_to_frame(:top)
|
|
404
427
|
end
|
|
428
|
+
else
|
|
429
|
+
raise ArgumentError, 'You must provide a frame element, :parent, or :top when calling switch_to_frame'
|
|
405
430
|
end
|
|
406
431
|
end
|
|
407
432
|
|
|
@@ -412,15 +437,15 @@ module Capybara
|
|
|
412
437
|
#
|
|
413
438
|
# @overload within_frame(element)
|
|
414
439
|
# @param [Capybara::Node::Element] frame element
|
|
415
|
-
# @overload within_frame([kind = :frame], locator, options
|
|
416
|
-
# @param [Symbol] kind Optional selector type (:
|
|
440
|
+
# @overload within_frame([kind = :frame], locator, **options)
|
|
441
|
+
# @param [Symbol] kind Optional selector type (:frame, :css, :xpath, etc.) - Defaults to :frame
|
|
417
442
|
# @param [String] locator The locator for the given selector kind. For :frame this is the name/id of a frame/iframe element
|
|
418
443
|
# @overload within_frame(index)
|
|
419
444
|
# @param [Integer] index index of a frame (0 based)
|
|
420
|
-
def within_frame(*args)
|
|
421
|
-
switch_to_frame(_find_frame(*args))
|
|
445
|
+
def within_frame(*args, **kw_args)
|
|
446
|
+
switch_to_frame(_find_frame(*args, **kw_args))
|
|
422
447
|
begin
|
|
423
|
-
yield
|
|
448
|
+
yield if block_given?
|
|
424
449
|
ensure
|
|
425
450
|
switch_to_frame(:parent)
|
|
426
451
|
end
|
|
@@ -447,22 +472,28 @@ module Capybara
|
|
|
447
472
|
end
|
|
448
473
|
|
|
449
474
|
##
|
|
450
|
-
# Open new window.
|
|
451
|
-
#
|
|
475
|
+
# Open a new window.
|
|
476
|
+
# The current window doesn't change as the result of this call.
|
|
452
477
|
# It should be switched to explicitly.
|
|
453
478
|
#
|
|
454
479
|
# @return [Capybara::Window] window that has been opened
|
|
455
480
|
#
|
|
456
|
-
def open_new_window
|
|
481
|
+
def open_new_window(kind = :tab)
|
|
457
482
|
window_opened_by do
|
|
458
|
-
driver.open_new_window
|
|
483
|
+
if driver.method(:open_new_window).arity.zero?
|
|
484
|
+
driver.open_new_window
|
|
485
|
+
else
|
|
486
|
+
driver.open_new_window(kind)
|
|
487
|
+
end
|
|
459
488
|
end
|
|
460
489
|
end
|
|
461
490
|
|
|
462
491
|
##
|
|
492
|
+
# Switch to the given window.
|
|
493
|
+
#
|
|
463
494
|
# @overload switch_to_window(&block)
|
|
464
495
|
# 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
|
|
496
|
+
# If window that matches block can't be found, the window will be switched back and {Capybara::WindowError} will be raised.
|
|
466
497
|
# @example
|
|
467
498
|
# window = switch_to_window { title == 'Page title' }
|
|
468
499
|
# @raise [Capybara::WindowError] if no window matches given block
|
|
@@ -471,20 +502,20 @@ module Capybara
|
|
|
471
502
|
# @raise [Capybara::Driver::Base#no_such_window_error] if nonexistent (e.g. closed) window was passed
|
|
472
503
|
#
|
|
473
504
|
# @return [Capybara::Window] window that has been switched to
|
|
474
|
-
# @raise [Capybara::ScopeError] if this method is invoked inside
|
|
475
|
-
#
|
|
505
|
+
# @raise [Capybara::ScopeError] if this method is invoked inside {#within} or
|
|
506
|
+
# {#within_frame} methods
|
|
476
507
|
# @raise [ArgumentError] if both or neither arguments were provided
|
|
477
508
|
#
|
|
478
509
|
def switch_to_window(window = nil, **options, &window_locator)
|
|
479
|
-
|
|
480
|
-
raise ArgumentError,
|
|
481
|
-
|
|
510
|
+
raise ArgumentError, '`switch_to_window` can take either a block or a window, not both' if window && window_locator
|
|
511
|
+
raise ArgumentError, '`switch_to_window`: either window or block should be provided' if !window && !window_locator
|
|
512
|
+
|
|
482
513
|
unless scopes.last.nil?
|
|
483
|
-
raise Capybara::ScopeError,
|
|
484
|
-
|
|
514
|
+
raise Capybara::ScopeError, '`switch_to_window` is not supposed to be invoked from '\
|
|
515
|
+
'`within` or `within_frame` blocks.'
|
|
485
516
|
end
|
|
486
517
|
|
|
487
|
-
_switch_to_window(window, options, &window_locator)
|
|
518
|
+
_switch_to_window(window, **options, &window_locator)
|
|
488
519
|
end
|
|
489
520
|
|
|
490
521
|
##
|
|
@@ -492,20 +523,20 @@ module Capybara
|
|
|
492
523
|
#
|
|
493
524
|
# 1. Switches to the given window (it can be located by window instance/lambda/string).
|
|
494
525
|
# 2. Executes the given block (within window located at previous step).
|
|
495
|
-
# 3. Switches back (this step will be invoked even if exception
|
|
526
|
+
# 3. Switches back (this step will be invoked even if an exception occurs at the second step).
|
|
496
527
|
#
|
|
497
528
|
# @overload within_window(window) { do_something }
|
|
498
|
-
# @param window [Capybara::Window] instance of
|
|
529
|
+
# @param window [Capybara::Window] instance of {Capybara::Window} class
|
|
499
530
|
# that will be switched to
|
|
500
531
|
# @raise [driver#no_such_window_error] if nonexistent (e.g. closed) window was passed
|
|
501
532
|
# @overload within_window(proc_or_lambda) { do_something }
|
|
502
|
-
# @param lambda [Proc]
|
|
533
|
+
# @param lambda [Proc] First window for which lambda
|
|
503
534
|
# returns a value other than false or nil will be switched to.
|
|
504
535
|
# @example
|
|
505
536
|
# within_window(->{ page.title == 'Page title' }) { click_button 'Submit' }
|
|
506
537
|
# @raise [Capybara::WindowError] if no window matching lambda was found
|
|
507
538
|
#
|
|
508
|
-
# @raise [Capybara::ScopeError] if this method is invoked inside
|
|
539
|
+
# @raise [Capybara::ScopeError] if this method is invoked inside {#within_frame} method
|
|
509
540
|
# @return value returned by the block
|
|
510
541
|
#
|
|
511
542
|
def within_window(window_or_proc)
|
|
@@ -518,11 +549,11 @@ module Capybara
|
|
|
518
549
|
when Proc
|
|
519
550
|
_switch_to_window { window_or_proc.call }
|
|
520
551
|
else
|
|
521
|
-
raise ArgumentError
|
|
552
|
+
raise ArgumentError, '`#within_window` requires a `Capybara::Window` instance or a lambda'
|
|
522
553
|
end
|
|
523
554
|
|
|
524
555
|
begin
|
|
525
|
-
yield
|
|
556
|
+
yield if block_given?
|
|
526
557
|
ensure
|
|
527
558
|
_switch_to_window(original) unless original == window_or_proc
|
|
528
559
|
end
|
|
@@ -535,11 +566,11 @@ module Capybara
|
|
|
535
566
|
# Get the window that has been opened by the passed block.
|
|
536
567
|
# It will wait for it to be opened (in the same way as other Capybara methods wait).
|
|
537
568
|
# 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}
|
|
569
|
+
# {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
570
|
#
|
|
540
571
|
# @overload window_opened_by(**options, &block)
|
|
541
572
|
# @param options [Hash]
|
|
542
|
-
# @option options [Numeric] :wait
|
|
573
|
+
# @option options [Numeric] :wait maximum wait time. Defaults to {Capybara.configure default_max_wait_time}
|
|
543
574
|
# @return [Capybara::Window] the window that has been opened within a block
|
|
544
575
|
# @raise [Capybara::WindowError] if block passed to window hasn't opened window
|
|
545
576
|
# or opened more than one window
|
|
@@ -548,11 +579,10 @@ module Capybara
|
|
|
548
579
|
old_handles = driver.window_handles
|
|
549
580
|
yield
|
|
550
581
|
|
|
551
|
-
|
|
552
|
-
document.synchronize(wait_time, errors: [Capybara::WindowError]) do
|
|
582
|
+
synchronize_windows(options) do
|
|
553
583
|
opened_handles = (driver.window_handles - old_handles)
|
|
554
584
|
if opened_handles.size != 1
|
|
555
|
-
raise Capybara::WindowError,
|
|
585
|
+
raise Capybara::WindowError, 'block passed to #window_opened_by '\
|
|
556
586
|
"opened #{opened_handles.size} windows instead of 1"
|
|
557
587
|
end
|
|
558
588
|
Window.new(self, opened_handles.first)
|
|
@@ -562,29 +592,30 @@ module Capybara
|
|
|
562
592
|
##
|
|
563
593
|
#
|
|
564
594
|
# Execute the given script, not returning a result. This is useful for scripts that return
|
|
565
|
-
# complex objects, such as jQuery statements.
|
|
566
|
-
#
|
|
595
|
+
# complex objects, such as jQuery statements. {#execute_script} should be used over
|
|
596
|
+
# {#evaluate_script} whenever possible.
|
|
567
597
|
#
|
|
568
598
|
# @param [String] script A string of JavaScript to execute
|
|
569
|
-
# @param args Optional arguments that will be passed to the script.
|
|
599
|
+
# @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
600
|
#
|
|
571
601
|
def execute_script(script, *args)
|
|
572
602
|
@touched = true
|
|
573
|
-
driver.execute_script(script, *args
|
|
603
|
+
driver.execute_script(script, *driver_args(args))
|
|
574
604
|
end
|
|
575
605
|
|
|
576
606
|
##
|
|
577
607
|
#
|
|
578
608
|
# Evaluate the given JavaScript and return the result. Be careful when using this with
|
|
579
|
-
# scripts that return complex objects, such as jQuery statements.
|
|
609
|
+
# scripts that return complex objects, such as jQuery statements. {#execute_script} might
|
|
580
610
|
# be a better alternative.
|
|
581
611
|
#
|
|
582
612
|
# @param [String] script A string of JavaScript to evaluate
|
|
613
|
+
# @param args Optional arguments that will be passed to the script
|
|
583
614
|
# @return [Object] The result of the evaluated JavaScript (may be driver specific)
|
|
584
615
|
#
|
|
585
616
|
def evaluate_script(script, *args)
|
|
586
617
|
@touched = true
|
|
587
|
-
result = driver.evaluate_script(script, *args
|
|
618
|
+
result = driver.evaluate_script(script.strip, *driver_args(args))
|
|
588
619
|
element_script_result(result)
|
|
589
620
|
end
|
|
590
621
|
|
|
@@ -593,11 +624,12 @@ module Capybara
|
|
|
593
624
|
# Evaluate the given JavaScript and obtain the result from a callback function which will be passed as the last argument to the script.
|
|
594
625
|
#
|
|
595
626
|
# @param [String] script A string of JavaScript to evaluate
|
|
627
|
+
# @param args Optional arguments that will be passed to the script
|
|
596
628
|
# @return [Object] The result of the evaluated JavaScript (may be driver specific)
|
|
597
629
|
#
|
|
598
630
|
def evaluate_async_script(script, *args)
|
|
599
631
|
@touched = true
|
|
600
|
-
result = driver.evaluate_async_script(script, *args
|
|
632
|
+
result = driver.evaluate_async_script(script, *driver_args(args))
|
|
601
633
|
element_script_result(result)
|
|
602
634
|
end
|
|
603
635
|
|
|
@@ -606,17 +638,17 @@ module Capybara
|
|
|
606
638
|
# Execute the block, accepting a alert.
|
|
607
639
|
#
|
|
608
640
|
# @!macro modal_params
|
|
609
|
-
# Expects a block whose actions will trigger the display modal to appear
|
|
641
|
+
# Expects a block whose actions will trigger the display modal to appear.
|
|
610
642
|
# @example
|
|
611
643
|
# $0 do
|
|
612
644
|
# click_link('link that triggers appearance of system modal')
|
|
613
645
|
# 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
|
|
646
|
+
# @overload $0(text, **options, &blk)
|
|
647
|
+
# @param text [String, Regexp] Text or regex to match against the text in the modal. If not provided any modal is matched.
|
|
648
|
+
# @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
649
|
# @yield Block whose actions will trigger the system modal
|
|
618
|
-
# @overload $0(options
|
|
619
|
-
# @option options [Numeric] :wait
|
|
650
|
+
# @overload $0(**options, &blk)
|
|
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}.
|
|
620
652
|
# @yield Block whose actions will trigger the system modal
|
|
621
653
|
# @return [String] the message shown in the modal
|
|
622
654
|
# @raise [Capybara::ModalNotFound] if modal dialog hasn't been found
|
|
@@ -668,19 +700,19 @@ module Capybara
|
|
|
668
700
|
|
|
669
701
|
##
|
|
670
702
|
#
|
|
671
|
-
# Save a snapshot of the page. If
|
|
672
|
-
#
|
|
703
|
+
# Save a snapshot of the page. If {Capybara.configure asset_host} is set it will inject `base` tag
|
|
704
|
+
# pointing to {Capybara.configure asset_host}.
|
|
673
705
|
#
|
|
674
|
-
# If invoked without arguments it will save file to
|
|
675
|
-
#
|
|
676
|
-
#
|
|
706
|
+
# If invoked without arguments it will save file to {Capybara.configure save_path}
|
|
707
|
+
# and file will be given randomly generated filename. If invoked with a relative path
|
|
708
|
+
# the path will be relative to {Capybara.configure save_path}.
|
|
677
709
|
#
|
|
678
710
|
# @param [String] path the path to where it should be saved
|
|
679
711
|
# @return [String] the path to which the file was saved
|
|
680
712
|
#
|
|
681
713
|
def save_page(path = nil)
|
|
682
|
-
prepare_path(path, 'html').tap do |
|
|
683
|
-
File.write(
|
|
714
|
+
prepare_path(path, 'html').tap do |p_path|
|
|
715
|
+
File.write(p_path, Capybara::Helpers.inject_asset_host(body, host: config.asset_host), mode: 'wb')
|
|
684
716
|
end
|
|
685
717
|
end
|
|
686
718
|
|
|
@@ -688,46 +720,44 @@ module Capybara
|
|
|
688
720
|
#
|
|
689
721
|
# Save a snapshot of the page and open it in a browser for inspection.
|
|
690
722
|
#
|
|
691
|
-
# If invoked without arguments it will save file to
|
|
692
|
-
#
|
|
693
|
-
#
|
|
723
|
+
# If invoked without arguments it will save file to {Capybara.configure save_path}
|
|
724
|
+
# and file will be given randomly generated filename. If invoked with a relative path
|
|
725
|
+
# the path will be relative to {Capybara.configure save_path}.
|
|
694
726
|
#
|
|
695
727
|
# @param [String] path the path to where it should be saved
|
|
696
728
|
#
|
|
697
729
|
def save_and_open_page(path = nil)
|
|
698
|
-
save_page(path).tap { |
|
|
730
|
+
save_page(path).tap { |s_path| open_file(s_path) }
|
|
699
731
|
end
|
|
700
732
|
|
|
701
733
|
##
|
|
702
734
|
#
|
|
703
735
|
# Save a screenshot of page.
|
|
704
736
|
#
|
|
705
|
-
# If invoked without arguments it will save file to
|
|
706
|
-
#
|
|
707
|
-
#
|
|
737
|
+
# If invoked without arguments it will save file to {Capybara.configure save_path}
|
|
738
|
+
# and file will be given randomly generated filename. If invoked with a relative path
|
|
739
|
+
# the path will be relative to {Capybara.configure save_path}.
|
|
708
740
|
#
|
|
709
741
|
# @param [String] path the path to where it should be saved
|
|
710
742
|
# @param [Hash] options a customizable set of options
|
|
711
743
|
# @return [String] the path to which the file was saved
|
|
712
744
|
def save_screenshot(path = nil, **options)
|
|
713
|
-
prepare_path(path, 'png').tap { |
|
|
745
|
+
prepare_path(path, 'png').tap { |p_path| driver.save_screenshot(p_path, **options) }
|
|
714
746
|
end
|
|
715
747
|
|
|
716
748
|
##
|
|
717
749
|
#
|
|
718
750
|
# Save a screenshot of the page and open it for inspection.
|
|
719
751
|
#
|
|
720
|
-
# If invoked without arguments it will save file to
|
|
721
|
-
#
|
|
722
|
-
#
|
|
752
|
+
# If invoked without arguments it will save file to {Capybara.configure save_path}
|
|
753
|
+
# and file will be given randomly generated filename. If invoked with a relative path
|
|
754
|
+
# the path will be relative to {Capybara.configure save_path}.
|
|
723
755
|
#
|
|
724
756
|
# @param [String] path the path to where it should be saved
|
|
725
757
|
# @param [Hash] options a customizable set of options
|
|
726
758
|
#
|
|
727
759
|
def save_and_open_screenshot(path = nil, **options)
|
|
728
|
-
|
|
729
|
-
save_screenshot(path, options).tap { |p| open_file(p) }
|
|
730
|
-
# rubocop:enable Lint/Debugger
|
|
760
|
+
save_screenshot(path, **options).tap { |s_path| open_file(s_path) }
|
|
731
761
|
end
|
|
732
762
|
|
|
733
763
|
def document
|
|
@@ -735,15 +765,32 @@ module Capybara
|
|
|
735
765
|
end
|
|
736
766
|
|
|
737
767
|
NODE_METHODS.each do |method|
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
768
|
+
if RUBY_VERSION >= '2.7'
|
|
769
|
+
class_eval <<~METHOD, __FILE__, __LINE__ + 1
|
|
770
|
+
def #{method}(...)
|
|
771
|
+
@touched = true
|
|
772
|
+
current_scope.#{method}(...)
|
|
773
|
+
end
|
|
774
|
+
METHOD
|
|
775
|
+
else
|
|
776
|
+
define_method method do |*args, &block|
|
|
777
|
+
@touched = true
|
|
778
|
+
current_scope.send(method, *args, &block)
|
|
779
|
+
end
|
|
741
780
|
end
|
|
742
781
|
end
|
|
743
782
|
|
|
744
783
|
DOCUMENT_METHODS.each do |method|
|
|
745
|
-
|
|
746
|
-
|
|
784
|
+
if RUBY_VERSION >= '2.7'
|
|
785
|
+
class_eval <<~METHOD, __FILE__, __LINE__ + 1
|
|
786
|
+
def #{method}(...)
|
|
787
|
+
document.#{method}(...)
|
|
788
|
+
end
|
|
789
|
+
METHOD
|
|
790
|
+
else
|
|
791
|
+
define_method method do |*args, &block|
|
|
792
|
+
document.send(method, *args, &block)
|
|
793
|
+
end
|
|
747
794
|
end
|
|
748
795
|
end
|
|
749
796
|
|
|
@@ -758,9 +805,9 @@ module Capybara
|
|
|
758
805
|
|
|
759
806
|
##
|
|
760
807
|
#
|
|
761
|
-
# Yield a block using a specific wait time
|
|
808
|
+
# Yield a block using a specific maximum wait time.
|
|
762
809
|
#
|
|
763
|
-
def using_wait_time(seconds)
|
|
810
|
+
def using_wait_time(seconds, &block)
|
|
764
811
|
if Capybara.threadsafe
|
|
765
812
|
begin
|
|
766
813
|
previous_wait_time = config.default_max_wait_time
|
|
@@ -770,17 +817,18 @@ module Capybara
|
|
|
770
817
|
config.default_max_wait_time = previous_wait_time
|
|
771
818
|
end
|
|
772
819
|
else
|
|
773
|
-
Capybara.using_wait_time(seconds)
|
|
820
|
+
Capybara.using_wait_time(seconds, &block)
|
|
774
821
|
end
|
|
775
822
|
end
|
|
776
823
|
|
|
777
824
|
##
|
|
778
825
|
#
|
|
779
|
-
#
|
|
780
|
-
#
|
|
826
|
+
# Accepts a block to set the configuration options if {Capybara.configure threadsafe} is `true`. Note that some options only have an effect
|
|
827
|
+
# if set at initialization time, so look at the configuration block that can be passed to the initializer too.
|
|
781
828
|
#
|
|
782
829
|
def configure
|
|
783
|
-
raise
|
|
830
|
+
raise 'Session configuration is only supported when Capybara.threadsafe == true' unless Capybara.threadsafe
|
|
831
|
+
|
|
784
832
|
yield config
|
|
785
833
|
end
|
|
786
834
|
|
|
@@ -796,16 +844,24 @@ module Capybara
|
|
|
796
844
|
end
|
|
797
845
|
end
|
|
798
846
|
|
|
847
|
+
def server_url
|
|
848
|
+
@server&.base_url
|
|
849
|
+
end
|
|
850
|
+
|
|
799
851
|
private
|
|
800
852
|
|
|
801
|
-
@@instance_created = false
|
|
853
|
+
@@instance_created = false # rubocop:disable Style/ClassVars
|
|
854
|
+
|
|
855
|
+
def driver_args(args)
|
|
856
|
+
args.map { |arg| arg.is_a?(Capybara::Node::Element) ? arg.base : arg }
|
|
857
|
+
end
|
|
802
858
|
|
|
803
859
|
def accept_modal(type, text_or_options, options, &blk)
|
|
804
|
-
driver.accept_modal(type, modal_options(text_or_options, options), &blk)
|
|
860
|
+
driver.accept_modal(type, **modal_options(text_or_options, **options), &blk)
|
|
805
861
|
end
|
|
806
862
|
|
|
807
863
|
def dismiss_modal(type, text_or_options, options, &blk)
|
|
808
|
-
driver.dismiss_modal(type, modal_options(text_or_options, options), &blk)
|
|
864
|
+
driver.dismiss_modal(type, **modal_options(text_or_options, **options), &blk)
|
|
809
865
|
end
|
|
810
866
|
|
|
811
867
|
def modal_options(text = nil, **options)
|
|
@@ -815,18 +871,20 @@ module Capybara
|
|
|
815
871
|
end
|
|
816
872
|
|
|
817
873
|
def open_file(path)
|
|
818
|
-
require
|
|
874
|
+
require 'launchy'
|
|
819
875
|
Launchy.open(path)
|
|
820
876
|
rescue LoadError
|
|
821
877
|
warn "File saved to #{path}.\nPlease install the launchy gem to open the file automatically."
|
|
822
878
|
end
|
|
823
879
|
|
|
824
880
|
def prepare_path(path, extension)
|
|
825
|
-
File.expand_path(path || default_fn(extension), config.save_path).tap
|
|
881
|
+
File.expand_path(path || default_fn(extension), config.save_path).tap do |p_path|
|
|
882
|
+
FileUtils.mkdir_p(File.dirname(p_path))
|
|
883
|
+
end
|
|
826
884
|
end
|
|
827
885
|
|
|
828
886
|
def default_fn(extension)
|
|
829
|
-
timestamp = Time.new.strftime(
|
|
887
|
+
timestamp = Time.new.strftime('%Y%m%d%H%M%S')
|
|
830
888
|
"capybara-#{timestamp}#{rand(10**10)}.#{extension}"
|
|
831
889
|
end
|
|
832
890
|
|
|
@@ -837,9 +895,9 @@ module Capybara
|
|
|
837
895
|
def element_script_result(arg)
|
|
838
896
|
case arg
|
|
839
897
|
when Array
|
|
840
|
-
arg.map { |
|
|
898
|
+
arg.map { |subarg| element_script_result(subarg) }
|
|
841
899
|
when Hash
|
|
842
|
-
arg.
|
|
900
|
+
arg.transform_values! { |value| element_script_result(value) }
|
|
843
901
|
when Capybara::Driver::Node
|
|
844
902
|
Capybara::Node::Element.new(self, arg, nil, nil)
|
|
845
903
|
else
|
|
@@ -847,14 +905,18 @@ module Capybara
|
|
|
847
905
|
end
|
|
848
906
|
end
|
|
849
907
|
|
|
850
|
-
def
|
|
908
|
+
def adjust_server_port(uri)
|
|
909
|
+
uri.port ||= @server.port if @server && config.always_include_port
|
|
910
|
+
end
|
|
911
|
+
|
|
912
|
+
def _find_frame(*args, **kw_args)
|
|
851
913
|
case args[0]
|
|
852
914
|
when Capybara::Node::Element
|
|
853
915
|
args[0]
|
|
854
|
-
when String,
|
|
855
|
-
find(:frame, *args)
|
|
916
|
+
when String, nil
|
|
917
|
+
find(:frame, *args, **kw_args)
|
|
856
918
|
when Symbol
|
|
857
|
-
find(*args)
|
|
919
|
+
find(*args, **kw_args)
|
|
858
920
|
when Integer
|
|
859
921
|
idx = args[0]
|
|
860
922
|
all(:frame, minimum: idx + 1)[idx]
|
|
@@ -863,31 +925,37 @@ module Capybara
|
|
|
863
925
|
end
|
|
864
926
|
end
|
|
865
927
|
|
|
866
|
-
def _switch_to_window(window = nil, **options)
|
|
867
|
-
raise Capybara::ScopeError,
|
|
868
|
-
raise Capybara::ScopeError,
|
|
928
|
+
def _switch_to_window(window = nil, **options, &window_locator)
|
|
929
|
+
raise Capybara::ScopeError, 'Window cannot be switched inside a `within_frame` block' if scopes.include?(:frame)
|
|
930
|
+
raise Capybara::ScopeError, 'Window cannot be switched inside a `within` block' unless scopes.last.nil?
|
|
869
931
|
|
|
870
932
|
if window
|
|
871
933
|
driver.switch_to_window(window.handle)
|
|
872
934
|
window
|
|
873
935
|
else
|
|
874
|
-
|
|
875
|
-
document.synchronize(wait_time, errors: [Capybara::WindowError]) do
|
|
936
|
+
synchronize_windows(options) do
|
|
876
937
|
original_window_handle = driver.current_window_handle
|
|
877
938
|
begin
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
return Window.new(self, handle) if yield
|
|
881
|
-
end
|
|
882
|
-
rescue => e
|
|
939
|
+
_switch_to_window_by_locator(&window_locator)
|
|
940
|
+
rescue StandardError
|
|
883
941
|
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"
|
|
942
|
+
raise
|
|
888
943
|
end
|
|
889
944
|
end
|
|
890
945
|
end
|
|
891
946
|
end
|
|
947
|
+
|
|
948
|
+
def _switch_to_window_by_locator
|
|
949
|
+
driver.window_handles.each do |handle|
|
|
950
|
+
driver.switch_to_window handle
|
|
951
|
+
return Window.new(self, handle) if yield
|
|
952
|
+
end
|
|
953
|
+
raise Capybara::WindowError, 'Could not find a window matching block/lambda'
|
|
954
|
+
end
|
|
955
|
+
|
|
956
|
+
def synchronize_windows(options, &block)
|
|
957
|
+
wait_time = Capybara::Queries::BaseQuery.wait(options, config.default_max_wait_time)
|
|
958
|
+
document.synchronize(wait_time, errors: [Capybara::WindowError], &block)
|
|
959
|
+
end
|
|
892
960
|
end
|
|
893
961
|
end
|