capybara 2.18.0 → 3.0.0.rc1
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 +26 -1
- data/README.md +12 -12
- data/lib/capybara.rb +13 -25
- data/lib/capybara/config.rb +11 -57
- data/lib/capybara/cucumber.rb +2 -3
- data/lib/capybara/driver/base.rb +5 -16
- data/lib/capybara/driver/node.rb +5 -4
- data/lib/capybara/dsl.rb +1 -0
- data/lib/capybara/helpers.rb +16 -28
- data/lib/capybara/minitest.rb +139 -138
- data/lib/capybara/minitest/spec.rb +15 -14
- data/lib/capybara/node/actions.rb +59 -81
- data/lib/capybara/node/base.rb +11 -18
- data/lib/capybara/node/document.rb +2 -2
- data/lib/capybara/node/document_matchers.rb +8 -8
- data/lib/capybara/node/element.rb +30 -40
- data/lib/capybara/node/finders.rb +62 -70
- data/lib/capybara/node/matchers.rb +48 -71
- data/lib/capybara/node/simple.rb +11 -17
- data/lib/capybara/queries/ancestor_query.rb +4 -6
- data/lib/capybara/queries/base_query.rb +18 -17
- data/lib/capybara/queries/current_path_query.rb +8 -24
- data/lib/capybara/queries/match_query.rb +3 -7
- data/lib/capybara/queries/selector_query.rb +92 -95
- data/lib/capybara/queries/sibling_query.rb +4 -4
- data/lib/capybara/queries/text_query.rb +37 -34
- data/lib/capybara/queries/title_query.rb +8 -11
- data/lib/capybara/rack_test/browser.rb +15 -18
- data/lib/capybara/rack_test/css_handlers.rb +6 -4
- data/lib/capybara/rack_test/driver.rb +6 -10
- data/lib/capybara/rack_test/form.rb +50 -40
- data/lib/capybara/rack_test/node.rb +70 -56
- data/lib/capybara/rails.rb +2 -6
- data/lib/capybara/result.rb +22 -22
- data/lib/capybara/rspec.rb +5 -10
- data/lib/capybara/rspec/compound.rb +5 -10
- data/lib/capybara/rspec/features.rb +17 -48
- data/lib/capybara/rspec/matcher_proxies.rb +31 -15
- data/lib/capybara/rspec/matchers.rb +70 -60
- data/lib/capybara/selector.rb +129 -117
- data/lib/capybara/selector/css.rb +6 -11
- data/lib/capybara/selector/filter.rb +1 -17
- data/lib/capybara/selector/filter_set.rb +17 -14
- data/lib/capybara/selector/filters/base.rb +7 -6
- data/lib/capybara/selector/filters/expression_filter.rb +6 -23
- data/lib/capybara/selector/filters/node_filter.rb +2 -12
- data/lib/capybara/selector/selector.rb +27 -33
- data/lib/capybara/selenium/driver.rb +113 -127
- data/lib/capybara/selenium/node.rb +148 -113
- data/lib/capybara/server.rb +3 -2
- data/lib/capybara/session.rb +137 -223
- data/lib/capybara/session/config.rb +47 -67
- data/lib/capybara/session/matchers.rb +8 -7
- data/lib/capybara/spec/public/test.js +26 -4
- data/lib/capybara/spec/session/accept_alert_spec.rb +1 -0
- data/lib/capybara/spec/session/accept_confirm_spec.rb +3 -2
- data/lib/capybara/spec/session/accept_prompt_spec.rb +1 -0
- data/lib/capybara/spec/session/all_spec.rb +31 -18
- data/lib/capybara/spec/session/ancestor_spec.rb +2 -4
- data/lib/capybara/spec/session/assert_all_of_selectors_spec.rb +6 -5
- data/lib/capybara/spec/session/assert_current_path.rb +12 -11
- data/lib/capybara/spec/session/assert_selector.rb +1 -0
- data/lib/capybara/spec/session/assert_text.rb +18 -17
- data/lib/capybara/spec/session/assert_title.rb +1 -0
- data/lib/capybara/spec/session/attach_file_spec.rb +14 -13
- data/lib/capybara/spec/session/body_spec.rb +1 -0
- data/lib/capybara/spec/session/check_spec.rb +7 -6
- data/lib/capybara/spec/session/choose_spec.rb +5 -4
- data/lib/capybara/spec/session/click_button_spec.rb +20 -28
- data/lib/capybara/spec/session/click_link_or_button_spec.rb +8 -7
- data/lib/capybara/spec/session/click_link_spec.rb +8 -7
- data/lib/capybara/spec/session/current_scope_spec.rb +4 -3
- data/lib/capybara/spec/session/current_url_spec.rb +7 -6
- data/lib/capybara/spec/session/dismiss_confirm_spec.rb +1 -1
- data/lib/capybara/spec/session/dismiss_prompt_spec.rb +1 -0
- data/lib/capybara/spec/session/element/assert_match_selector.rb +1 -1
- data/lib/capybara/spec/session/element/match_xpath_spec.rb +1 -1
- data/lib/capybara/spec/session/element/matches_selector_spec.rb +5 -5
- data/lib/capybara/spec/session/evaluate_async_script_spec.rb +3 -2
- data/lib/capybara/spec/session/evaluate_script_spec.rb +4 -3
- data/lib/capybara/spec/session/execute_script_spec.rb +4 -3
- data/lib/capybara/spec/session/fill_in_spec.rb +6 -5
- data/lib/capybara/spec/session/find_button_spec.rb +4 -3
- data/lib/capybara/spec/session/find_by_id_spec.rb +2 -1
- data/lib/capybara/spec/session/find_field_spec.rb +8 -14
- data/lib/capybara/spec/session/find_link_spec.rb +6 -5
- data/lib/capybara/spec/session/find_spec.rb +37 -31
- data/lib/capybara/spec/session/first_spec.rb +60 -33
- data/lib/capybara/spec/session/frame/switch_to_frame_spec.rb +2 -1
- data/lib/capybara/spec/session/frame/within_frame_spec.rb +9 -16
- data/lib/capybara/spec/session/go_back_spec.rb +1 -0
- data/lib/capybara/spec/session/go_forward_spec.rb +1 -0
- data/lib/capybara/spec/session/has_all_selectors_spec.rb +15 -15
- data/lib/capybara/spec/session/has_button_spec.rb +2 -1
- data/lib/capybara/spec/session/has_css_spec.rb +3 -2
- data/lib/capybara/spec/session/has_current_path_spec.rb +12 -28
- data/lib/capybara/spec/session/has_field_spec.rb +4 -3
- data/lib/capybara/spec/session/has_link_spec.rb +1 -0
- data/lib/capybara/spec/session/has_none_selectors_spec.rb +17 -17
- data/lib/capybara/spec/session/has_select_spec.rb +30 -29
- data/lib/capybara/spec/session/has_selector_spec.rb +5 -4
- data/lib/capybara/spec/session/has_table_spec.rb +2 -1
- data/lib/capybara/spec/session/has_text_spec.rb +6 -5
- data/lib/capybara/spec/session/has_title_spec.rb +1 -0
- data/lib/capybara/spec/session/has_xpath_spec.rb +1 -0
- data/lib/capybara/spec/session/headers.rb +2 -1
- data/lib/capybara/spec/session/html_spec.rb +1 -0
- data/lib/capybara/spec/session/node_spec.rb +91 -56
- data/lib/capybara/spec/session/node_wrapper_spec.rb +36 -0
- data/lib/capybara/spec/session/refresh_spec.rb +4 -2
- data/lib/capybara/spec/session/reset_session_spec.rb +1 -0
- data/lib/capybara/spec/session/response_code.rb +1 -0
- data/lib/capybara/spec/session/save_and_open_page_spec.rb +1 -0
- data/lib/capybara/spec/session/save_and_open_screenshot_spec.rb +6 -11
- data/lib/capybara/spec/session/save_page_spec.rb +1 -17
- data/lib/capybara/spec/session/save_screenshot_spec.rb +1 -1
- data/lib/capybara/spec/session/select_spec.rb +20 -20
- data/lib/capybara/spec/session/selectors_spec.rb +2 -2
- data/lib/capybara/spec/session/sibling_spec.rb +1 -1
- data/lib/capybara/spec/session/text_spec.rb +1 -0
- data/lib/capybara/spec/session/title_spec.rb +1 -1
- data/lib/capybara/spec/session/uncheck_spec.rb +4 -3
- data/lib/capybara/spec/session/unselect_spec.rb +6 -5
- data/lib/capybara/spec/session/visit_spec.rb +9 -3
- data/lib/capybara/spec/session/window/become_closed_spec.rb +2 -1
- data/lib/capybara/spec/session/window/current_window_spec.rb +1 -0
- data/lib/capybara/spec/session/window/open_new_window_spec.rb +1 -0
- data/lib/capybara/spec/session/window/switch_to_window_spec.rb +2 -1
- data/lib/capybara/spec/session/window/window_opened_by_spec.rb +2 -1
- data/lib/capybara/spec/session/window/window_spec.rb +12 -12
- data/lib/capybara/spec/session/window/windows_spec.rb +2 -3
- data/lib/capybara/spec/session/window/within_window_spec.rb +13 -68
- data/lib/capybara/spec/session/within_spec.rb +1 -0
- data/lib/capybara/spec/spec_helper.rb +26 -18
- data/lib/capybara/spec/test_app.rb +8 -9
- data/lib/capybara/spec/views/form.erb +1 -0
- data/lib/capybara/spec/views/with_html.erb +3 -1
- data/lib/capybara/spec/views/within_frames.erb +4 -1
- data/lib/capybara/version.rb +2 -1
- data/lib/capybara/window.rb +6 -10
- data/spec/basic_node_spec.rb +1 -0
- data/spec/capybara_spec.rb +9 -32
- data/spec/dsl_spec.rb +5 -13
- data/spec/filter_set_spec.rb +5 -4
- data/spec/fixtures/selenium_driver_rspec_failure.rb +2 -1
- data/spec/fixtures/selenium_driver_rspec_success.rb +3 -2
- data/spec/minitest_spec.rb +4 -3
- data/spec/minitest_spec_spec.rb +3 -2
- data/spec/per_session_config_spec.rb +9 -8
- data/spec/rack_test_spec.rb +21 -20
- data/spec/result_spec.rb +17 -16
- data/spec/rspec/features_spec.rb +17 -14
- data/spec/rspec/scenarios_spec.rb +5 -7
- data/spec/rspec/shared_spec_matchers.rb +96 -99
- data/spec/rspec/views_spec.rb +2 -1
- data/spec/rspec_matchers_spec.rb +19 -2
- data/spec/rspec_spec.rb +11 -15
- data/spec/selector_spec.rb +5 -6
- data/spec/selenium_spec_chrome.rb +7 -4
- data/spec/selenium_spec_marionette.rb +26 -12
- data/spec/server_spec.rb +33 -33
- data/spec/session_spec.rb +2 -1
- data/spec/shared_selenium_session.rb +27 -21
- data/spec/spec_helper.rb +2 -5
- metadata +66 -87
- data/lib/capybara/query.rb +0 -7
- data/spec/selenium_spec_firefox.rb +0 -68
@@ -1,4 +1,5 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
+
|
2
3
|
module Capybara
|
3
4
|
module Queries
|
4
5
|
class SiblingQuery < MatchQuery
|
@@ -7,7 +8,7 @@ module Capybara
|
|
7
8
|
@sibling_node = node
|
8
9
|
node.synchronize do
|
9
10
|
match_results = super(node.session.current_scope, exact)
|
10
|
-
node.all(:xpath, XPath.preceding_sibling
|
11
|
+
node.all(:xpath, XPath.preceding_sibling + XPath.following_sibling) do |el|
|
11
12
|
match_results.include?(el)
|
12
13
|
end
|
13
14
|
end
|
@@ -15,9 +16,8 @@ module Capybara
|
|
15
16
|
|
16
17
|
def description
|
17
18
|
desc = super
|
18
|
-
|
19
|
-
|
20
|
-
end
|
19
|
+
sibling_query = @sibling_node && @sibling_node.instance_variable_get(:@query)
|
20
|
+
desc += " that is a sibling of #{sibling_query.description}" if sibling_query
|
21
21
|
desc
|
22
22
|
end
|
23
23
|
end
|
@@ -1,22 +1,26 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
+
|
2
3
|
module Capybara
|
3
4
|
# @api private
|
4
5
|
module Queries
|
5
6
|
class TextQuery < BaseQuery
|
6
|
-
def initialize(
|
7
|
-
@type =
|
8
|
-
|
9
|
-
|
7
|
+
def initialize(type = nil, expected_text, session_options:, **options) # rubocop:disable Style/OptionalArguments
|
8
|
+
@type = if type.nil?
|
9
|
+
Capybara.ignore_hidden_elements || Capybara.visible_text_only ? :visible : :all
|
10
|
+
else
|
11
|
+
type
|
12
|
+
end
|
13
|
+
@expected_text = if expected_text.is_a?(Regexp)
|
14
|
+
expected_text
|
15
|
+
else
|
16
|
+
Capybara::Helpers.normalize_whitespace(expected_text)
|
17
|
+
end
|
18
|
+
@options = options
|
10
19
|
super(@options)
|
20
|
+
self.session_options = session_options
|
11
21
|
|
12
|
-
@
|
22
|
+
@search_regexp = Capybara::Helpers.to_regexp(@expected_text, exact: exact?)
|
13
23
|
|
14
|
-
@expected_text = args.shift
|
15
|
-
unless @expected_text.is_a?(Regexp)
|
16
|
-
@expected_text = Capybara::Helpers.normalize_whitespace(@expected_text)
|
17
|
-
end
|
18
|
-
@search_regexp = Capybara::Helpers.to_regexp(@expected_text, nil, exact?)
|
19
|
-
warn "Unused parameters passed to #{self.class.name} : #{args}" unless args.empty?
|
20
24
|
assert_valid_keys
|
21
25
|
end
|
22
26
|
|
@@ -42,48 +46,47 @@ module Capybara
|
|
42
46
|
end
|
43
47
|
end
|
44
48
|
|
45
|
-
|
49
|
+
private
|
46
50
|
|
47
51
|
def exact?
|
48
52
|
options.fetch(:exact, session_options.exact_text)
|
49
53
|
end
|
50
54
|
|
51
55
|
def build_message(report_on_invisible)
|
52
|
-
message =
|
56
|
+
message = "".dup
|
53
57
|
unless (COUNT_KEYS & @options.keys).empty?
|
54
58
|
message << " but found #{@count} #{Capybara::Helpers.declension('time', 'times', @count)}"
|
55
59
|
end
|
56
60
|
message << " in #{@actual_text.inspect}"
|
57
61
|
|
58
62
|
details_message = []
|
63
|
+
details_message << case_insensitive_message if @node and !@expected_text.is_a? Regexp
|
64
|
+
details_message << invisible_message if @node and check_visible_text? and report_on_invisible
|
65
|
+
details_message.compact!
|
59
66
|
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
if insensitive_count != @count
|
64
|
-
details_message << "it was found #{insensitive_count} #{Capybara::Helpers.declension("time", "times", insensitive_count)} using a case insensitive search"
|
65
|
-
end
|
66
|
-
end
|
67
|
+
message << ". (However, #{details_message.join(' and ')}.)" unless details_message.empty?
|
68
|
+
message
|
69
|
+
end
|
67
70
|
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
details_message << "it was found #{invisible_count} #{Capybara::Helpers.declension("time", "times", invisible_count)} including non-visible text"
|
74
|
-
end
|
75
|
-
rescue
|
76
|
-
# An error getting the non-visible text (if element goes out of scope) should not affect the response
|
77
|
-
end
|
71
|
+
def case_insensitive_message
|
72
|
+
insensitive_regexp = Capybara::Helpers.to_regexp(@expected_text, options: Regexp::IGNORECASE)
|
73
|
+
insensitive_count = @actual_text.scan(insensitive_regexp).size
|
74
|
+
if insensitive_count != @count
|
75
|
+
"it was found #{insensitive_count} #{Capybara::Helpers.declension("time", "times", insensitive_count)} using a case insensitive search"
|
78
76
|
end
|
77
|
+
end
|
79
78
|
|
80
|
-
|
81
|
-
|
82
|
-
|
79
|
+
def invisible_message
|
80
|
+
invisible_text = text(@node, :all)
|
81
|
+
invisible_count = invisible_text.scan(@search_regexp).size
|
82
|
+
if invisible_count != @count
|
83
|
+
"it was found #{invisible_count} #{Capybara::Helpers.declension("time", "times", invisible_count)} including non-visible text"
|
84
|
+
end
|
85
|
+
rescue # An error getting the non-visible text (if element goes out of scope) should not affect the response
|
83
86
|
end
|
84
87
|
|
85
88
|
def valid_keys
|
86
|
-
COUNT_KEYS + [
|
89
|
+
COUNT_KEYS + %i[wait exact]
|
87
90
|
end
|
88
91
|
|
89
92
|
def check_visible_text?
|
@@ -1,22 +1,19 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
+
|
2
3
|
module Capybara
|
3
4
|
# @api private
|
4
5
|
module Queries
|
5
6
|
class TitleQuery < BaseQuery
|
6
|
-
def initialize(expected_title, options
|
7
|
-
@expected_title = expected_title
|
7
|
+
def initialize(expected_title, **options)
|
8
|
+
@expected_title = expected_title.is_a?(Regexp) ? expected_title : Capybara::Helpers.normalize_whitespace(expected_title)
|
8
9
|
@options = options
|
9
10
|
super(@options)
|
10
|
-
|
11
|
-
@expected_title = Capybara::Helpers.normalize_whitespace(@expected_title)
|
12
|
-
end
|
13
|
-
@search_regexp = Capybara::Helpers.to_regexp(@expected_title, nil, options.fetch(:exact, false))
|
11
|
+
@search_regexp = Capybara::Helpers.to_regexp(@expected_title, exact: options.fetch(:exact, false))
|
14
12
|
assert_valid_keys
|
15
13
|
end
|
16
14
|
|
17
15
|
def resolves_for?(node)
|
18
|
-
@actual_title = node.title
|
19
|
-
@actual_title.match(@search_regexp)
|
16
|
+
(@actual_title = node.title).match(@search_regexp)
|
20
17
|
end
|
21
18
|
|
22
19
|
def failure_message
|
@@ -27,15 +24,15 @@ module Capybara
|
|
27
24
|
failure_message_helper(' not')
|
28
25
|
end
|
29
26
|
|
30
|
-
|
27
|
+
private
|
31
28
|
|
32
29
|
def failure_message_helper(negated = '')
|
33
|
-
verb =
|
30
|
+
verb = @expected_title.is_a?(Regexp) ? 'match' : 'include'
|
34
31
|
"expected #{@actual_title.inspect}#{negated} to #{verb} #{@expected_title.inspect}"
|
35
32
|
end
|
36
33
|
|
37
34
|
def valid_keys
|
38
|
-
[
|
35
|
+
%i[wait exact]
|
39
36
|
end
|
40
37
|
end
|
41
38
|
end
|
@@ -1,4 +1,5 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
+
|
2
3
|
class Capybara::RackTest::Browser
|
3
4
|
include ::Rack::Test::Methods
|
4
5
|
|
@@ -17,7 +18,7 @@ class Capybara::RackTest::Browser
|
|
17
18
|
driver.options
|
18
19
|
end
|
19
20
|
|
20
|
-
def visit(path, attributes
|
21
|
+
def visit(path, **attributes)
|
21
22
|
reset_host!
|
22
23
|
process_and_follow_redirects(:get, path, attributes)
|
23
24
|
end
|
@@ -28,23 +29,24 @@ class Capybara::RackTest::Browser
|
|
28
29
|
end
|
29
30
|
|
30
31
|
def submit(method, path, attributes)
|
31
|
-
path = request_path if
|
32
|
-
process_and_follow_redirects(method, path, attributes,
|
32
|
+
path = request_path if path.nil? || path.empty?
|
33
|
+
process_and_follow_redirects(method, path, attributes, 'HTTP_REFERER' => current_url)
|
33
34
|
end
|
34
35
|
|
35
|
-
def follow(method, path, attributes
|
36
|
+
def follow(method, path, **attributes)
|
36
37
|
return if path.gsub(/^#{Regexp.escape(request_path)}/, '').start_with?('#') || path.downcase.start_with?('javascript:')
|
37
|
-
process_and_follow_redirects(method, path, attributes,
|
38
|
+
process_and_follow_redirects(method, path, attributes, 'HTTP_REFERER' => current_url)
|
38
39
|
end
|
39
40
|
|
40
41
|
def process_and_follow_redirects(method, path, attributes = {}, env = {})
|
41
42
|
process(method, path, attributes, env)
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
43
|
+
|
44
|
+
return unless driver.follow_redirects?
|
45
|
+
|
46
|
+
driver.redirect_limit.times do
|
47
|
+
process(:get, last_response["Location"], {}, env) if last_response.redirect?
|
47
48
|
end
|
49
|
+
raise Capybara::InfiniteRedirectError, "redirected more than #{driver.redirect_limit} times, check for infinite redirects." if last_response.redirect?
|
48
50
|
end
|
49
51
|
|
50
52
|
def process(method, path, attributes = {}, env = {})
|
@@ -55,7 +57,7 @@ class Capybara::RackTest::Browser
|
|
55
57
|
else
|
56
58
|
new_uri.path = request_path if path.start_with?("?")
|
57
59
|
new_uri.path = "/" if new_uri.path.empty?
|
58
|
-
new_uri.path = request_path.sub(%r
|
60
|
+
new_uri.path = request_path.sub(%r{/[^/]*$}, '/') + new_uri.path unless new_uri.path.start_with?('/')
|
59
61
|
end
|
60
62
|
new_uri.scheme ||= @current_scheme
|
61
63
|
new_uri.host ||= @current_host
|
@@ -91,7 +93,7 @@ class Capybara::RackTest::Browser
|
|
91
93
|
end
|
92
94
|
|
93
95
|
def find(format, selector)
|
94
|
-
if format
|
96
|
+
if format == :css
|
95
97
|
dom.css(selector, Capybara::RackTest::CSSHandlers.new)
|
96
98
|
else
|
97
99
|
dom.xpath(selector)
|
@@ -105,12 +107,7 @@ class Capybara::RackTest::Browser
|
|
105
107
|
end
|
106
108
|
|
107
109
|
def title
|
108
|
-
|
109
|
-
dom.title
|
110
|
-
else
|
111
|
-
#old versions of nokogiri don't have #title - remove in 3.0
|
112
|
-
dom.xpath('/html/head/title | /html/title').first.text
|
113
|
-
end
|
110
|
+
dom.title
|
114
111
|
end
|
115
112
|
|
116
113
|
protected
|
@@ -1,11 +1,13 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
+
|
2
3
|
class Capybara::RackTest::CSSHandlers < BasicObject
|
3
4
|
include ::Kernel
|
4
|
-
|
5
|
-
def disabled
|
5
|
+
|
6
|
+
def disabled(list)
|
6
7
|
list.find_all { |node| node.has_attribute? 'disabled' }
|
7
|
-
end
|
8
|
-
|
8
|
+
end
|
9
|
+
|
10
|
+
def enabled(list)
|
9
11
|
list.find_all { |node| !node.has_attribute? 'disabled' }
|
10
12
|
end
|
11
13
|
end
|
@@ -1,4 +1,5 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
+
|
2
3
|
require 'rack/test'
|
3
4
|
require 'rack/utils'
|
4
5
|
require 'mini_mime'
|
@@ -10,10 +11,10 @@ class Capybara::RackTest::Driver < Capybara::Driver::Base
|
|
10
11
|
respect_data_method: false,
|
11
12
|
follow_redirects: true,
|
12
13
|
redirect_limit: 5
|
13
|
-
}
|
14
|
+
}.freeze
|
14
15
|
attr_reader :app, :options
|
15
16
|
|
16
|
-
def initialize(app, options
|
17
|
+
def initialize(app, **options)
|
17
18
|
raise ArgumentError, "rack-test requires a rack application, but none was given" unless app
|
18
19
|
@session = nil
|
19
20
|
@app = app
|
@@ -40,7 +41,7 @@ class Capybara::RackTest::Driver < Capybara::Driver::Base
|
|
40
41
|
browser.last_request
|
41
42
|
end
|
42
43
|
|
43
|
-
def visit(path, attributes
|
44
|
+
def visit(path, **attributes)
|
44
45
|
browser.visit(path, attributes)
|
45
46
|
end
|
46
47
|
|
@@ -52,7 +53,7 @@ class Capybara::RackTest::Driver < Capybara::Driver::Base
|
|
52
53
|
browser.submit(method, path, attributes)
|
53
54
|
end
|
54
55
|
|
55
|
-
def follow(method, path, attributes
|
56
|
+
def follow(method, path, **attributes)
|
56
57
|
browser.follow(method, path, attributes)
|
57
58
|
end
|
58
59
|
|
@@ -73,7 +74,7 @@ class Capybara::RackTest::Driver < Capybara::Driver::Base
|
|
73
74
|
end
|
74
75
|
|
75
76
|
def find_css(selector)
|
76
|
-
browser.find(:css,selector)
|
77
|
+
browser.find(:css, selector)
|
77
78
|
end
|
78
79
|
|
79
80
|
def html
|
@@ -92,11 +93,6 @@ class Capybara::RackTest::Driver < Capybara::Driver::Base
|
|
92
93
|
@browser = nil
|
93
94
|
end
|
94
95
|
|
95
|
-
# @deprecated This method is being removed
|
96
|
-
def browser_initialized?
|
97
|
-
super && !@browser.nil?
|
98
|
-
end
|
99
|
-
|
100
96
|
def get(*args, &block); browser.get(*args, &block); end
|
101
97
|
def post(*args, &block); browser.post(*args, &block); end
|
102
98
|
def put(*args, &block); browser.put(*args, &block); end
|
@@ -1,4 +1,5 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
+
|
2
3
|
class Capybara::RackTest::Form < Capybara::RackTest::Node
|
3
4
|
# This only needs to inherit from Rack::Test::UploadedFile because Rack::Test checks for
|
4
5
|
# the class specifically when determining whether to construct the request as multipart.
|
@@ -20,53 +21,21 @@ class Capybara::RackTest::Form < Capybara::RackTest::Node
|
|
20
21
|
def params(button)
|
21
22
|
params = make_params
|
22
23
|
|
23
|
-
form_element_types=[
|
24
|
-
form_elements_xpath=XPath.generate do |x|
|
25
|
-
xpath=x.descendant(*form_element_types).where(
|
26
|
-
xpath
|
27
|
-
xpath.where(
|
24
|
+
form_element_types = %i[input select textarea]
|
25
|
+
form_elements_xpath = XPath.generate do |x|
|
26
|
+
xpath = x.descendant(*form_element_types).where(!x.attr(:form))
|
27
|
+
xpath += x.anywhere(*form_element_types).where(x.attr(:form) == native[:id]) if native[:id]
|
28
|
+
xpath.where(!x.attr(:disabled))
|
28
29
|
end.to_s
|
29
30
|
|
30
31
|
native.xpath(form_elements_xpath).map do |field|
|
31
32
|
case field.name
|
32
33
|
when 'input'
|
33
|
-
|
34
|
-
if field['checked']
|
35
|
-
node=Capybara::RackTest::Node.new(self.driver, field)
|
36
|
-
merge_param!(params, field['name'].to_s, node.value.to_s)
|
37
|
-
end
|
38
|
-
elsif %w(submit image).include? field['type']
|
39
|
-
# TO DO identify the click button here (in document order, rather
|
40
|
-
# than leaving until the end of the params)
|
41
|
-
elsif field['type'] =='file'
|
42
|
-
if multipart?
|
43
|
-
file = \
|
44
|
-
if (value = field['value']).to_s.empty?
|
45
|
-
NilUploadedFile.new
|
46
|
-
else
|
47
|
-
mime_info = MiniMime.lookup_by_filename(value)
|
48
|
-
Rack::Test::UploadedFile.new(value, (mime_info && mime_info.content_type).to_s)
|
49
|
-
end
|
50
|
-
merge_param!(params, field['name'].to_s, file)
|
51
|
-
else
|
52
|
-
merge_param!(params, field['name'].to_s, File.basename(field['value'].to_s))
|
53
|
-
end
|
54
|
-
else
|
55
|
-
merge_param!(params, field['name'].to_s, field['value'].to_s)
|
56
|
-
end
|
34
|
+
add_input_param(field, params)
|
57
35
|
when 'select'
|
58
|
-
|
59
|
-
options = field.xpath(".//option[@selected]")
|
60
|
-
options.each do |option|
|
61
|
-
merge_param!(params, field['name'].to_s, (option['value'] || option.text).to_s)
|
62
|
-
end
|
63
|
-
else
|
64
|
-
option = field.xpath(".//option[@selected]").first
|
65
|
-
option ||= field.xpath('.//option').first
|
66
|
-
merge_param!(params, field['name'].to_s, (option['value'] || option.text).to_s) if option
|
67
|
-
end
|
36
|
+
add_select_param(field, params)
|
68
37
|
when 'textarea'
|
69
|
-
|
38
|
+
add_textarea_param(field, params)
|
70
39
|
end
|
71
40
|
end
|
72
41
|
merge_param!(params, button[:name], button[:value] || "") if button[:name]
|
@@ -111,4 +80,45 @@ private
|
|
111
80
|
ParamsHash.new
|
112
81
|
end
|
113
82
|
end
|
83
|
+
|
84
|
+
def add_input_param(field, params)
|
85
|
+
if %w[radio checkbox].include? field['type']
|
86
|
+
if field['checked']
|
87
|
+
node = Capybara::RackTest::Node.new(driver, field)
|
88
|
+
merge_param!(params, field['name'].to_s, node.value.to_s)
|
89
|
+
end
|
90
|
+
elsif %w[submit image].include? field['type']
|
91
|
+
# TODO: identify the click button here (in document order, rather
|
92
|
+
# than leaving until the end of the params)
|
93
|
+
elsif field['type'] == 'file'
|
94
|
+
if multipart?
|
95
|
+
file = if (value = field['value']).to_s.empty?
|
96
|
+
NilUploadedFile.new
|
97
|
+
else
|
98
|
+
mime_info = MiniMime.lookup_by_filename(value)
|
99
|
+
Rack::Test::UploadedFile.new(value, (mime_info && mime_info.content_type).to_s)
|
100
|
+
end
|
101
|
+
merge_param!(params, field['name'].to_s, file)
|
102
|
+
else
|
103
|
+
merge_param!(params, field['name'].to_s, File.basename(field['value'].to_s))
|
104
|
+
end
|
105
|
+
else
|
106
|
+
merge_param!(params, field['name'].to_s, field['value'].to_s)
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
def add_select_param(field, params)
|
111
|
+
if field['multiple'] == 'multiple'
|
112
|
+
field.xpath(".//option[@selected]").each do |option|
|
113
|
+
merge_param!(params, field['name'].to_s, (option['value'] || option.text).to_s)
|
114
|
+
end
|
115
|
+
else
|
116
|
+
option = field.xpath('.//option[@selected]').first || field.xpath('.//option').first
|
117
|
+
merge_param!(params, field['name'].to_s, (option['value'] || option.text).to_s) if option
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
def add_textarea_param(field, params)
|
122
|
+
merge_param!(params, field['name'].to_s, field['_capybara_raw_value'].to_s.gsub(/\n/, "\r\n"))
|
123
|
+
end
|
114
124
|
end
|
@@ -1,4 +1,5 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
+
|
2
3
|
class Capybara::RackTest::Node < Capybara::Driver::Node
|
3
4
|
def all_text
|
4
5
|
Capybara::Helpers.normalize_whitespace(native.text)
|
@@ -16,64 +17,45 @@ class Capybara::RackTest::Node < Capybara::Driver::Node
|
|
16
17
|
string_node.value
|
17
18
|
end
|
18
19
|
|
19
|
-
def set(value)
|
20
|
-
return if disabled?
|
21
|
-
|
22
|
-
|
23
|
-
return
|
24
|
-
end
|
20
|
+
def set(value, **options)
|
21
|
+
return if disabled? || readonly?
|
22
|
+
|
23
|
+
warn "Options passed to Node#set but the RackTest driver doesn't support any - ignoring" unless options.empty?
|
25
24
|
|
26
|
-
if (Array
|
27
|
-
raise TypeError
|
25
|
+
if value.is_a?(Array) && !multiple?
|
26
|
+
raise TypeError, "Value cannot be an Array when 'multiple' attribute is not present. Not a #{value.class}"
|
28
27
|
end
|
29
28
|
|
30
|
-
if radio?
|
31
|
-
|
32
|
-
elsif
|
33
|
-
|
34
|
-
elsif input_field?
|
35
|
-
set_input(value)
|
36
|
-
elsif textarea?
|
37
|
-
native['_capybara_raw_value'] = value.to_s
|
29
|
+
if radio? then set_radio(value)
|
30
|
+
elsif checkbox? then set_checkbox(value)
|
31
|
+
elsif input_field? then set_input(value)
|
32
|
+
elsif textarea? then native['_capybara_raw_value'] = value.to_s
|
38
33
|
end
|
39
34
|
end
|
40
35
|
|
41
36
|
def select_option
|
42
37
|
return if disabled?
|
43
|
-
|
44
|
-
select_node.find_xpath(".//option[@selected]").each { |node| node.native.remove_attribute("selected") }
|
45
|
-
end
|
38
|
+
deselect_options unless select_node.multiple?
|
46
39
|
native["selected"] = 'selected'
|
47
40
|
end
|
48
41
|
|
49
42
|
def unselect_option
|
50
|
-
|
51
|
-
raise Capybara::UnselectNotAllowed, "Cannot unselect option from single select box."
|
52
|
-
end
|
43
|
+
raise Capybara::UnselectNotAllowed, "Cannot unselect option from single select box." unless select_node.multiple?
|
53
44
|
native.remove_attribute('selected')
|
54
45
|
end
|
55
46
|
|
56
|
-
def click
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
elsif
|
62
|
-
((tag_name == 'button') and type.nil? or type == "submit")
|
47
|
+
def click(keys = [], offset = {})
|
48
|
+
raise ArgumentError, "The RackTest driver does not support click options" unless keys.empty? && offset.empty?
|
49
|
+
|
50
|
+
if link?
|
51
|
+
follow_link
|
52
|
+
elsif submits?
|
63
53
|
associated_form = form
|
64
54
|
Capybara::RackTest::Form.new(driver, associated_form).submit(self) if associated_form
|
65
|
-
elsif
|
55
|
+
elsif checkable?
|
66
56
|
set(!checked?)
|
67
|
-
elsif
|
68
|
-
|
69
|
-
find_xpath("//input[@id='#{native[:for]}']").first
|
70
|
-
else
|
71
|
-
find_xpath(".//input").first
|
72
|
-
end
|
73
|
-
|
74
|
-
if labelled_control && (labelled_control.checkbox? || labelled_control.radio?)
|
75
|
-
labelled_control.set(!labelled_control.checked?)
|
76
|
-
end
|
57
|
+
elsif tag_name == 'label'
|
58
|
+
click_label
|
77
59
|
end
|
78
60
|
end
|
79
61
|
|
@@ -94,10 +76,12 @@ class Capybara::RackTest::Node < Capybara::Driver::Node
|
|
94
76
|
end
|
95
77
|
|
96
78
|
def disabled?
|
97
|
-
if
|
98
|
-
|
79
|
+
return true if string_node.disabled?
|
80
|
+
|
81
|
+
if %w[option optgroup].include? tag_name
|
82
|
+
find_xpath("parent::*[self::optgroup or self::select]")[0].disabled?
|
99
83
|
else
|
100
|
-
|
84
|
+
!find_xpath("parent::fieldset[@disabled] | ancestor::*[not(self::legend) or preceding-sibling::legend][parent::fieldset[@disabled]]").empty?
|
101
85
|
end
|
102
86
|
end
|
103
87
|
|
@@ -135,6 +119,10 @@ protected
|
|
135
119
|
|
136
120
|
private
|
137
121
|
|
122
|
+
def deselect_options
|
123
|
+
select_node.find_xpath(".//option[@selected]").each { |node| node.native.remove_attribute("selected") }
|
124
|
+
end
|
125
|
+
|
138
126
|
def string_node
|
139
127
|
@string_node ||= Capybara::Node::Simple.new(native)
|
140
128
|
end
|
@@ -150,19 +138,19 @@ private
|
|
150
138
|
|
151
139
|
def form
|
152
140
|
if native[:form]
|
153
|
-
native.xpath("//form[@id='#{native[:form]}']")
|
141
|
+
native.xpath("//form[@id='#{native[:form]}']")
|
154
142
|
else
|
155
|
-
native.ancestors('form')
|
156
|
-
end
|
143
|
+
native.ancestors('form')
|
144
|
+
end.first
|
157
145
|
end
|
158
146
|
|
159
|
-
def set_radio(_value)
|
160
|
-
other_radios_xpath = XPath.generate { |x| x.anywhere(:input)[x.attr(:name)
|
147
|
+
def set_radio(_value) # rubocop:disable Naming/AccessorMethodName
|
148
|
+
other_radios_xpath = XPath.generate { |x| x.anywhere(:input)[x.attr(:name) == self[:name]] }.to_s
|
161
149
|
driver.dom.xpath(other_radios_xpath).each { |node| node.remove_attribute("checked") }
|
162
150
|
native['checked'] = 'checked'
|
163
151
|
end
|
164
152
|
|
165
|
-
def set_checkbox(value)
|
153
|
+
def set_checkbox(value) # rubocop:disable Naming/AccessorMethodName
|
166
154
|
if value && !native['checked']
|
167
155
|
native['checked'] = 'checked'
|
168
156
|
elsif !value && native['checked']
|
@@ -170,13 +158,13 @@ private
|
|
170
158
|
end
|
171
159
|
end
|
172
160
|
|
173
|
-
def set_input(value)
|
161
|
+
def set_input(value) # rubocop:disable Naming/AccessorMethodName
|
174
162
|
if text_or_password? && attribute_is_not_blank?(:maxlength)
|
175
163
|
# Browser behavior for maxlength="0" is inconsistent, so we stick with
|
176
164
|
# Firefox, allowing no input
|
177
165
|
value = value.to_s[0...self[:maxlength].to_i]
|
178
166
|
end
|
179
|
-
if Array
|
167
|
+
if value.is_a?(Array) # Assert multiple attribute is present
|
180
168
|
value.each do |v|
|
181
169
|
new_native = native.clone
|
182
170
|
new_native.remove_attribute('value')
|
@@ -185,11 +173,7 @@ private
|
|
185
173
|
end
|
186
174
|
native.remove
|
187
175
|
else
|
188
|
-
|
189
|
-
warn "Attempt to set readonly element with value: #{value} \n *This will raise an exception in a future version of Capybara"
|
190
|
-
else
|
191
|
-
native['value'] = value.to_s
|
192
|
-
end
|
176
|
+
native['value'] = value.to_s
|
193
177
|
end
|
194
178
|
end
|
195
179
|
|
@@ -197,6 +181,36 @@ private
|
|
197
181
|
self[attribute] && !self[attribute].empty?
|
198
182
|
end
|
199
183
|
|
184
|
+
def follow_link
|
185
|
+
method = self["data-method"] if driver.options[:respect_data_method]
|
186
|
+
method ||= :get
|
187
|
+
driver.follow(method, self[:href].to_s)
|
188
|
+
end
|
189
|
+
|
190
|
+
def click_label
|
191
|
+
labelled_control = if native[:for]
|
192
|
+
find_xpath("//input[@id='#{native[:for]}']")
|
193
|
+
else
|
194
|
+
find_xpath(".//input")
|
195
|
+
end.first
|
196
|
+
|
197
|
+
if labelled_control && (labelled_control.checkbox? || labelled_control.radio?)
|
198
|
+
labelled_control.set(!labelled_control.checked?)
|
199
|
+
end
|
200
|
+
end
|
201
|
+
|
202
|
+
def link?
|
203
|
+
tag_name == 'a' && !self[:href].nil?
|
204
|
+
end
|
205
|
+
|
206
|
+
def submits?
|
207
|
+
(tag_name == 'input' and %w[submit image].include?(type)) || (tag_name == 'button' and [nil, "submit"].include?(type))
|
208
|
+
end
|
209
|
+
|
210
|
+
def checkable?
|
211
|
+
tag_name == 'input' and %w[checkbox radio].include?(type)
|
212
|
+
end
|
213
|
+
|
200
214
|
protected
|
201
215
|
|
202
216
|
def checkbox?
|