capybara 2.18.0 → 3.0.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 +55 -1
- data/README.md +18 -17
- data/lib/capybara/config.rb +11 -58
- data/lib/capybara/cucumber.rb +2 -3
- data/lib/capybara/driver/base.rb +15 -16
- data/lib/capybara/driver/node.rb +5 -4
- data/lib/capybara/dsl.rb +1 -0
- data/lib/capybara/helpers.rb +19 -29
- data/lib/capybara/minitest/spec.rb +15 -14
- data/lib/capybara/minitest.rb +139 -138
- data/lib/capybara/node/actions.rb +60 -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 +50 -71
- data/lib/capybara/node/simple.rb +11 -17
- data/lib/capybara/queries/ancestor_query.rb +11 -7
- data/lib/capybara/queries/base_query.rb +22 -18
- 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 +35 -35
- 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 +93 -63
- data/lib/capybara/rails.rb +2 -6
- data/lib/capybara/result.rb +22 -22
- 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 -61
- data/lib/capybara/rspec.rb +5 -10
- data/lib/capybara/selector/css.rb +6 -11
- data/lib/capybara/selector/filter.rb +1 -17
- data/lib/capybara/selector/filter_set.rb +18 -15
- 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 +28 -34
- data/lib/capybara/selector.rb +129 -117
- data/lib/capybara/selenium/driver.rb +131 -125
- data/lib/capybara/selenium/node.rb +197 -115
- data/lib/capybara/server.rb +3 -2
- data/lib/capybara/session/config.rb +47 -67
- data/lib/capybara/session/matchers.rb +8 -7
- data/lib/capybara/session.rb +138 -224
- data/lib/capybara/spec/public/test.js +25 -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 +6 -8
- 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 +23 -23
- data/lib/capybara/spec/session/assert_title.rb +13 -3
- data/lib/capybara/spec/session/attach_file_spec.rb +51 -30
- 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 +24 -32
- 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 +17 -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 +30 -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 +9 -15
- 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/frame_title_spec.rb +23 -0
- data/lib/capybara/spec/session/frame/frame_url_spec.rb +23 -0
- data/lib/capybara/spec/session/frame/switch_to_frame_spec.rb +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 +9 -13
- 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 +6 -2
- data/lib/capybara/spec/session/reset_session_spec.rb +19 -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 +3 -3
- 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 +17 -3
- data/lib/capybara/spec/session/title_spec.rb +11 -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 +15 -71
- data/lib/capybara/spec/session/within_spec.rb +1 -0
- data/lib/capybara/spec/spec_helper.rb +34 -18
- data/lib/capybara/spec/test_app.rb +17 -9
- data/lib/capybara/spec/views/form.erb +7 -0
- data/lib/capybara/spec/views/with_html.erb +23 -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/lib/capybara.rb +28 -25
- data/spec/basic_node_spec.rb +1 -0
- data/spec/capybara_spec.rb +11 -50
- 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 +18 -2
- data/spec/rspec_spec.rb +11 -15
- data/spec/selector_spec.rb +5 -6
- data/spec/selenium_spec_chrome.rb +9 -4
- data/spec/selenium_spec_edge.rb +27 -0
- data/spec/selenium_spec_ie.rb +31 -0
- data/spec/selenium_spec_marionette.rb +28 -12
- data/spec/server_spec.rb +33 -33
- data/spec/session_spec.rb +2 -1
- data/spec/shared_selenium_session.rb +36 -22
- data/spec/spec_helper.rb +3 -6
- metadata +68 -85
- data/lib/capybara/query.rb +0 -7
- data/spec/selenium_spec_firefox.rb +0 -68
@@ -1,14 +1,30 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
+
|
2
3
|
class Capybara::Selenium::Node < Capybara::Driver::Node
|
4
|
+
SET_FORMATS = Hash.new(date: '%Y-%m-%d', time: '%H:%M', datetime: "%m%d%Y\t%I%M%P").merge(
|
5
|
+
firefox: {
|
6
|
+
date: '%Y-%m-%d',
|
7
|
+
time: '%H:%M',
|
8
|
+
datetime: "%m%d%Y\t%I%M%P"
|
9
|
+
},
|
10
|
+
chrome: {
|
11
|
+
date: '%m%d%Y',
|
12
|
+
time: '%I%M%P',
|
13
|
+
datetime: "%m%d%Y\t%I%M%P"
|
14
|
+
}
|
15
|
+
)
|
3
16
|
|
4
17
|
def visible_text
|
5
|
-
|
6
|
-
Capybara::Helpers.normalize_whitespace(native.text)
|
18
|
+
native.text
|
7
19
|
end
|
8
20
|
|
9
21
|
def all_text
|
10
22
|
text = driver.execute_script("return arguments[0].textContent", self)
|
11
|
-
|
23
|
+
text.gsub(/[\u200b\u200e\u200f]/, '')
|
24
|
+
.gsub(/[\ \n\f\t\v\u2028\u2029]+/, ' ')
|
25
|
+
.gsub(/\A[[:space:]&&[^\u00a0]]+/, "")
|
26
|
+
.gsub(/[[:space:]&&[^\u00a0]]+\z/, "")
|
27
|
+
.tr("\u00a0", ' ')
|
12
28
|
end
|
13
29
|
|
14
30
|
def [](name)
|
@@ -36,57 +52,31 @@ class Capybara::Selenium::Node < Capybara::Driver::Node
|
|
36
52
|
# :none => append the new value to the existing value <br/>
|
37
53
|
# :backspace => send backspace keystrokes to clear the field <br/>
|
38
54
|
# Array => an array of keys to send before the value being set, e.g. [[:command, 'a'], :backspace]
|
39
|
-
def set(value, options
|
40
|
-
|
41
|
-
type = self[:type]
|
42
|
-
|
43
|
-
if (Array === value) && !multiple?
|
44
|
-
raise ArgumentError.new "Value cannot be an Array when 'multiple' attribute is not present. Not a #{value.class}"
|
45
|
-
end
|
55
|
+
def set(value, **options)
|
56
|
+
raise ArgumentError, "Value cannot be an Array when 'multiple' attribute is not present. Not a #{value.class}" if value.is_a?(Array) && !multiple?
|
46
57
|
|
47
58
|
case tag_name
|
48
59
|
when 'input'
|
49
|
-
case type
|
60
|
+
case self[:type]
|
50
61
|
when 'radio'
|
51
62
|
click
|
52
63
|
when 'checkbox'
|
53
|
-
click if value ^
|
64
|
+
click if value ^ checked?
|
54
65
|
when 'file'
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
66
|
+
set_file(value)
|
67
|
+
when 'date'
|
68
|
+
set_date(value)
|
69
|
+
when 'time'
|
70
|
+
set_time(value)
|
71
|
+
when 'datetime-local'
|
72
|
+
set_datetime_local(value)
|
61
73
|
else
|
62
74
|
set_text(value, options)
|
63
75
|
end
|
64
76
|
when 'textarea'
|
65
77
|
set_text(value, options)
|
66
78
|
else
|
67
|
-
if content_editable?
|
68
|
-
#ensure we are focused on the element
|
69
|
-
click
|
70
|
-
|
71
|
-
script = <<-JS
|
72
|
-
var range = document.createRange();
|
73
|
-
var sel = window.getSelection();
|
74
|
-
arguments[0].focus();
|
75
|
-
range.selectNodeContents(arguments[0]);
|
76
|
-
sel.removeAllRanges();
|
77
|
-
sel.addRange(range);
|
78
|
-
JS
|
79
|
-
driver.execute_script script, self
|
80
|
-
|
81
|
-
if driver.chrome? || driver.firefox?
|
82
|
-
# chromedriver raises a can't focus element for child elements if we use native.send_keys
|
83
|
-
# we've already focused it so just use action api
|
84
|
-
driver.browser.action.send_keys(value.to_s).perform
|
85
|
-
else
|
86
|
-
# action api is really slow here just use native.send_keys
|
87
|
-
native.send_keys(value.to_s)
|
88
|
-
end
|
89
|
-
end
|
79
|
+
set_content_editable(value) if content_editable?
|
90
80
|
end
|
91
81
|
end
|
92
82
|
|
@@ -95,32 +85,56 @@ class Capybara::Selenium::Node < Capybara::Driver::Node
|
|
95
85
|
end
|
96
86
|
|
97
87
|
def unselect_option
|
98
|
-
raise Capybara::UnselectNotAllowed, "Cannot unselect option from single select box."
|
88
|
+
raise Capybara::UnselectNotAllowed, "Cannot unselect option from single select box." unless select_node.multiple?
|
99
89
|
native.click if selected?
|
100
90
|
end
|
101
91
|
|
102
|
-
def click
|
103
|
-
|
92
|
+
def click(keys = [], options = {})
|
93
|
+
if keys.empty? && !(options[:x] && options[:y])
|
94
|
+
native.click
|
95
|
+
else
|
96
|
+
scroll_if_needed do
|
97
|
+
action_with_modifiers(keys, options) do |a|
|
98
|
+
if options[:x] && options[:y]
|
99
|
+
a.click
|
100
|
+
else
|
101
|
+
a.click(native)
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
104
106
|
rescue => e
|
105
107
|
if e.is_a?(::Selenium::WebDriver::Error::ElementClickInterceptedError) ||
|
106
108
|
e.message =~ /Other element would receive the click/
|
107
109
|
begin
|
108
110
|
driver.execute_script("arguments[0].scrollIntoView({behavior: 'instant', block: 'center', inline: 'center'})", self)
|
109
|
-
rescue
|
111
|
+
rescue # Swallow error if scrollIntoView with options isn't supported
|
110
112
|
end
|
111
113
|
end
|
112
114
|
raise e
|
113
115
|
end
|
114
116
|
|
115
|
-
def right_click
|
117
|
+
def right_click(keys = [], options = {})
|
116
118
|
scroll_if_needed do
|
117
|
-
|
119
|
+
action_with_modifiers(keys, options) do |a|
|
120
|
+
if options[:x] && options[:y]
|
121
|
+
a.context_click
|
122
|
+
else
|
123
|
+
a.context_click(native)
|
124
|
+
end
|
125
|
+
end
|
118
126
|
end
|
119
127
|
end
|
120
128
|
|
121
|
-
def double_click
|
129
|
+
def double_click(keys = [], options = {})
|
122
130
|
scroll_if_needed do
|
123
|
-
|
131
|
+
action_with_modifiers(keys, options) do |a|
|
132
|
+
if options[:x] && options[:y]
|
133
|
+
a.double_click
|
134
|
+
else
|
135
|
+
a.double_click(native)
|
136
|
+
end
|
137
|
+
end
|
124
138
|
end
|
125
139
|
end
|
126
140
|
|
@@ -129,55 +143,38 @@ class Capybara::Selenium::Node < Capybara::Driver::Node
|
|
129
143
|
end
|
130
144
|
|
131
145
|
def hover
|
132
|
-
scroll_if_needed
|
133
|
-
driver.browser.action.move_to(native).perform
|
134
|
-
end
|
146
|
+
scroll_if_needed { driver.browser.action.move_to(native).perform }
|
135
147
|
end
|
136
148
|
|
137
149
|
def drag_to(element)
|
138
|
-
scroll_if_needed
|
139
|
-
driver.browser.action.drag_and_drop(native, element.native).perform
|
140
|
-
end
|
150
|
+
scroll_if_needed { driver.browser.action.drag_and_drop(native, element.native).perform }
|
141
151
|
end
|
142
152
|
|
143
153
|
def tag_name
|
144
154
|
native.tag_name.downcase
|
145
155
|
end
|
146
156
|
|
147
|
-
def visible?
|
148
|
-
|
149
|
-
|
150
|
-
end
|
151
|
-
|
152
|
-
def selected?
|
153
|
-
selected = native.selected?
|
154
|
-
selected and selected != "false"
|
155
|
-
end
|
157
|
+
def visible?; boolean_attr(native.displayed?); end
|
158
|
+
def readonly?; boolean_attr(self[:readonly]); end
|
159
|
+
def multiple?; boolean_attr(self[:multiple]); end
|
160
|
+
def selected?; boolean_attr(native.selected?); end
|
156
161
|
alias :checked? :selected?
|
157
162
|
|
158
163
|
def disabled?
|
164
|
+
return true unless native.enabled?
|
165
|
+
|
159
166
|
# workaround for selenium-webdriver/geckodriver reporting elements as enabled when they are nested in disabling elements
|
160
167
|
if driver.marionette?
|
161
|
-
if %w
|
162
|
-
|
168
|
+
if %w[option optgroup].include? tag_name
|
169
|
+
find_xpath("parent::*[self::optgroup or self::select]")[0].disabled?
|
163
170
|
else
|
164
|
-
!
|
171
|
+
!find_xpath("parent::fieldset[@disabled] | ancestor::*[not(self::legend) or preceding-sibling::legend][parent::fieldset[@disabled]]").empty?
|
165
172
|
end
|
166
173
|
else
|
167
|
-
|
174
|
+
false
|
168
175
|
end
|
169
176
|
end
|
170
177
|
|
171
|
-
def readonly?
|
172
|
-
readonly = self[:readonly]
|
173
|
-
readonly and readonly != "false"
|
174
|
-
end
|
175
|
-
|
176
|
-
def multiple?
|
177
|
-
multiple = self[:multiple]
|
178
|
-
multiple and multiple != "false"
|
179
|
-
end
|
180
|
-
|
181
178
|
def content_editable?
|
182
179
|
native.attribute('isContentEditable')
|
183
180
|
end
|
@@ -195,63 +192,57 @@ class Capybara::Selenium::Node < Capybara::Driver::Node
|
|
195
192
|
end
|
196
193
|
|
197
194
|
def path
|
198
|
-
path = find_xpath(
|
199
|
-
path.unshift self
|
195
|
+
path = find_xpath(XPath.ancestor_or_self).reverse
|
200
196
|
|
201
197
|
result = []
|
202
|
-
while node = path.shift
|
198
|
+
while (node = path.shift)
|
203
199
|
parent = path.first
|
204
|
-
|
200
|
+
selector = node.tag_name
|
205
201
|
if parent
|
206
202
|
siblings = parent.find_xpath(node.tag_name)
|
207
|
-
|
208
|
-
result.unshift node.tag_name
|
209
|
-
else
|
210
|
-
index = siblings.index(node)
|
211
|
-
result.unshift "#{node.tag_name}[#{index+1}]"
|
212
|
-
end
|
213
|
-
else
|
214
|
-
result.unshift node.tag_name
|
203
|
+
selector += "[#{siblings.index(node) + 1}]" unless siblings.size == 1
|
215
204
|
end
|
205
|
+
result.push selector
|
216
206
|
end
|
217
207
|
|
218
|
-
'/' + result.join('/')
|
208
|
+
'/' + result.reverse.join('/')
|
219
209
|
end
|
220
210
|
|
221
211
|
private
|
212
|
+
|
213
|
+
def boolean_attr(val)
|
214
|
+
val and val != "false"
|
215
|
+
end
|
216
|
+
|
222
217
|
# a reference to the select node if this is an option node
|
223
218
|
def select_node
|
224
|
-
find_xpath(
|
219
|
+
find_xpath(XPath.ancestor(:select)[1]).first
|
225
220
|
end
|
226
221
|
|
227
|
-
def set_text(value,
|
228
|
-
if
|
229
|
-
warn "Attempt to set readonly element with value: #{value} \n *This will raise an exception in a future version of Capybara"
|
230
|
-
elsif value.to_s.empty? && options[:clear].nil?
|
222
|
+
def set_text(value, clear: nil, **_unused)
|
223
|
+
if value.to_s.empty? && clear.nil?
|
231
224
|
native.clear
|
225
|
+
elsif clear == :backspace
|
226
|
+
# Clear field by sending the correct number of backspace keys.
|
227
|
+
backspaces = [:backspace] * self.value.to_s.length
|
228
|
+
native.send_keys(*(backspaces + [value.to_s]))
|
229
|
+
elsif clear == :none
|
230
|
+
native.send_keys(value.to_s)
|
231
|
+
elsif clear.is_a? Array
|
232
|
+
native.send_keys(*clear, value.to_s)
|
232
233
|
else
|
233
|
-
|
234
|
-
|
235
|
-
|
236
|
-
|
237
|
-
|
238
|
-
native.send_keys(value.to_s)
|
239
|
-
elsif options[:clear].is_a? Array
|
240
|
-
native.send_keys(*options[:clear], value.to_s)
|
241
|
-
else
|
242
|
-
# Clear field by JavaScript assignment of the value property.
|
243
|
-
# Script can change a readonly element which user input cannot, so
|
244
|
-
# don't execute if readonly.
|
245
|
-
driver.execute_script "arguments[0].value = ''", self
|
246
|
-
native.send_keys(value.to_s)
|
247
|
-
end
|
234
|
+
# Clear field by JavaScript assignment of the value property.
|
235
|
+
# Script can change a readonly element which user input cannot, so
|
236
|
+
# don't execute if readonly.
|
237
|
+
driver.execute_script "arguments[0].value = ''", self
|
238
|
+
native.send_keys(value.to_s)
|
248
239
|
end
|
249
240
|
end
|
250
241
|
|
251
|
-
def scroll_if_needed
|
252
|
-
|
242
|
+
def scroll_if_needed
|
243
|
+
yield
|
253
244
|
rescue ::Selenium::WebDriver::Error::MoveTargetOutOfBoundsError
|
254
|
-
script = <<-JS
|
245
|
+
script = <<-'JS'
|
255
246
|
try {
|
256
247
|
arguments[0].scrollIntoView({behavior: 'instant', block: 'center', inline: 'center'});
|
257
248
|
} catch(e) {
|
@@ -259,6 +250,97 @@ private
|
|
259
250
|
}
|
260
251
|
JS
|
261
252
|
driver.execute_script(script, self)
|
262
|
-
|
253
|
+
yield
|
254
|
+
end
|
255
|
+
|
256
|
+
def set_date(value) # rubocop:disable Naming/AccessorMethodName
|
257
|
+
if value.respond_to?(:to_date)
|
258
|
+
set_text(value.to_date.strftime(SET_FORMATS[driver.options[:browser].to_sym][:date]))
|
259
|
+
else
|
260
|
+
set_text(value)
|
261
|
+
end
|
262
|
+
end
|
263
|
+
|
264
|
+
def set_time(value) # rubocop:disable Naming/AccessorMethodName
|
265
|
+
if value.respond_to?(:to_time)
|
266
|
+
set_text(value.to_time.strftime(SET_FORMATS[driver.options[:browser].to_sym][:time]))
|
267
|
+
else
|
268
|
+
set_text(value)
|
269
|
+
end
|
270
|
+
end
|
271
|
+
|
272
|
+
def set_datetime_local(value) # rubocop:disable Naming/AccessorMethodName
|
273
|
+
if value.respond_to?(:to_time)
|
274
|
+
set_text(value.to_time.strftime(SET_FORMATS[driver.options[:browser].to_sym][:datetime]))
|
275
|
+
else
|
276
|
+
set_text(value)
|
277
|
+
end
|
278
|
+
end
|
279
|
+
|
280
|
+
def set_file(value) # rubocop:disable Naming/AccessorMethodName
|
281
|
+
path_names = value.to_s.empty? ? [] : value
|
282
|
+
if driver.marionette?
|
283
|
+
native.clear
|
284
|
+
Array(path_names).each { |p| native.send_keys(p) }
|
285
|
+
else
|
286
|
+
native.send_keys(Array(path_names).join("\n"))
|
287
|
+
end
|
288
|
+
end
|
289
|
+
|
290
|
+
def set_content_editable(value) # rubocop:disable Naming/AccessorMethodName
|
291
|
+
# Ensure we are focused on the element
|
292
|
+
click
|
293
|
+
|
294
|
+
script = <<-JS
|
295
|
+
var range = document.createRange();
|
296
|
+
var sel = window.getSelection();
|
297
|
+
arguments[0].focus();
|
298
|
+
range.selectNodeContents(arguments[0]);
|
299
|
+
sel.removeAllRanges();
|
300
|
+
sel.addRange(range);
|
301
|
+
JS
|
302
|
+
driver.execute_script script, self
|
303
|
+
|
304
|
+
# The action api has a speed problem but both chrome and firefox 58 raise errors
|
305
|
+
# if we use the faster direct send_keys. For now just send_keys to the element
|
306
|
+
# we've already focused.
|
307
|
+
# native.send_keys(value.to_s)
|
308
|
+
driver.browser.action.send_keys(value.to_s).perform
|
309
|
+
end
|
310
|
+
|
311
|
+
def action_with_modifiers(keys, x: nil, y: nil)
|
312
|
+
actions = driver.browser.action
|
313
|
+
actions.move_to(native, x, y)
|
314
|
+
modifiers_down(actions, keys)
|
315
|
+
yield actions
|
316
|
+
modifiers_up(actions, keys)
|
317
|
+
actions.perform
|
318
|
+
ensure
|
319
|
+
a = driver.browser.action
|
320
|
+
a.release_actions if a.respond_to?(:release_actions)
|
321
|
+
end
|
322
|
+
|
323
|
+
def modifiers_down(actions, keys)
|
324
|
+
keys.each do |key|
|
325
|
+
key = case key
|
326
|
+
when :ctrl then :control
|
327
|
+
when :command, :cmd then :meta
|
328
|
+
else
|
329
|
+
key
|
330
|
+
end
|
331
|
+
actions.key_down(key)
|
332
|
+
end
|
333
|
+
end
|
334
|
+
|
335
|
+
def modifiers_up(actions, keys)
|
336
|
+
keys.each do |key|
|
337
|
+
key = case key
|
338
|
+
when :ctrl then :control
|
339
|
+
when :command, :cmd then :meta
|
340
|
+
else
|
341
|
+
key
|
342
|
+
end
|
343
|
+
actions.key_up(key)
|
344
|
+
end
|
263
345
|
end
|
264
346
|
end
|
data/lib/capybara/server.rb
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
+
|
2
3
|
require 'uri'
|
3
4
|
require 'net/http'
|
4
5
|
require 'rack'
|
@@ -43,7 +44,7 @@ module Capybara
|
|
43
44
|
begin
|
44
45
|
@app.call(env)
|
45
46
|
rescue *@server_errors => e
|
46
|
-
@error
|
47
|
+
@error ||= e
|
47
48
|
raise e
|
48
49
|
ensure
|
49
50
|
@counter.decrement
|
@@ -60,7 +61,7 @@ module Capybara
|
|
60
61
|
|
61
62
|
attr_reader :app, :port, :host
|
62
63
|
|
63
|
-
def initialize(app, port=Capybara.server_port, host=Capybara.server_host, server_errors=Capybara.server_errors)
|
64
|
+
def initialize(app, port = Capybara.server_port, host = Capybara.server_host, server_errors = Capybara.server_errors)
|
64
65
|
@app = app
|
65
66
|
@server_thread = nil # suppress warnings
|
66
67
|
@host, @port, @server_errors = host, port, server_errors
|
@@ -1,61 +1,55 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
+
|
2
3
|
require 'delegate'
|
3
4
|
|
4
5
|
module Capybara
|
5
6
|
class SessionConfig
|
6
|
-
OPTIONS = [
|
7
|
-
|
8
|
-
|
9
|
-
|
7
|
+
OPTIONS = %i[always_include_port run_server default_selector default_max_wait_time ignore_hidden_elements
|
8
|
+
automatic_reload match exact exact_text raise_server_errors visible_text_only
|
9
|
+
automatic_label_click enable_aria_label save_path asset_host default_host app_host
|
10
|
+
server_host server_port server_errors].freeze
|
10
11
|
|
11
12
|
attr_accessor(*OPTIONS)
|
12
13
|
|
13
14
|
##
|
14
|
-
|
15
|
-
#
|
16
|
-
|
17
|
-
#
|
18
|
-
|
19
|
-
#
|
20
|
-
|
21
|
-
#
|
22
|
-
|
23
|
-
#
|
24
|
-
|
25
|
-
#
|
26
|
-
|
27
|
-
#
|
28
|
-
|
29
|
-
#
|
30
|
-
|
31
|
-
#
|
32
|
-
|
33
|
-
#
|
34
|
-
|
35
|
-
#
|
36
|
-
|
37
|
-
#
|
38
|
-
|
39
|
-
#
|
40
|
-
|
41
|
-
#
|
42
|
-
|
43
|
-
|
44
|
-
#
|
45
|
-
|
46
|
-
#
|
47
|
-
|
48
|
-
#
|
49
|
-
|
50
|
-
#
|
51
|
-
|
52
|
-
# See {Capybara.configure}
|
53
|
-
#@!method server_host
|
54
|
-
# See {Capybara.configure}
|
55
|
-
#@!method server_port
|
56
|
-
# See {Capybara.configure}
|
57
|
-
#@!method server_errors
|
58
|
-
# See {Capybara.configure}
|
15
|
+
# @!method always_include_port
|
16
|
+
# See {Capybara.configure}
|
17
|
+
# @!method run_server
|
18
|
+
# See {Capybara.configure}
|
19
|
+
# @!method default_selector
|
20
|
+
# See {Capybara.configure}
|
21
|
+
# @!method default_max_wait_time
|
22
|
+
# See {Capybara.configure}
|
23
|
+
# @!method ignore_hidden_elements
|
24
|
+
# See {Capybara.configure}
|
25
|
+
# @!method automatic_reload
|
26
|
+
# See {Capybara.configure}
|
27
|
+
# @!method match
|
28
|
+
# See {Capybara.configure}
|
29
|
+
# @!method exact
|
30
|
+
# See {Capybara.configure}
|
31
|
+
# @!method raise_server_errors
|
32
|
+
# See {Capybara.configure}
|
33
|
+
# @!method visible_text_only
|
34
|
+
# See {Capybara.configure}
|
35
|
+
# @!method automatic_label_click
|
36
|
+
# See {Capybara.configure}
|
37
|
+
# @!method enable_aria_label
|
38
|
+
# See {Capybara.configure}
|
39
|
+
# @!method save_path
|
40
|
+
# See {Capybara.configure}
|
41
|
+
# @!method asset_host
|
42
|
+
# See {Capybara.configure}
|
43
|
+
# @!method default_host
|
44
|
+
# See {Capybara.configure}
|
45
|
+
# @!method app_host
|
46
|
+
# See {Capybara.configure}
|
47
|
+
# @!method server_host
|
48
|
+
# See {Capybara.configure}
|
49
|
+
# @!method server_port
|
50
|
+
# See {Capybara.configure}
|
51
|
+
# @!method server_errors
|
52
|
+
# See {Capybara.configure}
|
59
53
|
|
60
54
|
remove_method :server_host
|
61
55
|
|
@@ -74,30 +68,16 @@ module Capybara
|
|
74
68
|
|
75
69
|
remove_method :app_host=
|
76
70
|
def app_host=(url)
|
77
|
-
raise ArgumentError
|
71
|
+
raise ArgumentError, "Capybara.app_host should be set to a url (http://www.example.com). Attempted to set #{url.inspect}." if url && url !~ URI::DEFAULT_PARSER.make_regexp
|
78
72
|
@app_host = url
|
79
73
|
end
|
80
74
|
|
81
75
|
remove_method :default_host=
|
82
76
|
def default_host=(url)
|
83
|
-
raise ArgumentError
|
77
|
+
raise ArgumentError, "Capybara.default_host should be set to a url (http://www.example.com). Attempted to set #{url.inspect}." if url && url !~ URI::DEFAULT_PARSER.make_regexp
|
84
78
|
@default_host = url
|
85
79
|
end
|
86
80
|
|
87
|
-
remove_method :save_and_open_page_path=
|
88
|
-
def save_and_open_page_path=(path)
|
89
|
-
warn "DEPRECATED: #save_and_open_page_path is deprecated, please use #save_path instead. \n"\
|
90
|
-
"Note: Behavior is slightly different with relative paths - see documentation" unless path.nil?
|
91
|
-
@save_and_open_page_path = path
|
92
|
-
end
|
93
|
-
|
94
|
-
remove_method :exact_options=
|
95
|
-
def exact_options=(opt)
|
96
|
-
@exact_options = opt
|
97
|
-
warn "DEPRECATED: #exact_options is deprecated, please scope your findes/actions and use the `:exact` "\
|
98
|
-
"option if similar functionality is needed."
|
99
|
-
end
|
100
|
-
|
101
81
|
def initialize_copy(other)
|
102
82
|
super
|
103
83
|
@server_errors = @server_errors.dup
|
@@ -106,9 +86,9 @@ module Capybara
|
|
106
86
|
|
107
87
|
class ReadOnlySessionConfig < SimpleDelegator
|
108
88
|
SessionConfig::OPTIONS.each do |m|
|
109
|
-
define_method "#{m}=" do |
|
89
|
+
define_method "#{m}=" do |_|
|
110
90
|
raise "Per session settings are only supported when Capybara.threadsafe == true"
|
111
91
|
end
|
112
92
|
end
|
113
93
|
end
|
114
|
-
end
|
94
|
+
end
|
@@ -1,4 +1,5 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
+
|
2
3
|
module Capybara
|
3
4
|
module SessionMatchers
|
4
5
|
##
|
@@ -18,8 +19,8 @@ module Capybara
|
|
18
19
|
# @raise [Capybara::ExpectationNotMet] if the assertion hasn't succeeded during wait time
|
19
20
|
# @return [true]
|
20
21
|
#
|
21
|
-
def assert_current_path(path, options
|
22
|
-
_verify_current_path(path,options) { |query| raise Capybara::ExpectationNotMet, query.failure_message unless query.resolves_for?(self) }
|
22
|
+
def assert_current_path(path, **options)
|
23
|
+
_verify_current_path(path, options) { |query| raise Capybara::ExpectationNotMet, query.failure_message unless query.resolves_for?(self) }
|
23
24
|
end
|
24
25
|
|
25
26
|
##
|
@@ -32,8 +33,8 @@ module Capybara
|
|
32
33
|
# @raise [Capybara::ExpectationNotMet] if the assertion hasn't succeeded during wait time
|
33
34
|
# @return [true]
|
34
35
|
#
|
35
|
-
def assert_no_current_path(path, options
|
36
|
-
_verify_current_path(path,options) { |query| raise Capybara::ExpectationNotMet, query.negative_failure_message if query.resolves_for?(self) }
|
36
|
+
def assert_no_current_path(path, **options)
|
37
|
+
_verify_current_path(path, options) { |query| raise Capybara::ExpectationNotMet, query.negative_failure_message if query.resolves_for?(self) }
|
37
38
|
end
|
38
39
|
|
39
40
|
##
|
@@ -45,7 +46,7 @@ module Capybara
|
|
45
46
|
# @macro current_path_query_params
|
46
47
|
# @return [Boolean]
|
47
48
|
#
|
48
|
-
def has_current_path?(path, options
|
49
|
+
def has_current_path?(path, **options)
|
49
50
|
assert_current_path(path, options)
|
50
51
|
rescue Capybara::ExpectationNotMet
|
51
52
|
return false
|
@@ -60,13 +61,13 @@ module Capybara
|
|
60
61
|
# @macro current_path_query_params
|
61
62
|
# @return [Boolean]
|
62
63
|
#
|
63
|
-
def has_no_current_path?(path, options
|
64
|
+
def has_no_current_path?(path, **options)
|
64
65
|
assert_no_current_path(path, options)
|
65
66
|
rescue Capybara::ExpectationNotMet
|
66
67
|
return false
|
67
68
|
end
|
68
69
|
|
69
|
-
|
70
|
+
private
|
70
71
|
|
71
72
|
def _verify_current_path(path, options)
|
72
73
|
query = Capybara::Queries::CurrentPathQuery.new(path, options)
|