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