Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
SeleniumHQ
GitHub Repository: SeleniumHQ/Selenium
Path: blob/trunk/rb/lib/selenium/webdriver/support/select.rb
1865 views
1
# frozen_string_literal: true
2
3
# Licensed to the Software Freedom Conservancy (SFC) under one
4
# or more contributor license agreements. See the NOTICE file
5
# distributed with this work for additional information
6
# regarding copyright ownership. The SFC licenses this file
7
# to you under the Apache License, Version 2.0 (the
8
# "License"); you may not use this file except in compliance
9
# with the License. You may obtain a copy of the License at
10
#
11
# http://www.apache.org/licenses/LICENSE-2.0
12
#
13
# Unless required by applicable law or agreed to in writing,
14
# software distributed under the License is distributed on an
15
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16
# KIND, either express or implied. See the License for the
17
# specific language governing permissions and limitations
18
# under the License.
19
20
module Selenium
21
module WebDriver
22
module Support
23
class Select
24
#
25
# @param [Element] element The select element to use
26
#
27
28
def initialize(element)
29
tag_name = element.tag_name
30
31
raise ArgumentError, "unexpected tag name #{tag_name.inspect}" unless tag_name.casecmp('select').zero?
32
33
@element = element
34
@multi = ![nil, 'false'].include?(element.dom_attribute(:multiple))
35
end
36
37
#
38
# Does this select element support selecting multiple options?
39
#
40
# @return [Boolean]
41
#
42
43
def multiple?
44
@multi
45
end
46
47
#
48
# Get all options for this select element
49
#
50
# @return [Array<Element>]
51
#
52
53
def options
54
@element.find_elements tag_name: 'option'
55
end
56
57
#
58
# Get all selected options for this select element
59
#
60
# @return [Array<Element>]
61
#
62
63
def selected_options
64
options.select(&:selected?)
65
end
66
67
#
68
# Get the first selected option in this select element
69
#
70
# @raise [Error::NoSuchElementError] if no options are selected
71
# @return [Element]
72
#
73
74
def first_selected_option
75
option = options.find(&:selected?)
76
return option if option
77
78
raise Error::NoSuchElementError, 'no options are selected'
79
end
80
81
#
82
# Select options by visible text, index or value.
83
#
84
# When selecting by :text, selects options that display text matching the argument. That is, when given "Bar" this
85
# would select an option like:
86
#
87
# <option value="foo">Bar</option>
88
#
89
# When selecting by :value, selects all options that have a value matching the argument. That is, when given "foo" this
90
# would select an option like:
91
#
92
# <option value="foo">Bar</option>
93
#
94
# When selecting by :index, selects the option at the given index. This is done by examining the "index" attribute of an
95
# element, and not merely by counting.
96
#
97
# @param [:text, :index, :value] how How to find the option
98
# @param [String] what What value to find the option by.
99
#
100
101
def select_by(how, what)
102
case how
103
when :text
104
select_by_text what
105
when :index
106
select_by_index what
107
when :value
108
select_by_value what
109
else
110
raise ArgumentError, "can't select options by #{how.inspect}"
111
end
112
end
113
114
#
115
# Deselect options by visible text, index or value.
116
#
117
# @param [:text, :index, :value] how How to find the option
118
# @param [String] what What value to find the option by.
119
# @raise [Error::UnsupportedOperationError] if the element does not support multiple selections.
120
#
121
# @see Select#select_by
122
#
123
124
def deselect_by(how, what)
125
case how
126
when :text
127
deselect_by_text what
128
when :value
129
deselect_by_value what
130
when :index
131
deselect_by_index what
132
else
133
raise ArgumentError, "can't deselect options by #{how.inspect}"
134
end
135
end
136
137
#
138
# Select all unselected options. Only valid if the element supports multiple selections.
139
#
140
# @raise [Error::UnsupportedOperationError] if the element does not support multiple selections.
141
#
142
143
def select_all
144
raise Error::UnsupportedOperationError, 'you may only select all options of a multi-select' unless multiple?
145
146
options.each { |e| select_option e }
147
end
148
149
#
150
# Deselect all selected options. Only valid if the element supports multiple selections.
151
#
152
# @raise [Error::UnsupportedOperationError] if the element does not support multiple selections.
153
#
154
155
def deselect_all
156
raise Error::UnsupportedOperationError, 'you may only deselect all options of a multi-select' unless multiple?
157
158
options.each { |e| deselect_option e }
159
end
160
161
private
162
163
def select_by_text(text)
164
opts = find_by_text text
165
166
return select_options(opts) unless opts.empty?
167
168
raise Error::NoSuchElementError, "cannot locate element with text: #{text.inspect}"
169
end
170
171
def select_by_index(index)
172
opts = find_by_index index
173
174
return select_option(opts.first) unless opts.empty?
175
176
raise Error::NoSuchElementError, "cannot locate element with index: #{index.inspect}"
177
end
178
179
def select_by_value(value)
180
opts = find_by_value value
181
182
return select_options(opts) unless opts.empty?
183
184
raise Error::NoSuchElementError, "cannot locate option with value: #{value.inspect}"
185
end
186
187
def deselect_by_text(text)
188
raise Error::UnsupportedOperationError, 'you may only deselect option of a multi-select' unless multiple?
189
190
opts = find_by_text text
191
192
return deselect_options(opts) unless opts.empty?
193
194
raise Error::NoSuchElementError, "cannot locate element with text: #{text.inspect}"
195
end
196
197
def deselect_by_value(value)
198
raise Error::UnsupportedOperationError, 'you may only deselect option of a multi-select' unless multiple?
199
200
opts = find_by_value value
201
202
return deselect_options(opts) unless opts.empty?
203
204
raise Error::NoSuchElementError, "cannot locate option with value: #{value.inspect}"
205
end
206
207
def deselect_by_index(index)
208
raise Error::UnsupportedOperationError, 'you may only deselect option of a multi-select' unless multiple?
209
210
opts = find_by_index index
211
212
return deselect_option(opts.first) unless opts.empty?
213
214
raise Error::NoSuchElementError, "cannot locate option with index: #{index}"
215
end
216
217
def select_option(option)
218
raise Error::UnsupportedOperationError, 'You may not select a disabled option' unless option.enabled?
219
220
option.click unless option.selected?
221
end
222
223
def deselect_option(option)
224
option.click if option.selected?
225
end
226
227
def select_options(opts)
228
if multiple?
229
opts.each { |o| select_option o }
230
else
231
select_option opts.first
232
end
233
end
234
235
def deselect_options(opts)
236
if multiple?
237
opts.each { |o| deselect_option o }
238
else
239
deselect_option opts.first
240
end
241
end
242
243
def find_by_text(text)
244
xpath = ".//option[normalize-space(.) = #{Escaper.escape text}]"
245
opts = @element.find_elements(xpath: xpath)
246
247
return opts unless opts.empty? && /\s+/.match?(text)
248
249
longest_word = text.split(/\s+/).max_by(&:length)
250
if longest_word.empty?
251
candidates = options
252
else
253
xpath = ".//option[contains(., #{Escaper.escape longest_word})]"
254
candidates = @element.find_elements(xpath: xpath)
255
end
256
257
return Array(candidates.find { |option| text == option.text }) unless multiple?
258
259
candidates.select { |option| text == option.text }
260
end
261
262
def find_by_index(index)
263
options.select { |option| option.property(:index) == index }
264
end
265
266
def find_by_value(value)
267
@element.find_elements(xpath: ".//option[@value = #{Escaper.escape value}]")
268
end
269
end # Select
270
end # Support
271
end # WebDriver
272
end # Selenium
273
274