capybara 3.13.2 → 3.40.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.yardopts +1 -0
- data/History.md +587 -16
- data/README.md +240 -90
- data/lib/capybara/config.rb +24 -11
- data/lib/capybara/cucumber.rb +1 -1
- data/lib/capybara/driver/base.rb +8 -0
- data/lib/capybara/driver/node.rb +20 -4
- data/lib/capybara/dsl.rb +5 -3
- data/lib/capybara/helpers.rb +25 -4
- data/lib/capybara/minitest/spec.rb +174 -90
- data/lib/capybara/minitest.rb +256 -142
- data/lib/capybara/node/actions.rb +123 -77
- data/lib/capybara/node/base.rb +20 -12
- data/lib/capybara/node/document.rb +2 -2
- data/lib/capybara/node/document_matchers.rb +3 -3
- data/lib/capybara/node/element.rb +223 -117
- data/lib/capybara/node/finders.rb +81 -71
- data/lib/capybara/node/matchers.rb +271 -134
- data/lib/capybara/node/simple.rb +18 -5
- data/lib/capybara/node/whitespace_normalizer.rb +81 -0
- data/lib/capybara/queries/active_element_query.rb +18 -0
- data/lib/capybara/queries/ancestor_query.rb +8 -9
- data/lib/capybara/queries/base_query.rb +3 -2
- data/lib/capybara/queries/current_path_query.rb +15 -5
- data/lib/capybara/queries/selector_query.rb +364 -54
- data/lib/capybara/queries/sibling_query.rb +8 -6
- data/lib/capybara/queries/style_query.rb +2 -2
- data/lib/capybara/queries/text_query.rb +13 -1
- data/lib/capybara/queries/title_query.rb +1 -1
- data/lib/capybara/rack_test/browser.rb +76 -11
- data/lib/capybara/rack_test/driver.rb +10 -5
- data/lib/capybara/rack_test/errors.rb +6 -0
- data/lib/capybara/rack_test/form.rb +31 -9
- data/lib/capybara/rack_test/node.rb +74 -23
- 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 +44 -20
- data/lib/capybara/rspec/matcher_proxies.rb +13 -11
- data/lib/capybara/rspec/matchers/base.rb +31 -16
- data/lib/capybara/rspec/matchers/compound.rb +1 -1
- 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 +2 -2
- data/lib/capybara/rspec/matchers/have_selector.rb +21 -21
- data/lib/capybara/rspec/matchers/have_sibling.rb +27 -0
- data/lib/capybara/rspec/matchers/have_text.rb +4 -4
- data/lib/capybara/rspec/matchers/have_title.rb +2 -2
- data/lib/capybara/rspec/matchers/match_selector.rb +3 -3
- data/lib/capybara/rspec/matchers/match_style.rb +7 -2
- data/lib/capybara/rspec/matchers/spatial_sugar.rb +39 -0
- data/lib/capybara/rspec/matchers.rb +111 -68
- data/lib/capybara/rspec.rb +2 -0
- data/lib/capybara/selector/builders/css_builder.rb +11 -7
- data/lib/capybara/selector/builders/xpath_builder.rb +5 -3
- data/lib/capybara/selector/css.rb +11 -9
- 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_set.rb +19 -18
- data/lib/capybara/selector/filters/base.rb +11 -2
- data/lib/capybara/selector/filters/locator_filter.rb +13 -3
- data/lib/capybara/selector/regexp_disassembler.rb +11 -7
- data/lib/capybara/selector/selector.rb +50 -440
- data/lib/capybara/selector/xpath_extensions.rb +17 -0
- data/lib/capybara/selector.rb +473 -482
- 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 +174 -62
- data/lib/capybara/selenium/driver_specializations/chrome_driver.rb +74 -18
- data/lib/capybara/selenium/driver_specializations/edge_driver.rb +128 -0
- data/lib/capybara/selenium/driver_specializations/firefox_driver.rb +37 -3
- data/lib/capybara/selenium/driver_specializations/internet_explorer_driver.rb +14 -1
- 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 +68 -45
- data/lib/capybara/selenium/extensions/html5_drag.rb +192 -22
- data/lib/capybara/selenium/extensions/modifier_keys_stack.rb +28 -0
- data/lib/capybara/selenium/extensions/scroll.rb +8 -10
- data/lib/capybara/selenium/node.rb +268 -72
- data/lib/capybara/selenium/nodes/chrome_node.rb +105 -9
- data/lib/capybara/selenium/nodes/edge_node.rb +110 -0
- data/lib/capybara/selenium/nodes/firefox_node.rb +51 -61
- 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 +1 -1
- data/lib/capybara/selenium/patches/persistent_client.rb +20 -0
- data/lib/capybara/server/animation_disabler.rb +43 -21
- data/lib/capybara/server/checker.rb +6 -2
- data/lib/capybara/server/middleware.rb +25 -13
- data/lib/capybara/server.rb +20 -4
- data/lib/capybara/session/config.rb +15 -11
- data/lib/capybara/session/matchers.rb +11 -11
- data/lib/capybara/session.rb +162 -131
- data/lib/capybara/spec/public/offset.js +6 -0
- data/lib/capybara/spec/public/test.js +105 -6
- data/lib/capybara/spec/session/accept_alert_spec.rb +1 -1
- data/lib/capybara/spec/session/active_element_spec.rb +31 -0
- data/lib/capybara/spec/session/all_spec.rb +89 -15
- data/lib/capybara/spec/session/ancestor_spec.rb +5 -0
- data/lib/capybara/spec/session/assert_current_path_spec.rb +5 -2
- data/lib/capybara/spec/session/assert_text_spec.rb +26 -22
- data/lib/capybara/spec/session/attach_file_spec.rb +64 -31
- data/lib/capybara/spec/session/check_spec.rb +26 -4
- data/lib/capybara/spec/session/choose_spec.rb +14 -2
- data/lib/capybara/spec/session/click_button_spec.rb +109 -61
- data/lib/capybara/spec/session/click_link_or_button_spec.rb +9 -0
- data/lib/capybara/spec/session/click_link_spec.rb +23 -1
- data/lib/capybara/spec/session/current_scope_spec.rb +1 -1
- data/lib/capybara/spec/session/current_url_spec.rb +11 -1
- data/lib/capybara/spec/session/element/matches_selector_spec.rb +40 -39
- data/lib/capybara/spec/session/evaluate_script_spec.rb +12 -0
- data/lib/capybara/spec/session/fill_in_spec.rb +46 -5
- data/lib/capybara/spec/session/find_link_spec.rb +10 -0
- data/lib/capybara/spec/session/find_spec.rb +80 -7
- data/lib/capybara/spec/session/first_spec.rb +2 -2
- data/lib/capybara/spec/session/frame/switch_to_frame_spec.rb +14 -1
- data/lib/capybara/spec/session/frame/within_frame_spec.rb +14 -1
- data/lib/capybara/spec/session/has_all_selectors_spec.rb +5 -5
- data/lib/capybara/spec/session/has_ancestor_spec.rb +46 -0
- data/lib/capybara/spec/session/has_any_selectors_spec.rb +6 -2
- data/lib/capybara/spec/session/has_button_spec.rb +81 -0
- data/lib/capybara/spec/session/has_css_spec.rb +45 -8
- data/lib/capybara/spec/session/has_current_path_spec.rb +22 -7
- data/lib/capybara/spec/session/has_element_spec.rb +47 -0
- data/lib/capybara/spec/session/has_field_spec.rb +59 -1
- data/lib/capybara/spec/session/has_link_spec.rb +40 -0
- data/lib/capybara/spec/session/has_none_selectors_spec.rb +7 -7
- data/lib/capybara/spec/session/has_select_spec.rb +42 -8
- data/lib/capybara/spec/session/has_selector_spec.rb +19 -4
- data/lib/capybara/spec/session/has_sibling_spec.rb +50 -0
- data/lib/capybara/spec/session/has_table_spec.rb +177 -0
- data/lib/capybara/spec/session/has_text_spec.rb +31 -3
- data/lib/capybara/spec/session/html_spec.rb +1 -1
- data/lib/capybara/spec/session/matches_style_spec.rb +6 -4
- data/lib/capybara/spec/session/node_spec.rb +697 -23
- data/lib/capybara/spec/session/node_wrapper_spec.rb +1 -1
- data/lib/capybara/spec/session/refresh_spec.rb +2 -1
- data/lib/capybara/spec/session/reset_session_spec.rb +21 -7
- data/lib/capybara/spec/session/save_and_open_screenshot_spec.rb +2 -2
- data/lib/capybara/spec/session/save_page_spec.rb +4 -4
- data/lib/capybara/spec/session/save_screenshot_spec.rb +4 -4
- data/lib/capybara/spec/session/scroll_spec.rb +9 -7
- data/lib/capybara/spec/session/select_spec.rb +5 -10
- data/lib/capybara/spec/session/selectors_spec.rb +24 -3
- data/lib/capybara/spec/session/uncheck_spec.rb +3 -3
- data/lib/capybara/spec/session/unselect_spec.rb +1 -1
- data/lib/capybara/spec/session/visit_spec.rb +20 -0
- data/lib/capybara/spec/session/window/become_closed_spec.rb +20 -17
- data/lib/capybara/spec/session/window/switch_to_window_spec.rb +1 -1
- data/lib/capybara/spec/session/window/window_opened_by_spec.rb +1 -1
- data/lib/capybara/spec/session/window/window_spec.rb +54 -57
- data/lib/capybara/spec/session/window/windows_spec.rb +2 -2
- data/lib/capybara/spec/session/within_spec.rb +36 -0
- data/lib/capybara/spec/spec_helper.rb +30 -19
- data/lib/capybara/spec/test_app.rb +122 -34
- data/lib/capybara/spec/views/animated.erb +49 -0
- data/lib/capybara/spec/views/form.erb +86 -8
- data/lib/capybara/spec/views/frame_child.erb +3 -2
- 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 +10 -10
- 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 +2 -1
- data/lib/capybara/spec/views/spatial.erb +31 -0
- data/lib/capybara/spec/views/tables.erb +67 -0
- data/lib/capybara/spec/views/with_animation.erb +39 -4
- data/lib/capybara/spec/views/with_base_tag.erb +2 -2
- data/lib/capybara/spec/views/with_dragula.erb +24 -0
- data/lib/capybara/spec/views/with_fixed_header_footer.erb +2 -1
- data/lib/capybara/spec/views/with_hover.erb +3 -2
- data/lib/capybara/spec/views/with_hover1.erb +10 -0
- data/lib/capybara/spec/views/with_html.erb +34 -6
- data/lib/capybara/spec/views/with_jquery_animation.erb +24 -0
- data/lib/capybara/spec/views/with_js.erb +7 -4
- data/lib/capybara/spec/views/with_jstree.erb +26 -0
- data/lib/capybara/spec/views/with_namespace.erb +1 -0
- data/lib/capybara/spec/views/with_scope.erb +2 -2
- data/lib/capybara/spec/views/with_scope_other.erb +6 -0
- data/lib/capybara/spec/views/with_shadow.erb +31 -0
- data/lib/capybara/spec/views/with_slow_unload.erb +2 -1
- data/lib/capybara/spec/views/with_sortable_js.erb +21 -0
- data/lib/capybara/spec/views/with_unload_alert.erb +1 -0
- data/lib/capybara/spec/views/with_windows.erb +1 -1
- data/lib/capybara/spec/views/within_frames.erb +1 -1
- data/lib/capybara/version.rb +1 -1
- data/lib/capybara/window.rb +14 -18
- data/lib/capybara.rb +91 -126
- data/spec/basic_node_spec.rb +30 -16
- data/spec/capybara_spec.rb +40 -28
- data/spec/counter_spec.rb +35 -0
- data/spec/css_builder_spec.rb +3 -1
- data/spec/css_splitter_spec.rb +1 -1
- data/spec/dsl_spec.rb +33 -22
- data/spec/filter_set_spec.rb +5 -5
- data/spec/fixtures/selenium_driver_rspec_failure.rb +3 -3
- data/spec/fixtures/selenium_driver_rspec_success.rb +3 -3
- data/spec/minitest_spec.rb +24 -2
- data/spec/minitest_spec_spec.rb +60 -45
- data/spec/per_session_config_spec.rb +1 -1
- data/spec/rack_test_spec.rb +131 -98
- data/spec/regexp_dissassembler_spec.rb +53 -39
- data/spec/result_spec.rb +68 -66
- data/spec/rspec/features_spec.rb +9 -4
- data/spec/rspec/scenarios_spec.rb +6 -2
- data/spec/rspec/shared_spec_matchers.rb +137 -98
- data/spec/rspec_matchers_spec.rb +25 -0
- data/spec/rspec_spec.rb +23 -21
- data/spec/sauce_spec_chrome.rb +43 -0
- data/spec/selector_spec.rb +77 -21
- data/spec/selenium_spec_chrome.rb +141 -39
- data/spec/selenium_spec_chrome_remote.rb +32 -17
- data/spec/selenium_spec_edge.rb +36 -8
- data/spec/selenium_spec_firefox.rb +110 -68
- data/spec/selenium_spec_firefox_remote.rb +22 -15
- data/spec/selenium_spec_ie.rb +29 -22
- data/spec/selenium_spec_safari.rb +162 -0
- data/spec/server_spec.rb +153 -81
- data/spec/session_spec.rb +11 -4
- data/spec/shared_selenium_node.rb +79 -0
- data/spec/shared_selenium_session.rb +179 -74
- data/spec/spec_helper.rb +80 -5
- data/spec/whitespace_normalizer_spec.rb +54 -0
- data/spec/xpath_builder_spec.rb +3 -1
- metadata +218 -30
- data/lib/capybara/spec/session/source_spec.rb +0 -0
- data/lib/capybara/spec/views/with_title.erb +0 -5
@@ -1,6 +1,6 @@
|
|
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
4
|
# because of the methods being tested. In tests using Capybara this type of behavior should be implemented
|
5
5
|
# using Capybara provided assertions with builtin waiting behavior.
|
6
6
|
|
@@ -25,9 +25,9 @@ Capybara::SpecHelper.spec 'node' do
|
|
25
25
|
|
26
26
|
describe '#query_scope' do
|
27
27
|
it 'should have a reference to the element the query was evaluated on if there is one' do
|
28
|
-
|
29
|
-
expect(
|
30
|
-
expect(
|
28
|
+
node = @session.find(:css, '#first')
|
29
|
+
expect(node.query_scope).to eq(node.session.document)
|
30
|
+
expect(node.find(:css, '#foo').query_scope).to eq(node)
|
31
31
|
end
|
32
32
|
end
|
33
33
|
|
@@ -109,12 +109,24 @@ Capybara::SpecHelper.spec 'node' do
|
|
109
109
|
expect(@session.first('//input').value).to eq('')
|
110
110
|
end
|
111
111
|
|
112
|
-
|
113
|
-
|
114
|
-
|
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
|
115
125
|
|
116
|
-
|
117
|
-
|
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
|
118
130
|
end
|
119
131
|
|
120
132
|
it 'should use global default options' do
|
@@ -145,6 +157,18 @@ Capybara::SpecHelper.spec 'node' do
|
|
145
157
|
expect(@session.find(:css, '#existing_content_editable_child_parent').text).to eq("Some content\nWYSIWYG")
|
146
158
|
end
|
147
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
|
148
172
|
end
|
149
173
|
|
150
174
|
describe '#tag_name' do
|
@@ -183,7 +207,7 @@ Capybara::SpecHelper.spec 'node' do
|
|
183
207
|
|
184
208
|
it 'should see a disabled fieldset as disabled' do
|
185
209
|
@session.visit('/form')
|
186
|
-
expect(@session.find(:
|
210
|
+
expect(@session.find(:xpath, './/fieldset[@id="form_disabled_fieldset"]')).to be_disabled
|
187
211
|
end
|
188
212
|
|
189
213
|
context 'in a disabled fieldset' do
|
@@ -221,8 +245,9 @@ Capybara::SpecHelper.spec 'node' do
|
|
221
245
|
end
|
222
246
|
|
223
247
|
describe '#visible?' do
|
248
|
+
before { Capybara.ignore_hidden_elements = false }
|
249
|
+
|
224
250
|
it 'should extract node visibility' do
|
225
|
-
Capybara.ignore_hidden_elements = false
|
226
251
|
expect(@session.first('//a')).to be_visible
|
227
252
|
|
228
253
|
expect(@session.find('//div[@id="hidden"]')).not_to be_visible
|
@@ -232,11 +257,113 @@ Capybara::SpecHelper.spec 'node' do
|
|
232
257
|
expect(@session.find('//input[@id="hidden_input"]')).not_to be_visible
|
233
258
|
end
|
234
259
|
|
260
|
+
it 'template elements should not be visible' do
|
261
|
+
expect(@session.find('//template')).not_to be_visible
|
262
|
+
end
|
263
|
+
|
235
264
|
it 'should be boolean' do
|
236
|
-
Capybara.ignore_hidden_elements = false
|
237
265
|
expect(@session.first('//a').visible?).to be true
|
238
266
|
expect(@session.find('//div[@id="hidden"]').visible?).to be false
|
239
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
|
240
367
|
end
|
241
368
|
|
242
369
|
describe '#checked?' do
|
@@ -273,8 +400,8 @@ Capybara::SpecHelper.spec 'node' do
|
|
273
400
|
|
274
401
|
describe '#==' do
|
275
402
|
it 'preserve object identity' do
|
276
|
-
expect(@session.find('//h1') == @session.find('//h1')).to be true # rubocop:disable Lint/
|
277
|
-
expect(@session.find('//h1') === @session.find('//h1')).to be true # rubocop:disable Style/CaseEquality, Lint/
|
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
|
278
405
|
expect(@session.find('//h1').eql?(@session.find('//h1'))).to be false
|
279
406
|
end
|
280
407
|
|
@@ -294,6 +421,17 @@ Capybara::SpecHelper.spec 'node' do
|
|
294
421
|
element = @session.find(:link, 'Second Link')
|
295
422
|
expect(@session.find(:xpath, element.path)).to eq(element)
|
296
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
|
297
435
|
end
|
298
436
|
|
299
437
|
describe '#trigger', requires: %i[js trigger] do
|
@@ -328,6 +466,260 @@ Capybara::SpecHelper.spec 'node' do
|
|
328
466
|
link.drag_to target
|
329
467
|
expect(@session).to have_xpath('//div[contains(., "Dropped!")]')
|
330
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")]')
|
722
|
+
end
|
331
723
|
end
|
332
724
|
|
333
725
|
describe '#hover', requires: [:hover] do
|
@@ -344,6 +736,15 @@ Capybara::SpecHelper.spec 'node' do
|
|
344
736
|
@session.find(:css, '.wrapper.scroll_needed').hover
|
345
737
|
expect(@session.find(:css, '.wrapper.scroll_needed .hidden_until_hover', visible: false)).to be_visible
|
346
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
|
347
748
|
end
|
348
749
|
|
349
750
|
describe '#click' do
|
@@ -396,10 +797,11 @@ Capybara::SpecHelper.spec 'node' do
|
|
396
797
|
end
|
397
798
|
|
398
799
|
it 'should allow to adjust the click offset', requires: [:js] do
|
800
|
+
Capybara.w3c_click_offset = false
|
399
801
|
@session.visit('with_js')
|
400
802
|
@session.find(:css, '#click-test').click(x: 5, y: 5)
|
401
803
|
link = @session.find(:link, 'has-been-clicked')
|
402
|
-
locations = link.text.match(/^Has been clicked at (?<x>[\d
|
804
|
+
locations = link.text.match(/^Has been clicked at (?<x>[\d.-]+),(?<y>[\d.-]+)$/)
|
403
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
|
404
806
|
# integer/float conversions and rounding.
|
405
807
|
expect(locations[:x].to_f).to be_within(1).of(5)
|
@@ -420,7 +822,7 @@ Capybara::SpecHelper.spec 'node' do
|
|
420
822
|
end
|
421
823
|
|
422
824
|
it 'should retry clicking', requires: [:js] do
|
423
|
-
@session.visit('/
|
825
|
+
@session.visit('/animated')
|
424
826
|
obscured = @session.find(:css, '#obscured')
|
425
827
|
@session.execute_script <<~JS
|
426
828
|
setTimeout(function(){ $('#cover').hide(); }, 700)
|
@@ -429,7 +831,7 @@ Capybara::SpecHelper.spec 'node' do
|
|
429
831
|
end
|
430
832
|
|
431
833
|
it 'should allow to retry longer', requires: [:js] do
|
432
|
-
@session.visit('/
|
834
|
+
@session.visit('/animated')
|
433
835
|
obscured = @session.find(:css, '#obscured')
|
434
836
|
@session.execute_script <<~JS
|
435
837
|
setTimeout(function(){ $('#cover').hide(); }, 3000)
|
@@ -438,13 +840,74 @@ Capybara::SpecHelper.spec 'node' do
|
|
438
840
|
end
|
439
841
|
|
440
842
|
it 'should not retry clicking when wait is disabled', requires: [:js] do
|
441
|
-
@session.visit('/
|
843
|
+
@session.visit('/animated')
|
442
844
|
obscured = @session.find(:css, '#obscured')
|
443
845
|
@session.execute_script <<~JS
|
444
846
|
setTimeout(function(){ $('#cover').hide(); }, 2000)
|
445
847
|
JS
|
446
848
|
expect { obscured.click(wait: 0) }.to(raise_error { |e| expect(e).to be_an_invalid_element_error(@session) })
|
447
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
|
901
|
+
end
|
902
|
+
|
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
|
448
911
|
end
|
449
912
|
|
450
913
|
describe '#double_click', requires: [:js] do
|
@@ -461,10 +924,11 @@ Capybara::SpecHelper.spec 'node' do
|
|
461
924
|
end
|
462
925
|
|
463
926
|
it 'should allow to adjust the offset', requires: [:js] do
|
927
|
+
Capybara.w3c_click_offset = false
|
464
928
|
@session.visit('with_js')
|
465
929
|
@session.find(:css, '#click-test').double_click(x: 10, y: 5)
|
466
930
|
link = @session.find(:link, 'has-been-double-clicked')
|
467
|
-
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.-]+)$/)
|
468
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
|
469
933
|
# to integer/float conversions and rounding.
|
470
934
|
expect(locations[:x].to_f).to be_within(1).of(10)
|
@@ -472,13 +936,65 @@ Capybara::SpecHelper.spec 'node' do
|
|
472
936
|
end
|
473
937
|
|
474
938
|
it 'should retry clicking', requires: [:js] do
|
475
|
-
@session.visit('/
|
939
|
+
@session.visit('/animated')
|
476
940
|
obscured = @session.find(:css, '#obscured')
|
477
941
|
@session.execute_script <<~JS
|
478
942
|
setTimeout(function(){ $('#cover').hide(); }, 700)
|
479
943
|
JS
|
480
944
|
expect { obscured.double_click }.not_to raise_error
|
481
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
|
482
998
|
end
|
483
999
|
|
484
1000
|
describe '#right_click', requires: [:js] do
|
@@ -495,10 +1011,11 @@ Capybara::SpecHelper.spec 'node' do
|
|
495
1011
|
end
|
496
1012
|
|
497
1013
|
it 'should allow to adjust the offset', requires: [:js] do
|
1014
|
+
Capybara.w3c_click_offset = false
|
498
1015
|
@session.visit('with_js')
|
499
1016
|
@session.find(:css, '#click-test').right_click(x: 10, y: 10)
|
500
1017
|
link = @session.find(:link, 'has-been-right-clicked')
|
501
|
-
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.-]+)$/)
|
502
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
|
503
1020
|
# to integer/float conversions and rounding
|
504
1021
|
expect(locations[:x].to_f).to be_within(1).of(10)
|
@@ -506,13 +1023,74 @@ Capybara::SpecHelper.spec 'node' do
|
|
506
1023
|
end
|
507
1024
|
|
508
1025
|
it 'should retry clicking', requires: [:js] do
|
509
|
-
@session.visit('/
|
1026
|
+
@session.visit('/animated')
|
510
1027
|
obscured = @session.find(:css, '#obscured')
|
511
1028
|
@session.execute_script <<~JS
|
512
1029
|
setTimeout(function(){ $('#cover').hide(); }, 700)
|
513
1030
|
JS
|
514
1031
|
expect { obscured.right_click }.not_to raise_error
|
515
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
|
516
1094
|
end
|
517
1095
|
|
518
1096
|
describe '#send_keys', requires: [:send_keys] do
|
@@ -562,7 +1140,7 @@ Capybara::SpecHelper.spec 'node' do
|
|
562
1140
|
end
|
563
1141
|
|
564
1142
|
describe '#evaluate_script', requires: %i[js es_args] do
|
565
|
-
it 'should evaluate the given script in the context of the element and
|
1143
|
+
it 'should evaluate the given script in the context of the element and return whatever it produces' do
|
566
1144
|
@session.visit('/with_js')
|
567
1145
|
el = @session.find(:css, '#with_change_event')
|
568
1146
|
expect(el.evaluate_script('this.value')).to eq('default value')
|
@@ -594,6 +1172,18 @@ Capybara::SpecHelper.spec 'node' do
|
|
594
1172
|
expect(el).to be_instance_of(Capybara::Node::Element)
|
595
1173
|
expect(el).to eq(change)
|
596
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
|
597
1187
|
end
|
598
1188
|
|
599
1189
|
describe '#evaluate_async_script', requires: %i[js es_args] do
|
@@ -611,7 +1201,91 @@ Capybara::SpecHelper.spec 'node' do
|
|
611
1201
|
end
|
612
1202
|
end
|
613
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
|
+
|
614
1266
|
describe '#reload', requires: [:js] do
|
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
|
+
|
615
1289
|
context 'without automatic reload' do
|
616
1290
|
before { Capybara.automatic_reload = false }
|
617
1291
|
|