capybara 3.32.2
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 +7 -0
- data/.yardopts +1 -0
- data/History.md +1813 -0
- data/License.txt +22 -0
- data/README.md +1099 -0
- data/lib/capybara.rb +511 -0
- data/lib/capybara/config.rb +94 -0
- data/lib/capybara/cucumber.rb +27 -0
- data/lib/capybara/driver/base.rb +170 -0
- data/lib/capybara/driver/node.rb +139 -0
- data/lib/capybara/dsl.rb +65 -0
- data/lib/capybara/helpers.rb +108 -0
- data/lib/capybara/minitest.rb +386 -0
- data/lib/capybara/minitest/spec.rb +264 -0
- data/lib/capybara/node/actions.rb +420 -0
- data/lib/capybara/node/base.rb +143 -0
- data/lib/capybara/node/document.rb +48 -0
- data/lib/capybara/node/document_matchers.rb +67 -0
- data/lib/capybara/node/element.rb +606 -0
- data/lib/capybara/node/finders.rb +325 -0
- data/lib/capybara/node/matchers.rb +883 -0
- data/lib/capybara/node/simple.rb +208 -0
- data/lib/capybara/queries/ancestor_query.rb +27 -0
- data/lib/capybara/queries/base_query.rb +106 -0
- data/lib/capybara/queries/current_path_query.rb +51 -0
- data/lib/capybara/queries/match_query.rb +26 -0
- data/lib/capybara/queries/selector_query.rb +710 -0
- data/lib/capybara/queries/sibling_query.rb +26 -0
- data/lib/capybara/queries/style_query.rb +45 -0
- data/lib/capybara/queries/text_query.rb +110 -0
- data/lib/capybara/queries/title_query.rb +39 -0
- data/lib/capybara/rack_test/browser.rb +140 -0
- data/lib/capybara/rack_test/css_handlers.rb +13 -0
- data/lib/capybara/rack_test/driver.rb +109 -0
- data/lib/capybara/rack_test/errors.rb +6 -0
- data/lib/capybara/rack_test/form.rb +127 -0
- data/lib/capybara/rack_test/node.rb +325 -0
- data/lib/capybara/rails.rb +16 -0
- data/lib/capybara/registrations/drivers.rb +36 -0
- data/lib/capybara/registrations/patches/puma_ssl.rb +27 -0
- data/lib/capybara/registrations/servers.rb +44 -0
- data/lib/capybara/result.rb +190 -0
- data/lib/capybara/rspec.rb +29 -0
- data/lib/capybara/rspec/features.rb +23 -0
- data/lib/capybara/rspec/matcher_proxies.rb +82 -0
- data/lib/capybara/rspec/matchers.rb +201 -0
- 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 +38 -0
- data/lib/capybara/rspec/matchers/spatial_sugar.rb +39 -0
- data/lib/capybara/selector.rb +233 -0
- data/lib/capybara/selector/builders/css_builder.rb +84 -0
- data/lib/capybara/selector/builders/xpath_builder.rb +69 -0
- data/lib/capybara/selector/css.rb +102 -0
- data/lib/capybara/selector/definition.rb +276 -0
- data/lib/capybara/selector/definition/button.rb +51 -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 +27 -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 +46 -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/filter.rb +5 -0
- data/lib/capybara/selector/filter_set.rb +124 -0
- data/lib/capybara/selector/filters/base.rb +77 -0
- data/lib/capybara/selector/filters/expression_filter.rb +22 -0
- data/lib/capybara/selector/filters/locator_filter.rb +29 -0
- data/lib/capybara/selector/filters/node_filter.rb +31 -0
- data/lib/capybara/selector/regexp_disassembler.rb +214 -0
- data/lib/capybara/selector/selector.rb +147 -0
- data/lib/capybara/selector/xpath_extensions.rb +17 -0
- 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 +496 -0
- data/lib/capybara/selenium/driver_specializations/chrome_driver.rb +119 -0
- data/lib/capybara/selenium/driver_specializations/edge_driver.rb +126 -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 +78 -0
- data/lib/capybara/selenium/logger_suppressor.rb +34 -0
- data/lib/capybara/selenium/node.rb +610 -0
- data/lib/capybara/selenium/nodes/chrome_node.rb +119 -0
- data/lib/capybara/selenium/nodes/edge_node.rb +104 -0
- data/lib/capybara/selenium/nodes/firefox_node.rb +131 -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 +47 -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.rb +126 -0
- data/lib/capybara/server/animation_disabler.rb +58 -0
- data/lib/capybara/server/checker.rb +44 -0
- data/lib/capybara/server/middleware.rb +69 -0
- data/lib/capybara/session.rb +942 -0
- data/lib/capybara/session/config.rb +124 -0
- data/lib/capybara/session/matchers.rb +87 -0
- data/lib/capybara/spec/fixtures/another_test_file.txt +1 -0
- data/lib/capybara/spec/fixtures/capybara.jpg +3 -0
- data/lib/capybara/spec/fixtures/no_extension +1 -0
- data/lib/capybara/spec/fixtures/test_file.txt +1 -0
- data/lib/capybara/spec/public/jquery-ui.js +13 -0
- data/lib/capybara/spec/public/jquery.js +5 -0
- data/lib/capybara/spec/public/offset.js +6 -0
- data/lib/capybara/spec/public/test.js +268 -0
- data/lib/capybara/spec/session/accept_alert_spec.rb +81 -0
- data/lib/capybara/spec/session/accept_confirm_spec.rb +32 -0
- data/lib/capybara/spec/session/accept_prompt_spec.rb +78 -0
- data/lib/capybara/spec/session/all_spec.rb +278 -0
- data/lib/capybara/spec/session/ancestor_spec.rb +88 -0
- data/lib/capybara/spec/session/assert_all_of_selectors_spec.rb +140 -0
- data/lib/capybara/spec/session/assert_current_path_spec.rb +75 -0
- 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_spec.rb +258 -0
- data/lib/capybara/spec/session/assert_title_spec.rb +93 -0
- data/lib/capybara/spec/session/attach_file_spec.rb +216 -0
- data/lib/capybara/spec/session/body_spec.rb +23 -0
- data/lib/capybara/spec/session/check_spec.rb +235 -0
- data/lib/capybara/spec/session/choose_spec.rb +121 -0
- data/lib/capybara/spec/session/click_button_spec.rb +506 -0
- data/lib/capybara/spec/session/click_link_or_button_spec.rb +129 -0
- data/lib/capybara/spec/session/click_link_spec.rb +229 -0
- data/lib/capybara/spec/session/current_scope_spec.rb +31 -0
- data/lib/capybara/spec/session/current_url_spec.rb +115 -0
- data/lib/capybara/spec/session/dismiss_confirm_spec.rb +36 -0
- data/lib/capybara/spec/session/dismiss_prompt_spec.rb +21 -0
- data/lib/capybara/spec/session/element/assert_match_selector_spec.rb +38 -0
- data/lib/capybara/spec/session/element/match_css_spec.rb +31 -0
- data/lib/capybara/spec/session/element/match_xpath_spec.rb +25 -0
- data/lib/capybara/spec/session/element/matches_selector_spec.rb +120 -0
- data/lib/capybara/spec/session/evaluate_async_script_spec.rb +23 -0
- data/lib/capybara/spec/session/evaluate_script_spec.rb +49 -0
- data/lib/capybara/spec/session/execute_script_spec.rb +28 -0
- data/lib/capybara/spec/session/fill_in_spec.rb +286 -0
- data/lib/capybara/spec/session/find_button_spec.rb +74 -0
- data/lib/capybara/spec/session/find_by_id_spec.rb +33 -0
- data/lib/capybara/spec/session/find_field_spec.rb +113 -0
- data/lib/capybara/spec/session/find_link_spec.rb +70 -0
- data/lib/capybara/spec/session/find_spec.rb +531 -0
- data/lib/capybara/spec/session/first_spec.rb +156 -0
- data/lib/capybara/spec/session/frame/frame_title_spec.rb +23 -0
- data/lib/capybara/spec/session/frame/frame_url_spec.rb +23 -0
- data/lib/capybara/spec/session/frame/switch_to_frame_spec.rb +116 -0
- data/lib/capybara/spec/session/frame/within_frame_spec.rb +112 -0
- data/lib/capybara/spec/session/go_back_spec.rb +12 -0
- data/lib/capybara/spec/session/go_forward_spec.rb +14 -0
- data/lib/capybara/spec/session/has_all_selectors_spec.rb +69 -0
- data/lib/capybara/spec/session/has_ancestor_spec.rb +46 -0
- data/lib/capybara/spec/session/has_any_selectors_spec.rb +25 -0
- data/lib/capybara/spec/session/has_button_spec.rb +69 -0
- data/lib/capybara/spec/session/has_css_spec.rb +374 -0
- data/lib/capybara/spec/session/has_current_path_spec.rb +138 -0
- data/lib/capybara/spec/session/has_field_spec.rb +349 -0
- data/lib/capybara/spec/session/has_link_spec.rb +39 -0
- data/lib/capybara/spec/session/has_none_selectors_spec.rb +78 -0
- data/lib/capybara/spec/session/has_select_spec.rb +310 -0
- data/lib/capybara/spec/session/has_selector_spec.rb +202 -0
- data/lib/capybara/spec/session/has_sibling_spec.rb +50 -0
- data/lib/capybara/spec/session/has_table_spec.rb +198 -0
- data/lib/capybara/spec/session/has_text_spec.rb +394 -0
- data/lib/capybara/spec/session/has_title_spec.rb +71 -0
- data/lib/capybara/spec/session/has_xpath_spec.rb +149 -0
- data/lib/capybara/spec/session/headers_spec.rb +8 -0
- data/lib/capybara/spec/session/html_spec.rb +47 -0
- data/lib/capybara/spec/session/matches_style_spec.rb +35 -0
- data/lib/capybara/spec/session/node_spec.rb +1292 -0
- data/lib/capybara/spec/session/node_wrapper_spec.rb +39 -0
- data/lib/capybara/spec/session/refresh_spec.rb +33 -0
- data/lib/capybara/spec/session/reset_session_spec.rb +148 -0
- data/lib/capybara/spec/session/response_code_spec.rb +8 -0
- data/lib/capybara/spec/session/save_and_open_page_spec.rb +21 -0
- data/lib/capybara/spec/session/save_and_open_screenshot_spec.rb +43 -0
- data/lib/capybara/spec/session/save_page_spec.rb +110 -0
- data/lib/capybara/spec/session/save_screenshot_spec.rb +55 -0
- data/lib/capybara/spec/session/screenshot_spec.rb +18 -0
- data/lib/capybara/spec/session/scroll_spec.rb +117 -0
- data/lib/capybara/spec/session/select_spec.rb +229 -0
- data/lib/capybara/spec/session/selectors_spec.rb +98 -0
- data/lib/capybara/spec/session/sibling_spec.rb +52 -0
- data/lib/capybara/spec/session/source_spec.rb +0 -0
- data/lib/capybara/spec/session/text_spec.rb +74 -0
- data/lib/capybara/spec/session/title_spec.rb +29 -0
- data/lib/capybara/spec/session/uncheck_spec.rb +100 -0
- data/lib/capybara/spec/session/unselect_spec.rb +116 -0
- data/lib/capybara/spec/session/visit_spec.rb +204 -0
- data/lib/capybara/spec/session/window/become_closed_spec.rb +89 -0
- data/lib/capybara/spec/session/window/current_window_spec.rb +28 -0
- data/lib/capybara/spec/session/window/open_new_window_spec.rb +31 -0
- data/lib/capybara/spec/session/window/switch_to_window_spec.rb +132 -0
- data/lib/capybara/spec/session/window/window_opened_by_spec.rb +99 -0
- data/lib/capybara/spec/session/window/window_spec.rb +203 -0
- data/lib/capybara/spec/session/window/windows_spec.rb +34 -0
- data/lib/capybara/spec/session/window/within_window_spec.rb +157 -0
- data/lib/capybara/spec/session/within_spec.rb +199 -0
- data/lib/capybara/spec/spec_helper.rb +134 -0
- data/lib/capybara/spec/test_app.rb +226 -0
- data/lib/capybara/spec/views/animated.erb +49 -0
- data/lib/capybara/spec/views/buttons.erb +5 -0
- data/lib/capybara/spec/views/fieldsets.erb +30 -0
- data/lib/capybara/spec/views/form.erb +685 -0
- data/lib/capybara/spec/views/frame_child.erb +18 -0
- data/lib/capybara/spec/views/frame_one.erb +10 -0
- data/lib/capybara/spec/views/frame_parent.erb +9 -0
- data/lib/capybara/spec/views/frame_two.erb +9 -0
- data/lib/capybara/spec/views/header_links.erb +8 -0
- data/lib/capybara/spec/views/host_links.erb +13 -0
- data/lib/capybara/spec/views/initial_alert.erb +10 -0
- data/lib/capybara/spec/views/obscured.erb +47 -0
- data/lib/capybara/spec/views/offset.erb +32 -0
- data/lib/capybara/spec/views/path.erb +13 -0
- data/lib/capybara/spec/views/popup_one.erb +9 -0
- data/lib/capybara/spec/views/popup_two.erb +9 -0
- data/lib/capybara/spec/views/postback.erb +14 -0
- data/lib/capybara/spec/views/react.erb +45 -0
- data/lib/capybara/spec/views/scroll.erb +20 -0
- data/lib/capybara/spec/views/spatial.erb +31 -0
- data/lib/capybara/spec/views/tables.erb +130 -0
- data/lib/capybara/spec/views/with_animation.erb +74 -0
- data/lib/capybara/spec/views/with_base_tag.erb +11 -0
- data/lib/capybara/spec/views/with_count.erb +8 -0
- data/lib/capybara/spec/views/with_dragula.erb +22 -0
- data/lib/capybara/spec/views/with_fixed_header_footer.erb +17 -0
- data/lib/capybara/spec/views/with_hover.erb +24 -0
- data/lib/capybara/spec/views/with_hover1.erb +10 -0
- data/lib/capybara/spec/views/with_html.erb +208 -0
- data/lib/capybara/spec/views/with_html5_svg.erb +20 -0
- data/lib/capybara/spec/views/with_html_entities.erb +2 -0
- data/lib/capybara/spec/views/with_js.erb +160 -0
- data/lib/capybara/spec/views/with_jstree.erb +26 -0
- data/lib/capybara/spec/views/with_namespace.erb +20 -0
- data/lib/capybara/spec/views/with_scope.erb +42 -0
- data/lib/capybara/spec/views/with_scope_other.erb +6 -0
- data/lib/capybara/spec/views/with_simple_html.erb +2 -0
- data/lib/capybara/spec/views/with_slow_unload.erb +17 -0
- data/lib/capybara/spec/views/with_sortable_js.erb +21 -0
- data/lib/capybara/spec/views/with_title.erb +5 -0
- data/lib/capybara/spec/views/with_unload_alert.erb +14 -0
- data/lib/capybara/spec/views/with_windows.erb +54 -0
- data/lib/capybara/spec/views/within_frames.erb +15 -0
- data/lib/capybara/version.rb +5 -0
- data/lib/capybara/window.rb +146 -0
- data/spec/basic_node_spec.rb +154 -0
- data/spec/capybara_spec.rb +112 -0
- data/spec/css_builder_spec.rb +101 -0
- data/spec/css_splitter_spec.rb +38 -0
- data/spec/dsl_spec.rb +276 -0
- data/spec/filter_set_spec.rb +46 -0
- data/spec/fixtures/capybara.csv +1 -0
- data/spec/fixtures/certificate.pem +25 -0
- data/spec/fixtures/key.pem +27 -0
- data/spec/fixtures/selenium_driver_rspec_failure.rb +13 -0
- data/spec/fixtures/selenium_driver_rspec_success.rb +13 -0
- data/spec/minitest_spec.rb +163 -0
- data/spec/minitest_spec_spec.rb +162 -0
- data/spec/per_session_config_spec.rb +68 -0
- data/spec/rack_test_spec.rb +268 -0
- data/spec/regexp_dissassembler_spec.rb +250 -0
- data/spec/result_spec.rb +196 -0
- data/spec/rspec/features_spec.rb +99 -0
- data/spec/rspec/scenarios_spec.rb +19 -0
- data/spec/rspec/shared_spec_matchers.rb +947 -0
- data/spec/rspec/views_spec.rb +14 -0
- data/spec/rspec_matchers_spec.rb +62 -0
- data/spec/rspec_spec.rb +145 -0
- data/spec/sauce_spec_chrome.rb +43 -0
- data/spec/selector_spec.rb +513 -0
- data/spec/selenium_spec_chrome.rb +188 -0
- data/spec/selenium_spec_chrome_remote.rb +96 -0
- data/spec/selenium_spec_edge.rb +47 -0
- data/spec/selenium_spec_firefox.rb +208 -0
- data/spec/selenium_spec_firefox_remote.rb +80 -0
- data/spec/selenium_spec_ie.rb +150 -0
- data/spec/selenium_spec_safari.rb +148 -0
- data/spec/server_spec.rb +292 -0
- data/spec/session_spec.rb +91 -0
- data/spec/shared_selenium_node.rb +83 -0
- data/spec/shared_selenium_session.rb +476 -0
- data/spec/spec_helper.rb +100 -0
- data/spec/xpath_builder_spec.rb +93 -0
- metadata +753 -0
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
Capybara::SpecHelper.spec '#has_title?' do
|
|
4
|
+
before do
|
|
5
|
+
@session.visit('/with_js')
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
it 'should be true if the page has the given title' do
|
|
9
|
+
expect(@session).to have_title('with_js')
|
|
10
|
+
expect(@session.has_title?('with_js')).to be true
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
it 'should allow regexp matches' do
|
|
14
|
+
expect(@session).to have_title(/w[a-z]{3}_js/)
|
|
15
|
+
expect(@session).not_to have_title(/monkey/)
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
it 'should wait for title', requires: [:js] do
|
|
19
|
+
@session.click_link('Change title')
|
|
20
|
+
expect(@session).to have_title('changed title')
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
it 'should be false if the page has not the given title' do
|
|
24
|
+
expect(@session).not_to have_title('monkey')
|
|
25
|
+
expect(@session.has_title?('monkey')).to be false
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
it 'should default to exact: false matching' do
|
|
29
|
+
expect(@session).to have_title('with_js', exact: false)
|
|
30
|
+
expect(@session).to have_title('with_', exact: false)
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
it 'should match exactly if exact: true option passed' do
|
|
34
|
+
expect(@session).to have_title('with_js', exact: true)
|
|
35
|
+
expect(@session).not_to have_title('with_', exact: true)
|
|
36
|
+
expect(@session.has_title?('with_js', exact: true)).to be true
|
|
37
|
+
expect(@session.has_title?('with_', exact: true)).to be false
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
it 'should match partial if exact: false option passed' do
|
|
41
|
+
expect(@session).to have_title('with_js', exact: false)
|
|
42
|
+
expect(@session).to have_title('with_', exact: false)
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
Capybara::SpecHelper.spec '#has_no_title?' do
|
|
47
|
+
before do
|
|
48
|
+
@session.visit('/with_js')
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
it 'should be false if the page has the given title' do
|
|
52
|
+
expect(@session).not_to have_no_title('with_js')
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
it 'should allow regexp matches' do
|
|
56
|
+
expect(@session).not_to have_no_title(/w[a-z]{3}_js/)
|
|
57
|
+
expect(@session).to have_no_title(/monkey/)
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
it 'should wait for title to disappear', requires: [:js] do
|
|
61
|
+
Capybara.using_wait_time(5) do
|
|
62
|
+
@session.click_link('Change title') # triggers title change after 400ms
|
|
63
|
+
expect(@session).to have_no_title('with_js')
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
it 'should be true if the page has not the given title' do
|
|
68
|
+
expect(@session).to have_no_title('monkey')
|
|
69
|
+
expect(@session.has_no_title?('monkey')).to be true
|
|
70
|
+
end
|
|
71
|
+
end
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
Capybara::SpecHelper.spec '#has_xpath?' do
|
|
4
|
+
before do
|
|
5
|
+
@session.visit('/with_html')
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
it 'should be true if the given selector is on the page' do
|
|
9
|
+
expect(@session).to have_xpath('//p')
|
|
10
|
+
expect(@session).to have_xpath("//p//a[@id='foo']")
|
|
11
|
+
expect(@session).to have_xpath("//p[contains(.,'est')]")
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
it 'should support :id option' do
|
|
15
|
+
expect(@session).to have_xpath('//h2', id: 'h2one')
|
|
16
|
+
expect(@session).to have_xpath('//h2')
|
|
17
|
+
expect(@session).to have_xpath('//h2', id: /h2o/)
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
it 'should support :class option' do
|
|
21
|
+
expect(@session).to have_xpath('//li', class: 'guitarist')
|
|
22
|
+
expect(@session).to have_xpath('//li', class: /guitar/)
|
|
23
|
+
expect(@session).to have_xpath('//li', class: /guitar|drummer/)
|
|
24
|
+
expect(@session).to have_xpath('//li', class: %w[beatle guitarist])
|
|
25
|
+
expect(@session).to have_xpath('//li', class: /.*/)
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
it 'should be false if the given selector is not on the page' do
|
|
29
|
+
expect(@session).not_to have_xpath('//abbr')
|
|
30
|
+
expect(@session).not_to have_xpath("//p//a[@id='doesnotexist']")
|
|
31
|
+
expect(@session).not_to have_xpath("//p[contains(.,'thisstringisnotonpage')]")
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
it 'should use xpath even if default selector is CSS' do
|
|
35
|
+
Capybara.default_selector = :css
|
|
36
|
+
expect(@session).not_to have_xpath("//p//a[@id='doesnotexist']")
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
it 'should respect scopes' do
|
|
40
|
+
@session.within "//p[@id='first']" do
|
|
41
|
+
expect(@session).to have_xpath(".//a[@id='foo']")
|
|
42
|
+
expect(@session).not_to have_xpath(".//a[@id='red']")
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
it 'should wait for content to appear', requires: [:js] do
|
|
47
|
+
Capybara.using_wait_time(3) do
|
|
48
|
+
@session.visit('/with_js')
|
|
49
|
+
@session.click_link('Click me') # updates page after 500ms
|
|
50
|
+
expect(@session).to have_xpath("//input[@type='submit' and @value='New Here']")
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
context 'with count' do
|
|
55
|
+
it 'should be true if the content occurs the given number of times' do
|
|
56
|
+
expect(@session).to have_xpath('//p', count: 3)
|
|
57
|
+
expect(@session).to have_xpath("//p//a[@id='foo']", count: 1)
|
|
58
|
+
expect(@session).to have_xpath("//p[contains(.,'est')]", count: 1)
|
|
59
|
+
expect(@session).to have_xpath("//p//a[@id='doesnotexist']", count: 0)
|
|
60
|
+
expect(@session).to have_xpath('//li', class: /guitar|drummer/, count: 4)
|
|
61
|
+
expect(@session).to have_xpath('//li', id: /john|paul/, class: /guitar|drummer/, count: 2)
|
|
62
|
+
expect(@session).to have_xpath('//li', class: %w[beatle guitarist], count: 2)
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
it 'should be false if the content occurs a different number of times than the given' do
|
|
66
|
+
expect(@session).not_to have_xpath('//p', count: 6)
|
|
67
|
+
expect(@session).not_to have_xpath("//p//a[@id='foo']", count: 2)
|
|
68
|
+
expect(@session).not_to have_xpath("//p[contains(.,'est')]", count: 5)
|
|
69
|
+
expect(@session).not_to have_xpath("//p//a[@id='doesnotexist']", count: 1)
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
context 'with text' do
|
|
74
|
+
it 'should discard all matches where the given string is not contained' do
|
|
75
|
+
expect(@session).to have_xpath('//p//a', text: 'Redirect', count: 1)
|
|
76
|
+
expect(@session).not_to have_xpath('//p', text: 'Doesnotexist')
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
it 'should discard all matches where the given regexp is not matched' do
|
|
80
|
+
expect(@session).to have_xpath('//p//a', text: /re[dab]i/i, count: 1)
|
|
81
|
+
expect(@session).not_to have_xpath('//p//a', text: /Red$/)
|
|
82
|
+
end
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
Capybara::SpecHelper.spec '#has_no_xpath?' do
|
|
87
|
+
before do
|
|
88
|
+
@session.visit('/with_html')
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
it 'should be false if the given selector is on the page' do
|
|
92
|
+
expect(@session).not_to have_no_xpath('//p')
|
|
93
|
+
expect(@session).not_to have_no_xpath("//p//a[@id='foo']")
|
|
94
|
+
expect(@session).not_to have_no_xpath("//p[contains(.,'est')]")
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
it 'should be true if the given selector is not on the page' do
|
|
98
|
+
expect(@session).to have_no_xpath('//abbr')
|
|
99
|
+
expect(@session).to have_no_xpath("//p//a[@id='doesnotexist']")
|
|
100
|
+
expect(@session).to have_no_xpath("//p[contains(.,'thisstringisnotonpage')]")
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
it 'should use xpath even if default selector is CSS' do
|
|
104
|
+
Capybara.default_selector = :css
|
|
105
|
+
expect(@session).to have_no_xpath("//p//a[@id='doesnotexist']")
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
it 'should respect scopes' do
|
|
109
|
+
@session.within "//p[@id='first']" do
|
|
110
|
+
expect(@session).not_to have_no_xpath(".//a[@id='foo']")
|
|
111
|
+
expect(@session).to have_no_xpath(".//a[@id='red']")
|
|
112
|
+
end
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
it 'should wait for content to disappear', requires: [:js] do
|
|
116
|
+
Capybara.default_max_wait_time = 2
|
|
117
|
+
@session.visit('/with_js')
|
|
118
|
+
@session.click_link('Click me')
|
|
119
|
+
expect(@session).to have_no_xpath("//p[@id='change']")
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
context 'with count' do
|
|
123
|
+
it 'should be false if the content occurs the given number of times' do
|
|
124
|
+
expect(@session).not_to have_no_xpath('//p', count: 3)
|
|
125
|
+
expect(@session).not_to have_no_xpath("//p//a[@id='foo']", count: 1)
|
|
126
|
+
expect(@session).not_to have_no_xpath("//p[contains(.,'est')]", count: 1)
|
|
127
|
+
expect(@session).not_to have_no_xpath("//p//a[@id='doesnotexist']", count: 0)
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
it 'should be true if the content occurs a different number of times than the given' do
|
|
131
|
+
expect(@session).to have_no_xpath('//p', count: 6)
|
|
132
|
+
expect(@session).to have_no_xpath("//p//a[@id='foo']", count: 2)
|
|
133
|
+
expect(@session).to have_no_xpath("//p[contains(.,'est')]", count: 5)
|
|
134
|
+
expect(@session).to have_no_xpath("//p//a[@id='doesnotexist']", count: 1)
|
|
135
|
+
end
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
context 'with text' do
|
|
139
|
+
it 'should discard all matches where the given string is contained' do
|
|
140
|
+
expect(@session).not_to have_no_xpath('//p//a', text: 'Redirect', count: 1)
|
|
141
|
+
expect(@session).to have_no_xpath('//p', text: 'Doesnotexist')
|
|
142
|
+
end
|
|
143
|
+
|
|
144
|
+
it 'should discard all matches where the given regexp is matched' do
|
|
145
|
+
expect(@session).not_to have_no_xpath('//p//a', text: /re[dab]i/i, count: 1)
|
|
146
|
+
expect(@session).to have_no_xpath('//p//a', text: /Red$/)
|
|
147
|
+
end
|
|
148
|
+
end
|
|
149
|
+
end
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
Capybara::SpecHelper.spec '#response_headers' do
|
|
4
|
+
it 'should return response headers', requires: [:response_headers] do
|
|
5
|
+
@session.visit('/with_simple_html')
|
|
6
|
+
expect(@session.response_headers['Content-Type']).to match %r{text/html}
|
|
7
|
+
end
|
|
8
|
+
end
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# Note: This file uses `sleep` to sync up parts of the tests. This is only implemented like this
|
|
4
|
+
# because of the methods being tested. In tests using Capybara this type of behavior should be implemented
|
|
5
|
+
# using Capybara provided assertions with builtin waiting behavior.
|
|
6
|
+
|
|
7
|
+
Capybara::SpecHelper.spec '#html' do
|
|
8
|
+
it 'should return the unmodified page body' do
|
|
9
|
+
@session.visit('/')
|
|
10
|
+
expect(@session.html).to include('Hello world!')
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
it 'should return the current state of the page', requires: [:js] do
|
|
14
|
+
@session.visit('/with_js')
|
|
15
|
+
sleep 1
|
|
16
|
+
expect(@session.html).to include('I changed it')
|
|
17
|
+
expect(@session.html).not_to include('This is text')
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
Capybara::SpecHelper.spec '#source' do
|
|
22
|
+
it 'should return the unmodified page source' do
|
|
23
|
+
@session.visit('/')
|
|
24
|
+
expect(@session.source).to include('Hello world!')
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
it 'should return the current state of the page', requires: [:js] do
|
|
28
|
+
@session.visit('/with_js')
|
|
29
|
+
sleep 1
|
|
30
|
+
expect(@session.source).to include('I changed it')
|
|
31
|
+
expect(@session.source).not_to include('This is text')
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
Capybara::SpecHelper.spec '#body' do
|
|
36
|
+
it 'should return the unmodified page source' do
|
|
37
|
+
@session.visit('/')
|
|
38
|
+
expect(@session.body).to include('Hello world!')
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
it 'should return the current state of the page', requires: [:js] do
|
|
42
|
+
@session.visit('/with_js')
|
|
43
|
+
sleep 1
|
|
44
|
+
expect(@session.body).to include('I changed it')
|
|
45
|
+
expect(@session.body).not_to include('This is text')
|
|
46
|
+
end
|
|
47
|
+
end
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
Capybara::SpecHelper.spec '#matches_style?', requires: [:css] do
|
|
4
|
+
before do
|
|
5
|
+
@session.visit('/with_html')
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
it 'should be true if the element has the given style' do
|
|
9
|
+
expect(@session.find(:css, '#first')).to match_style(display: 'block')
|
|
10
|
+
expect(@session.find(:css, '#first').matches_style?(display: 'block')).to be true
|
|
11
|
+
expect(@session.find(:css, '#second')).to match_style('display' => 'inline')
|
|
12
|
+
expect(@session.find(:css, '#second').matches_style?('display' => 'inline')).to be true
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
it 'should be false if the element does not have the given style' do
|
|
16
|
+
expect(@session.find(:css, '#first').matches_style?('display' => 'inline')).to be false
|
|
17
|
+
expect(@session.find(:css, '#second').matches_style?(display: 'block')).to be false
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
it 'allows Regexp for value matching' do
|
|
21
|
+
expect(@session.find(:css, '#first')).to match_style(display: /^bl/)
|
|
22
|
+
expect(@session.find(:css, '#first').matches_style?('display' => /^bl/)).to be true
|
|
23
|
+
expect(@session.find(:css, '#first').matches_style?(display: /^in/)).to be false
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
it 'deprecated has_style?' do
|
|
27
|
+
expect { have_style(display: /^bl/) }.to \
|
|
28
|
+
output(/have_style is deprecated/).to_stderr
|
|
29
|
+
|
|
30
|
+
el = @session.find(:css, '#first')
|
|
31
|
+
allow(el).to receive(:warn).and_return(nil)
|
|
32
|
+
el.has_style?('display' => /^bl/)
|
|
33
|
+
expect(el).to have_received(:warn)
|
|
34
|
+
end
|
|
35
|
+
end
|
|
@@ -0,0 +1,1292 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# Note: This file uses `sleep` to sync up parts of the tests. This is only implemented like this
|
|
4
|
+
# because of the methods being tested. In tests using Capybara this type of behavior should be implemented
|
|
5
|
+
# using Capybara provided assertions with builtin waiting behavior.
|
|
6
|
+
|
|
7
|
+
Capybara::SpecHelper.spec 'node' do
|
|
8
|
+
before do
|
|
9
|
+
@session.visit('/with_html')
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
it 'should act like a session object' do
|
|
13
|
+
@session.visit('/form')
|
|
14
|
+
@form = @session.find(:css, '#get-form')
|
|
15
|
+
expect(@form).to have_field('Middle Name')
|
|
16
|
+
expect(@form).to have_no_field('Languages')
|
|
17
|
+
@form.fill_in('Middle Name', with: 'Monkey')
|
|
18
|
+
@form.click_button('med')
|
|
19
|
+
expect(extract_results(@session)['middle_name']).to eq('Monkey')
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
it 'should scope CSS selectors' do
|
|
23
|
+
expect(@session.find(:css, '#second')).to have_no_css('h1')
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
describe '#query_scope' do
|
|
27
|
+
it 'should have a reference to the element the query was evaluated on if there is one' do
|
|
28
|
+
node = @session.find(:css, '#first')
|
|
29
|
+
expect(node.query_scope).to eq(node.session.document)
|
|
30
|
+
expect(node.find(:css, '#foo').query_scope).to eq(node)
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
describe '#text' do
|
|
35
|
+
it 'should extract node texts' do
|
|
36
|
+
expect(@session.all('//a')[0].text).to eq('labore')
|
|
37
|
+
expect(@session.all('//a')[1].text).to eq('ullamco')
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
it 'should return document text on /html selector' do
|
|
41
|
+
@session.visit('/with_simple_html')
|
|
42
|
+
expect(@session.all('/html')[0].text).to eq('Bar')
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
describe '#[]' do
|
|
47
|
+
it 'should extract node attributes' do
|
|
48
|
+
expect(@session.all('//a')[0][:class]).to eq('simple')
|
|
49
|
+
expect(@session.all('//a')[1][:id]).to eq('foo')
|
|
50
|
+
expect(@session.all('//input')[0][:type]).to eq('text')
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
it 'should extract boolean node attributes' do
|
|
54
|
+
expect(@session.find('//input[@id="checked_field"]')[:checked]).to be_truthy
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
describe '#style', requires: [:css] do
|
|
59
|
+
it 'should return the computed style value' do
|
|
60
|
+
expect(@session.find(:css, '#first').style('display')).to eq('display' => 'block')
|
|
61
|
+
expect(@session.find(:css, '#second').style(:display)).to eq('display' => 'inline')
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
it 'should return multiple style values' do
|
|
65
|
+
expect(@session.find(:css, '#first').style('display', :'line-height')).to eq('display' => 'block', 'line-height' => '25px')
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
describe '#value' do
|
|
70
|
+
it 'should allow retrieval of the value' do
|
|
71
|
+
expect(@session.find('//textarea[@id="normal"]').value).to eq('banana')
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
it 'should not swallow extra newlines in textarea' do
|
|
75
|
+
expect(@session.find('//textarea[@id="additional_newline"]').value).to eq("\nbanana")
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
it 'should not swallow leading newlines for set content in textarea' do
|
|
79
|
+
@session.find('//textarea[@id="normal"]').set("\nbanana")
|
|
80
|
+
expect(@session.find('//textarea[@id="normal"]').value).to eq("\nbanana")
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
it 'return any HTML content in textarea' do
|
|
84
|
+
@session.find('//textarea[1]').set('some <em>html</em> here')
|
|
85
|
+
expect(@session.find('//textarea[1]').value).to eq('some <em>html</em> here')
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
it "defaults to 'on' for checkbox" do
|
|
89
|
+
@session.visit('/form')
|
|
90
|
+
expect(@session.find('//input[@id="valueless_checkbox"]').value).to eq('on')
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
it "defaults to 'on' for radio buttons" do
|
|
94
|
+
@session.visit('/form')
|
|
95
|
+
expect(@session.find('//input[@id="valueless_radio"]').value).to eq('on')
|
|
96
|
+
end
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
describe '#set' do
|
|
100
|
+
it 'should allow assignment of field value' do
|
|
101
|
+
expect(@session.first('//input').value).to eq('monkey')
|
|
102
|
+
@session.first('//input').set('gorilla')
|
|
103
|
+
expect(@session.first('//input').value).to eq('gorilla')
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
it 'should fill the field even if the caret was not at the end', requires: [:js] do
|
|
107
|
+
@session.execute_script("var el = document.getElementById('test_field'); el.focus(); el.setSelectionRange(0, 0);")
|
|
108
|
+
@session.first('//input').set('')
|
|
109
|
+
expect(@session.first('//input').value).to eq('')
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
if ENV['CAPYBARA_THOROUGH']
|
|
113
|
+
it 'should raise if the text field is readonly' do
|
|
114
|
+
expect { @session.first('//input[@readonly]').set('changed') }.to raise_error(Capybara::ReadOnlyElementError)
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
it 'should raise if the textarea is readonly' do
|
|
118
|
+
expect { @session.first('//textarea[@readonly]').set('changed') }.to raise_error(Capybara::ReadOnlyElementError)
|
|
119
|
+
end
|
|
120
|
+
else
|
|
121
|
+
it 'should not change if the text field is readonly' do
|
|
122
|
+
@session.first('//input[@readonly]').set('changed')
|
|
123
|
+
expect(@session.first('//input[@readonly]').value).to eq 'should not change'
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
it 'should not change if the textarea is readonly' do
|
|
127
|
+
@session.first('//textarea[@readonly]').set('changed')
|
|
128
|
+
expect(@session.first('//textarea[@readonly]').value).to eq 'textarea should not change'
|
|
129
|
+
end
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
it 'should use global default options' do
|
|
133
|
+
Capybara.default_set_options = { clear: :backspace }
|
|
134
|
+
element = @session.first(:fillable_field, type: 'text')
|
|
135
|
+
allow(element.base).to receive(:set)
|
|
136
|
+
element.set('gorilla')
|
|
137
|
+
expect(element.base).to have_received(:set).with('gorilla', clear: :backspace)
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
context 'with a contenteditable element', requires: [:js] do
|
|
141
|
+
it 'should allow me to change the contents' do
|
|
142
|
+
@session.visit('/with_js')
|
|
143
|
+
@session.find(:css, '#existing_content_editable').set('WYSIWYG')
|
|
144
|
+
expect(@session.find(:css, '#existing_content_editable').text).to eq('WYSIWYG')
|
|
145
|
+
end
|
|
146
|
+
|
|
147
|
+
it 'should allow me to set the contents' do
|
|
148
|
+
@session.visit('/with_js')
|
|
149
|
+
@session.find(:css, '#blank_content_editable').set('WYSIWYG')
|
|
150
|
+
expect(@session.find(:css, '#blank_content_editable').text).to eq('WYSIWYG')
|
|
151
|
+
end
|
|
152
|
+
|
|
153
|
+
it 'should allow me to change the contents of a child element' do
|
|
154
|
+
@session.visit('/with_js')
|
|
155
|
+
@session.find(:css, '#existing_content_editable_child').set('WYSIWYG')
|
|
156
|
+
expect(@session.find(:css, '#existing_content_editable_child').text).to eq('WYSIWYG')
|
|
157
|
+
expect(@session.find(:css, '#existing_content_editable_child_parent').text).to eq("Some content\nWYSIWYG")
|
|
158
|
+
end
|
|
159
|
+
end
|
|
160
|
+
end
|
|
161
|
+
|
|
162
|
+
describe '#tag_name' do
|
|
163
|
+
it 'should extract node tag name' do
|
|
164
|
+
expect(@session.all('//a')[0].tag_name).to eq('a')
|
|
165
|
+
expect(@session.all('//a')[1].tag_name).to eq('a')
|
|
166
|
+
expect(@session.all('//p')[1].tag_name).to eq('p')
|
|
167
|
+
end
|
|
168
|
+
end
|
|
169
|
+
|
|
170
|
+
describe '#disabled?' do
|
|
171
|
+
it 'should extract disabled node' do
|
|
172
|
+
@session.visit('/form')
|
|
173
|
+
expect(@session.find('//input[@id="customer_name"]')).to be_disabled
|
|
174
|
+
expect(@session.find('//input[@id="customer_email"]')).not_to be_disabled
|
|
175
|
+
end
|
|
176
|
+
|
|
177
|
+
it 'should see disabled options as disabled' do
|
|
178
|
+
@session.visit('/form')
|
|
179
|
+
expect(@session.find('//select[@id="form_title"]/option[1]')).not_to be_disabled
|
|
180
|
+
expect(@session.find('//select[@id="form_title"]/option[@disabled]')).to be_disabled
|
|
181
|
+
end
|
|
182
|
+
|
|
183
|
+
it 'should see enabled options in disabled select as disabled' do
|
|
184
|
+
@session.visit('/form')
|
|
185
|
+
expect(@session.find('//select[@id="form_disabled_select"]/option')).to be_disabled
|
|
186
|
+
expect(@session.find('//select[@id="form_disabled_select"]/optgroup/option')).to be_disabled
|
|
187
|
+
expect(@session.find('//select[@id="form_title"]/option[1]')).not_to be_disabled
|
|
188
|
+
end
|
|
189
|
+
|
|
190
|
+
it 'should see enabled options in disabled optgroup as disabled' do
|
|
191
|
+
@session.visit('/form')
|
|
192
|
+
expect(@session.find('//option', text: 'A.B.1')).to be_disabled
|
|
193
|
+
expect(@session.find('//option', text: 'A.2')).not_to be_disabled
|
|
194
|
+
end
|
|
195
|
+
|
|
196
|
+
it 'should see a disabled fieldset as disabled' do
|
|
197
|
+
@session.visit('/form')
|
|
198
|
+
expect(@session.find(:xpath, './/fieldset[@id="form_disabled_fieldset"]')).to be_disabled
|
|
199
|
+
end
|
|
200
|
+
|
|
201
|
+
context 'in a disabled fieldset' do
|
|
202
|
+
# https://html.spec.whatwg.org/#the-fieldset-element
|
|
203
|
+
it 'should see elements not in first legend as disabled' do
|
|
204
|
+
@session.visit('/form')
|
|
205
|
+
expect(@session.find('//input[@id="form_disabled_fieldset_child"]')).to be_disabled
|
|
206
|
+
expect(@session.find('//input[@id="form_disabled_fieldset_second_legend_child"]')).to be_disabled
|
|
207
|
+
expect(@session.find('//input[@id="form_enabled_fieldset_child"]')).not_to be_disabled
|
|
208
|
+
end
|
|
209
|
+
|
|
210
|
+
it 'should see elements in first legend as enabled' do
|
|
211
|
+
@session.visit('/form')
|
|
212
|
+
expect(@session.find('//input[@id="form_disabled_fieldset_legend_child"]')).not_to be_disabled
|
|
213
|
+
end
|
|
214
|
+
|
|
215
|
+
it 'should sees options not in first legend as disabled' do
|
|
216
|
+
@session.visit('/form')
|
|
217
|
+
expect(@session.find('//option', text: 'Disabled Child Option')).to be_disabled
|
|
218
|
+
end
|
|
219
|
+
end
|
|
220
|
+
|
|
221
|
+
it 'should be boolean' do
|
|
222
|
+
@session.visit('/form')
|
|
223
|
+
expect(@session.find('//select[@id="form_disabled_select"]/option').disabled?).to be true
|
|
224
|
+
expect(@session.find('//select[@id="form_disabled_select2"]/option').disabled?).to be true
|
|
225
|
+
expect(@session.find('//select[@id="form_title"]/option[1]').disabled?).to be false
|
|
226
|
+
end
|
|
227
|
+
|
|
228
|
+
it 'should be disabled for all elements that are CSS :disabled' do
|
|
229
|
+
@session.visit('/form')
|
|
230
|
+
# sanity check
|
|
231
|
+
expect(@session.all(:css, ':disabled')).to all(be_disabled)
|
|
232
|
+
end
|
|
233
|
+
end
|
|
234
|
+
|
|
235
|
+
describe '#visible?' do
|
|
236
|
+
before { Capybara.ignore_hidden_elements = false }
|
|
237
|
+
|
|
238
|
+
it 'should extract node visibility' do
|
|
239
|
+
expect(@session.first('//a')).to be_visible
|
|
240
|
+
|
|
241
|
+
expect(@session.find('//div[@id="hidden"]')).not_to be_visible
|
|
242
|
+
expect(@session.find('//div[@id="hidden_via_ancestor"]')).not_to be_visible
|
|
243
|
+
expect(@session.find('//div[@id="hidden_attr"]')).not_to be_visible
|
|
244
|
+
expect(@session.find('//a[@id="hidden_attr_via_ancestor"]')).not_to be_visible
|
|
245
|
+
expect(@session.find('//input[@id="hidden_input"]')).not_to be_visible
|
|
246
|
+
end
|
|
247
|
+
|
|
248
|
+
it 'template elements should not be visible' do
|
|
249
|
+
expect(@session.find('//template')).not_to be_visible
|
|
250
|
+
end
|
|
251
|
+
|
|
252
|
+
it 'should be boolean' do
|
|
253
|
+
expect(@session.first('//a').visible?).to be true
|
|
254
|
+
expect(@session.find('//div[@id="hidden"]').visible?).to be false
|
|
255
|
+
end
|
|
256
|
+
|
|
257
|
+
it 'closed details > summary elements and descendants should be visible' do
|
|
258
|
+
expect(@session.find(:css, '#closed_details summary')).to be_visible
|
|
259
|
+
expect(@session.find(:css, '#closed_details summary h6')).to be_visible
|
|
260
|
+
end
|
|
261
|
+
|
|
262
|
+
it 'details non-summary descendants should be non-visible when closed' do
|
|
263
|
+
descendants = @session.all(:css, '#closed_details > *:not(summary), #closed_details > *:not(summary) *', minimum: 2)
|
|
264
|
+
expect(descendants).not_to include(be_visible)
|
|
265
|
+
end
|
|
266
|
+
|
|
267
|
+
it 'deatils descendants should be visible when open' do
|
|
268
|
+
descendants = @session.all(:css, '#open_details *')
|
|
269
|
+
expect(descendants).to all(be_visible)
|
|
270
|
+
end
|
|
271
|
+
|
|
272
|
+
it 'works when details is toggled open and closed' do
|
|
273
|
+
@session.find(:css, '#closed_details > summary').click
|
|
274
|
+
expect(@session).to have_css('#closed_details *', visible: true, count: 5)
|
|
275
|
+
.and(have_no_css('#closed_details *', visible: :hidden))
|
|
276
|
+
|
|
277
|
+
@session.find(:css, '#closed_details > summary').click
|
|
278
|
+
descendants_css = '#closed_details > *:not(summary), #closed_details > *:not(summary) *'
|
|
279
|
+
expect(@session).to have_no_css(descendants_css, visible: true)
|
|
280
|
+
.and(have_css(descendants_css, visible: :hidden, count: 3))
|
|
281
|
+
end
|
|
282
|
+
end
|
|
283
|
+
|
|
284
|
+
describe '#obscured?', requires: [:css] do
|
|
285
|
+
it 'should see non visible elements as obscured' do
|
|
286
|
+
Capybara.ignore_hidden_elements = false
|
|
287
|
+
expect(@session.find('//div[@id="hidden"]')).to be_obscured
|
|
288
|
+
expect(@session.find('//div[@id="hidden_via_ancestor"]')).to be_obscured
|
|
289
|
+
expect(@session.find('//div[@id="hidden_attr"]')).to be_obscured
|
|
290
|
+
expect(@session.find('//a[@id="hidden_attr_via_ancestor"]')).to be_obscured
|
|
291
|
+
expect(@session.find('//input[@id="hidden_input"]')).to be_obscured
|
|
292
|
+
end
|
|
293
|
+
|
|
294
|
+
it 'should see non-overlapped elements as not obscured' do
|
|
295
|
+
@session.visit('/obscured')
|
|
296
|
+
expect(@session.find(:css, '#cover')).not_to be_obscured
|
|
297
|
+
end
|
|
298
|
+
|
|
299
|
+
it 'should see elements only overlapped by descendants as not obscured' do
|
|
300
|
+
expect(@session.first(:css, 'p:not(.para)')).not_to be_obscured
|
|
301
|
+
end
|
|
302
|
+
|
|
303
|
+
it 'should see elements outside the viewport as obscured' do
|
|
304
|
+
@session.visit('/obscured')
|
|
305
|
+
off = @session.find(:css, '#offscreen')
|
|
306
|
+
off_wrapper = @session.find(:css, '#offscreen_wrapper')
|
|
307
|
+
expect(off).to be_obscured
|
|
308
|
+
expect(off_wrapper).to be_obscured
|
|
309
|
+
@session.scroll_to(off_wrapper)
|
|
310
|
+
expect(off_wrapper).not_to be_obscured
|
|
311
|
+
expect(off).to be_obscured
|
|
312
|
+
off_wrapper.scroll_to(off)
|
|
313
|
+
expect(off).not_to be_obscured
|
|
314
|
+
expect(off_wrapper).not_to be_obscured
|
|
315
|
+
end
|
|
316
|
+
|
|
317
|
+
it 'should see overlapped elements as obscured' do
|
|
318
|
+
@session.visit('/obscured')
|
|
319
|
+
expect(@session.find(:css, '#obscured')).to be_obscured
|
|
320
|
+
end
|
|
321
|
+
|
|
322
|
+
it 'should be boolean' do
|
|
323
|
+
Capybara.ignore_hidden_elements = false
|
|
324
|
+
expect(@session.first('//a').obscured?).to be false
|
|
325
|
+
expect(@session.find('//div[@id="hidden"]').obscured?).to be true
|
|
326
|
+
end
|
|
327
|
+
|
|
328
|
+
it 'should work in frames' do
|
|
329
|
+
@session.visit('/obscured')
|
|
330
|
+
frame = @session.find(:css, '#frameOne')
|
|
331
|
+
@session.within_frame(frame) do
|
|
332
|
+
div = @session.find(:css, '#divInFrameOne')
|
|
333
|
+
expect(div).to be_obscured
|
|
334
|
+
@session.scroll_to div
|
|
335
|
+
expect(div).not_to be_obscured
|
|
336
|
+
end
|
|
337
|
+
end
|
|
338
|
+
|
|
339
|
+
it 'should work in nested iframes' do
|
|
340
|
+
@session.visit('/obscured')
|
|
341
|
+
frame = @session.find(:css, '#nestedFrames')
|
|
342
|
+
@session.within_frame(frame) do
|
|
343
|
+
@session.within_frame(:css, '#childFrame') do
|
|
344
|
+
gcframe = @session.find(:css, '#grandchildFrame2')
|
|
345
|
+
@session.within_frame(gcframe) do
|
|
346
|
+
expect(@session.find(:css, '#divInFrameTwo')).to be_obscured
|
|
347
|
+
end
|
|
348
|
+
@session.scroll_to(gcframe)
|
|
349
|
+
@session.within_frame(gcframe) do
|
|
350
|
+
expect(@session.find(:css, '#divInFrameTwo')).not_to be_obscured
|
|
351
|
+
end
|
|
352
|
+
end
|
|
353
|
+
end
|
|
354
|
+
end
|
|
355
|
+
end
|
|
356
|
+
|
|
357
|
+
describe '#checked?' do
|
|
358
|
+
it 'should extract node checked state' do
|
|
359
|
+
@session.visit('/form')
|
|
360
|
+
expect(@session.find('//input[@id="gender_female"]')).to be_checked
|
|
361
|
+
expect(@session.find('//input[@id="gender_male"]')).not_to be_checked
|
|
362
|
+
expect(@session.first('//h1')).not_to be_checked
|
|
363
|
+
end
|
|
364
|
+
|
|
365
|
+
it 'should be boolean' do
|
|
366
|
+
@session.visit('/form')
|
|
367
|
+
expect(@session.find('//input[@id="gender_female"]').checked?).to be true
|
|
368
|
+
expect(@session.find('//input[@id="gender_male"]').checked?).to be false
|
|
369
|
+
expect(@session.find('//input[@id="no_attr_value_checked"]').checked?).to be true
|
|
370
|
+
end
|
|
371
|
+
end
|
|
372
|
+
|
|
373
|
+
describe '#selected?' do
|
|
374
|
+
it 'should extract node selected state' do
|
|
375
|
+
@session.visit('/form')
|
|
376
|
+
expect(@session.find('//option[@value="en"]')).to be_selected
|
|
377
|
+
expect(@session.find('//option[@value="sv"]')).not_to be_selected
|
|
378
|
+
expect(@session.first('//h1')).not_to be_selected
|
|
379
|
+
end
|
|
380
|
+
|
|
381
|
+
it 'should be boolean' do
|
|
382
|
+
@session.visit('/form')
|
|
383
|
+
expect(@session.find('//option[@value="en"]').selected?).to be true
|
|
384
|
+
expect(@session.find('//option[@value="sv"]').selected?).to be false
|
|
385
|
+
expect(@session.first('//h1').selected?).to be false
|
|
386
|
+
end
|
|
387
|
+
end
|
|
388
|
+
|
|
389
|
+
describe '#==' do
|
|
390
|
+
it 'preserve object identity' do
|
|
391
|
+
expect(@session.find('//h1') == @session.find('//h1')).to be true # rubocop:disable Lint/UselessComparison
|
|
392
|
+
expect(@session.find('//h1') === @session.find('//h1')).to be true # rubocop:disable Style/CaseEquality, Lint/UselessComparison
|
|
393
|
+
expect(@session.find('//h1').eql?(@session.find('//h1'))).to be false
|
|
394
|
+
end
|
|
395
|
+
|
|
396
|
+
it 'returns false for unrelated object' do
|
|
397
|
+
expect(@session.find('//h1') == 'Not Capybara::Node::Base').to be false
|
|
398
|
+
end
|
|
399
|
+
end
|
|
400
|
+
|
|
401
|
+
describe '#path' do
|
|
402
|
+
# Testing for specific XPaths here doesn't make sense since there
|
|
403
|
+
# are many that can refer to the same element
|
|
404
|
+
before do
|
|
405
|
+
@session.visit('/path')
|
|
406
|
+
end
|
|
407
|
+
|
|
408
|
+
it 'returns xpath which points to itself' do
|
|
409
|
+
element = @session.find(:link, 'Second Link')
|
|
410
|
+
expect(@session.find(:xpath, element.path)).to eq(element)
|
|
411
|
+
end
|
|
412
|
+
end
|
|
413
|
+
|
|
414
|
+
describe '#trigger', requires: %i[js trigger] do
|
|
415
|
+
it 'should allow triggering of custom JS events' do
|
|
416
|
+
@session.visit('/with_js')
|
|
417
|
+
@session.find(:css, '#with_focus_event').trigger(:focus)
|
|
418
|
+
expect(@session).to have_css('#focus_event_triggered')
|
|
419
|
+
end
|
|
420
|
+
end
|
|
421
|
+
|
|
422
|
+
describe '#drag_to', requires: %i[js drag] do
|
|
423
|
+
it 'should drag and drop an object' do
|
|
424
|
+
@session.visit('/with_js')
|
|
425
|
+
element = @session.find('//div[@id="drag"]')
|
|
426
|
+
target = @session.find('//div[@id="drop"]')
|
|
427
|
+
element.drag_to(target)
|
|
428
|
+
expect(@session).to have_xpath('//div[contains(., "Dropped!")]')
|
|
429
|
+
end
|
|
430
|
+
|
|
431
|
+
it 'should drag and drop if scrolling is needed' do
|
|
432
|
+
@session.visit('/with_js')
|
|
433
|
+
element = @session.find('//div[@id="drag_scroll"]')
|
|
434
|
+
target = @session.find('//div[@id="drop_scroll"]')
|
|
435
|
+
element.drag_to(target)
|
|
436
|
+
expect(@session).to have_xpath('//div[contains(., "Dropped!")]')
|
|
437
|
+
end
|
|
438
|
+
|
|
439
|
+
it 'should drag a link' do
|
|
440
|
+
@session.visit('/with_js')
|
|
441
|
+
link = @session.find_link('drag_link')
|
|
442
|
+
target = @session.find(:id, 'drop')
|
|
443
|
+
link.drag_to target
|
|
444
|
+
expect(@session).to have_xpath('//div[contains(., "Dropped!")]')
|
|
445
|
+
end
|
|
446
|
+
|
|
447
|
+
it 'should work with Dragula' do
|
|
448
|
+
@session.visit('/with_dragula')
|
|
449
|
+
@session.within(:css, '#sortable') do
|
|
450
|
+
src = @session.find('div', text: 'Item 1')
|
|
451
|
+
target = @session.find('div', text: 'Item 3')
|
|
452
|
+
src.drag_to target
|
|
453
|
+
expect(@session).to have_content(/Item 2.*Item 1/, normalize_ws: true)
|
|
454
|
+
end
|
|
455
|
+
end
|
|
456
|
+
|
|
457
|
+
it 'should work with jsTree' do
|
|
458
|
+
@session.visit('/with_jstree')
|
|
459
|
+
@session.within(:css, '#container') do
|
|
460
|
+
@session.assert_text(/A.*B.*C/m)
|
|
461
|
+
source = @session.find(:css, '#j1_1_anchor')
|
|
462
|
+
target = @session.find(:css, '#j1_2_anchor')
|
|
463
|
+
|
|
464
|
+
source.drag_to(target)
|
|
465
|
+
|
|
466
|
+
@session.assert_no_text(/A.*B.*C/m)
|
|
467
|
+
@session.assert_text(/B.*C/m)
|
|
468
|
+
end
|
|
469
|
+
end
|
|
470
|
+
|
|
471
|
+
it 'should simulate a single held down modifier key' do
|
|
472
|
+
%I[
|
|
473
|
+
alt
|
|
474
|
+
ctrl
|
|
475
|
+
meta
|
|
476
|
+
shift
|
|
477
|
+
].each do |modifier_key|
|
|
478
|
+
@session.visit('/with_js')
|
|
479
|
+
|
|
480
|
+
element = @session.find('//div[@id="drag"]')
|
|
481
|
+
target = @session.find('//div[@id="drop"]')
|
|
482
|
+
|
|
483
|
+
element.drag_to(target, drop_modifiers: modifier_key)
|
|
484
|
+
expect(@session).to have_css('div.drag_start', exact_text: 'Dragged!')
|
|
485
|
+
expect(@session).to have_xpath("//div[contains(., 'Dropped!-#{modifier_key}')]")
|
|
486
|
+
end
|
|
487
|
+
end
|
|
488
|
+
|
|
489
|
+
it 'should simulate multiple held down modifier keys' do
|
|
490
|
+
@session.visit('/with_js')
|
|
491
|
+
|
|
492
|
+
element = @session.find('//div[@id="drag"]')
|
|
493
|
+
target = @session.find('//div[@id="drop"]')
|
|
494
|
+
|
|
495
|
+
modifier_keys = %I[alt ctrl meta shift]
|
|
496
|
+
|
|
497
|
+
element.drag_to(target, drop_modifiers: modifier_keys)
|
|
498
|
+
expect(@session).to have_xpath("//div[contains(., 'Dropped!-#{modifier_keys.join('-')}')]")
|
|
499
|
+
end
|
|
500
|
+
|
|
501
|
+
it 'should support key aliases' do
|
|
502
|
+
{ control: :ctrl,
|
|
503
|
+
command: :meta,
|
|
504
|
+
cmd: :meta }.each do |(key_alias, key)|
|
|
505
|
+
@session.visit('/with_js')
|
|
506
|
+
|
|
507
|
+
element = @session.find('//div[@id="drag"]')
|
|
508
|
+
target = @session.find('//div[@id="drop"]')
|
|
509
|
+
|
|
510
|
+
element.drag_to(target, drop_modifiers: [key_alias])
|
|
511
|
+
expect(target).to have_text("Dropped!-#{key}", exact: true)
|
|
512
|
+
end
|
|
513
|
+
end
|
|
514
|
+
|
|
515
|
+
context 'HTML5', requires: %i[js html5_drag] do
|
|
516
|
+
it 'should HTML5 drag and drop an object' do
|
|
517
|
+
@session.visit('/with_js')
|
|
518
|
+
element = @session.find('//div[@id="drag_html5"]')
|
|
519
|
+
target = @session.find('//div[@id="drop_html5"]')
|
|
520
|
+
element.drag_to(target)
|
|
521
|
+
expect(@session).to have_xpath('//div[contains(., "HTML5 Dropped string: text/plain drag_html5")]')
|
|
522
|
+
end
|
|
523
|
+
|
|
524
|
+
it 'should HTML5 drag and drop an object child' do
|
|
525
|
+
@session.visit('/with_js')
|
|
526
|
+
element = @session.find('//div[@id="drag_html5"]/p')
|
|
527
|
+
target = @session.find('//div[@id="drop_html5"]')
|
|
528
|
+
element.drag_to(target)
|
|
529
|
+
expect(@session).to have_xpath('//div[contains(., "HTML5 Dropped string: text/plain drag_html5")]')
|
|
530
|
+
end
|
|
531
|
+
|
|
532
|
+
it 'should set clientX/Y in dragover events' do
|
|
533
|
+
@session.visit('/with_js')
|
|
534
|
+
element = @session.find('//div[@id="drag_html5"]')
|
|
535
|
+
target = @session.find('//div[@id="drop_html5"]')
|
|
536
|
+
element.drag_to(target)
|
|
537
|
+
expect(@session).to have_css('div.log', text: /DragOver with client position: [1-9]\d*,[1-9]\d*/, count: 2)
|
|
538
|
+
end
|
|
539
|
+
|
|
540
|
+
it 'should preserve clientX/Y from last dragover event' do
|
|
541
|
+
@session.visit('/with_js')
|
|
542
|
+
element = @session.find('//div[@id="drag_html5"]')
|
|
543
|
+
target = @session.find('//div[@id="drop_html5"]')
|
|
544
|
+
element.drag_to(target)
|
|
545
|
+
|
|
546
|
+
conditions = %w[DragLeave Drop DragEnd].map do |text|
|
|
547
|
+
have_css('div.log', text: text)
|
|
548
|
+
end
|
|
549
|
+
expect(@session).to(conditions.reduce { |memo, cond| memo.and(cond) })
|
|
550
|
+
|
|
551
|
+
# The first "DragOver" div is inserted by the last dragover event dispatched
|
|
552
|
+
drag_over_div = @session.first('//div[@class="log" and starts-with(text(), "DragOver")]')
|
|
553
|
+
position = drag_over_div.text.sub('DragOver ', '')
|
|
554
|
+
|
|
555
|
+
expect(@session).to have_css('div.log', text: /DragLeave #{position}/, count: 1)
|
|
556
|
+
expect(@session).to have_css('div.log', text: /Drop #{position}/, count: 1)
|
|
557
|
+
expect(@session).to have_css('div.log', text: /DragEnd #{position}/, count: 1)
|
|
558
|
+
end
|
|
559
|
+
|
|
560
|
+
it 'should not HTML5 drag and drop on a non HTML5 drop element' do
|
|
561
|
+
@session.visit('/with_js')
|
|
562
|
+
element = @session.find('//div[@id="drag_html5"]')
|
|
563
|
+
target = @session.find('//div[@id="drop_html5"]')
|
|
564
|
+
target.execute_script("$(this).removeClass('drop');")
|
|
565
|
+
element.drag_to(target)
|
|
566
|
+
sleep 1
|
|
567
|
+
expect(@session).not_to have_xpath('//div[contains(., "HTML5 Dropped")]')
|
|
568
|
+
end
|
|
569
|
+
|
|
570
|
+
it 'should HTML5 drag and drop when scrolling needed' do
|
|
571
|
+
@session.visit('/with_js')
|
|
572
|
+
element = @session.find('//div[@id="drag_html5_scroll"]')
|
|
573
|
+
target = @session.find('//div[@id="drop_html5_scroll"]')
|
|
574
|
+
element.drag_to(target)
|
|
575
|
+
expect(@session).to have_xpath('//div[contains(., "HTML5 Dropped string: text/plain drag_html5_scroll")]')
|
|
576
|
+
end
|
|
577
|
+
|
|
578
|
+
it 'should drag HTML5 default draggable elements' do
|
|
579
|
+
@session.visit('/with_js')
|
|
580
|
+
link = @session.find_link('drag_link_html5')
|
|
581
|
+
target = @session.find(:id, 'drop_html5')
|
|
582
|
+
link.drag_to target
|
|
583
|
+
expect(@session).to have_xpath('//div[contains(., "HTML5 Dropped")]')
|
|
584
|
+
end
|
|
585
|
+
|
|
586
|
+
it 'should work with SortableJS' do
|
|
587
|
+
@session.visit('/with_sortable_js')
|
|
588
|
+
@session.within(:css, '#sortable') do
|
|
589
|
+
src = @session.find('div', text: 'Item 1')
|
|
590
|
+
target = @session.find('div', text: 'Item 3')
|
|
591
|
+
src.drag_to target
|
|
592
|
+
expect(@session).to have_content(/Item 3.*Item 1/, normalize_ws: true)
|
|
593
|
+
end
|
|
594
|
+
end
|
|
595
|
+
|
|
596
|
+
it 'should drag HTML5 default draggable element child' do
|
|
597
|
+
@session.visit('/with_js')
|
|
598
|
+
source = @session.find_link('drag_link_html5').find(:css, 'p')
|
|
599
|
+
target = @session.find(:id, 'drop_html5')
|
|
600
|
+
source.drag_to target
|
|
601
|
+
expect(@session).to have_xpath('//div[contains(., "HTML5 Dropped")]')
|
|
602
|
+
end
|
|
603
|
+
|
|
604
|
+
it 'should simulate a single held down modifier key' do
|
|
605
|
+
%I[alt ctrl meta shift].each do |modifier_key|
|
|
606
|
+
@session.visit('/with_js')
|
|
607
|
+
|
|
608
|
+
element = @session.find('//div[@id="drag_html5"]')
|
|
609
|
+
target = @session.find('//div[@id="drop_html5"]')
|
|
610
|
+
|
|
611
|
+
element.drag_to(target, drop_modifiers: modifier_key)
|
|
612
|
+
|
|
613
|
+
expect(@session).to have_css('div.drag_start', exact_text: 'HTML5 Dragged!')
|
|
614
|
+
expect(@session).to have_xpath("//div[contains(., 'HTML5 Dropped string: text/plain drag_html5-#{modifier_key}')]")
|
|
615
|
+
end
|
|
616
|
+
end
|
|
617
|
+
|
|
618
|
+
it 'should simulate multiple held down modifier keys' do
|
|
619
|
+
@session.visit('/with_js')
|
|
620
|
+
|
|
621
|
+
element = @session.find('//div[@id="drag_html5"]')
|
|
622
|
+
target = @session.find('//div[@id="drop_html5"]')
|
|
623
|
+
|
|
624
|
+
modifier_keys = %I[alt ctrl meta shift]
|
|
625
|
+
|
|
626
|
+
element.drag_to(target, drop_modifiers: modifier_keys)
|
|
627
|
+
expect(@session).to have_xpath("//div[contains(., 'HTML5 Dropped string: text/plain drag_html5-#{modifier_keys.join('-')}')]")
|
|
628
|
+
end
|
|
629
|
+
|
|
630
|
+
it 'should support key aliases' do
|
|
631
|
+
{ control: :ctrl,
|
|
632
|
+
command: :meta,
|
|
633
|
+
cmd: :meta }.each do |(key_alias, key)|
|
|
634
|
+
@session.visit('/with_js')
|
|
635
|
+
|
|
636
|
+
element = @session.find('//div[@id="drag_html5"]')
|
|
637
|
+
target = @session.find('//div[@id="drop_html5"]')
|
|
638
|
+
|
|
639
|
+
element.drag_to(target, drop_modifiers: [key_alias])
|
|
640
|
+
expect(target).to have_text(%r{^HTML5 Dropped string: text/plain drag_html5-#{key}$}m, exact: true)
|
|
641
|
+
end
|
|
642
|
+
end
|
|
643
|
+
end
|
|
644
|
+
end
|
|
645
|
+
|
|
646
|
+
describe 'Element#drop', requires: %i[js html5_drag] do
|
|
647
|
+
it 'can drop a file' do
|
|
648
|
+
@session.visit('/with_js')
|
|
649
|
+
target = @session.find('//div[@id="drop_html5"]')
|
|
650
|
+
target.drop(
|
|
651
|
+
with_os_path_separators(File.expand_path('../fixtures/capybara.jpg', File.dirname(__FILE__)))
|
|
652
|
+
)
|
|
653
|
+
expect(@session).to have_xpath('//div[contains(., "HTML5 Dropped file: capybara.jpg")]')
|
|
654
|
+
end
|
|
655
|
+
|
|
656
|
+
it 'can drop multiple files' do
|
|
657
|
+
@session.visit('/with_js')
|
|
658
|
+
target = @session.find('//div[@id="drop_html5"]')
|
|
659
|
+
target.drop(
|
|
660
|
+
with_os_path_separators(File.expand_path('../fixtures/capybara.jpg', File.dirname(__FILE__))),
|
|
661
|
+
with_os_path_separators(File.expand_path('../fixtures/test_file.txt', File.dirname(__FILE__)))
|
|
662
|
+
)
|
|
663
|
+
expect(@session).to have_xpath('//div[contains(., "HTML5 Dropped file: capybara.jpg")]')
|
|
664
|
+
expect(@session).to have_xpath('//div[contains(., "HTML5 Dropped file: test_file.txt")]')
|
|
665
|
+
end
|
|
666
|
+
|
|
667
|
+
it 'can drop strings' do
|
|
668
|
+
@session.visit('/with_js')
|
|
669
|
+
target = @session.find('//div[@id="drop_html5"]')
|
|
670
|
+
target.drop('text/plain' => 'Some dropped text')
|
|
671
|
+
expect(@session).to have_xpath('//div[contains(., "HTML5 Dropped string: text/plain Some dropped text")]')
|
|
672
|
+
end
|
|
673
|
+
|
|
674
|
+
it 'can drop multiple strings' do
|
|
675
|
+
@session.visit('/with_js')
|
|
676
|
+
target = @session.find('//div[@id="drop_html5"]')
|
|
677
|
+
target.drop('text/plain' => 'Some dropped text', 'text/url' => 'http://www.google.com')
|
|
678
|
+
expect(@session).to have_xpath('//div[contains(., "HTML5 Dropped string: text/plain Some dropped text")]')
|
|
679
|
+
expect(@session).to have_xpath('//div[contains(., "HTML5 Dropped string: text/url http://www.google.com")]')
|
|
680
|
+
end
|
|
681
|
+
end
|
|
682
|
+
|
|
683
|
+
describe '#hover', requires: [:hover] do
|
|
684
|
+
it 'should allow hovering on an element' do
|
|
685
|
+
@session.visit('/with_hover')
|
|
686
|
+
expect(@session.find(:css, '.wrapper:not(.scroll_needed) .hidden_until_hover', visible: false)).not_to be_visible
|
|
687
|
+
@session.find(:css, '.wrapper:not(.scroll_needed)').hover
|
|
688
|
+
expect(@session.find(:css, '.wrapper:not(.scroll_needed) .hidden_until_hover', visible: false)).to be_visible
|
|
689
|
+
end
|
|
690
|
+
|
|
691
|
+
it 'should allow hovering on an element that needs to be scrolled into view' do
|
|
692
|
+
@session.visit('/with_hover')
|
|
693
|
+
expect(@session.find(:css, '.wrapper.scroll_needed .hidden_until_hover', visible: false)).not_to be_visible
|
|
694
|
+
@session.find(:css, '.wrapper.scroll_needed').hover
|
|
695
|
+
expect(@session.find(:css, '.wrapper.scroll_needed .hidden_until_hover', visible: false)).to be_visible
|
|
696
|
+
end
|
|
697
|
+
|
|
698
|
+
it 'should hover again after following a link and back' do
|
|
699
|
+
@session.visit('/with_hover')
|
|
700
|
+
@session.find(:css, '.wrapper:not(.scroll_needed)').hover
|
|
701
|
+
@session.click_link('Other hover page')
|
|
702
|
+
@session.click_link('Go back')
|
|
703
|
+
@session.find(:css, '.wrapper:not(.scroll_needed)').hover
|
|
704
|
+
expect(@session.find(:css, '.wrapper:not(.scroll_needed) .hidden_until_hover', visible: false)).to be_visible
|
|
705
|
+
end
|
|
706
|
+
end
|
|
707
|
+
|
|
708
|
+
describe '#click' do
|
|
709
|
+
it 'should not follow a link if no href' do
|
|
710
|
+
@session.find(:css, '#link_placeholder').click
|
|
711
|
+
expect(@session.current_url).to match(%r{/with_html$})
|
|
712
|
+
end
|
|
713
|
+
|
|
714
|
+
it 'should go to the same page if href is blank' do
|
|
715
|
+
@session.find(:css, '#link_blank_href').click
|
|
716
|
+
sleep 1
|
|
717
|
+
expect(@session).to have_current_path('/with_html')
|
|
718
|
+
end
|
|
719
|
+
|
|
720
|
+
it 'should be able to check a checkbox' do
|
|
721
|
+
@session.visit('form')
|
|
722
|
+
cbox = @session.find(:checkbox, 'form_terms_of_use')
|
|
723
|
+
expect(cbox).not_to be_checked
|
|
724
|
+
cbox.click
|
|
725
|
+
expect(cbox).to be_checked
|
|
726
|
+
end
|
|
727
|
+
|
|
728
|
+
it 'should be able to uncheck a checkbox' do
|
|
729
|
+
@session.visit('/form')
|
|
730
|
+
cbox = @session.find(:checkbox, 'form_pets_dog')
|
|
731
|
+
expect(cbox).to be_checked
|
|
732
|
+
cbox.click
|
|
733
|
+
expect(cbox).not_to be_checked
|
|
734
|
+
end
|
|
735
|
+
|
|
736
|
+
it 'should be able to select a radio button' do
|
|
737
|
+
@session.visit('/form')
|
|
738
|
+
radio = @session.find(:radio_button, 'gender_male')
|
|
739
|
+
expect(radio).not_to be_checked
|
|
740
|
+
radio.click
|
|
741
|
+
expect(radio).to be_checked
|
|
742
|
+
end
|
|
743
|
+
|
|
744
|
+
it 'should allow modifiers', requires: [:js] do
|
|
745
|
+
@session.visit('/with_js')
|
|
746
|
+
@session.find(:css, '#click-test').click(:shift)
|
|
747
|
+
expect(@session).to have_link('Has been shift clicked')
|
|
748
|
+
end
|
|
749
|
+
|
|
750
|
+
it 'should allow multiple modifiers', requires: [:js] do
|
|
751
|
+
@session.visit('with_js')
|
|
752
|
+
@session.find(:css, '#click-test').click(:control, :alt, :meta, :shift)
|
|
753
|
+
# Selenium with Chrome on OSX ctrl-click generates a right click so just verify all keys but not click type
|
|
754
|
+
expect(@session).to have_link('alt control meta shift')
|
|
755
|
+
end
|
|
756
|
+
|
|
757
|
+
it 'should allow to adjust the click offset', requires: [:js] do
|
|
758
|
+
@session.visit('with_js')
|
|
759
|
+
@session.find(:css, '#click-test').click(x: 5, y: 5)
|
|
760
|
+
link = @session.find(:link, 'has-been-clicked')
|
|
761
|
+
locations = link.text.match(/^Has been clicked at (?<x>[\d\.-]+),(?<y>[\d\.-]+)$/)
|
|
762
|
+
# Resulting click location should be very close to 0, 0 relative to top left corner of the element, but may not be exact due to
|
|
763
|
+
# integer/float conversions and rounding.
|
|
764
|
+
expect(locations[:x].to_f).to be_within(1).of(5)
|
|
765
|
+
expect(locations[:y].to_f).to be_within(1).of(5)
|
|
766
|
+
end
|
|
767
|
+
|
|
768
|
+
it 'should raise error if both x and y values are not passed' do
|
|
769
|
+
@session.visit('with_js')
|
|
770
|
+
el = @session.find(:css, '#click-test')
|
|
771
|
+
expect { el.click(x: 5) }.to raise_error ArgumentError
|
|
772
|
+
expect { el.click(x: nil, y: 3) }.to raise_error ArgumentError
|
|
773
|
+
end
|
|
774
|
+
|
|
775
|
+
it 'should be able to click a table row', requires: [:js] do
|
|
776
|
+
@session.visit('/tables')
|
|
777
|
+
tr = @session.find(:css, '#agent_table tr:first-child').click
|
|
778
|
+
expect(tr).to have_css('label', text: 'Clicked')
|
|
779
|
+
end
|
|
780
|
+
|
|
781
|
+
it 'should retry clicking', requires: [:js] do
|
|
782
|
+
@session.visit('/animated')
|
|
783
|
+
obscured = @session.find(:css, '#obscured')
|
|
784
|
+
@session.execute_script <<~JS
|
|
785
|
+
setTimeout(function(){ $('#cover').hide(); }, 700)
|
|
786
|
+
JS
|
|
787
|
+
expect { obscured.click }.not_to raise_error
|
|
788
|
+
end
|
|
789
|
+
|
|
790
|
+
it 'should allow to retry longer', requires: [:js] do
|
|
791
|
+
@session.visit('/animated')
|
|
792
|
+
obscured = @session.find(:css, '#obscured')
|
|
793
|
+
@session.execute_script <<~JS
|
|
794
|
+
setTimeout(function(){ $('#cover').hide(); }, 3000)
|
|
795
|
+
JS
|
|
796
|
+
expect { obscured.click(wait: 4) }.not_to raise_error
|
|
797
|
+
end
|
|
798
|
+
|
|
799
|
+
it 'should not retry clicking when wait is disabled', requires: [:js] do
|
|
800
|
+
@session.visit('/animated')
|
|
801
|
+
obscured = @session.find(:css, '#obscured')
|
|
802
|
+
@session.execute_script <<~JS
|
|
803
|
+
setTimeout(function(){ $('#cover').hide(); }, 2000)
|
|
804
|
+
JS
|
|
805
|
+
expect { obscured.click(wait: 0) }.to(raise_error { |e| expect(e).to be_an_invalid_element_error(@session) })
|
|
806
|
+
end
|
|
807
|
+
|
|
808
|
+
context 'offset', requires: [:js] do
|
|
809
|
+
before do
|
|
810
|
+
@session.visit('/offset')
|
|
811
|
+
end
|
|
812
|
+
|
|
813
|
+
let :clicker do
|
|
814
|
+
@session.find(:id, 'clicker')
|
|
815
|
+
end
|
|
816
|
+
|
|
817
|
+
context 'when w3c_click_offset is false' do
|
|
818
|
+
before do
|
|
819
|
+
Capybara.w3c_click_offset = false
|
|
820
|
+
end
|
|
821
|
+
|
|
822
|
+
it 'should offset from top left of element' do
|
|
823
|
+
clicker.click(x: 10, y: 5)
|
|
824
|
+
expect(@session).to have_text(/clicked at 110,105/)
|
|
825
|
+
end
|
|
826
|
+
|
|
827
|
+
it 'should offset outside the element' do
|
|
828
|
+
clicker.click(x: -15, y: -10)
|
|
829
|
+
expect(@session).to have_text(/clicked at 85,90/)
|
|
830
|
+
end
|
|
831
|
+
|
|
832
|
+
it 'should default to click the middle' do
|
|
833
|
+
clicker.click
|
|
834
|
+
expect(@session).to have_text(/clicked at 150,150/)
|
|
835
|
+
end
|
|
836
|
+
end
|
|
837
|
+
|
|
838
|
+
context 'when w3c_click_offset is true' do
|
|
839
|
+
before do
|
|
840
|
+
Capybara.w3c_click_offset = true
|
|
841
|
+
end
|
|
842
|
+
|
|
843
|
+
it 'should offset from center of element' do
|
|
844
|
+
clicker.click(x: 10, y: 5)
|
|
845
|
+
expect(@session).to have_text(/clicked at 160,155/)
|
|
846
|
+
end
|
|
847
|
+
|
|
848
|
+
it 'should offset outside from center of element' do
|
|
849
|
+
clicker.click(x: -65, y: -60)
|
|
850
|
+
expect(@session).to have_text(/clicked at 85,90/)
|
|
851
|
+
end
|
|
852
|
+
|
|
853
|
+
it 'should default to click the middle' do
|
|
854
|
+
clicker.click
|
|
855
|
+
expect(@session).to have_text(/clicked at 150,150/)
|
|
856
|
+
end
|
|
857
|
+
end
|
|
858
|
+
end
|
|
859
|
+
|
|
860
|
+
context 'delay', requires: [:js] do
|
|
861
|
+
it 'should delay the mouse up' do
|
|
862
|
+
@session.visit('with_js')
|
|
863
|
+
@session.find(:css, '#click-test').click(delay: 2)
|
|
864
|
+
delay = @session.evaluate_script('window.click_delay')
|
|
865
|
+
expect(delay).to be >= 2
|
|
866
|
+
end
|
|
867
|
+
end
|
|
868
|
+
end
|
|
869
|
+
|
|
870
|
+
describe '#double_click', requires: [:js] do
|
|
871
|
+
it 'should double click an element' do
|
|
872
|
+
@session.visit('/with_js')
|
|
873
|
+
@session.find(:css, '#click-test').double_click
|
|
874
|
+
expect(@session.find(:css, '#has-been-double-clicked')).to be_truthy
|
|
875
|
+
end
|
|
876
|
+
|
|
877
|
+
it 'should allow modifiers', requires: [:js] do
|
|
878
|
+
@session.visit('/with_js')
|
|
879
|
+
@session.find(:css, '#click-test').double_click(:alt)
|
|
880
|
+
expect(@session).to have_link('Has been alt double clicked')
|
|
881
|
+
end
|
|
882
|
+
|
|
883
|
+
it 'should allow to adjust the offset', requires: [:js] do
|
|
884
|
+
@session.visit('with_js')
|
|
885
|
+
@session.find(:css, '#click-test').double_click(x: 10, y: 5)
|
|
886
|
+
link = @session.find(:link, 'has-been-double-clicked')
|
|
887
|
+
locations = link.text.match(/^Has been double clicked at (?<x>[\d\.-]+),(?<y>[\d\.-]+)$/)
|
|
888
|
+
# Resulting click location should be very close to 10, 5 relative to top left corner of the element, but may not be exact due
|
|
889
|
+
# to integer/float conversions and rounding.
|
|
890
|
+
expect(locations[:x].to_f).to be_within(1).of(10)
|
|
891
|
+
expect(locations[:y].to_f).to be_within(1).of(5)
|
|
892
|
+
end
|
|
893
|
+
|
|
894
|
+
it 'should retry clicking', requires: [:js] do
|
|
895
|
+
@session.visit('/animated')
|
|
896
|
+
obscured = @session.find(:css, '#obscured')
|
|
897
|
+
@session.execute_script <<~JS
|
|
898
|
+
setTimeout(function(){ $('#cover').hide(); }, 700)
|
|
899
|
+
JS
|
|
900
|
+
expect { obscured.double_click }.not_to raise_error
|
|
901
|
+
end
|
|
902
|
+
|
|
903
|
+
context 'offset', requires: [:js] do
|
|
904
|
+
before do
|
|
905
|
+
@session.visit('/offset')
|
|
906
|
+
end
|
|
907
|
+
|
|
908
|
+
let :clicker do
|
|
909
|
+
@session.find(:id, 'clicker')
|
|
910
|
+
end
|
|
911
|
+
|
|
912
|
+
context 'when w3c_click_offset is false' do
|
|
913
|
+
before do
|
|
914
|
+
Capybara.w3c_click_offset = false
|
|
915
|
+
end
|
|
916
|
+
|
|
917
|
+
it 'should offset from top left of element' do
|
|
918
|
+
clicker.double_click(x: 10, y: 5)
|
|
919
|
+
expect(@session).to have_text(/clicked at 110,105/)
|
|
920
|
+
end
|
|
921
|
+
|
|
922
|
+
it 'should offset outside the element' do
|
|
923
|
+
clicker.double_click(x: -15, y: -10)
|
|
924
|
+
expect(@session).to have_text(/clicked at 85,90/)
|
|
925
|
+
end
|
|
926
|
+
|
|
927
|
+
it 'should default to click the middle' do
|
|
928
|
+
clicker.double_click
|
|
929
|
+
expect(@session).to have_text(/clicked at 150,150/)
|
|
930
|
+
end
|
|
931
|
+
end
|
|
932
|
+
|
|
933
|
+
context 'when w3c_click_offset is true' do
|
|
934
|
+
before do
|
|
935
|
+
Capybara.w3c_click_offset = true
|
|
936
|
+
end
|
|
937
|
+
|
|
938
|
+
it 'should offset from center of element' do
|
|
939
|
+
clicker.double_click(x: 10, y: 5)
|
|
940
|
+
expect(@session).to have_text(/clicked at 160,155/)
|
|
941
|
+
end
|
|
942
|
+
|
|
943
|
+
it 'should offset outside from center of element' do
|
|
944
|
+
clicker.double_click(x: -65, y: -60)
|
|
945
|
+
expect(@session).to have_text(/clicked at 85,90/)
|
|
946
|
+
end
|
|
947
|
+
|
|
948
|
+
it 'should default to click the middle' do
|
|
949
|
+
clicker.double_click
|
|
950
|
+
expect(@session).to have_text(/clicked at 150,150/)
|
|
951
|
+
end
|
|
952
|
+
end
|
|
953
|
+
end
|
|
954
|
+
end
|
|
955
|
+
|
|
956
|
+
describe '#right_click', requires: [:js] do
|
|
957
|
+
it 'should right click an element' do
|
|
958
|
+
@session.visit('/with_js')
|
|
959
|
+
@session.find(:css, '#click-test').right_click
|
|
960
|
+
expect(@session.find(:css, '#has-been-right-clicked')).to be_truthy
|
|
961
|
+
end
|
|
962
|
+
|
|
963
|
+
it 'should allow modifiers', requires: [:js] do
|
|
964
|
+
@session.visit('/with_js')
|
|
965
|
+
@session.find(:css, '#click-test').right_click(:meta)
|
|
966
|
+
expect(@session).to have_link('Has been meta right clicked')
|
|
967
|
+
end
|
|
968
|
+
|
|
969
|
+
it 'should allow to adjust the offset', requires: [:js] do
|
|
970
|
+
@session.visit('with_js')
|
|
971
|
+
@session.find(:css, '#click-test').right_click(x: 10, y: 10)
|
|
972
|
+
link = @session.find(:link, 'has-been-right-clicked')
|
|
973
|
+
locations = link.text.match(/^Has been right clicked at (?<x>[\d\.-]+),(?<y>[\d\.-]+)$/)
|
|
974
|
+
# Resulting click location should be very close to 10, 10 relative to top left corner of the element, but may not be exact due
|
|
975
|
+
# to integer/float conversions and rounding
|
|
976
|
+
expect(locations[:x].to_f).to be_within(1).of(10)
|
|
977
|
+
expect(locations[:y].to_f).to be_within(1).of(10)
|
|
978
|
+
end
|
|
979
|
+
|
|
980
|
+
it 'should retry clicking', requires: [:js] do
|
|
981
|
+
@session.visit('/animated')
|
|
982
|
+
obscured = @session.find(:css, '#obscured')
|
|
983
|
+
@session.execute_script <<~JS
|
|
984
|
+
setTimeout(function(){ $('#cover').hide(); }, 700)
|
|
985
|
+
JS
|
|
986
|
+
expect { obscured.right_click }.not_to raise_error
|
|
987
|
+
end
|
|
988
|
+
|
|
989
|
+
context 'offset', requires: [:js] do
|
|
990
|
+
before do
|
|
991
|
+
@session.visit('/offset')
|
|
992
|
+
end
|
|
993
|
+
|
|
994
|
+
let :clicker do
|
|
995
|
+
@session.find(:id, 'clicker')
|
|
996
|
+
end
|
|
997
|
+
|
|
998
|
+
context 'when w3c_click_offset is false' do
|
|
999
|
+
before do
|
|
1000
|
+
Capybara.w3c_click_offset = false
|
|
1001
|
+
end
|
|
1002
|
+
|
|
1003
|
+
it 'should offset from top left of element' do
|
|
1004
|
+
clicker.right_click(x: 10, y: 5)
|
|
1005
|
+
expect(@session).to have_text(/clicked at 110,105/)
|
|
1006
|
+
end
|
|
1007
|
+
|
|
1008
|
+
it 'should offset outside the element' do
|
|
1009
|
+
clicker.right_click(x: -15, y: -10)
|
|
1010
|
+
expect(@session).to have_text(/clicked at 85,90/)
|
|
1011
|
+
end
|
|
1012
|
+
|
|
1013
|
+
it 'should default to click the middle' do
|
|
1014
|
+
clicker.right_click
|
|
1015
|
+
expect(@session).to have_text(/clicked at 150,150/)
|
|
1016
|
+
end
|
|
1017
|
+
end
|
|
1018
|
+
|
|
1019
|
+
context 'when w3c_click_offset is true' do
|
|
1020
|
+
before do
|
|
1021
|
+
Capybara.w3c_click_offset = true
|
|
1022
|
+
end
|
|
1023
|
+
|
|
1024
|
+
it 'should offset from center of element' do
|
|
1025
|
+
clicker.right_click(x: 10, y: 5)
|
|
1026
|
+
expect(@session).to have_text(/clicked at 160,155/)
|
|
1027
|
+
end
|
|
1028
|
+
|
|
1029
|
+
it 'should offset outside from center of element' do
|
|
1030
|
+
clicker.right_click(x: -65, y: -60)
|
|
1031
|
+
expect(@session).to have_text(/clicked at 85,90/)
|
|
1032
|
+
end
|
|
1033
|
+
|
|
1034
|
+
it 'should default to click the middle' do
|
|
1035
|
+
clicker.right_click
|
|
1036
|
+
expect(@session).to have_text(/clicked at 150,150/)
|
|
1037
|
+
end
|
|
1038
|
+
end
|
|
1039
|
+
end
|
|
1040
|
+
|
|
1041
|
+
context 'delay', requires: [:js] do
|
|
1042
|
+
it 'should delay the mouse up' do
|
|
1043
|
+
@session.visit('with_js')
|
|
1044
|
+
@session.find(:css, '#click-test').right_click(delay: 2)
|
|
1045
|
+
delay = @session.evaluate_script('window.right_click_delay')
|
|
1046
|
+
expect(delay).to be >= 2
|
|
1047
|
+
end
|
|
1048
|
+
end
|
|
1049
|
+
end
|
|
1050
|
+
|
|
1051
|
+
describe '#send_keys', requires: [:send_keys] do
|
|
1052
|
+
it 'should send a string of keys to an element' do
|
|
1053
|
+
@session.visit('/form')
|
|
1054
|
+
@session.find(:css, '#address1_city').send_keys('Oceanside')
|
|
1055
|
+
expect(@session.find(:css, '#address1_city').value).to eq 'Oceanside'
|
|
1056
|
+
end
|
|
1057
|
+
|
|
1058
|
+
it 'should send special characters' do
|
|
1059
|
+
@session.visit('/form')
|
|
1060
|
+
@session.find(:css, '#address1_city').send_keys('Ocean', :space, 'sie', :left, 'd')
|
|
1061
|
+
expect(@session.find(:css, '#address1_city').value).to eq 'Ocean side'
|
|
1062
|
+
end
|
|
1063
|
+
|
|
1064
|
+
it 'should allow for multiple simultaneous keys' do
|
|
1065
|
+
@session.visit('/form')
|
|
1066
|
+
@session.find(:css, '#address1_city').send_keys([:shift, 'o'], 'ceanside')
|
|
1067
|
+
expect(@session.find(:css, '#address1_city').value).to eq 'Oceanside'
|
|
1068
|
+
end
|
|
1069
|
+
|
|
1070
|
+
it 'should hold modifiers at top level' do
|
|
1071
|
+
@session.visit('/form')
|
|
1072
|
+
@session.find(:css, '#address1_city').send_keys('ocean', :shift, 'side')
|
|
1073
|
+
expect(@session.find(:css, '#address1_city').value).to eq 'oceanSIDE'
|
|
1074
|
+
end
|
|
1075
|
+
|
|
1076
|
+
it 'should generate key events', requires: %i[send_keys js] do
|
|
1077
|
+
@session.visit('/with_js')
|
|
1078
|
+
@session.find(:css, '#with-key-events').send_keys([:shift, 't'], [:shift, 'w'])
|
|
1079
|
+
expect(@session.find(:css, '#key-events-output')).to have_text('keydown:16 keydown:84 keydown:16 keydown:87')
|
|
1080
|
+
end
|
|
1081
|
+
end
|
|
1082
|
+
|
|
1083
|
+
describe '#execute_script', requires: %i[js es_args] do
|
|
1084
|
+
it 'should execute the given script in the context of the element and return nothing' do
|
|
1085
|
+
@session.visit('/with_js')
|
|
1086
|
+
expect(@session.find(:css, '#change').execute_script("this.textContent = 'Funky Doodle'")).to be_nil
|
|
1087
|
+
expect(@session).to have_css('#change', text: 'Funky Doodle')
|
|
1088
|
+
end
|
|
1089
|
+
|
|
1090
|
+
it 'should pass arguments to the script' do
|
|
1091
|
+
@session.visit('/with_js')
|
|
1092
|
+
@session.find(:css, '#change').execute_script('this.textContent = arguments[0]', 'Doodle Funk')
|
|
1093
|
+
expect(@session).to have_css('#change', text: 'Doodle Funk')
|
|
1094
|
+
end
|
|
1095
|
+
end
|
|
1096
|
+
|
|
1097
|
+
describe '#evaluate_script', requires: %i[js es_args] do
|
|
1098
|
+
it 'should evaluate the given script in the context of the element and return whatever it produces' do
|
|
1099
|
+
@session.visit('/with_js')
|
|
1100
|
+
el = @session.find(:css, '#with_change_event')
|
|
1101
|
+
expect(el.evaluate_script('this.value')).to eq('default value')
|
|
1102
|
+
end
|
|
1103
|
+
|
|
1104
|
+
it 'should ignore leading whitespace' do
|
|
1105
|
+
@session.visit('/with_js')
|
|
1106
|
+
expect(@session.find(:css, '#change').evaluate_script('
|
|
1107
|
+
2 + 3
|
|
1108
|
+
')).to eq 5
|
|
1109
|
+
end
|
|
1110
|
+
|
|
1111
|
+
it 'should pass arguments to the script' do
|
|
1112
|
+
@session.visit('/with_js')
|
|
1113
|
+
@session.find(:css, '#change').evaluate_script('this.textContent = arguments[0]', 'Doodle Funk')
|
|
1114
|
+
expect(@session).to have_css('#change', text: 'Doodle Funk')
|
|
1115
|
+
end
|
|
1116
|
+
|
|
1117
|
+
it 'should pass multiple arguments' do
|
|
1118
|
+
@session.visit('/with_js')
|
|
1119
|
+
change = @session.find(:css, '#change')
|
|
1120
|
+
expect(change.evaluate_script('arguments[0] + arguments[1]', 2, 3)).to eq 5
|
|
1121
|
+
end
|
|
1122
|
+
|
|
1123
|
+
it 'should support returning elements' do
|
|
1124
|
+
@session.visit('/with_js')
|
|
1125
|
+
change = @session.find(:css, '#change') # ensure page has loaded and element is available
|
|
1126
|
+
el = change.evaluate_script('this')
|
|
1127
|
+
expect(el).to be_instance_of(Capybara::Node::Element)
|
|
1128
|
+
expect(el).to eq(change)
|
|
1129
|
+
end
|
|
1130
|
+
|
|
1131
|
+
it 'should support multiple statements via IIFE' do
|
|
1132
|
+
@session.visit('/with_js')
|
|
1133
|
+
change = @session.find(:css, '#change') # ensure page has loaded and element is available
|
|
1134
|
+
res = change.evaluate_script(<<~JS, 3)
|
|
1135
|
+
(function(n){
|
|
1136
|
+
var el = this;
|
|
1137
|
+
return [el, n];
|
|
1138
|
+
}).apply(this, arguments)
|
|
1139
|
+
JS
|
|
1140
|
+
expect(res).to eq [change, 3]
|
|
1141
|
+
end
|
|
1142
|
+
end
|
|
1143
|
+
|
|
1144
|
+
describe '#evaluate_async_script', requires: %i[js es_args] do
|
|
1145
|
+
it 'should evaluate the given script in the context of the element' do
|
|
1146
|
+
@session.visit('/with_js')
|
|
1147
|
+
el = @session.find(:css, '#with_change_event')
|
|
1148
|
+
expect(el.evaluate_async_script('arguments[0](this.value)')).to eq('default value')
|
|
1149
|
+
end
|
|
1150
|
+
|
|
1151
|
+
it 'should support returning elements after asynchronous operation' do
|
|
1152
|
+
@session.visit('/with_js')
|
|
1153
|
+
change = @session.find(:css, '#change') # ensure page has loaded and element is available
|
|
1154
|
+
el = change.evaluate_async_script('var cb = arguments[0]; setTimeout(function(el){ cb(el) }, 100, this)')
|
|
1155
|
+
expect(el).to eq(change)
|
|
1156
|
+
end
|
|
1157
|
+
end
|
|
1158
|
+
|
|
1159
|
+
describe '#reload', requires: [:js] do
|
|
1160
|
+
it 'should reload elements found via ancestor with CSS' do
|
|
1161
|
+
@session.visit('/with_js')
|
|
1162
|
+
node = @session.find(:css, '#reload-me em').ancestor(:css, 'div')
|
|
1163
|
+
node.reload
|
|
1164
|
+
expect(node[:id]).to eq 'reload-me'
|
|
1165
|
+
end
|
|
1166
|
+
|
|
1167
|
+
it 'should reload elements found via ancestor with XPath' do
|
|
1168
|
+
@session.visit('/with_js')
|
|
1169
|
+
node = @session.find(:css, '#reload-me em').ancestor(:xpath, './/div')
|
|
1170
|
+
node.reload
|
|
1171
|
+
expect(node[:id]).to eq 'reload-me'
|
|
1172
|
+
end
|
|
1173
|
+
|
|
1174
|
+
it 'should reload elements found via sibling' do
|
|
1175
|
+
@session.visit('/with_js')
|
|
1176
|
+
node = @session.find(:css, '#the-list li', text: 'Item 1').sibling(:css, 'li')
|
|
1177
|
+
expect(node.text).to eq 'Item 2'
|
|
1178
|
+
node.reload
|
|
1179
|
+
expect(node.text).to eq 'Item 2'
|
|
1180
|
+
end
|
|
1181
|
+
|
|
1182
|
+
context 'without automatic reload' do
|
|
1183
|
+
before { Capybara.automatic_reload = false }
|
|
1184
|
+
|
|
1185
|
+
after { Capybara.automatic_reload = true }
|
|
1186
|
+
|
|
1187
|
+
it 'should reload the current context of the node' do
|
|
1188
|
+
@session.visit('/with_js')
|
|
1189
|
+
node = @session.find(:css, '#reload-me')
|
|
1190
|
+
@session.click_link('Reload!')
|
|
1191
|
+
sleep(0.3)
|
|
1192
|
+
expect(node.reload.text).to eq('has been reloaded')
|
|
1193
|
+
expect(node.text).to eq('has been reloaded')
|
|
1194
|
+
end
|
|
1195
|
+
|
|
1196
|
+
it 'should reload a parent node' do
|
|
1197
|
+
@session.visit('/with_js')
|
|
1198
|
+
node = @session.find(:css, '#reload-me').find(:css, 'em')
|
|
1199
|
+
@session.click_link('Reload!')
|
|
1200
|
+
sleep(0.3)
|
|
1201
|
+
expect(node.reload.text).to eq('has been reloaded')
|
|
1202
|
+
expect(node.text).to eq('has been reloaded')
|
|
1203
|
+
end
|
|
1204
|
+
|
|
1205
|
+
it 'should not automatically reload' do
|
|
1206
|
+
@session.visit('/with_js')
|
|
1207
|
+
node = @session.find(:css, '#reload-me')
|
|
1208
|
+
@session.click_link('Reload!')
|
|
1209
|
+
sleep(0.3)
|
|
1210
|
+
expect do
|
|
1211
|
+
expect(node).to have_text('has been reloaded')
|
|
1212
|
+
end.to(raise_error do |error|
|
|
1213
|
+
expect(error).to be_an_invalid_element_error(@session)
|
|
1214
|
+
end)
|
|
1215
|
+
end
|
|
1216
|
+
end
|
|
1217
|
+
|
|
1218
|
+
context 'with automatic reload' do
|
|
1219
|
+
before do
|
|
1220
|
+
Capybara.default_max_wait_time = 4
|
|
1221
|
+
end
|
|
1222
|
+
|
|
1223
|
+
it 'should reload the current context of the node automatically' do
|
|
1224
|
+
@session.visit('/with_js')
|
|
1225
|
+
node = @session.find(:css, '#reload-me')
|
|
1226
|
+
@session.click_link('Reload!')
|
|
1227
|
+
sleep(1)
|
|
1228
|
+
expect(node.text).to eq('has been reloaded')
|
|
1229
|
+
end
|
|
1230
|
+
|
|
1231
|
+
it 'should reload a parent node automatically' do
|
|
1232
|
+
@session.visit('/with_js')
|
|
1233
|
+
node = @session.find(:css, '#reload-me').find(:css, 'em')
|
|
1234
|
+
@session.click_link('Reload!')
|
|
1235
|
+
sleep(1)
|
|
1236
|
+
expect(node.text).to eq('has been reloaded')
|
|
1237
|
+
end
|
|
1238
|
+
|
|
1239
|
+
it 'should reload a node automatically when using find' do
|
|
1240
|
+
@session.visit('/with_js')
|
|
1241
|
+
node = @session.find(:css, '#reload-me')
|
|
1242
|
+
@session.click_link('Reload!')
|
|
1243
|
+
sleep(1)
|
|
1244
|
+
expect(node.find(:css, 'a').text).to eq('has been reloaded')
|
|
1245
|
+
end
|
|
1246
|
+
|
|
1247
|
+
it "should not reload nodes which haven't been found with reevaluateable queries" do
|
|
1248
|
+
@session.visit('/with_js')
|
|
1249
|
+
node = @session.all(:css, '#the-list li')[1]
|
|
1250
|
+
@session.click_link('Fetch new list!')
|
|
1251
|
+
sleep(1)
|
|
1252
|
+
|
|
1253
|
+
expect do
|
|
1254
|
+
expect(node).to have_text('Foo')
|
|
1255
|
+
end.to(raise_error { |error| expect(error).to be_an_invalid_element_error(@session) })
|
|
1256
|
+
expect do
|
|
1257
|
+
expect(node).to have_text('Bar')
|
|
1258
|
+
end.to(raise_error { |error| expect(error).to be_an_invalid_element_error(@session) })
|
|
1259
|
+
end
|
|
1260
|
+
|
|
1261
|
+
it 'should reload nodes with options' do
|
|
1262
|
+
@session.visit('/with_js')
|
|
1263
|
+
node = @session.find(:css, 'em', text: 'reloaded')
|
|
1264
|
+
@session.click_link('Reload!')
|
|
1265
|
+
sleep(1)
|
|
1266
|
+
expect(node.text).to eq('has been reloaded')
|
|
1267
|
+
end
|
|
1268
|
+
end
|
|
1269
|
+
end
|
|
1270
|
+
|
|
1271
|
+
context 'when #synchronize raises server errors' do
|
|
1272
|
+
it 'sets an explanatory exception as the cause of server exceptions', requires: %i[server js] do
|
|
1273
|
+
quietly { @session.visit('/error') }
|
|
1274
|
+
expect do
|
|
1275
|
+
@session.find(:css, 'span')
|
|
1276
|
+
end.to(raise_error(TestApp::TestAppError) do |e|
|
|
1277
|
+
expect(e.cause).to be_a Capybara::CapybaraError
|
|
1278
|
+
expect(e.cause.message).to match(/Your application server raised an error/)
|
|
1279
|
+
end)
|
|
1280
|
+
end
|
|
1281
|
+
|
|
1282
|
+
it 'sets an explanatory exception as the cause of server exceptions with errors with initializers', requires: %i[server js] do
|
|
1283
|
+
quietly { @session.visit('/other_error') }
|
|
1284
|
+
expect do
|
|
1285
|
+
@session.find(:css, 'span')
|
|
1286
|
+
end.to(raise_error(TestApp::TestAppOtherError) do |e|
|
|
1287
|
+
expect(e.cause).to be_a Capybara::CapybaraError
|
|
1288
|
+
expect(e.cause.message).to match(/Your application server raised an error/)
|
|
1289
|
+
end)
|
|
1290
|
+
end
|
|
1291
|
+
end
|
|
1292
|
+
end
|