selenium-webdriver 2.13.0 → 2.14.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.
- data/CHANGES +20 -0
- data/lib/selenium/webdriver.rb +1 -22
- data/lib/selenium/webdriver/android/bridge.rb +5 -0
- data/lib/selenium/webdriver/chrome/bridge.rb +2 -0
- data/lib/selenium/webdriver/chrome/profile.rb +2 -2
- data/lib/selenium/webdriver/common/driver.rb +2 -0
- data/lib/selenium/webdriver/common/driver_extensions/uploads_files.rb +2 -0
- data/lib/selenium/webdriver/common/element.rb +1 -1
- data/lib/selenium/webdriver/common/error.rb +133 -12
- data/lib/selenium/webdriver/common/file_reaper.rb +5 -0
- data/lib/selenium/webdriver/common/options.rb +3 -3
- data/lib/selenium/webdriver/common/profile_helper.rb +3 -3
- data/lib/selenium/webdriver/common/proxy.rb +4 -0
- data/lib/selenium/webdriver/common/search_context.rb +4 -3
- data/lib/selenium/webdriver/common/socket_poller.rb +1 -0
- data/lib/selenium/webdriver/common/zipper.rb +4 -0
- data/lib/selenium/webdriver/firefox/extension/webdriver.xpi +0 -0
- data/lib/selenium/webdriver/firefox/profile.rb +1 -1
- data/lib/selenium/webdriver/ie/native/win32/IEDriver.dll +0 -0
- data/lib/selenium/webdriver/ie/native/x64/IEDriver.dll +0 -0
- data/lib/selenium/webdriver/iphone/bridge.rb +4 -0
- data/lib/selenium/webdriver/remote/capabilities.rb +10 -2
- data/lib/selenium/webdriver/remote/http/common.rb +2 -2
- data/lib/selenium/webdriver/support.rb +2 -1
- data/lib/selenium/webdriver/support/select.rb +218 -227
- metadata +194 -237
Binary file
|
@@ -89,7 +89,7 @@ module Selenium
|
|
89
89
|
raise ArgumentError, "preference values must be plain strings: #{key.inspect} => #{value.inspect}"
|
90
90
|
end
|
91
91
|
|
92
|
-
value = value
|
92
|
+
value = MultiJson.encode(value)
|
93
93
|
when TrueClass, FalseClass, Integer, Float
|
94
94
|
value = value.to_s
|
95
95
|
else
|
Binary file
|
Binary file
|
@@ -85,7 +85,15 @@ module Selenium
|
|
85
85
|
|
86
86
|
def iphone(opts = {})
|
87
87
|
new({
|
88
|
-
:browser_name => "
|
88
|
+
:browser_name => "iPhone",
|
89
|
+
:platform => :mac,
|
90
|
+
:javascript_enabled => true
|
91
|
+
}.merge(opts))
|
92
|
+
end
|
93
|
+
|
94
|
+
def ipad(opts = {})
|
95
|
+
new({
|
96
|
+
:browser_name => "iPad",
|
89
97
|
:platform => :mac,
|
90
98
|
:javascript_enabled => true
|
91
99
|
}.merge(opts))
|
@@ -206,7 +214,7 @@ module Selenium
|
|
206
214
|
end
|
207
215
|
|
208
216
|
def to_json(*args)
|
209
|
-
as_json
|
217
|
+
MultiJson.encode as_json
|
210
218
|
end
|
211
219
|
|
212
220
|
def ==(other)
|
@@ -23,7 +23,7 @@ module Selenium
|
|
23
23
|
headers = DEFAULT_HEADERS.dup
|
24
24
|
|
25
25
|
if command_hash
|
26
|
-
payload = command_hash
|
26
|
+
payload = MultiJson.encode(command_hash)
|
27
27
|
headers["Content-Type"] = "#{CONTENT_TYPE}; charset=utf-8"
|
28
28
|
headers["Content-Length"] = payload.bytesize.to_s if [:post, :put].include?(verb)
|
29
29
|
|
@@ -55,7 +55,7 @@ module Selenium
|
|
55
55
|
|
56
56
|
if content_type.include? CONTENT_TYPE
|
57
57
|
raise Error::WebDriverError, "empty body: #{content_type.inspect} (#{code})\n#{body}" if body.empty?
|
58
|
-
Response.new(code,
|
58
|
+
Response.new(code, MultiJson.decode(body))
|
59
59
|
elsif code == 204
|
60
60
|
Response.new(code)
|
61
61
|
else
|
@@ -1,3 +1,4 @@
|
|
1
1
|
require 'selenium/webdriver/support/event_firing_bridge'
|
2
2
|
require 'selenium/webdriver/support/abstract_event_listener'
|
3
|
-
require 'selenium/webdriver/support/block_event_listener'
|
3
|
+
require 'selenium/webdriver/support/block_event_listener'
|
4
|
+
require 'selenium/webdriver/support/select'
|
@@ -3,6 +3,10 @@ module Selenium
|
|
3
3
|
module Support
|
4
4
|
class Select
|
5
5
|
|
6
|
+
#
|
7
|
+
# @param [Element] element The select element to use
|
8
|
+
#
|
9
|
+
|
6
10
|
def initialize(element)
|
7
11
|
tag_name = element.tag_name
|
8
12
|
|
@@ -17,6 +21,8 @@ module Selenium
|
|
17
21
|
#
|
18
22
|
# Does this select element support selecting multiple options?
|
19
23
|
#
|
24
|
+
# @return [Boolean]
|
25
|
+
#
|
20
26
|
|
21
27
|
def multiple?
|
22
28
|
@multi
|
@@ -25,14 +31,18 @@ module Selenium
|
|
25
31
|
#
|
26
32
|
# Get all options for this select element
|
27
33
|
#
|
34
|
+
# @return [Array<Element>]
|
35
|
+
#
|
28
36
|
|
29
37
|
def options
|
30
|
-
@element.find_elements :tag_name
|
38
|
+
@element.find_elements :tag_name, 'option'
|
31
39
|
end
|
32
40
|
|
33
41
|
#
|
34
42
|
# Get all selected options for this select element
|
35
43
|
#
|
44
|
+
# @return [Array<Element>]
|
45
|
+
#
|
36
46
|
|
37
47
|
def selected_options
|
38
48
|
options.select { |e| e.selected? }
|
@@ -41,6 +51,9 @@ module Selenium
|
|
41
51
|
#
|
42
52
|
# Get the first selected option in this select element
|
43
53
|
#
|
54
|
+
# @raise [Error::NoSuchElementError] if no options are selected
|
55
|
+
# @return [Element]
|
56
|
+
#
|
44
57
|
|
45
58
|
def first_selected_option
|
46
59
|
option = options.find { |e| e.selected? }
|
@@ -50,253 +63,231 @@ module Selenium
|
|
50
63
|
#
|
51
64
|
# Select options by visible text, index or value.
|
52
65
|
#
|
66
|
+
# When selecting by :text, selects options that display text matching the argument. That is, when given "Bar" this
|
67
|
+
# would select an option like:
|
68
|
+
#
|
69
|
+
# <option value="foo">Bar</option>
|
70
|
+
#
|
71
|
+
# When slecting by :value, selects all options that have a value matching the argument. That is, when given "foo" this
|
72
|
+
# would select an option like:
|
73
|
+
#
|
74
|
+
# <option value="foo">Bar</option>
|
75
|
+
#
|
76
|
+
# When selecting by :index, selects the option at the given index. This is done by examining the "index" attribute of an
|
77
|
+
# element, and not merely by counting.
|
78
|
+
#
|
79
|
+
# @param [:text, :index, :value] how How to find the option
|
80
|
+
# @param [String] what What value to find the option by.
|
81
|
+
#
|
53
82
|
|
54
|
-
def
|
83
|
+
def select_by(how, what)
|
55
84
|
case how
|
56
|
-
when :text
|
57
|
-
|
85
|
+
when :text
|
86
|
+
select_by_text what
|
87
|
+
when :index
|
88
|
+
select_by_index what
|
89
|
+
when :value
|
90
|
+
select_by_value what
|
58
91
|
else
|
59
92
|
raise ArgumentError, "can't select options by #{how.inspect}"
|
60
93
|
end
|
61
94
|
end
|
62
95
|
|
63
|
-
#
|
64
|
-
#
|
65
|
-
#
|
66
|
-
#
|
67
|
-
#
|
68
|
-
#
|
69
|
-
#
|
70
|
-
#
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
#
|
86
|
-
#
|
87
|
-
#
|
88
|
-
#
|
89
|
-
#
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
#
|
100
|
-
#
|
101
|
-
#
|
102
|
-
#
|
103
|
-
# matched = true;
|
104
|
-
# }
|
105
|
-
# }
|
106
|
-
# }
|
107
|
-
#
|
108
|
-
# if (!matched) {
|
109
|
-
# throw new NoSuchElementException("Cannot locate element with text: " + text);
|
110
|
-
# }
|
111
|
-
# }
|
112
|
-
#
|
113
|
-
# private String getLongestSubstringWithoutSpace(String s) {
|
114
|
-
# String result = "";
|
115
|
-
# StringTokenizer st = new StringTokenizer(s, " ");
|
116
|
-
# while (st.hasMoreTokens()) {
|
117
|
-
# String t = st.nextToken();
|
118
|
-
# if (t.length() > result.length()) {
|
119
|
-
# result = t;
|
120
|
-
# }
|
121
|
-
# }
|
122
|
-
# return result;
|
123
|
-
# }
|
124
|
-
#
|
125
|
-
# /**
|
126
|
-
# * Select the option at the given index. This is done by examing the "index" attribute of an
|
127
|
-
# * element, and not merely by counting.
|
128
|
-
# *
|
129
|
-
# * @param index The option at this index will be selected
|
130
|
-
# */
|
131
|
-
# public void selectByIndex(int index) {
|
132
|
-
# String match = String.valueOf(index);
|
133
|
-
#
|
134
|
-
# boolean matched = false;
|
135
|
-
# for (WebElement option : getOptions()) {
|
136
|
-
# if (match.equals(option.getAttribute("index"))) {
|
137
|
-
# setSelected(option);
|
138
|
-
# if (!isMultiple()) {
|
139
|
-
# return;
|
140
|
-
# }
|
141
|
-
# matched = true;
|
142
|
-
# }
|
143
|
-
# }
|
144
|
-
# if (!matched) {
|
145
|
-
# throw new NoSuchElementException("Cannot locate option with index: " + index);
|
146
|
-
# }
|
147
|
-
# }
|
148
|
-
#
|
149
|
-
# /**
|
150
|
-
# * Select all options that have a value matching the argument. That is, when given "foo" this
|
151
|
-
# * would select an option like:
|
152
|
-
# *
|
153
|
-
# * <option value="foo">Bar</option>
|
154
|
-
# *
|
155
|
-
# * @param value The value to match against
|
156
|
-
# */
|
157
|
-
# public void selectByValue(String value) {
|
158
|
-
# StringBuilder builder = new StringBuilder(".//option[@value = ");
|
159
|
-
# builder.append(escapeQuotes(value));
|
160
|
-
# builder.append("]");
|
161
|
-
# List<WebElement> options = element.findElements(By.xpath(builder.toString()));
|
162
|
-
#
|
163
|
-
# boolean matched = false;
|
164
|
-
# for (WebElement option : options) {
|
165
|
-
# setSelected(option);
|
166
|
-
# if (!isMultiple()) {
|
167
|
-
# return;
|
168
|
-
# }
|
169
|
-
# matched = true;
|
170
|
-
# }
|
171
|
-
#
|
172
|
-
# if (!matched) {
|
173
|
-
# throw new NoSuchElementException("Cannot locate option with value: " + value);
|
174
|
-
# }
|
175
|
-
# }
|
176
|
-
#
|
177
|
-
|
178
|
-
#
|
179
|
-
# Clear all selected entries. Only valid if the element supports multiple selections.
|
96
|
+
#
|
97
|
+
# Deselect options by visible text, index or value.
|
98
|
+
#
|
99
|
+
# @param [:text, :index, :value] how How to find the option
|
100
|
+
# @param [String] what What value to find the option by.
|
101
|
+
#
|
102
|
+
# @see Select#select_by
|
103
|
+
#
|
104
|
+
|
105
|
+
def deselect_by(how, what)
|
106
|
+
case how
|
107
|
+
when :text
|
108
|
+
deselect_by_text what
|
109
|
+
when :value
|
110
|
+
deselect_by_value what
|
111
|
+
when :index
|
112
|
+
deselect_by_index what
|
113
|
+
else
|
114
|
+
raise ArgumentError, "can't deselect options by #{how.inspect}"
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
#
|
119
|
+
# Select all unselected options. Only valid if the element supports multiple selections.
|
120
|
+
#
|
121
|
+
# @raise [Error::UnsupportedOperationError] if the element does not support multiple selections.
|
122
|
+
#
|
123
|
+
|
124
|
+
def select_all
|
125
|
+
unless multiple?
|
126
|
+
raise Error::UnsupportedOperationError, 'you may only select all options of a multi-select'
|
127
|
+
end
|
128
|
+
|
129
|
+
options.each { |e| select_option e }
|
130
|
+
end
|
131
|
+
|
132
|
+
#
|
133
|
+
# Deselect all selected options. Only valid if the element supports multiple selections.
|
134
|
+
#
|
135
|
+
# @raise [Error::UnsupportedOperationError] if the element does not support multiple selections.
|
180
136
|
#
|
181
137
|
|
182
138
|
def deselect_all
|
183
139
|
unless multiple?
|
184
140
|
raise Error::UnsupportedOperationError, 'you may only deselect all options of a multi-select'
|
185
141
|
end
|
186
|
-
|
142
|
+
|
187
143
|
options.each { |e| deselect_option e }
|
188
144
|
end
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
raise NotImplementedError
|
198
|
-
else
|
199
|
-
raise ArgumentError, "can't deselect options by #{how.inspect}"
|
145
|
+
|
146
|
+
private
|
147
|
+
|
148
|
+
def select_by_text(text)
|
149
|
+
opts = find_by_text text
|
150
|
+
|
151
|
+
if opts.empty?
|
152
|
+
raise Error::NoSuchElementError, "cannot locate element with text: #{text.inspect}"
|
200
153
|
end
|
154
|
+
|
155
|
+
select_options opts
|
201
156
|
end
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
|
225
|
-
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
|
233
|
-
|
234
|
-
|
235
|
-
|
236
|
-
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
|
241
|
-
|
242
|
-
|
243
|
-
|
244
|
-
|
245
|
-
|
246
|
-
|
247
|
-
|
248
|
-
|
249
|
-
|
250
|
-
|
251
|
-
|
252
|
-
|
253
|
-
# List<WebElement> options = element.findElements(By.xpath(builder.toString()));
|
254
|
-
# for (WebElement option : options) {
|
255
|
-
# if (option.isSelected()) {
|
256
|
-
# option.click();
|
257
|
-
# }
|
258
|
-
# }
|
259
|
-
# }
|
260
|
-
#
|
261
|
-
# protected String escapeQuotes(String toEscape) {
|
262
|
-
# // Convert strings with both quotes and ticks into: foo'"bar -> concat("foo'", '"', "bar")
|
263
|
-
# if (toEscape.indexOf("\"") > -1 && toEscape.indexOf("'") > -1) {
|
264
|
-
# boolean quoteIsLast = false;
|
265
|
-
# if (toEscape.indexOf("\"") == toEscape.length() - 1) {
|
266
|
-
# quoteIsLast = true;
|
267
|
-
# }
|
268
|
-
# String[] substrings = toEscape.split("\"");
|
269
|
-
#
|
270
|
-
# StringBuilder quoted = new StringBuilder("concat(");
|
271
|
-
# for (int i = 0; i < substrings.length; i++) {
|
272
|
-
# quoted.append("\"").append(substrings[i]).append("\"");
|
273
|
-
# quoted
|
274
|
-
# .append(((i == substrings.length - 1) ? (quoteIsLast ? ", '\"')" : ")") : ", '\"', "));
|
275
|
-
# }
|
276
|
-
# return quoted.toString();
|
277
|
-
# }
|
278
|
-
#
|
279
|
-
# // Escape string with just a quote into being single quoted: f"oo -> 'f"oo'
|
280
|
-
# if (toEscape.indexOf("\"") > -1) {
|
281
|
-
# return String.format("'%s'", toEscape);
|
282
|
-
# }
|
283
|
-
#
|
284
|
-
# // Otherwise return the quoted string
|
285
|
-
# return String.format("\"%s\"", toEscape);
|
286
|
-
# }
|
287
|
-
#
|
288
|
-
|
157
|
+
|
158
|
+
def select_by_index(index)
|
159
|
+
opts = find_by_index index
|
160
|
+
|
161
|
+
if opts.empty?
|
162
|
+
raise Error::NoSuchElementError, "cannot locate element with index: #{index.inspect}"
|
163
|
+
end
|
164
|
+
|
165
|
+
select_options opts
|
166
|
+
end
|
167
|
+
|
168
|
+
def select_by_value(value)
|
169
|
+
opts = find_by_value value
|
170
|
+
|
171
|
+
if opts.empty?
|
172
|
+
raise Error::NoSuchElementError, "cannot locate option with value: #{value.inspect}"
|
173
|
+
end
|
174
|
+
|
175
|
+
select_options opts
|
176
|
+
end
|
177
|
+
|
178
|
+
def deselect_by_text(text)
|
179
|
+
opts = find_by_text text
|
180
|
+
|
181
|
+
if opts.empty?
|
182
|
+
raise Error::NoSuchElementError, "cannot locate element with text: #{text.inspect}"
|
183
|
+
end
|
184
|
+
|
185
|
+
deselect_options opts
|
186
|
+
end
|
187
|
+
|
188
|
+
def deselect_by_value(value)
|
189
|
+
opts = find_by_value value
|
190
|
+
|
191
|
+
if opts.empty?
|
192
|
+
raise Error::NoSuchElementError, "cannot locate option with value: #{value.inspect}"
|
193
|
+
end
|
194
|
+
|
195
|
+
deselect_options opts
|
196
|
+
end
|
197
|
+
|
198
|
+
def deselect_by_index(index)
|
199
|
+
opts = find_by_index index
|
200
|
+
|
201
|
+
if opts.empty?
|
202
|
+
raise Error::NoSuchElementError, "cannot locate option with index: #{index}"
|
203
|
+
end
|
204
|
+
|
205
|
+
deselect_options opts
|
206
|
+
end
|
207
|
+
|
289
208
|
private
|
290
|
-
|
291
|
-
def select_option(
|
292
|
-
|
209
|
+
|
210
|
+
def select_option(option)
|
211
|
+
option.click unless option.selected?
|
212
|
+
end
|
213
|
+
|
214
|
+
def deselect_option(option)
|
215
|
+
option.click if option.selected?
|
293
216
|
end
|
294
|
-
|
295
|
-
def
|
296
|
-
|
217
|
+
|
218
|
+
def select_options(opts)
|
219
|
+
if multiple?
|
220
|
+
opts.each { |o| select_option o }
|
221
|
+
else
|
222
|
+
select_option opts.first
|
223
|
+
end
|
224
|
+
end
|
225
|
+
|
226
|
+
def deselect_options(opts)
|
227
|
+
if multiple?
|
228
|
+
opts.each { |o| deselect_option o }
|
229
|
+
else
|
230
|
+
deselect_option opts.first
|
231
|
+
end
|
297
232
|
end
|
298
233
|
|
299
|
-
|
234
|
+
def find_by_text(text)
|
235
|
+
xpath = ".//option[normalize-space(.) = #{Escaper.escape text}]"
|
236
|
+
opts = @element.find_elements(:xpath, xpath)
|
237
|
+
|
238
|
+
if opts.empty? && text =~ /\s+/
|
239
|
+
longest_word = text.split(/\s+/).max_by { |item| item.length }
|
240
|
+
|
241
|
+
if longest_word.empty?
|
242
|
+
candidates = options
|
243
|
+
else
|
244
|
+
xpath = ".//option[contains(., #{Escaper.escape longest_word})]"
|
245
|
+
candidates = @element.find_elements(:xpath, xpath)
|
246
|
+
end
|
247
|
+
|
248
|
+
if multiple?
|
249
|
+
candidates.select { |option| text == option.text }
|
250
|
+
else
|
251
|
+
Array(candidates.find { |option| text == option.text })
|
252
|
+
end
|
253
|
+
else
|
254
|
+
opts
|
255
|
+
end
|
256
|
+
end
|
257
|
+
|
258
|
+
def find_by_index(index)
|
259
|
+
index = index.to_s
|
260
|
+
options.select { |option| option.attribute(:index) == index }
|
261
|
+
end
|
262
|
+
|
263
|
+
def find_by_value(value)
|
264
|
+
@element.find_elements(:xpath, ".//option[@value = #{Escaper.escape value}]")
|
265
|
+
end
|
266
|
+
|
267
|
+
#
|
268
|
+
# @api private
|
269
|
+
#
|
270
|
+
|
271
|
+
module Escaper
|
272
|
+
def self.escape(str)
|
273
|
+
if str.include?('"') && str.include?("'")
|
274
|
+
parts = str.split('"', -1).map { |part| %{"#{part}"} }
|
275
|
+
|
276
|
+
quoted = parts.join(%{, '"', }).
|
277
|
+
gsub(/^"", |, ""$/, '')
|
278
|
+
|
279
|
+
"concat(#{quoted})"
|
280
|
+
elsif str.include?('"')
|
281
|
+
# escape string with just a quote into being single quoted: f"oo -> 'f"oo'
|
282
|
+
"'#{str}'"
|
283
|
+
else
|
284
|
+
# otherwise return the quoted string
|
285
|
+
%{"#{str}"}
|
286
|
+
end
|
287
|
+
end
|
288
|
+
end # Escaper
|
289
|
+
|
290
|
+
end # Select
|
300
291
|
end
|
301
292
|
end
|
302
293
|
end
|