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