selenium-webdriver 2.13.0 → 2.14.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|