capybara 3.3.1 → 3.4.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/History.md +16 -0
- data/README.md +5 -7
- data/lib/capybara.rb +7 -6
- data/lib/capybara/config.rb +1 -1
- data/lib/capybara/dsl.rb +2 -2
- data/lib/capybara/helpers.rb +3 -3
- data/lib/capybara/minitest/spec.rb +3 -3
- data/lib/capybara/node/actions.rb +18 -18
- data/lib/capybara/node/base.rb +1 -1
- data/lib/capybara/node/element.rb +2 -2
- data/lib/capybara/node/finders.rb +6 -6
- data/lib/capybara/node/matchers.rb +5 -5
- data/lib/capybara/node/simple.rb +2 -2
- data/lib/capybara/queries/ancestor_query.rb +1 -1
- data/lib/capybara/queries/base_query.rb +12 -11
- data/lib/capybara/queries/current_path_query.rb +1 -1
- data/lib/capybara/queries/selector_query.rb +39 -15
- data/lib/capybara/queries/sibling_query.rb +1 -1
- data/lib/capybara/queries/text_query.rb +1 -1
- data/lib/capybara/rack_test/browser.rb +7 -7
- data/lib/capybara/rack_test/driver.rb +1 -1
- data/lib/capybara/rack_test/form.rb +7 -7
- data/lib/capybara/rack_test/node.rb +16 -16
- data/lib/capybara/rails.rb +1 -1
- data/lib/capybara/result.rb +8 -4
- data/lib/capybara/rspec/features.rb +4 -4
- data/lib/capybara/rspec/matchers.rb +6 -6
- data/lib/capybara/selector.rb +106 -90
- data/lib/capybara/selector/css.rb +4 -4
- data/lib/capybara/selector/filter_set.rb +52 -8
- data/lib/capybara/selector/selector.rb +39 -15
- data/lib/capybara/selenium/driver.rb +10 -10
- data/lib/capybara/selenium/driver_specializations/chrome_driver.rb +8 -0
- data/lib/capybara/selenium/node.rb +9 -10
- data/lib/capybara/selenium/nodes/chrome_node.rb +18 -0
- data/lib/capybara/selenium/nodes/marionette_node.rb +32 -7
- data/lib/capybara/server.rb +3 -3
- data/lib/capybara/server/animation_disabler.rb +1 -1
- data/lib/capybara/server/middleware.rb +1 -1
- data/lib/capybara/session.rb +23 -19
- data/lib/capybara/session/config.rb +18 -3
- data/lib/capybara/spec/public/test.js +1 -1
- data/lib/capybara/spec/session/accept_alert_spec.rb +10 -10
- 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/all_spec.rb +33 -32
- data/lib/capybara/spec/session/ancestor_spec.rb +19 -19
- data/lib/capybara/spec/session/assert_all_of_selectors_spec.rb +38 -38
- data/lib/capybara/spec/session/assert_current_path_spec.rb +16 -16
- data/lib/capybara/spec/session/assert_selector_spec.rb +53 -53
- data/lib/capybara/spec/session/assert_style_spec.rb +3 -3
- data/lib/capybara/spec/session/assert_text_spec.rb +31 -30
- data/lib/capybara/spec/session/assert_title_spec.rb +12 -12
- data/lib/capybara/spec/session/attach_file_spec.rb +51 -52
- data/lib/capybara/spec/session/body_spec.rb +6 -6
- data/lib/capybara/spec/session/check_spec.rb +52 -47
- data/lib/capybara/spec/session/choose_spec.rb +32 -32
- data/lib/capybara/spec/session/click_button_spec.rb +103 -103
- data/lib/capybara/spec/session/click_link_or_button_spec.rb +24 -23
- data/lib/capybara/spec/session/click_link_spec.rb +49 -48
- data/lib/capybara/spec/session/current_scope_spec.rb +7 -7
- data/lib/capybara/spec/session/current_url_spec.rb +26 -27
- data/lib/capybara/spec/session/dismiss_confirm_spec.rb +3 -3
- data/lib/capybara/spec/session/dismiss_prompt_spec.rb +2 -2
- data/lib/capybara/spec/session/element/assert_match_selector_spec.rb +8 -8
- data/lib/capybara/spec/session/element/match_css_spec.rb +10 -10
- data/lib/capybara/spec/session/element/match_xpath_spec.rb +6 -6
- data/lib/capybara/spec/session/element/matches_selector_spec.rb +51 -51
- data/lib/capybara/spec/session/evaluate_async_script_spec.rb +7 -7
- data/lib/capybara/spec/session/evaluate_script_spec.rb +15 -8
- data/lib/capybara/spec/session/execute_script_spec.rb +7 -7
- data/lib/capybara/spec/session/fill_in_spec.rb +43 -42
- data/lib/capybara/spec/session/find_button_spec.rb +23 -23
- data/lib/capybara/spec/session/find_by_id_spec.rb +7 -7
- data/lib/capybara/spec/session/find_field_spec.rb +32 -30
- data/lib/capybara/spec/session/find_link_spec.rb +21 -21
- data/lib/capybara/spec/session/find_spec.rb +153 -135
- data/lib/capybara/spec/session/first_spec.rb +41 -41
- data/lib/capybara/spec/session/frame/frame_title_spec.rb +5 -5
- data/lib/capybara/spec/session/frame/frame_url_spec.rb +5 -5
- data/lib/capybara/spec/session/frame/switch_to_frame_spec.rb +17 -17
- data/lib/capybara/spec/session/frame/within_frame_spec.rb +31 -17
- 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 +17 -17
- data/lib/capybara/spec/session/has_button_spec.rb +13 -13
- data/lib/capybara/spec/session/has_css_spec.rb +133 -131
- data/lib/capybara/spec/session/has_current_path_spec.rb +29 -29
- data/lib/capybara/spec/session/has_field_spec.rb +58 -58
- data/lib/capybara/spec/session/has_link_spec.rb +4 -4
- data/lib/capybara/spec/session/has_none_selectors_spec.rb +24 -24
- data/lib/capybara/spec/session/has_select_spec.rb +43 -43
- data/lib/capybara/spec/session/has_selector_spec.rb +71 -71
- data/lib/capybara/spec/session/has_style_spec.rb +3 -3
- data/lib/capybara/spec/session/has_table_spec.rb +4 -4
- data/lib/capybara/spec/session/has_text_spec.rb +53 -52
- data/lib/capybara/spec/session/has_title_spec.rb +14 -14
- data/lib/capybara/spec/session/has_xpath_spec.rb +39 -38
- data/lib/capybara/spec/session/headers_spec.rb +1 -1
- data/lib/capybara/spec/session/html_spec.rb +6 -6
- data/lib/capybara/spec/session/node_spec.rb +129 -123
- data/lib/capybara/spec/session/node_wrapper_spec.rb +10 -7
- data/lib/capybara/spec/session/refresh_spec.rb +4 -7
- data/lib/capybara/spec/session/reset_session_spec.rb +28 -28
- data/lib/capybara/spec/session/response_code_spec.rb +1 -1
- data/lib/capybara/spec/session/save_and_open_page_spec.rb +2 -2
- data/lib/capybara/spec/session/save_page_spec.rb +37 -37
- data/lib/capybara/spec/session/save_screenshot_spec.rb +6 -6
- data/lib/capybara/spec/session/screenshot_spec.rb +2 -2
- data/lib/capybara/spec/session/select_spec.rb +81 -81
- data/lib/capybara/spec/session/selectors_spec.rb +17 -17
- data/lib/capybara/spec/session/sibling_spec.rb +9 -9
- data/lib/capybara/spec/session/text_spec.rb +23 -23
- data/lib/capybara/spec/session/title_spec.rb +5 -5
- data/lib/capybara/spec/session/uncheck_spec.rb +24 -20
- data/lib/capybara/spec/session/unselect_spec.rb +37 -37
- data/lib/capybara/spec/session/visit_spec.rb +48 -49
- data/lib/capybara/spec/session/window/current_window_spec.rb +1 -1
- data/lib/capybara/spec/session/window/switch_to_window_spec.rb +16 -16
- data/lib/capybara/spec/session/window/window_opened_by_spec.rb +2 -2
- data/lib/capybara/spec/session/window/window_spec.rb +4 -4
- data/lib/capybara/spec/session/window/within_window_spec.rb +14 -14
- data/lib/capybara/spec/session/within_spec.rb +41 -41
- data/lib/capybara/spec/spec_helper.rb +11 -9
- data/lib/capybara/spec/test_app.rb +18 -17
- data/lib/capybara/spec/views/form.erb +29 -31
- data/lib/capybara/spec/views/with_html.erb +2 -2
- data/lib/capybara/version.rb +1 -1
- data/spec/basic_node_spec.rb +23 -23
- data/spec/capybara_spec.rb +20 -20
- data/spec/css_splitter_spec.rb +7 -7
- data/spec/dsl_spec.rb +37 -32
- data/spec/filter_set_spec.rb +4 -4
- data/spec/fixtures/selenium_driver_rspec_failure.rb +1 -1
- data/spec/fixtures/selenium_driver_rspec_success.rb +1 -1
- data/spec/minitest_spec.rb +4 -4
- data/spec/minitest_spec_spec.rb +23 -23
- data/spec/per_session_config_spec.rb +5 -5
- data/spec/rack_test_spec.rb +44 -44
- data/spec/result_spec.rb +14 -14
- data/spec/rspec/features_spec.rb +13 -13
- data/spec/rspec/scenarios_spec.rb +4 -4
- data/spec/rspec/shared_spec_matchers.rb +282 -281
- data/spec/rspec/views_spec.rb +3 -3
- data/spec/rspec_matchers_spec.rb +10 -10
- data/spec/rspec_spec.rb +29 -29
- data/spec/selector_spec.rb +64 -64
- data/spec/selenium_spec_chrome.rb +14 -22
- data/spec/selenium_spec_chrome_remote.rb +28 -8
- data/spec/selenium_spec_edge.rb +9 -4
- data/spec/selenium_spec_firefox_remote.rb +87 -0
- data/spec/selenium_spec_ie.rb +9 -4
- data/spec/selenium_spec_marionette.rb +42 -18
- data/spec/server_spec.rb +29 -27
- data/spec/session_spec.rb +17 -17
- data/spec/shared_selenium_session.rb +70 -52
- data/spec/spec_helper.rb +1 -1
- metadata +4 -2
@@ -19,7 +19,7 @@ module Capybara
|
|
19
19
|
# * Locator: The id of the element to match
|
20
20
|
#
|
21
21
|
# * **:field** - Select field elements (input [not of type submit, image, or hidden], textarea, select)
|
22
|
-
# * Locator: Matches against the id, name, or placeholder
|
22
|
+
# * Locator: Matches against the id, Capybara.test_id attribute, name, or placeholder
|
23
23
|
# * Filters:
|
24
24
|
# * :id (String) — Matches the id attribute
|
25
25
|
# * :name (String) — Matches the name attribute
|
@@ -50,7 +50,7 @@ module Capybara
|
|
50
50
|
# * :href (String, Regexp, nil) — Matches the normalized href of the link, if nil will find <a> elements with no href attribute
|
51
51
|
#
|
52
52
|
# * **:button** - Find buttons ( input [of type submit, reset, image, button] or button elements )
|
53
|
-
# * Locator: Matches the id, value, or title attributes, string content of a button, or the alt attribute of an image type button or of a descendant image of a button
|
53
|
+
# * Locator: Matches the id, Capybara.test_id attribute, value, or title attributes, string content of a button, or the alt attribute of an image type button or of a descendant image of a button
|
54
54
|
# * Filters:
|
55
55
|
# * :id (String) — Matches the id attribute
|
56
56
|
# * :title (String) — Matches the title attribute
|
@@ -62,7 +62,7 @@ module Capybara
|
|
62
62
|
# * Locator: See :link and :button selectors
|
63
63
|
#
|
64
64
|
# * **:fillable_field** - Find text fillable fields ( textarea, input [not of type submit, image, radio, checkbox, hidden, file] )
|
65
|
-
# * Locator: Matches against the id, name, or placeholder
|
65
|
+
# * Locator: Matches against the id, Capybara.test_id attribute, name, or placeholder
|
66
66
|
# * Filters:
|
67
67
|
# * :id (String) — Matches the id attribute
|
68
68
|
# * :name (String) — Matches the name attribute
|
@@ -74,7 +74,7 @@ module Capybara
|
|
74
74
|
# * :multiple (Boolean) — Match fields that accept multiple values
|
75
75
|
#
|
76
76
|
# * **:radio_button** - Find radio buttons
|
77
|
-
# * Locator: Match id, name, or associated label text
|
77
|
+
# * Locator: Match id, Capybara.test_id attribute, name, or associated label text
|
78
78
|
# * Filters:
|
79
79
|
# * :id (String) — Matches the id attribute
|
80
80
|
# * :name (String) — Matches the name attribute
|
@@ -85,7 +85,7 @@ module Capybara
|
|
85
85
|
# * :option (String) — Match the value
|
86
86
|
#
|
87
87
|
# * **:checkbox** - Find checkboxes
|
88
|
-
# * Locator: Match id, name, or associated label text
|
88
|
+
# * Locator: Match id, Capybara.test_id attribute, name, or associated label text
|
89
89
|
# * Filters:
|
90
90
|
# * *:id (String) — Matches the id attribute
|
91
91
|
# * *:name (String) — Matches the name attribute
|
@@ -96,7 +96,7 @@ module Capybara
|
|
96
96
|
# * *:option (String) — Match the value
|
97
97
|
#
|
98
98
|
# * **:select** - Find select elements
|
99
|
-
# * Locator: Match id, name, placeholder, or associated label text
|
99
|
+
# * Locator: Match id, Capybara.test_id attribute, name, placeholder, or associated label text
|
100
100
|
# * Filters:
|
101
101
|
# * :id (String) — Matches the id attribute
|
102
102
|
# * :name (String) — Matches the name attribute
|
@@ -126,7 +126,7 @@ module Capybara
|
|
126
126
|
# * Locator:
|
127
127
|
#
|
128
128
|
# * **:file_field** - Find file input elements
|
129
|
-
# * Locator: Match id, name, or associated label text
|
129
|
+
# * Locator: Match id, Capybara.test_id attribute, name, or associated label text
|
130
130
|
# * Filters:
|
131
131
|
# * :id (String) — Matches the id attribute
|
132
132
|
# * :name (String) — Matches the name attribute
|
@@ -190,6 +190,10 @@ module Capybara
|
|
190
190
|
@expression = nil
|
191
191
|
@expression_filters = {}
|
192
192
|
@default_visibility = nil
|
193
|
+
@config = {
|
194
|
+
enable_aria_label: false,
|
195
|
+
test_id: nil
|
196
|
+
}
|
193
197
|
instance_eval(&block)
|
194
198
|
end
|
195
199
|
|
@@ -287,11 +291,12 @@ module Capybara
|
|
287
291
|
# @return [String] Description of the selector when used with the options passed
|
288
292
|
def_delegator :@filter_set, :description
|
289
293
|
|
290
|
-
def call(locator, **options)
|
294
|
+
def call(locator, selector_config: {}, **options)
|
295
|
+
@config.merge! selector_config
|
291
296
|
if format
|
292
297
|
@expression.call(locator, options)
|
293
298
|
else
|
294
|
-
warn
|
299
|
+
warn 'Selector has no format'
|
295
300
|
end
|
296
301
|
end
|
297
302
|
|
@@ -349,15 +354,25 @@ module Capybara
|
|
349
354
|
def_delegators :@filter_set, :node_filter, :expression_filter, :filter
|
350
355
|
|
351
356
|
def filter_set(name, filters_to_use = nil)
|
352
|
-
|
353
|
-
filter_selector = filters_to_use.nil? ? ->(*) { true } : ->(n, _) { filters_to_use.include? n }
|
354
|
-
@filter_set.expression_filters.merge!(f_set.expression_filters.select(&filter_selector))
|
355
|
-
@filter_set.node_filters.merge!(f_set.node_filters.select(&filter_selector))
|
356
|
-
f_set.descriptions.each { |desc| @filter_set.describe(&desc) }
|
357
|
+
@filter_set.import(name, filters_to_use)
|
357
358
|
end
|
358
359
|
|
359
360
|
def_delegator :@filter_set, :describe
|
360
361
|
|
362
|
+
def describe_expression_filters(&block)
|
363
|
+
if block_given?
|
364
|
+
describe(:expression_filters, &block)
|
365
|
+
else
|
366
|
+
describe(:expression_filters) do |**options|
|
367
|
+
describe_all_expression_filters(options)
|
368
|
+
end
|
369
|
+
end
|
370
|
+
end
|
371
|
+
|
372
|
+
def describe_node_filters(&block)
|
373
|
+
describe(:node_filters, &block)
|
374
|
+
end
|
375
|
+
|
361
376
|
##
|
362
377
|
#
|
363
378
|
# Set the default visibility mode that shouble be used if no visibile option is passed when using the selector.
|
@@ -378,7 +393,15 @@ module Capybara
|
|
378
393
|
|
379
394
|
private
|
380
395
|
|
381
|
-
def
|
396
|
+
def enable_aria_label
|
397
|
+
@config[:enable_aria_label]
|
398
|
+
end
|
399
|
+
|
400
|
+
def test_id
|
401
|
+
@config[:test_id]
|
402
|
+
end
|
403
|
+
|
404
|
+
def locate_field(xpath, locator, **_options)
|
382
405
|
return xpath if locator.nil?
|
383
406
|
locate_xpath = xpath # Need to save original xpath for the label wrap
|
384
407
|
locator = locator.to_s
|
@@ -387,6 +410,7 @@ module Capybara
|
|
387
410
|
XPath.attr(:placeholder) == locator,
|
388
411
|
XPath.attr(:id) == XPath.anywhere(:label)[XPath.string.n.is(locator)].attr(:for)].reduce(:|)
|
389
412
|
attr_matchers |= XPath.attr(:'aria-label').is(locator) if enable_aria_label
|
413
|
+
attr_matchers |= XPath.attr(test_id) == locator if test_id
|
390
414
|
|
391
415
|
locate_xpath = locate_xpath[attr_matchers]
|
392
416
|
locate_xpath + XPath.descendant(:label)[XPath.string.n.is(locator)].descendant(xpath)
|
@@ -14,7 +14,7 @@ class Capybara::Selenium::Driver < Capybara::Driver::Base
|
|
14
14
|
|
15
15
|
def self.load_selenium
|
16
16
|
require 'selenium-webdriver'
|
17
|
-
warn "Warning: You're using an unsupported version of selenium-webdriver, please upgrade." if Gem.loaded_specs[
|
17
|
+
warn "Warning: You're using an unsupported version of selenium-webdriver, please upgrade." if Gem.loaded_specs['selenium-webdriver'].version < Gem::Version.new('3.5.0')
|
18
18
|
rescue LoadError => e
|
19
19
|
raise e if e.message !~ /selenium-webdriver/
|
20
20
|
raise LoadError, "Capybara's selenium driver is unable to load `selenium-webdriver`, please install the gem and add `gem 'selenium-webdriver'` to your Gemfile if you are using bundler."
|
@@ -99,7 +99,7 @@ class Capybara::Selenium::Driver < Capybara::Driver::Base
|
|
99
99
|
end
|
100
100
|
|
101
101
|
def evaluate_script(script, *args)
|
102
|
-
result = execute_script("return #{script}", *args)
|
102
|
+
result = execute_script("return #{script.strip}", *args)
|
103
103
|
unwrap_script_result(result)
|
104
104
|
end
|
105
105
|
|
@@ -135,12 +135,12 @@ class Capybara::Selenium::Driver < Capybara::Driver::Base
|
|
135
135
|
# to about:blank, so we rescue this error and do nothing
|
136
136
|
# instead.
|
137
137
|
end
|
138
|
-
@browser.navigate.to(
|
138
|
+
@browser.navigate.to('about:blank')
|
139
139
|
end
|
140
140
|
navigated = true
|
141
141
|
|
142
142
|
# Ensure the page is empty and trigger an UnhandledAlertError for any modals that appear during unload
|
143
|
-
until find_xpath(
|
143
|
+
until find_xpath('/html/body/*').empty?
|
144
144
|
raise Capybara::ExpectationNotMet, 'Timed out waiting for Selenium session reset' if timer.expired?
|
145
145
|
sleep 0.05
|
146
146
|
end
|
@@ -153,11 +153,11 @@ class Capybara::Selenium::Driver < Capybara::Driver::Base
|
|
153
153
|
sleep 0.25 # allow time for the modal to be handled
|
154
154
|
rescue modal_error
|
155
155
|
# The alert is now gone
|
156
|
-
if current_url !=
|
156
|
+
if current_url != 'about:blank'
|
157
157
|
begin
|
158
158
|
# If navigation has not occurred attempt again and accept alert
|
159
159
|
# since FF may have dismissed the alert at first attempt
|
160
|
-
@browser.navigate.to(
|
160
|
+
@browser.navigate.to('about:blank')
|
161
161
|
sleep 0.1 # slight wait for alert
|
162
162
|
@browser.switch_to.alert.accept
|
163
163
|
rescue modal_error # rubocop:disable Metrics/BlockNesting, Lint/HandleExceptions
|
@@ -219,7 +219,7 @@ class Capybara::Selenium::Driver < Capybara::Driver::Base
|
|
219
219
|
end
|
220
220
|
|
221
221
|
def close_window(handle)
|
222
|
-
raise ArgumentError,
|
222
|
+
raise ArgumentError, 'Not allowed to close the primary window' if handle == window_handles.first
|
223
223
|
within_given_window(handle) do
|
224
224
|
browser.close
|
225
225
|
end
|
@@ -328,15 +328,15 @@ private
|
|
328
328
|
if @browser.respond_to? :session_storage
|
329
329
|
@browser.session_storage.clear
|
330
330
|
else
|
331
|
-
warn
|
331
|
+
warn 'sessionStorage clear requested but is not available for this driver'
|
332
332
|
end
|
333
333
|
end
|
334
334
|
|
335
|
-
if options[:clear_local_storage]
|
335
|
+
if options[:clear_local_storage] # rubocop:disable Style/GuardClause
|
336
336
|
if @browser.respond_to? :local_storage
|
337
337
|
@browser.local_storage.clear
|
338
338
|
else
|
339
|
-
warn
|
339
|
+
warn 'localStorage clear requested but is not available for this driver'
|
340
340
|
end
|
341
341
|
end
|
342
342
|
end
|
@@ -1,5 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require 'capybara/selenium/nodes/chrome_node'
|
4
|
+
|
3
5
|
module Capybara::Selenium::Driver::ChromeDriver
|
4
6
|
def fullscreen_window(handle)
|
5
7
|
within_given_window(handle) do
|
@@ -32,4 +34,10 @@ module Capybara::Selenium::Driver::ChromeDriver
|
|
32
34
|
window_handles.slice(1..-1).each { |win| close_window(win) }
|
33
35
|
super
|
34
36
|
end
|
37
|
+
|
38
|
+
private
|
39
|
+
|
40
|
+
def build_node(native_node)
|
41
|
+
::Capybara::Selenium::ChromeNode.new(self, native_node)
|
42
|
+
end
|
35
43
|
end
|
@@ -6,11 +6,11 @@ class Capybara::Selenium::Node < Capybara::Driver::Node
|
|
6
6
|
end
|
7
7
|
|
8
8
|
def all_text
|
9
|
-
text = driver.execute_script(
|
9
|
+
text = driver.execute_script('return arguments[0].textContent', self)
|
10
10
|
text.gsub(/[\u200b\u200e\u200f]/, '')
|
11
11
|
.gsub(/[\ \n\f\t\v\u2028\u2029]+/, ' ')
|
12
|
-
.gsub(/\A[[:space:]&&[^\u00a0]]+/,
|
13
|
-
.gsub(/[[:space:]&&[^\u00a0]]+\z/,
|
12
|
+
.gsub(/\A[[:space:]&&[^\u00a0]]+/, '')
|
13
|
+
.gsub(/[[:space:]&&[^\u00a0]]+\z/, '')
|
14
14
|
.tr("\u00a0", ' ')
|
15
15
|
end
|
16
16
|
|
@@ -21,8 +21,8 @@ class Capybara::Selenium::Node < Capybara::Driver::Node
|
|
21
21
|
end
|
22
22
|
|
23
23
|
def value
|
24
|
-
if tag_name ==
|
25
|
-
native.find_elements(:css,
|
24
|
+
if tag_name == 'select' && multiple?
|
25
|
+
native.find_elements(:css, 'option:checked').map { |n| n[:value] || n.text }
|
26
26
|
else
|
27
27
|
native[:value]
|
28
28
|
end
|
@@ -78,7 +78,7 @@ class Capybara::Selenium::Node < Capybara::Driver::Node
|
|
78
78
|
end
|
79
79
|
|
80
80
|
def unselect_option
|
81
|
-
raise Capybara::UnselectNotAllowed,
|
81
|
+
raise Capybara::UnselectNotAllowed, 'Cannot unselect option from single select box.' unless select_node.multiple?
|
82
82
|
native.click if selected?
|
83
83
|
end
|
84
84
|
|
@@ -142,8 +142,7 @@ class Capybara::Selenium::Node < Capybara::Driver::Node
|
|
142
142
|
def disabled?
|
143
143
|
return true unless native.enabled?
|
144
144
|
# WebDriver only defines `disabled?` for form controls but fieldset makes sense too
|
145
|
-
|
146
|
-
false
|
145
|
+
tag_name == 'fieldset' && find_xpath('ancestor-or-self::fieldset[@disabled]').any?
|
147
146
|
end
|
148
147
|
|
149
148
|
def content_editable?
|
@@ -192,7 +191,7 @@ private
|
|
192
191
|
end
|
193
192
|
|
194
193
|
def boolean_attr(val)
|
195
|
-
val && (val !=
|
194
|
+
val && (val != 'false')
|
196
195
|
end
|
197
196
|
|
198
197
|
# a reference to the select node if this is an option node
|
@@ -206,7 +205,7 @@ private
|
|
206
205
|
elsif clear == :backspace
|
207
206
|
# Clear field by sending the correct number of backspace keys.
|
208
207
|
backspaces = [:backspace] * self.value.to_s.length
|
209
|
-
native.send_keys(*(backspaces + [value.to_s]))
|
208
|
+
native.send_keys(*([:end] + backspaces + [value.to_s]))
|
210
209
|
elsif clear == :none
|
211
210
|
native.send_keys(value.to_s)
|
212
211
|
elsif clear.is_a? Array
|
@@ -0,0 +1,18 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class Capybara::Selenium::ChromeNode < Capybara::Selenium::Node
|
4
|
+
def set_file(value) # rubocop:disable Naming/AccessorMethodName
|
5
|
+
super(value)
|
6
|
+
rescue ::Selenium::WebDriver::Error::ExpectedError => e
|
7
|
+
if e.message =~ /File not found : .+\n.+/m
|
8
|
+
raise ArgumentError, "Selenium with remote Chrome doesn't currently support multiple file upload"
|
9
|
+
end
|
10
|
+
raise
|
11
|
+
end
|
12
|
+
|
13
|
+
private
|
14
|
+
|
15
|
+
def bridge
|
16
|
+
driver.browser.send(:bridge)
|
17
|
+
end
|
18
|
+
end
|
@@ -4,28 +4,53 @@ class Capybara::Selenium::MarionetteNode < Capybara::Selenium::Node
|
|
4
4
|
def click(keys = [], **options)
|
5
5
|
super
|
6
6
|
rescue ::Selenium::WebDriver::Error::ElementNotInteractableError
|
7
|
-
if tag_name ==
|
8
|
-
warn
|
9
|
-
|
7
|
+
if tag_name == 'tr'
|
8
|
+
warn 'You are attempting to click a table row which has issues in geckodriver/marionette - see https://github.com/mozilla/geckodriver/issues/1228. ' \
|
9
|
+
'Your test should probably be clicking on a table cell like a user would. Clicking the first cell in the row instead.'
|
10
10
|
return find_css('th:first-child,td:first-child')[0].click
|
11
11
|
end
|
12
12
|
raise
|
13
13
|
end
|
14
14
|
|
15
15
|
def disabled?
|
16
|
-
|
16
|
+
# Not sure exactly what version of FF fixed the below issue, but it is definitely fixed in 61+
|
17
|
+
return super unless driver.browser.capabilities[:browser_version].to_f < 61.0
|
17
18
|
|
19
|
+
return true if super
|
18
20
|
# workaround for selenium-webdriver/geckodriver reporting elements as enabled when they are nested in disabling elements
|
19
21
|
if %w[option optgroup].include? tag_name
|
20
|
-
find_xpath(
|
22
|
+
find_xpath('parent::*[self::optgroup or self::select]')[0].disabled?
|
21
23
|
else
|
22
|
-
!find_xpath(
|
24
|
+
!find_xpath('parent::fieldset[@disabled] | ancestor::*[not(self::legend) or preceding-sibling::legend][parent::fieldset[@disabled]]').empty?
|
23
25
|
end
|
24
26
|
end
|
25
27
|
|
26
28
|
def set_file(value) # rubocop:disable Naming/AccessorMethodName
|
27
29
|
path_names = value.to_s.empty? ? [] : value
|
28
30
|
native.clear
|
29
|
-
Array(path_names).each
|
31
|
+
Array(path_names).each do |path|
|
32
|
+
unless driver.browser.respond_to?(:upload)
|
33
|
+
if (fd = bridge.file_detector)
|
34
|
+
local_file = fd.call([path])
|
35
|
+
path = upload(local_file) if local_file
|
36
|
+
end
|
37
|
+
end
|
38
|
+
native.send_keys(path)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
private
|
43
|
+
|
44
|
+
def bridge
|
45
|
+
driver.browser.send(:bridge)
|
46
|
+
end
|
47
|
+
|
48
|
+
def upload(local_file)
|
49
|
+
unless File.file?(local_file)
|
50
|
+
raise Error::WebDriverError, "you may only upload files: #{local_file.inspect}"
|
51
|
+
end
|
52
|
+
|
53
|
+
result = bridge.http.call(:post, "session/#{bridge.session_id}/file", file: Selenium::WebDriver::Zipper.zip_file(local_file))
|
54
|
+
result['value']
|
30
55
|
end
|
31
56
|
end
|
data/lib/capybara/server.rb
CHANGED
@@ -18,7 +18,7 @@ module Capybara
|
|
18
18
|
attr_reader :app, :port, :host
|
19
19
|
|
20
20
|
def initialize(app, *deprecated_options, port: Capybara.server_port, host: Capybara.server_host, reportable_errors: Capybara.server_errors, extra_middleware: [])
|
21
|
-
warn
|
21
|
+
warn 'Positional arguments, other than the application, to Server#new are deprecated, please use keyword arguments' unless deprecated_options.empty?
|
22
22
|
@app = app
|
23
23
|
@extra_middleware = extra_middleware
|
24
24
|
@server_thread = nil # suppress warnings
|
@@ -56,7 +56,7 @@ module Capybara
|
|
56
56
|
def wait_for_pending_requests
|
57
57
|
timer = Capybara::Helpers.timer(expire_in: 60)
|
58
58
|
while pending_requests?
|
59
|
-
raise
|
59
|
+
raise 'Requests did not finish in 60 seconds' if timer.expired?
|
60
60
|
sleep 0.01
|
61
61
|
end
|
62
62
|
end
|
@@ -71,7 +71,7 @@ module Capybara
|
|
71
71
|
|
72
72
|
timer = Capybara::Helpers.timer(expire_in: 60)
|
73
73
|
until responsive?
|
74
|
-
raise
|
74
|
+
raise 'Rack application timed out during boot' if timer.expired?
|
75
75
|
@server_thread.join(0.1)
|
76
76
|
end
|
77
77
|
end
|
data/lib/capybara/session.rb
CHANGED
@@ -75,12 +75,12 @@ module Capybara
|
|
75
75
|
attr_accessor :synchronized
|
76
76
|
|
77
77
|
def initialize(mode, app = nil)
|
78
|
-
raise TypeError,
|
78
|
+
raise TypeError, 'The second parameter to Session::new should be a rack app if passed.' if app && !app.respond_to?(:call)
|
79
79
|
@@instance_created = true
|
80
80
|
@mode = mode
|
81
81
|
@app = app
|
82
82
|
if block_given?
|
83
|
-
raise
|
83
|
+
raise 'A configuration block is only accepted when Capybara.threadsafe == true' unless Capybara.threadsafe
|
84
84
|
yield config
|
85
85
|
end
|
86
86
|
@server = if config.run_server && @app && driver.needs_server?
|
@@ -141,7 +141,7 @@ module Capybara
|
|
141
141
|
# Force an explanation for the error being raised as the exception cause
|
142
142
|
begin
|
143
143
|
if config.raise_server_errors
|
144
|
-
raise CapybaraError,
|
144
|
+
raise CapybaraError, 'Your application server raised an error - It has been raised in your test code because Capybara.raise_server_errors == true'
|
145
145
|
end
|
146
146
|
rescue CapybaraError
|
147
147
|
# needed to get the cause set correctly in JRuby -- otherwise we could just do raise @server.error
|
@@ -190,7 +190,7 @@ module Capybara
|
|
190
190
|
uri = ::Addressable::URI.parse(current_url)
|
191
191
|
|
192
192
|
# Addressable doesn't support opaque URIs - we want nil here
|
193
|
-
return nil if uri&.scheme ==
|
193
|
+
return nil if uri&.scheme == 'about'
|
194
194
|
|
195
195
|
path = uri&.path
|
196
196
|
path unless path&.empty?
|
@@ -383,7 +383,7 @@ module Capybara
|
|
383
383
|
when :parent
|
384
384
|
if scopes.last != :frame
|
385
385
|
raise Capybara::ScopeError, "`switch_to_frame(:parent)` cannot be called from inside a descendant frame's "\
|
386
|
-
|
386
|
+
'`within` block.'
|
387
387
|
end
|
388
388
|
scopes.pop
|
389
389
|
driver.switch_to_frame(:parent)
|
@@ -392,11 +392,13 @@ module Capybara
|
|
392
392
|
if idx
|
393
393
|
if scopes.slice(idx..-1).any? { |scope| ![:frame, nil].include?(scope) }
|
394
394
|
raise Capybara::ScopeError, "`switch_to_frame(:top)` cannot be called from inside a descendant frame's "\
|
395
|
-
|
395
|
+
'`within` block.'
|
396
396
|
end
|
397
397
|
scopes.slice!(idx..-1)
|
398
398
|
driver.switch_to_frame(:top)
|
399
399
|
end
|
400
|
+
else
|
401
|
+
raise ArgumentError, 'You must provide a frame element, :parent, or :top when calling switch_to_frame'
|
400
402
|
end
|
401
403
|
end
|
402
404
|
|
@@ -408,7 +410,7 @@ module Capybara
|
|
408
410
|
# @overload within_frame(element)
|
409
411
|
# @param [Capybara::Node::Element] frame element
|
410
412
|
# @overload within_frame([kind = :frame], locator, **options)
|
411
|
-
# @param [Symbol] kind Optional selector type (:
|
413
|
+
# @param [Symbol] kind Optional selector type (:frame, :css, :xpath, etc.) - Defaults to :frame
|
412
414
|
# @param [String] locator The locator for the given selector kind. For :frame this is the name/id of a frame/iframe element
|
413
415
|
# @overload within_frame(index)
|
414
416
|
# @param [Integer] index index of a frame (0 based)
|
@@ -471,11 +473,11 @@ module Capybara
|
|
471
473
|
# @raise [ArgumentError] if both or neither arguments were provided
|
472
474
|
#
|
473
475
|
def switch_to_window(window = nil, **options, &window_locator)
|
474
|
-
raise ArgumentError,
|
475
|
-
raise ArgumentError,
|
476
|
+
raise ArgumentError, '`switch_to_window` can take either a block or a window, not both' if window && block_given?
|
477
|
+
raise ArgumentError, '`switch_to_window`: either window or block should be provided' if !window && !block_given?
|
476
478
|
unless scopes.last.nil?
|
477
|
-
raise Capybara::ScopeError,
|
478
|
-
|
479
|
+
raise Capybara::ScopeError, '`switch_to_window` is not supposed to be invoked from '\
|
480
|
+
'`within` or `within_frame` blocks.'
|
479
481
|
end
|
480
482
|
|
481
483
|
_switch_to_window(window, options, &window_locator)
|
@@ -512,7 +514,7 @@ module Capybara
|
|
512
514
|
when Proc
|
513
515
|
_switch_to_window { window_or_proc.call }
|
514
516
|
else
|
515
|
-
raise ArgumentError(
|
517
|
+
raise ArgumentError('`#within_window` requires a `Capybara::Window` instance or a lambda')
|
516
518
|
end
|
517
519
|
|
518
520
|
begin
|
@@ -546,7 +548,7 @@ module Capybara
|
|
546
548
|
document.synchronize(wait_time, errors: [Capybara::WindowError]) do
|
547
549
|
opened_handles = (driver.window_handles - old_handles)
|
548
550
|
if opened_handles.size != 1
|
549
|
-
raise Capybara::WindowError,
|
551
|
+
raise Capybara::WindowError, 'block passed to #window_opened_by '\
|
550
552
|
"opened #{opened_handles.size} windows instead of 1"
|
551
553
|
end
|
552
554
|
Window.new(self, opened_handles.first)
|
@@ -774,7 +776,7 @@ module Capybara
|
|
774
776
|
# if set at initialization time, so look at the configuration block that can be passed to the initializer too
|
775
777
|
#
|
776
778
|
def configure
|
777
|
-
raise
|
779
|
+
raise 'Session configuration is only supported when Capybara.threadsafe == true' unless Capybara.threadsafe
|
778
780
|
yield config
|
779
781
|
end
|
780
782
|
|
@@ -813,7 +815,7 @@ module Capybara
|
|
813
815
|
end
|
814
816
|
|
815
817
|
def open_file(path)
|
816
|
-
require
|
818
|
+
require 'launchy'
|
817
819
|
Launchy.open(path)
|
818
820
|
rescue LoadError
|
819
821
|
warn "File saved to #{path}.\nPlease install the launchy gem to open the file automatically."
|
@@ -824,7 +826,7 @@ module Capybara
|
|
824
826
|
end
|
825
827
|
|
826
828
|
def default_fn(extension)
|
827
|
-
timestamp = Time.new.strftime(
|
829
|
+
timestamp = Time.new.strftime('%Y%m%d%H%M%S')
|
828
830
|
"capybara-#{timestamp}#{rand(10**10)}.#{extension}"
|
829
831
|
end
|
830
832
|
|
@@ -846,6 +848,8 @@ module Capybara
|
|
846
848
|
end
|
847
849
|
|
848
850
|
def _find_frame(*args)
|
851
|
+
return find(:frame) if args.length.zero?
|
852
|
+
|
849
853
|
case args[0]
|
850
854
|
when Capybara::Node::Element
|
851
855
|
args[0]
|
@@ -862,8 +866,8 @@ module Capybara
|
|
862
866
|
end
|
863
867
|
|
864
868
|
def _switch_to_window(window = nil, **options)
|
865
|
-
raise Capybara::ScopeError,
|
866
|
-
raise Capybara::ScopeError,
|
869
|
+
raise Capybara::ScopeError, 'Window cannot be switched inside a `within_frame` block' if scopes.include?(:frame)
|
870
|
+
raise Capybara::ScopeError, 'Window cannot be switch inside a `within` block' unless scopes.last.nil?
|
867
871
|
|
868
872
|
if window
|
869
873
|
driver.switch_to_window(window.handle)
|
@@ -882,7 +886,7 @@ module Capybara
|
|
882
886
|
raise e
|
883
887
|
else
|
884
888
|
driver.switch_to_window(original_window_handle)
|
885
|
-
raise Capybara::WindowError,
|
889
|
+
raise Capybara::WindowError, 'Could not find a window matching block/lambda'
|
886
890
|
end
|
887
891
|
end
|
888
892
|
end
|