capybara 2.15.0 → 3.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/History.md +137 -2
- data/README.md +36 -25
- data/lib/capybara/config.rb +11 -57
- data/lib/capybara/cucumber.rb +2 -3
- data/lib/capybara/driver/base.rb +19 -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 +16 -13
- data/lib/capybara/minitest.rb +140 -137
- data/lib/capybara/node/actions.rb +68 -89
- 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 +32 -42
- data/lib/capybara/node/finders.rb +64 -71
- data/lib/capybara/node/matchers.rb +50 -71
- data/lib/capybara/node/simple.rb +11 -17
- data/lib/capybara/queries/ancestor_query.rb +12 -8
- data/lib/capybara/queries/base_query.rb +22 -18
- data/lib/capybara/queries/current_path_query.rb +12 -25
- data/lib/capybara/queries/match_query.rb +3 -7
- data/lib/capybara/queries/selector_query.rb +100 -96
- data/lib/capybara/queries/sibling_query.rb +5 -5
- 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 +52 -39
- 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 +116 -58
- 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 +172 -163
- data/lib/capybara/selenium/node.rb +218 -104
- data/lib/capybara/server.rb +3 -2
- data/lib/capybara/session/config.rb +47 -59
- data/lib/capybara/session/matchers.rb +23 -14
- data/lib/capybara/session.rb +175 -229
- data/lib/capybara/spec/fixtures/no_extension +1 -0
- data/lib/capybara/spec/public/test.js +38 -6
- 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 +30 -1
- 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 +31 -23
- data/lib/capybara/spec/session/assert_title.rb +13 -3
- data/lib/capybara/spec/session/attach_file_spec.rb +57 -29
- 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 +19 -8
- 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 +23 -0
- data/lib/capybara/spec/session/evaluate_script_spec.rb +5 -4
- 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 +69 -0
- 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 +49 -22
- data/lib/capybara/spec/session/has_field_spec.rb +4 -3
- data/lib/capybara/spec/session/has_link_spec.rb +5 -4
- data/lib/capybara/spec/session/has_none_selectors_spec.rb +76 -0
- data/lib/capybara/spec/session/has_select_spec.rb +32 -31
- 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 +107 -58
- 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 +21 -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 +7 -6
- data/lib/capybara/spec/session/visit_spec.rb +64 -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 +36 -18
- data/lib/capybara/spec/test_app.rb +17 -9
- data/lib/capybara/spec/views/form.erb +7 -0
- data/lib/capybara/spec/views/initial_alert.erb +10 -0
- data/lib/capybara/spec/views/with_fixed_header_footer.erb +17 -0
- data/lib/capybara/spec/views/with_hover.erb +5 -0
- data/lib/capybara/spec/views/with_html.erb +27 -1
- data/lib/capybara/spec/views/with_js.erb +11 -0
- 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 +29 -26
- data/spec/basic_node_spec.rb +1 -0
- data/spec/capybara_spec.rb +16 -69
- 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 +13 -4
- data/spec/minitest_spec_spec.rb +12 -3
- 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 +20 -11
- data/spec/selenium_spec_edge.rb +27 -0
- data/spec/selenium_spec_ie.rb +31 -0
- data/spec/selenium_spec_marionette.rb +38 -12
- data/spec/server_spec.rb +33 -33
- data/spec/session_spec.rb +2 -1
- data/spec/shared_selenium_session.rb +82 -22
- data/spec/spec_helper.rb +3 -6
- metadata +76 -81
- data/lib/capybara/query.rb +0 -7
- data/spec/selenium_spec_firefox.rb +0 -68
@@ -1,13 +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
|
+
)
|
16
|
+
|
3
17
|
def visible_text
|
4
|
-
|
5
|
-
Capybara::Helpers.normalize_whitespace(native.text)
|
18
|
+
native.text
|
6
19
|
end
|
7
20
|
|
8
21
|
def all_text
|
9
22
|
text = driver.execute_script("return arguments[0].textContent", self)
|
10
|
-
|
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", ' ')
|
11
28
|
end
|
12
29
|
|
13
30
|
def [](name)
|
@@ -35,57 +52,31 @@ class Capybara::Selenium::Node < Capybara::Driver::Node
|
|
35
52
|
# :none => append the new value to the existing value <br/>
|
36
53
|
# :backspace => send backspace keystrokes to clear the field <br/>
|
37
54
|
# Array => an array of keys to send before the value being set, e.g. [[:command, 'a'], :backspace]
|
38
|
-
def set(value, options
|
39
|
-
|
40
|
-
type = self[:type]
|
41
|
-
|
42
|
-
if (Array === value) && !multiple?
|
43
|
-
raise ArgumentError.new "Value cannot be an Array when 'multiple' attribute is not present. Not a #{value.class}"
|
44
|
-
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?
|
45
57
|
|
46
58
|
case tag_name
|
47
59
|
when 'input'
|
48
|
-
case type
|
60
|
+
case self[:type]
|
49
61
|
when 'radio'
|
50
62
|
click
|
51
63
|
when 'checkbox'
|
52
|
-
click if value ^
|
64
|
+
click if value ^ checked?
|
53
65
|
when 'file'
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
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)
|
60
73
|
else
|
61
74
|
set_text(value, options)
|
62
75
|
end
|
63
76
|
when 'textarea'
|
64
77
|
set_text(value, options)
|
65
78
|
else
|
66
|
-
if content_editable?
|
67
|
-
#ensure we are focused on the element
|
68
|
-
click
|
69
|
-
|
70
|
-
script = <<-JS
|
71
|
-
var range = document.createRange();
|
72
|
-
var sel = window.getSelection();
|
73
|
-
arguments[0].focus();
|
74
|
-
range.selectNodeContents(arguments[0]);
|
75
|
-
sel.removeAllRanges();
|
76
|
-
sel.addRange(range);
|
77
|
-
JS
|
78
|
-
driver.execute_script script, self
|
79
|
-
|
80
|
-
if (driver.chrome?) || (driver.firefox? && !driver.marionette?)
|
81
|
-
# chromedriver raises a can't focus element for child elements if we use native.send_keys
|
82
|
-
# we've already focused it so just use action api
|
83
|
-
driver.browser.action.send_keys(value.to_s).perform
|
84
|
-
else
|
85
|
-
# action api is really slow here just use native.send_keys
|
86
|
-
native.send_keys(value.to_s)
|
87
|
-
end
|
88
|
-
end
|
79
|
+
set_content_editable(value) if content_editable?
|
89
80
|
end
|
90
81
|
end
|
91
82
|
|
@@ -94,20 +85,57 @@ class Capybara::Selenium::Node < Capybara::Driver::Node
|
|
94
85
|
end
|
95
86
|
|
96
87
|
def unselect_option
|
97
|
-
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?
|
98
89
|
native.click if selected?
|
99
90
|
end
|
100
91
|
|
101
|
-
def click
|
102
|
-
|
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
|
106
|
+
rescue => e
|
107
|
+
if e.is_a?(::Selenium::WebDriver::Error::ElementClickInterceptedError) ||
|
108
|
+
e.message =~ /Other element would receive the click/
|
109
|
+
begin
|
110
|
+
driver.execute_script("arguments[0].scrollIntoView({behavior: 'instant', block: 'center', inline: 'center'})", self)
|
111
|
+
rescue # Swallow error if scrollIntoView with options isn't supported
|
112
|
+
end
|
113
|
+
end
|
114
|
+
raise e
|
103
115
|
end
|
104
116
|
|
105
|
-
def right_click
|
106
|
-
|
117
|
+
def right_click(keys = [], options = {})
|
118
|
+
scroll_if_needed do
|
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
|
126
|
+
end
|
107
127
|
end
|
108
128
|
|
109
|
-
def double_click
|
110
|
-
|
129
|
+
def double_click(keys = [], options = {})
|
130
|
+
scroll_if_needed do
|
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
|
138
|
+
end
|
111
139
|
end
|
112
140
|
|
113
141
|
def send_keys(*args)
|
@@ -115,51 +143,38 @@ class Capybara::Selenium::Node < Capybara::Driver::Node
|
|
115
143
|
end
|
116
144
|
|
117
145
|
def hover
|
118
|
-
driver.browser.action.move_to(native).perform
|
146
|
+
scroll_if_needed { driver.browser.action.move_to(native).perform }
|
119
147
|
end
|
120
148
|
|
121
149
|
def drag_to(element)
|
122
|
-
driver.browser.action.drag_and_drop(native, element.native).perform
|
150
|
+
scroll_if_needed { driver.browser.action.drag_and_drop(native, element.native).perform }
|
123
151
|
end
|
124
152
|
|
125
153
|
def tag_name
|
126
154
|
native.tag_name.downcase
|
127
155
|
end
|
128
156
|
|
129
|
-
def visible?
|
130
|
-
|
131
|
-
|
132
|
-
end
|
133
|
-
|
134
|
-
def selected?
|
135
|
-
selected = native.selected?
|
136
|
-
selected and selected != "false"
|
137
|
-
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
|
138
161
|
alias :checked? :selected?
|
139
162
|
|
140
163
|
def disabled?
|
164
|
+
return true unless native.enabled?
|
165
|
+
|
141
166
|
# workaround for selenium-webdriver/geckodriver reporting elements as enabled when they are nested in disabling elements
|
142
167
|
if driver.marionette?
|
143
|
-
if %w
|
144
|
-
|
168
|
+
if %w[option optgroup].include? tag_name
|
169
|
+
find_xpath("parent::*[self::optgroup or self::select]")[0].disabled?
|
145
170
|
else
|
146
|
-
!
|
171
|
+
!find_xpath("parent::fieldset[@disabled] | ancestor::*[not(self::legend) or preceding-sibling::legend][parent::fieldset[@disabled]]").empty?
|
147
172
|
end
|
148
173
|
else
|
149
|
-
|
174
|
+
false
|
150
175
|
end
|
151
176
|
end
|
152
177
|
|
153
|
-
def readonly?
|
154
|
-
readonly = self[:readonly]
|
155
|
-
readonly and readonly != "false"
|
156
|
-
end
|
157
|
-
|
158
|
-
def multiple?
|
159
|
-
multiple = self[:multiple]
|
160
|
-
multiple and multiple != "false"
|
161
|
-
end
|
162
|
-
|
163
178
|
def content_editable?
|
164
179
|
native.attribute('isContentEditable')
|
165
180
|
end
|
@@ -177,56 +192,155 @@ class Capybara::Selenium::Node < Capybara::Driver::Node
|
|
177
192
|
end
|
178
193
|
|
179
194
|
def path
|
180
|
-
path = find_xpath(
|
181
|
-
path.unshift self
|
195
|
+
path = find_xpath(XPath.ancestor_or_self).reverse
|
182
196
|
|
183
197
|
result = []
|
184
|
-
while node = path.shift
|
198
|
+
while (node = path.shift)
|
185
199
|
parent = path.first
|
186
|
-
|
200
|
+
selector = node.tag_name
|
187
201
|
if parent
|
188
202
|
siblings = parent.find_xpath(node.tag_name)
|
189
|
-
|
190
|
-
result.unshift node.tag_name
|
191
|
-
else
|
192
|
-
index = siblings.index(node)
|
193
|
-
result.unshift "#{node.tag_name}[#{index+1}]"
|
194
|
-
end
|
195
|
-
else
|
196
|
-
result.unshift node.tag_name
|
203
|
+
selector += "[#{siblings.index(node) + 1}]" unless siblings.size == 1
|
197
204
|
end
|
205
|
+
result.push selector
|
198
206
|
end
|
199
207
|
|
200
|
-
'/' + result.join('/')
|
208
|
+
'/' + result.reverse.join('/')
|
201
209
|
end
|
202
210
|
|
203
211
|
private
|
212
|
+
|
213
|
+
def boolean_attr(val)
|
214
|
+
val and val != "false"
|
215
|
+
end
|
216
|
+
|
204
217
|
# a reference to the select node if this is an option node
|
205
218
|
def select_node
|
206
|
-
find_xpath(
|
219
|
+
find_xpath(XPath.ancestor(:select)[1]).first
|
220
|
+
end
|
221
|
+
|
222
|
+
def set_text(value, clear: nil, **_unused)
|
223
|
+
if value.to_s.empty? && clear.nil?
|
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)
|
233
|
+
else
|
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)
|
239
|
+
end
|
240
|
+
end
|
241
|
+
|
242
|
+
def scroll_if_needed
|
243
|
+
yield
|
244
|
+
rescue ::Selenium::WebDriver::Error::MoveTargetOutOfBoundsError
|
245
|
+
script = <<-'JS'
|
246
|
+
try {
|
247
|
+
arguments[0].scrollIntoView({behavior: 'instant', block: 'center', inline: 'center'});
|
248
|
+
} catch(e) {
|
249
|
+
arguments[0].scrollIntoView(true);
|
250
|
+
}
|
251
|
+
JS
|
252
|
+
driver.execute_script(script, self)
|
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
|
207
278
|
end
|
208
279
|
|
209
|
-
def
|
210
|
-
|
211
|
-
|
212
|
-
elsif value.to_s.empty?
|
280
|
+
def set_file(value) # rubocop:disable Naming/AccessorMethodName
|
281
|
+
path_names = value.to_s.empty? ? [] : value
|
282
|
+
if driver.marionette?
|
213
283
|
native.clear
|
284
|
+
Array(path_names).each { |p| native.send_keys(p) }
|
214
285
|
else
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
|
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
|
223
340
|
else
|
224
|
-
|
225
|
-
# Script can change a readonly element which user input cannot, so
|
226
|
-
# don't execute if readonly.
|
227
|
-
driver.execute_script "arguments[0].value = ''", self
|
228
|
-
native.send_keys(value.to_s)
|
341
|
+
key
|
229
342
|
end
|
343
|
+
actions.key_up(key)
|
230
344
|
end
|
231
345
|
end
|
232
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,60 +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
|
-
#@!method server_host
|
53
|
-
# See {Capybara.configure}
|
54
|
-
#@!method server_port
|
55
|
-
# See {Capybara.configure}
|
56
|
-
#@!method server_errors
|
57
|
-
# 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}
|
58
53
|
|
59
54
|
remove_method :server_host
|
60
55
|
|
@@ -73,23 +68,16 @@ module Capybara
|
|
73
68
|
|
74
69
|
remove_method :app_host=
|
75
70
|
def app_host=(url)
|
76
|
-
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
|
77
72
|
@app_host = url
|
78
73
|
end
|
79
74
|
|
80
75
|
remove_method :default_host=
|
81
76
|
def default_host=(url)
|
82
|
-
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
|
83
78
|
@default_host = url
|
84
79
|
end
|
85
80
|
|
86
|
-
remove_method :save_and_open_page_path=
|
87
|
-
def save_and_open_page_path=(path)
|
88
|
-
warn "DEPRECATED: #save_and_open_page_path is deprecated, please use #save_path instead. \n"\
|
89
|
-
"Note: Behavior is slightly different with relative paths - see documentation" unless path.nil?
|
90
|
-
@save_and_open_page_path = path
|
91
|
-
end
|
92
|
-
|
93
81
|
def initialize_copy(other)
|
94
82
|
super
|
95
83
|
@server_errors = @server_errors.dup
|
@@ -98,9 +86,9 @@ module Capybara
|
|
98
86
|
|
99
87
|
class ReadOnlySessionConfig < SimpleDelegator
|
100
88
|
SessionConfig::OPTIONS.each do |m|
|
101
|
-
define_method "#{m}=" do |
|
89
|
+
define_method "#{m}=" do |_|
|
102
90
|
raise "Per session settings are only supported when Capybara.threadsafe == true"
|
103
91
|
end
|
104
92
|
end
|
105
93
|
end
|
106
|
-
end
|
94
|
+
end
|
@@ -1,45 +1,52 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
+
|
2
3
|
module Capybara
|
3
4
|
module SessionMatchers
|
4
5
|
##
|
5
6
|
# Asserts that the page has the given path.
|
6
|
-
# By default this will compare against the
|
7
|
+
# By default, if passed a full url this will compare against the full url,
|
8
|
+
# if passed a path only the path+query portion will be compared, if passed a regexp
|
9
|
+
# the comparison will depend on the :url option
|
7
10
|
#
|
8
11
|
# @!macro current_path_query_params
|
9
12
|
# @overload $0(string, options = {})
|
10
13
|
# @param string [String] The string that the current 'path' should equal
|
11
14
|
# @overload $0(regexp, options = {})
|
12
15
|
# @param regexp [Regexp] The regexp that the current 'path' should match to
|
13
|
-
# @option options [
|
14
|
-
# @option options [Boolean] :
|
15
|
-
# @option options [
|
16
|
+
# @option options [Boolean] :url (true if `string` ia a full url, otherwise false) Whether the compare should be done against the full current url or just the path
|
17
|
+
# @option options [Boolean] :ignore_query (false) Whether the query portion of the current url/path should be ignored
|
18
|
+
# @option options [Numeric] :wait (Capybara.default_max_wait_time) Maximum time that Capybara will wait for the current url/path to eq/match given string/regexp argument
|
16
19
|
# @raise [Capybara::ExpectationNotMet] if the assertion hasn't succeeded during wait time
|
17
20
|
# @return [true]
|
18
21
|
#
|
19
|
-
def assert_current_path(path, options
|
20
|
-
_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) }
|
21
24
|
end
|
22
25
|
|
23
26
|
##
|
24
27
|
# Asserts that the page doesn't have the given path.
|
25
|
-
# By default this will compare against the
|
28
|
+
# By default, if passed a full url this will compare against the full url,
|
29
|
+
# if passed a path only the path+query portion will be compared, if passed a regexp
|
30
|
+
# the comparison will depend on the :url option
|
26
31
|
#
|
27
32
|
# @macro current_path_query_params
|
28
33
|
# @raise [Capybara::ExpectationNotMet] if the assertion hasn't succeeded during wait time
|
29
34
|
# @return [true]
|
30
35
|
#
|
31
|
-
def assert_no_current_path(path, options
|
32
|
-
_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) }
|
33
38
|
end
|
34
39
|
|
35
40
|
##
|
36
41
|
# Checks if the page has the given path.
|
37
|
-
# By default this will compare against the
|
42
|
+
# By default, if passed a full url this will compare against the full url,
|
43
|
+
# if passed a path only the path+query portion will be compared, if passed a regexp
|
44
|
+
# the comparison will depend on the :url option
|
38
45
|
#
|
39
46
|
# @macro current_path_query_params
|
40
47
|
# @return [Boolean]
|
41
48
|
#
|
42
|
-
def has_current_path?(path, options
|
49
|
+
def has_current_path?(path, **options)
|
43
50
|
assert_current_path(path, options)
|
44
51
|
rescue Capybara::ExpectationNotMet
|
45
52
|
return false
|
@@ -47,18 +54,20 @@ module Capybara
|
|
47
54
|
|
48
55
|
##
|
49
56
|
# Checks if the page doesn't have the given path.
|
50
|
-
# By default this will compare against the
|
57
|
+
# By default, if passed a full url this will compare against the full url,
|
58
|
+
# if passed a path only the path+query portion will be compared, if passed a regexp
|
59
|
+
# the comparison will depend on the :url option
|
51
60
|
#
|
52
61
|
# @macro current_path_query_params
|
53
62
|
# @return [Boolean]
|
54
63
|
#
|
55
|
-
def has_no_current_path?(path, options
|
64
|
+
def has_no_current_path?(path, **options)
|
56
65
|
assert_no_current_path(path, options)
|
57
66
|
rescue Capybara::ExpectationNotMet
|
58
67
|
return false
|
59
68
|
end
|
60
69
|
|
61
|
-
|
70
|
+
private
|
62
71
|
|
63
72
|
def _verify_current_path(path, options)
|
64
73
|
query = Capybara::Queries::CurrentPathQuery.new(path, options)
|