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